这有点难以解释,但我会尽我所能。我将提出问题,因为它适用于一种方法,但要知道这个解决方案应该足够通用,以适用于给定的任何可能的方法。请考虑以下事项:
我有一个常规的旧全局方法:
void MyMethod( int a, int b )
{
cout << a << " , " << b << endl;
}
我有另一个方法就是调用这个方法。
void Caller( void* method, void* data, int size )
{
// convert method to some function calling convention
// call the method here with the given data
}
这个调用者应该能够在内部调用任何方法,而不需要知道它需要多少参数以及它们的数据类型。所有它真正了解的方法是方法的地址和整个参数列表的大小(以字节为单位)。
如此简单地说,如何调用任意方法并将其传递给任意数量的数据,以便将其解释为参数?
基本上,如果不修改MyMethod
,如何将void* data
推入Caller
内用作参数的寄存器?这可能吗?我并不担心安全性或便携性。
在调用Caller
之前,我有一个void *数组,指向可以传递给内部调用方法的数据。我不确定这是否是解决这个问题的最佳方法。
我正在编写一个脚本系统,实质上可以从脚本中调用方法。因此,这些方法存储在查找表中,其中每个方法都有一个字符串名称,并且对要调用的实际方法有一个void *。在执行时我知道方法需要多少参数以及参数是什么类型(当在查找表中给出方法时,类型被存储为元数据)。这允许我将作为脚本中的参数的字符串值转换为它们实际应该的值(使用自定义转换系统)。但转换器返回一个void *,因为你这样称呼它:
string s = "123456";
void* result = Converter::Convert( "string*", "int", &s );
我可以保证result
中存储的值实际上是所请求的类型(如果存在此类型对的转换器),但无法转换为此类型,因为类型名称仅仅是以字符串形式提供。这使得转换器具有灵活性,并且真正无关紧要。但它使处理它返回的值变得复杂。所以在脚本中我会打这样的电话:
MyMethod( 111, 222 )
然后解析它,方法名称将用于查找方法地址,然后转换器将它找到的值转换为预期的数据类型,但将它们返回为void*
。然后调用Caller,传入方法地址,它已经转换的参数,一个字节数组,以及参数数据数组的大小,以字节为单位。在这一点上,我需要调用该方法并传递这些参数。同样,我无法修改它正在调用的现有方法。
我已经研究过汇编来传递这些数据,但是你似乎必须让方法裸以直接在汇编中读取参数或者做其他事情,而且我以前从未在汇编中工作过。虽然如果解决方案在于装配,我可以学习一些。
请不要因为含糊不清而贬低;如果您需要更多上下文,我可以提供它。只是评论。我真的很感激。
答案 0 :(得分:1)
稍微更改实施细节,以下是如何做你想做的事情
#include <iostream>
#include <boost/any.hpp>
#include <vector>
#include <functional>
#include <map>
#include <string>
using namespace std;
template<class F>
struct ConstructCaller{};
template<class T, int i>
struct TypeAndInt
{
enum{idx = i};
typedef T type;
};
template<class... T>
struct TypeList{};
template<class A, class B>
struct CombineTypeList{};
template<class... T1, class... T2>
struct CombineTypeList<TypeList<T1...>, TypeList<T2...>>
{
typedef TypeList<T1..., T2...> type;
};
template<int idx, class... T>
struct ToTypeAndIntList{
};
template<int idx,class T0, class T1, class... T>
struct ToTypeAndIntList<idx, T0,T1,T...>{
typedef typename CombineTypeList<TypeList<TypeAndInt<T0, idx> >, typename ToTypeAndIntList<idx+1,T1,T...>::type>::type type;
};
template<int idx, class T0>
struct ToTypeAndIntList<idx,T0>{
typedef TypeList < TypeAndInt<T0, idx> > type;
};
template<class... P>
struct ConstructCaller<void(*)(P...)>
{
typedef void(*FuncType)(P...);
FuncType f_;
template<class T>
typename T::type Get(const vector<boost::any>& vec){
return boost::any_cast<typename T::type>(vec.at(T::idx));
}
template<class... TI>
void DoCall(TypeList<TI...>, const vector<boost::any>& vec){
return f_(Get<TI>(vec)...);
}
void operator()(const vector<boost::any>& vec){
typedef typename ToTypeAndIntList<0, P...>::type List_t;
return DoCall(List_t{}, vec);
}
};
std::map < std::string, std::function<void(const std::vector<boost::any>&)>> func_map;
template<class F>
void RegisterFunction(std::string name, F f){
ConstructCaller<F> c;
c.f_ = f;
func_map[name] = c;
}
void MyMethod(int a, int b)
{
cout << a << " , " << b << endl;
}
void MyMethod2(std::string a, int b)
{
cout << a << " , " << b << endl;
}
int main(){
RegisterFunction("MyMethod", &MyMethod);
RegisterFunction("MyMethod2", &MyMethod2);
std::vector<boost::any> vec;
vec.push_back(1);
vec.push_back(2);
func_map["MyMethod"](vec);
vec.clear();
vec.push_back(std::string("Hello World"));
vec.push_back(2);
func_map["MyMethod2"](vec);
}
如此处所示,这仅适用于具有void返回类型的全局函数。 此解决方案还使用boost :: any,它可以存储任何类型,稍后您可以从中提取类型。因此,使用它注册您的功能。然后创建一个boost :: any向量,并将任意值放入向量中。然后查找函数名称并调用示例main。
如果您有任何问题,请与我们联系。