我将提出适用于一种方法的问题,但要知道该解决方案应该足够通用以适用于任何可能的给定方法。请考虑以下事项:
我有一个常规的旧全局方法:
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* 数据
推送到寄存器中用作 Caller
中的参数?这可能吗?我不关心安全性或便携性。
在调用 Caller
之前,我有一个 void* 数组,它指向可以传递给内部调用方法的数据。我不确定这是否是解决此问题的最佳方法。
我正在编写一个脚本系统,本质上,它可以从脚本中调用方法。所以这些方法存储在一个查找表中,其中每个方法都有一个字符串名称,并且有一个 void* 指向要调用的实际方法。在执行时,我知道该方法需要多少个参数以及这些参数的类型(当在查找表中为该方法指定一个条目时,这些类型存储为元数据)。这允许我将作为脚本中参数的字符串值转换为它们实际应有的值(使用自定义转换系统)。但是转换器返回一个 void*,因为您这样调用它:
string s = "123456";
void* result = Converter::Convert( "string*", "int", &s );
我可以保证 result
中存储的值实际上是请求的类型(如果存在此类型对的转换器),但无法转换为此类型,因为类型名称仅作为字符串提供。这使得转换器灵活且真正与类型无关。但它使处理它返回的值变得复杂。所以在脚本中我会这样调用:
MyMethod( 111, 222 )
这将被解析,方法名称将用于查找方法地址,然后转换器将它找到的值转换为预期的数据类型,但将它们返回为 void*
.然后将调用 Caller,传入方法地址、它已转换的参数(作为字节数组)以及参数数据数组的大小(以字节为单位)。此时我需要调用该方法并传递这些参数。同样,我无法修改它正在调用的现有方法。
我研究过程序集来传递这些数据,但似乎您必须使方法裸露才能直接在程序集中读取参数,或者做其他事情,而且我以前从未真正使用过程序集。尽管如果解决方案在于汇编,我也愿意学习一些。
最佳答案
稍微改变实现细节,这里是如何做你想做的
#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 vector 并将任意值放入 vector 中。然后查找函数名称并像示例 main 中那样调用。
如果您有任何问题,请告诉我。
关于c++ - 不指定参数的调用方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20528100/