Вызов C ++ (член) функционирует динамически

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;

Я хотел бы вызвать функции на этой карте, учитывая имена функций и параметры как строки. Моя проблема заключается не в преобразовании параметров (например, с boost::lexical_cast), а в том, что вы boost::lexical_cast нужный тип указателя функции и вызвали функцию. Если я разрешу 8 типов и максимум 8 параметров, это уже много ветвей в моем коде. Чего я хочу избежать (псевдокод):

    switch (md.param_types.size())
    {
    case 0:
         cast function pointer, call it
         break;
    case 1:
         switch (md.param_types[0])
         {
         case t_int:
                    int param = boost::lexical_cast(param_strings[0]);
                  cast function pointer, call with param
         case ...
         }
         break;
    case 2:
         switch (md.param_types[0]) {
         case t_int:
             int param = boost::lexical_cast(param_strings[0]);
             switch (md.param_types[1]) {...} // second param type..
         }
         break;
    case 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

т.е. нет ветвления для вызова функции. Как я могу сделать это с наименьшим количеством кода шаблона (возможно, с поддержкой произвольного количества параметров)? Вы можете думать об этом как о создании привязок к пользовательскому языку сценария.

Теги:
reflection
bind

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 (не повторяйте себя) и возможность поиска всех экземпляров функции.

Я знаю, что эта программа не обрабатывает ошибки (переполнение, нулевые указатели и т.д.),

отметка

Ещё вопросы

Сообщество Overcoder
Наверх
Меню