Контейнер разных функторов - PullRequest
4 голосов
/ 21 декабря 2011

Я пытаюсь найти способ иметь контейнер функторов, чтобы я мог передать значение в функторы и изменить его, однако у меня возникают проблемы с тем, чтобы функторы не были ограничены в том, какие типы они могут быть передано и количество аргументов, которые они могут принять.

Мое фактическое использование для этого состоит в том, что у меня есть ряд функторов, которые все так или иначе меняют трехмерный вектор на основе входных данных. Имея возможность хранить эти функторы в контейнере, я могу манипулировать порядком, в котором они вызываются, и получать в результате другой результирующий вектор, перебирая контейнер, передавая каждому функтору вектор. Я использую эти векторы, чтобы определить положение, цвет, ускорение и т. Д. Частицы. Таким образом, в конечном итоге я могу создавать совершенно разные эффекты частиц, применяя этот модульный подход, и в конечном итоге я могу задать порядок функторов в файле во время выполнения. Это моя конечная цель:)

Единственный способ, которым я сделал эту работу, - это наследование и набор пустых указателей, что делает код чрезвычайно сложным для отслеживания, отладки и использования.

Вот мой код, который, надеюсь, продемонстрирует, что я пытаюсь сделать лучше, чем то, что я напечатал выше. Пожалуйста, имейте в виду, что я вне моей зоны комфорта, поэтому этот код может быть ужасным, и некоторые из вас, гуру, хотят избить меня палкой.

#include <iostream>
#include <vector>

//the base functor class
struct Functor {
    //an integer so we can tell how many arguments the functor takes
    unsigned num_arguments;
    Functor() : num_arguments(0) {}

    //I'm making the arguments default to 0 so the compiler doesn't complain about not giving enough arguments
    virtual void operator()(void* R, void* A1 = 0, void* A2 = 0, void* A3 = 0) = 0;
};

template<typename R, typename A1>
struct Double : public Functor {
    Double() { num_arguments = 1; }
    void operator()(void* i, void* a, void*, void*) {
        //having to cast everything out of void pointers so it can be used
        A1& first = *static_cast<A1*>(a);

        *static_cast<R*>(i) = first * 2;
    }
};

template<typename R, typename A1, typename A2>
struct Sub : public Functor {
    Sub() { num_arguments = 2; }
    void operator()(void* i, void* a, void* b, void*) {
        //having to cast everything out of void pointers so it can be used
        A1& first = *static_cast<A1*>(a);
        A2& second = *static_cast<A2*>(b);

        *static_cast<R*>(i) = first - second;
    }
};

int main() {
    //our container for the functors
    std::vector<Functor*> functors;
    functors.push_back(new Double<int, int>);
    functors.push_back(new Sub<int, int, int>);

    for(int i = 0; i < functors.size(); ++i) {
        int result;
        int first = 1, second = 2;
        Functor& f = *functors[i];

        if(f.num_arguments == 1) {
            f(&result, &first);
        } else if(f.num_arguments == 2){
            f(&result, &first, &second);
        }

        std::cout << result << std::endl;
    }

    Functor* float_sub = new Sub<float, float, float>;
    float result;
    float first = 0.5f, second = 2.0f;
    (*float_sub)(&result, &first, &second);
    std::cout << result << std::endl;

    functors.push_back(float_sub);

    //The functors vector now contains 3 different types of functors:
    //One that doubles an integer
    //One that subtracts two integers
    //One that subtracts two floats

    std::cin.get();
    return 0;
}

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

Редактировать

Хорошо, так что, узнав о stdarg и boost::any, я думаю, что я вижу способ сделать эту работу красиво и пробую это:)


Раствор 2

Хорошо, я переработал код, используя boost::any и cstdarg для того, что я считаю лучшим решением. Это не использует пустые указатели и не ограничивает количество аргументов, которые могут иметь функторы. Более новый код также позволяет передавать по значению, используя пустые указатели, все должно быть по ссылке, что может вызвать проблемы при попытке сделать: Sub(&result, 1, 1)

#include <iostream>
#include <vector>
#include <cstdarg>
#include <boost\any.hpp>

struct Functor {
    unsigned num_arguments;
    Functor() : num_arguments(0) {}

    virtual void operator()(boost::any, ...) = 0;
};

template<typename R, typename A1>
struct Double : public Functor {
    Double() { num_arguments = 1; }
    void operator()(boost::any r, ...) {
        R* out = boost::any_cast<R*>(r);

        va_list args;
        va_start(args, r);
        A1 first = va_arg(args, A1);
        va_end(args);

        *out = first * 2;
    }
};

template<typename R, typename A1, typename A2>
struct Sub : public Functor {
    Sub() { num_arguments = 2; }
    void operator()(boost::any r, ...) {
        R* out = boost::any_cast<R*>(r);

        va_list args;
        va_start(args, r);
        A1 first = va_arg(args, A1);
        A2 second = va_arg(args, A2);
        va_end(args);

        *out = first - second;
    }
};

int main() {
    std::vector<Functor*> functors;

    functors.push_back(new Double<int, int>);
    functors.push_back(new Sub<int, int, int>);
    functors.push_back(new Sub<int, float, float>);

    int result = 0;

    for(int i = 0; i < functors.size(); ++i) {
        (*functors[i])(&result, 2, 2);
        std::cout << result << std::endl;
    }

    std::cin.get();
    return 0;
}

и теперь у меня наконец есть привилегия для голосования: D

Ответы [ 2 ]

4 голосов
/ 21 декабря 2011

Задумывались ли вы об использовании списков переменных с переменным аргументом из библиотеки stdarg ?Я не уверен, что это лучше решение, но оно другое.Например:

#include <vector>
#include <cstdarg>
#include <iostream>

using namespace std;

template< typename type >
void sub( void* out, ... ){
    // Initialize the variadic argument list.
    va_list args;
    va_start( args, out );

    // Extract our arguments.
    type lhs = va_arg( args, type );
    type rhs = va_arg( args, type );

    // Close the variadic argument list.
    va_end( args );

    // Function logic goes here.
    *(static_cast<type*>(out)) = lhs - rhs;
}

int main( void ){
    typedef void (*functor)( void* out, ... );  // Function type.
    typedef vector< functor > FunctorList;      // Function list type.

    FunctorList fList;
    fList.push_back( &sub<int> );

    int diff;
    fList[0]( &diff, 3, 5 );
    cout << diff << endl;

    return 0;
}
2 голосов
/ 21 декабря 2011

Я не осознавал, что вы специально хотели получить функциональные объекты (следовало щелкнуть из-за использования вами «функтора», а не «указателя функции»). В любом случае, вот мой предыдущий ответ с использованием функциональных объектов. Работает точно так же:

#include <vector>
#include <cstdarg>
#include <iostream>

using namespace std;

struct FunctorBase {
    virtual void operator()( void* out, ... ) = 0;
}; // end FunctorBase

template< typename T >
struct Subtract : public FunctorBase {
    void operator()( void* out, ... ){
        // Initialize the variadic argument list.
        va_list args;
        va_start( args, out );

        // Extract our arguments
        T lhs = va_arg( args, T );
        T rhs = va_arg( args, T );

        // Close the variadic argument list.
        va_end( args );

        // Function logic goes here.
        *(static_cast<T*>(out)) = lhs - rhs;
    }
};

int main( void ){
    typedef vector< FunctorBase* > FunctorList; // Function list type.

    FunctorList fList;
    fList.push_back( new Subtract<int>() );

    int diff;
    (*fList[0])( &diff, 3, 5 );
    cout << diff << endl;

    return 0;
}
...