Лучший способ связать вложенные функции в моем интерпретаторе - PullRequest
0 голосов
/ 22 января 2012

Я пытаюсь написать интерпретатор, который превратит строку вроде:

vector(random(0, 1), 2, 3)

в связанную функцию, используя boost::bind например:

bind(&vector, bind(&random, 0, 1), 2, 3)

Предполагаемое использование для системы частиц, поэтому я хочу иметь возможность передавать ей частицы, что достигается добавлением лямбда-подобия:

bind(&vector, bind(&random, 0, 1, _1), 2, 3, _1)

Эти связанные функции затем определяются как: typedef boost::function<boost::any(Particle*)> bound_particle_func;, поэтому я могу сформировать своего рода список вызовов функций, передавая каждую частицу в этот список, чтобы управлять их поведением и, таким образом, создавать эффект.

Я могу легкозаставить интерпретатор обрабатывать что-то вроде vector(1, 2, 3), но когда дело доходит до вложенных функций, вещи становятся беспорядочными, и я чувствую, что я запутываю вещи.

Поскольку синтаксический анализатор в настоящее время обрабатывает вложенные функции рекурсивным способом, я могу 't напрямую связывает лямбда-значение со встроенной функцией.

, поэтому вместо того, чтобы заканчиваться на bind(&vector, bind(&random, 0, 1, _1), 2, 3, _1), я фактически получаю bind(&vector, bound random function, 2, 3, _1), который не передает связанную функцию частице.

Я не знаю, как лучше справляться с вложенными функциями.

Фактический код, который у меня есть на данный момент (который не компилируется):

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);
}

Функция bound_particle_func parse(token_it& it); просто передает соответствующие токены соответствующей функции, подобной приведенной выше.

1 Ответ

0 голосов
/ 22 января 2012

Не используйте boost::function или boost::bind. Вы не можете передать boost::function чему-то, что нуждается в int.

Так как вам нужен только один тип отложенного аргумента, и вам нужно передать его всем вашим функциям, гораздо проще свернуть ваши собственные объекты функций.


Альтернативой, которая использует boost::function и boost::bind, будет использование чего-то подобного:

Vector3 f_vector(bound_particle_func x, bound_particle_func y,  
                   bound_particle_func z, Particle* p = 0)
{
   return vector (boost::get<float>(x(p)), boost::get<float>(y(p)), 
                    boost::get<float>(z(p)), p);
}

и аналогично для других ваших функций. Числа представлены связанными функциями, которые просто возвращают свой связанный аргумент float и игнорируют свой аргумент Particle*.

...