C ++如何推广类函数参数来处理多种类型的函数指针?

时间:2011-06-20 14:12:57

标签: c++ templates function pointers abstraction

我不知道我所要求的是可行的,愚蠢的还是简单的。 我最近才开始沉浸在模板函数和类中,我想知道以下场景是否可行: 一个包含要调用的函数指针的类。函数指针不能是特定的,而是抽象的,因此无论何时调用类的构造函数,它都可以接受不同类型的函数指针。当调用类的execute函数时,它会使用参数(或参数)执行在构造时分配的函数指针。 基本上,抽象在整个设计中保留,并在用户上留下要传递的函数指针和参数。以下代码尚未经过测试,只是为了演示我正在尝试做的事情:

void (*foo)(double);
void (*bar)(double,double);
void (*blah)(float);

class Instruction
{
    protected:
      double var_a;
      double var_b;
      void (*ptr2func)(double);
      void (*ptr2func)(double,double);  
    public:
      template <typename F> Instruction(F arg1, F arg2, F arg3)
      {
         Instruction::ptr2func = &arg1;
         var_a = arg2;
         var_b = arg3;
      };    
      void execute()
      {
         (*ptr2func)(var_a);
      };
};

我不喜欢我必须在可能的可重载函数指针类中保留一个列表的事实。我怎么可能改进上面的内容以尽可能地概括它,以便它可以使用任何类型的函数指针抛出它? 请记住,我将保留这些实例化对象的容器并按顺序执行每个函数指针。 谢谢 ! 编辑:也许这个类应该是它自己的模板,以便于使用许多不同的函数指针? 编辑2:我找到了解决问题的方法,仅供将来参考,不知道它是否正确,但它有效:

class Instruction
{
  protected:
    double arg1,arg2,arg3;
  public:
    virtual void execute() = 0;
};

template <class F> class MoveArm : public Instruction
{
  private:
    F (func);    
  public:
   template <typename T> 
   MoveArm(const T& f,double a, double b)
   {
     arg1 = a;
     arg2 = b;
     func = &f;
   };

   void execute()
   {
     (func)(arg1,arg2);
   };
};

但是在导入函数时,它们的函数指针需要是typedef'd:

void doIt(double a, double b)
{
   std::cout << "moving arm at: " << a << " " << b << std::endl;
};

typedef void (*ptr2func)(double,double);

int main(int argc, char **argv) {

   MoveArm<ptr2func>  arm(doIt,0.5,2.3);
   arm.execute();

   return 0;
}

3 个答案:

答案 0 :(得分:5)

如果您可以使用C ++ 0x和可变参数模板,则可以使用std::functionstd::bind和完美转发的组合来实现此目的:

#include <iostream>
#include <functional>

template <typename Result = void>
class instruction
{
public:
        template <typename Func, typename... Args>
        instruction(Func func, Args&&... args)
        {
                m_func = std::bind(func, std::forward<Args>(args)...);
        }

        Result execute()
        {
                return m_func();
        }
private:
        std::function<Result ()> m_func;
};

double add(double a, double b)
{
        return (a + b);
}

int main()
{
        instruction<double> test(&add, 1.5, 2.0);
        std::cout << "result: " << test.execute() << std::endl;
}

输出示例:http://ideone.com/9HYWo

在C ++ 98/03中,如果你需要支持可变数量的参数,你可能需要自己重载构造函数以获得最多的N参数。您还可以使用boost::functionboost::bind代替std::等效项。然后还有forwarding problem的问题,因此要完美转发,您需要根据需要支持的参数数量进行指数级的重载。 Boost有一个preprocessor library,您可以使用它来生成所需的重载,而无需手动编写所有重载;但这很复杂。

以下是如何使用C ++ 98/03进行操作的示例,假设您传递给instruction的函数不需要通过可修改引用来获取参数,为此,您还需要为P1& p1而不仅仅是const P1& p1而重载。

#include <iostream>
#include <boost/function.hpp>
#include <boost/bind.hpp>

template <typename Result = void>
class instruction
{
public:
        template <typename Func>
        instruction(Func func)
        {
                m_func = func;
        }

        template <typename Func, typename P1>
        instruction(Func func, const P1& p1)
        {
                m_func = boost::bind(func, p1);
        }

        template <typename Func, typename P1, typename P2>
        instruction(Func func, const P1& p1, const P2& p2)
        {
                m_func = boost::bind(func, p1, p2);
        }

        template <typename Func, typename P1, typename P2, typename P3>
        instruction(Func func, const P1& p1, const P2& p2, const P3& p3)
        {
                m_func = boost::bind(func, p1, p2, p3);
        }

        Result execute()
        {
                return m_func();
        }
private:
        boost::function<Result ()> m_func;
};

double add(double a, double b)
{
        return (a + b);
}

int main()
{
        instruction<double> test(&add, 1.5, 2.0);
        std::cout << "result: " << test.execute() << std::endl;
}

示例:http://ideone.com/iyXp1

答案 1 :(得分:2)

我还创建了一个带有一些示例用法的C ++ 0x版本。你可以更好地使用reko_t给出的那个,但我发布了这个。这个使用递归来解析带有值的元组,从而使用一个元组来存储传递给函数的参数。请注意,这个不使用完美转发。如果你使用它,你可能想要添加它。

#include <iostream>
#include <string>
#include <tuple>

using namespace std;

template<unsigned N>
struct FunctionCaller
{
    template<typename ... Typenames, typename ... Args>
    static void call(void (*func)(Typenames ...), tuple<Typenames ...> tuple, Args ... args)
    {
        FunctionCaller<N-1>::call(func, tuple, get<N-1>(tuple), args ...);
    }
};

template<>
struct FunctionCaller<0u>
{
    template<typename ... Typenames, typename ... Args>
    static void call(void (*func)(Typenames ...), tuple<Typenames ...> tuple, Args ... args)
    {
        func(args ...);
    }
};

template<typename ... Typenames>
class Instruction
{
    public:
    typedef void (*FuncType)(Typenames ...);

    protected:
    std::tuple<Typenames ...> d_args;
    FuncType d_function;

    public:
    Instruction(FuncType function, Typenames ... args):
        d_args(args ...),
        d_function(function)
    {
    }

    void execute()
    {
        FunctionCaller<sizeof...(Typenames)>::call(d_function, d_args);
    }
};

void test1()
{
    cout << "Hello World" << endl;
}

void test2(int a, string b, double c)
{
    cout << a << b << c << endl;
}

int main(int argc, char** argv)
{
    Instruction<> i1(test1);
    Instruction<int, string, double> i2(test2, 5, "/2 = ", 2.5);
    i1.execute();
    i2.execute();
    return 0;
}

答案 2 :(得分:1)

嗯,你在做什么是正确的。但由于所有指针在C ++中都具有相同的大小,因此可以存储一个指针(void类型):

void *funcptr;

并在需要时将其转换为必要的类型:

static_cast<(*void)(double,double)>(funcptr)(var_a, var_b);

但请注意,只有在不能使用更好的技术时才使用它,但我不知道你是不是告诉我们更大的图片。


您可能需要查看boost::function