You can still define resolveVar() and resolveFunc() methods in your custom expression classes, but you now also have the option to use variable blocks instead. These are useful for re-using variables and managing thread evaluation. Within your custom expression class, you would skip the usual resolveVar() and resolveFunc() implementations:

    class MyExpr:public KSeExpr::Expression {

    // These are now optional:
    //   resolveVar()
    //   resolveFunc()
    };

Here's how you might write an example function to run an expression that uses variable blocks. This example assumes you have a particle data struct p, with numParticles, numAttributes. A variable block contains variable names and types, but doesn't care what the values are.

    void f(const std::string& s, MyParticleData* p, int outputDim=3){

        // set up the bindings for each of the particle attributes
        KSeExpr::VarBlockCreator blockCreator;
        std::vector variableHandles;
        std::vector attrs;
        for(int i=0; inumAttrs(); i++) {
            MyParticleAttr attr;
            p->getAttrInfo(i,attr);
            attrs.push_back(attr);
            int handle = blockCreator.registerVariable(attr.name,
                                                       TypeVec(attr.dim));
            variableHandles.push_back(handle);
        }

        // set up binding for the output
        int outputVariableHandle=blockCreator.registerVariable("__output",
                                                               TypeVec(outputDim));

        // make an expression with the appropriate bindings
        MyExpr e(s);
        e.setDesiredType(TypeVec(outputDim));
        e.setVarBlockCreator(&blockCreator);
        if(!e.isValid()) throw std::runtime_error(e.parseError()):

        // create the variable block, set up pointers to data
        KSeExpr::VarBlock block = blockCreator.create();
        for(int i=0; inumAttrs(); i++) {
            block.Pointer(variableHandles[i]) = p->data(attrs[i]);
        }
        std::vector outputData(p->numParticles()*outputDim);
        block.Pointer(outputVariableHandle)=outputData.data();

        // evaluate multiple expressions at once; inlines expressions
        e.evalMultiple(&block, outputVariableHandle, 0, p->numParticles());
    }

To parallelize evaluation per particle, a simple parallel_for can be used:

        // evaluate multiple expressions at once, inlines expressions
        tbb::parallel_for(blocked_range(0,p->numParticles()),[](blocked_range r)) {
            VarBlock myBlock=block.clone(); //should be cheap
            e.evalMultiple(&myblock, outputVariableHandle, r.start, r.end);
        }