0

假设我有一些反射元数据,其中包含以下信息:

enum class type { t_int, t_double, t_str /* etc... */ };

struct meta_data
{
    void* function;
    void* instance;
    std::vector<type> param_types;
};

std::map<std::string, meta_data> ftable;

我想调用这张地图中的函数,给定函数名和参数都是字符串。我的问题不是转换参数(例如 with boost::lexical_cast),而是转换为正确类型的函数指针并调用函数。如果我允许可能的 8 种类型和最多 8 个参数,那我的代码中已经有很多分支了。我想避免的(伪代码):

    开关(md.param_types.size())
    {
    案例0:
         转换函数指针,调用它
         休息;
    情况1:
         开关(md.param_types[0])
         {
         案例 t_int:
                    int param = boost::lexical_cast(param_strings[0]);
                  转换函数指针,使用参数调用
         案子 ...
         }
         休息;
    案例2:
         开关(md.param_types[0]){
         案例 t_int:
             int param = boost::lexical_cast(param_strings[0]);
             switch (md.param_types[1]) {...} // 第二个参数类型..
         }
         休息;
    案例n...
    }

随着参数的数量和可能的类型,这很快就会爆炸。我正在寻找一些解决方案(伪代码):

for (auto& p : paramter_strings)
{
    convert p to a variable of matching type (type id comes from meta_data).
    store value
}

call function with stored values

即函数调用没有分支。如何使用最少的样板代码(可能支持任意数量的参数)来做到这一点?您可以将其视为创建与自定义脚本语言的绑定。

4

1 回答 1

0

我想出了一种方法。

假设您有一个 unsigned int V 向量,并且您知道该向量的每个元素都是小于 N(或者说,20)的非负数。以下是将向量 V 更改为正整数的方法:

n = sequence_to_code(V,N); // or n = encode(V,20U);

这是代码。

long sequence_to_long(const std::vector<unsigned int> & L,
                      unsigned long n) {
  long result = 0L;
  std::vector<unsigned int>::const_iterator w=L.begin(),e=L.end();
  if(w!=e) {
    result += (*w)+1;
    unsigned long the_pow = n;
    unsigned int i = 1U;
    ++w;
    while(w!=e) {
      result += (*w+1)*(the_pow);
      ++w;++i;the_pow *= n;
    }
  }
  return result;
}

实际上,我可能应该返回“unsigned long”。

此外,您可以在创建文本文件的程序中使用相同的例程。该文本文件将包含 C++ 代码。假设您创建了“the_defines.hpp”。我将举例说明...

例如,假设我们有 t_int=0; t_double = 1, d_str = 2 并且只有三种类型。然后“the_define.hpp”可能是文件:

#define TYPE_EMPTY 0U
#define TYPE_INT   1U
#define TYPE_DOUBLE 2U
#define TYPE_STR    3U
#define TYPE_INT_INT 4U
#define TYPE_DOUBLE_INT 5U

那么可以通过以下方式使用此代码:

std::vector<unsigned int> L;
// add entries to L
long n = sequence_to_long(L,3UL);
switch(n) {
  case TYPE_INT:
    std::cout << "an integer\n";
    break;
  case TYPE_INT_DOUBLE:
    std::cout << "two args; first is an int; second is a double\n:
    break;
}

当然,您可以创建一个包含很长枚举代码的文本文件(如果您想避免#define)。例如,

enum class extended_type { 
  type_int,
  type_double,
  type_str,
  type_int_int,
  type double_int,

等等。

您还可以编写一个创建(一个或多个)文本文件的程序。这些文本文件也可以由 C++ 代码编写。例如,您创建的文件可能是:

swtich(n) {
  case empty:
    FILLIN
    break; 
  case t_int:
    FILLIN
    break;

依此类推,直到

  case t_str_str:
    FILLIN;
    break;
 }

我还建议使用内联函数或常规函数进行强制转换。例如,

inline int inside_int(foo f) {
  const bar & b = * reinterpret_cast<const bar *>(f.pointer());
  return b.d_x;
}

我推荐这样做是因为 DRY(不要重复自己)并且能够搜索该函数的所有实例。

我知道这些程序不处理错误(溢出、零指针等)

标记

于 2015-07-08T03:51:45.863 回答