我正在尝试创建一种通用的方法来包装 C++ 函数(来自不同的语言)。我有一个参数列表(和一个迭代器)和一个要在参数列表上调用的特定 C++ 函数。我试图找到某种方式将参数列表解压缩为我的函数的参数。
我目前的做法是:
- 使用 Boost::FunctionTypes 以序列的形式获取函数的参数。
- 使用参数类型的值进行转换,创建一个包含参数值的 Boost::Fusion 列表。
- 使用 boost::fusion 的调用使用创建的列表调用函数。
步骤 (1) 和 (2) 看起来都相当简单。但是,我不知道如何进行第二步(或者如果它甚至可能——混合编译时和运行时似乎有点奇怪)。
有没有人知道如何做第二步,或者解决这个问题的更好方法?
我知道 Boost::Python 必须做类似的事情,但是代码有点密集,以便很好地了解正在发生的事情。
更新
我有一个部分解决方案,它适用于(至少)简单的情况。处理参考仍然存在问题。
希望有人可以发布更好的解决方案。
该列表包含指向基类 A 的指针,所有其他使用的类都从该基类派生。我有两个子类 B 和 C,它们包含不同的值类型(int 和 string)。
运算符 convert 接受给定的类并获取基础值。这些值通过转换收集到一个序列中,然后将其提供给调用。
class A {
public:
A() {}
virtual ~A() {}
};
class B: public A {
protected:
int value;
public:
B() {}
B(int v): value(v) {}
int getValue() { return value; }
};
class C: public A {
protected:
string value;
public:
C() {}
C(const string &v): value(v) {}
string &getValue() { return value; }
};
// this pattern was copied from the test files from the fusion library
struct convert {
// keep a reference to the parameter we're going to unpack
list<A *>::iterator ¶m;
convert(list<A *>::iterator ¶m): param(param) {}
template<typename Sig>
struct result;
template <typename T>
struct result<convert(T)> {
typedef T type;
};
// this must be specialized in order to properly deal with the return types
template <typename T>
T operator ()(T type) const {}
};
template <>
int convert::operator ()(int type) const {
B *b = dynamic_cast<B *>(*param++);
if (b != NULL) return b->getValue();
throw error("illegal cast");
}
template <>
string convert::operator ()(string type) const {
C *c = dynamic_cast<C *>(*param++);
if (c != NULL) return c->getValue();
throw error("illegal cast");
}
最后,调用一个函数:
// create a parameter list (usually this would be passed to us)
list<A *> params;
params.push_back(new B(2));
params.push_back(new C("test"));
// point to beginning of parameter
list<A *>::iterator pos = params.begin();
// foo is the function we're going to call,
typedef BOOST_TYPEOF(foo) params_type;
// use the parameter list of foo to unpack the parameter list
auto passedParams = fusion::as_list(fusion::transform(function_types::parameter_types<params_type>(), trans(pos)));
// finally, call foo with the sequence that was created by the transform
fusion::invoke(foo, passedParams);