2013-10-08 3 views
0

Предположим, что у меня есть некоторые отражения метаданных, который имеет следующую информацию:Вызывающие ++ (участник) функции C динамически

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), а в том, что вы выбрали нужный тип указателя функции и вызвали функцию. Если я разрешу 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 

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

+0

к сведению, что в большинстве реализаций есть определенные типы указателей функций, которые больше, чем пустота *. Также обратите внимание, что идея вашего стека не будет работать вообще, поскольку соглашения о вызовах широко варьируются, а также часто зависят от типа переданного параметра. Единственное, что приходит мне на ум, которое могло бы помочь вам, - это рекурсивные вызовы шаблонов, добавляющие один за другим параметр, а затем, наконец, вызов «реальной вещи». Но вся идея настолько безумна ... – PlasmaHH

+0

Вы отметили этот вопрос как 'lua'. Это стек Lua или стек архитектуры? Кроме того, если вы вызываете функции-члены, то функция 'function' должна иметь тип' void (dummy :: * function)() 'по крайней мере, а затем вы можете' reinterpret_cast' ввести правильный тип функции. – Simple

+0

@ Простой, никакой тег lua не был ошибкой, у меня был черновик вопроса на этом ПК, который я удалил, но забыл повторно пометить. исправлено. –

ответ

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

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

Марка

Смежные вопросы