在不指定参数的情况下调用方法

Call Method Without Specifying Parameters

本文关键字:情况下 调用 方法 参数      更新时间:2023-10-16

我将提出这个问题,因为它适用于一种方法,但要知道这个解决方案应该足够通用,可以应用于任何可能的方法。考虑以下内容:

我有一个常规的旧全局方法:

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* dataCaller推送到用作参数的寄存器中?这可能吗?我不关心安全性或便携性。

在调用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的向量,并将您的任意值放入该向量中。然后查找函数名称并像示例main中那样调用。

如果你有任何问题,请告诉我。