我不知道我所要求的是可行的,愚蠢的还是简单的。 我最近才开始沉浸在模板函数和类中,我想知道以下场景是否可行: 一个包含要调用的函数指针的类。函数指针不能是特定的,而是抽象的,因此无论何时调用类的构造函数,它都可以接受不同类型的函数指针。当调用类的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;
}
答案 0 :(得分:5)
如果您可以使用C ++ 0x和可变参数模板,则可以使用std::function
,std::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;
}
在C ++ 98/03中,如果你需要支持可变数量的参数,你可能需要自己重载构造函数以获得最多的N参数。您还可以使用boost::function
和boost::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;
}
答案 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
。