我试图找出一种方法来拥有一个仿函数容器,以便我可以将一个值传递给仿函数并对其进行修改,但是我无法让仿函数不受限制它们可以传递的类型和他们可以接受的论点数量。

我对此的实际用途是我有一系列仿函数,它们都根据输入以某种方式改变 3D vector 。通过能够将这些仿函数存储在容器中,我可以操纵它们被调用的顺序,并通过遍历容器传递每个仿函数 vector 来最终得到不同的结果 vector 。我使用这些 vector 来确定粒子的位置、颜色、加速度等。所以最终我可以通过采用这种模块化方法创建截然不同的粒子效果,最终我可以在运行时通过文件定义仿函数顺序。这是我的最终目标:)

我完成这项工作的唯一方法是使用继承和一堆空指针,这使得代码极难遵循、调试和使用。

到目前为止,这是我的代码,希望能比我在上面输入的代码更好地展示我正在尝试做的事情。请注意,我离自己的舒适区很远,所以这段代码可能很可怕,让你们中的一些大师想用棍子打败我。

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

边注:
我期待有人告诉我使用 boost 库中的某某。虽然我很高兴知道有这个选项,但我仍然想知道一种更好的方法来实现它,而无需任何外部库,因为这对我自己来说是一种学习练习。

编辑

好的,所以在了解了 stdargboost::any 之后,我想我可以看到一种使这项工作很好地工作的方法,并且正在尝试:)

解决方案2

好的,我已经使用 boost::anycstdarg 重新编写了代码,我认为这是一个更好的解决方案。这不使用空指针,也不限制仿函数可以拥有的参数数量。较新的代码还允许您按值传入,使用空指针,一切都必须按引用,这会导致尝试执行的问题: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

最佳答案

你有没有想过使用 variadic argument lists from the stdarg library ?我不确定这是一个更好的解决方案,但它是不同的。例如:

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

关于c++ - 不同仿函数的容器,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/8581902/

10-11 18:32