【问题标题】:Call Method Without Specifying Parameters不指定参数调用方法
【发布时间】:2013-12-29 22:55:18
【问题描述】:

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

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

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,传入方法地址、它已转换的参数(作为字节数组)以及参数数据数组的大小(以字节为单位)。正是在这一点上,我需要调用该方法并传递这些参数。同样,我无法修改它正在调用的现有方法。

我已经研究过汇编以传递这些数据,但似乎您必须使方法裸露才能直接在汇编中读取参数或执行其他操作,而且我以前从未真正在汇编中工作过。虽然如果解决方案在于组装,我可以学习一些。

【问题讨论】:

  • 是的,很抱歉没有这样标记它。
  • 你知道std::functionstd::bind吗?
  • 如果你可以使用c++11,像std::function这样的东西有用吗?
  • 为什么是程序集标签?
  • 装配标签是因为我不确定装配是否会参与解决方案。我试图远离 STL/Boost,因为我正在编写这些组件作为更大系统的一部分,并且它是一种“除非你绝对必须”处理这些库的方法。

标签: c++ pointers c++11 methods


【解决方案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 中那样调用。

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

【讨论】:

  • 今晚晚些时候,当我回到我的机器旁时,我会检查您的解决方案。为此,我需要远离 Boost/STL,但我们确实有一个反映 boost::any 的内部变体。所以谢谢。我会让你知道它是如何工作的。
猜你喜欢
  • 1970-01-01
  • 2017-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多