I am attempting to write a interpreter that will turn a string like:
vector(random(0, 1), 2, 3)
into a bound function using boost::bind like:
bind(&vector, bind(&random, 0, 1), 2, 3)
The intended use is for a particle system so I want the ability to be able to pass a particle to these which is achieved by adding a lambda like:
bind(&vector, bind(&random, 0, 1, _1), 2, 3, _1)
These bound functions are then defined as:
typedef boost::function<boost::any(Particle*)> bound_particle_func; so I can form a sort of list of function calls passing each particle to this list to manipulate their behaviour and thus create an effect.
I can easily make the interpreter handle something like vector(1, 2, 3) but when it comes to nested functions things are getting messy and I feel like I’m bodging things.
Because the parser currently handles nested functions in a recursive manner I can’t directly bind the lambda value to the embedded function.
so instead of ending up with
bind(&vector, bind(&random, 0, 1, _1), 2, 3, _1)
I actually end up with
bind(&vector, bound random function, 2, 3, _1) which doesn’t pass the bound function the particle.
I don’t know how to better deal with nested functions.
The actual code I have at the moment (which isn’t compilable) is:
typedef std::vector<Token>::iterator token_it;
typedef boost::function<boost::any(Particle*)> bound_particle_func;
Vector3 vector(float x, float y, float z, Particle* p = 0);
bound_particle_func parse(token_it& it);
bound_particle_func ParseVector(std::vector<Token>& tokens) {
const static int arg_count = 3;
std::vector<boost::variant<float, bound_particle_func>> args(arg_count);
int type[] = { 0, 0, 0 };
for(token_it it = tokens.begin(); it != tokens.end(); ++it) {
Token& t = *it;
if(t.type == Type::FLOAT) {
args.push_back(t.float_value);
} else if(t.type == Type::FUNCTION) {
args.push_back(parse(it));
type[args.size() - 1] = 1;
} else {
throw std::runtime_error("Type error: expected float");
}
if(args.size() > arg_count) {
throw std::runtime_error("Too many arguments for function `vector`");
}
}
return boost::bind(&vector, (type[0] == 0 ? boost::get<float>(args[0]) : boost::get<bound_particle_func>(args[0])),
(type[1] == 0 ? boost::get<float>(args[1]) : boost::get<bound_particle_func>(args[1])),
(type[2] == 0 ? boost::get<float>(args[2]) : boost::get<bound_particle_func>(args[2])),
boost::lambda::_1);
}
The bound_particle_func parse(token_it& it); function just passes the relevant tokens to the appropriate function like the one above.
Do not use
boost::functionorboost::bind. You cannot passboost::functionto something that needs anint.Since you only need one type of deferred argument, and you need to pass it to all your functions, it is much easier to roll your own function objects.
An alternative that does use
boost::functionandboost::bindwould be to use something like that:and likewise for your other functions. Numbers are represented by bound functions that just return their bound
floatargument and ignore theirParticle*argument.