2013-07-08 5 views
1

Предположим, что кто-то сделал отдельные функции следующим образом отдельно для каждого человека:функция динамического вызова функции

void John_age(void); 
void Tom_age(void); 
void Kate_age(void); 
void Cathy_age(void); 

................... .

для определения возраста.

Теперь я хочу, чтобы сделать такую ​​функцию для вызова этих функций, используя только имена данного лица, как:

void age(char* NAME){...} 

вызвать определенную функцию для этого человека «NAME».

void NAME_age(void); 

Есть ли какой-либо простой способ сделать это на C++ или C? Я бы очень признателен вам за вашу помощь. Благодарю.

IDE, который я использую для микроконтроллеров, делает исполняемые функции в формате void X_functionName(void); для каждого отдельного контакта X. поэтому я искал более общий подход, чтобы называть их легко, используя такие функции, как void customName(const char* X).

+4

Указатели функций – aaronman

+8

Карта имен в возрасте вместо функций. И почему вы хотите взять неконстантный 'char *', если вы его не модифицируете? Это настоящая боль, когда это происходит. – chris

+0

Вы можете использовать ту же структуру с именем поля и указателем функции. получить информацию из этого [вопроса и его ответов] (http://stackoverflow.com/questions/12971995/is-pointers-to-function-as-struct-member-useful-in-c) –

ответ

3

Простейшая, но не масштабируемое решение что-то вроде этого (в C++):

void age(std::string name) { 
    if(name == "John") { 
     John_age(); 
    } 
    else if(name == "Tom") { 
     Tom_age(); 
    } 
    else if(name == "Kate") { 
     Kate_age(); 
    } 
    // and so on 
} 

Простой, масштабируемый, но неаккуратно решение использовать макросы:

#define AGE(name) name##_age() 

и звоните без кавычек:

AGE(John); 
+0

вы уверены, что макрос работает? OP имел имя как _parameter_ , – TooTone

+0

Да. Я протестировал его. –

+1

Имя считывается в память, а не в источнике, например 'char name [80]; fgets (name, 80, stdin); 'where I type in" John " –

2

есть ли простой способ сделать это в C++ или C

В C++ нет, в связи с ужасным именем коверкая (если вы не сделать std::unordered_map<void (*)(), std::string> все возможные функции). Но в C вы можете сделать это:

void *hndl = dlopen(NULL, RTLD_NOW); // or dlopen(RTLD_DEFAULT, RTLD_NOW) 
void (*fptr)(void) = dlsym(hndl, "func_name"); 
fptr(); 
+4

POSIX только и полагается на предположение, что функция имеет значение 'default' [видимость] (http://gcc.gnu.org/wiki/Visibility). – Fanael

+0

@ Fanael Yup. Я знаю. Но что еще, правда? (Если OP хочет это сделать, он может сделать эквивалентный пример Windows, прочитав документацию. И если функции являются «статическими», все еще существует возможность хэш-таблицы.) –

+0

благодарим за ваши ответы. На самом деле я не знаком с RTLD_NOW и его не видно. :(Я использую Xcode в mac для компиляции. –

6

Это довольно просто. Создайте карту имен для возрастных функций.
typedefs упрощает работу с указателями функций, а статическая локальная карта заставляет ее инициализироваться только один раз, а затем «кэшировать» результаты. Неупорядоченные карты идеально подходят для такого рода вещей.

C++ 11:

void age(const std::string& NAME){ 
    static const std::unordered_map<std::string, void(*)()> age_lut = { 
      {"John",John_age}, 
      {"Tom",Tom_age}, 
      {"Kate",Kate_age}, 
      {"Cathy",Cathy_age}, 
     }; 
    return age_lut.at(Name); //throws std::out_of_range if it doesn't exist 
} 

C: (Здесь я использую линейную карту вместо хэша, как на C++, потому что я ленивый)

typedef void(*)() get_age_func_type; 
typedef struct { 
    const char* name; 
    get_age_func_type func; 
} age_lut_type; 

age_lut_type age_lookup_table[] = { 
      {"John",John_age}, 
      {"Tom",Tom_age}, 
      {"Kate",Kate_age}, 
      {"Cathy",Cathy_age}, 
     }; 
const unsigned age_lookup_table_size = 
     sizeof(age_lookup_table)/sizeof(age_lut_type); 

bool age(char* NAME){ 
    bool found = false; 
    //if you have a large number of functions, 
    //sort them in the initialization function, and 
    //use a binary search here instead of linear. 
    for(int i=0; i<age_lookup_table_size ; ++i) { 
     if (stricmp(age_lookup_table[i], NAME)==0) { 
      age_lookup_table[i].func(); 
      found = true; 
      break; 
     } 
    } 
    return found; 
} 

Все это код с верхней части головы и, вероятно, не совсем компилируется, как есть.

В действительности, I высоко рекомендовать не иметь функцию на человека, использовать данные вместо этого. Если это абсолютно необходимо, используйте перечисление вместо строки, чтобы идентифицировать их.

+0

И в C++ 11 вы можете инициализировать его inline. – chris

+1

Почему это произошло. Это выглядело хорошо для меня. (Является ли что-то немного странным: целая куча ответов, похоже, была немедленно отключена, я обновлял эту страницу и наблюдал за ее развитием.) – TooTone

+0

Правильно, я полностью забыл, что C++ 11 мог сделать это inline –

3

В C++, вы создаете std::map указателей на функции:

typedef void (*Age_Function_Pointer)(void); // Establish synonym for function syntax. 

typedef std::map<std::string, Age_Function_Pointer> Lookup_Table; 

unsigned int age(char const * name) 
{ 
    static bool table_is_initialized = false; 
    Lookup_Table name_func_map; 
    if (!table_is_initialized) 
    { 
    name_func_map["Cathy"] = Cathy_age; 
    name_func_map["Kate"] = Kate_age; 
    name_func_map["Tom"] = Tom_age; 
    name_func_map["John"] = John_age; 
    } 
    std::string name_key = name; 
    Lookup_Table::const_iterator iterator = name_func_map.find(name_key); 
    unsigned int persons_age = 0U; 
    if (iterator != name_func_map.end()) 
    { 
    persons_age = (*(iterator.second))(); 
    } 
    return persons_age; 
} 

Аналогично в C вы можете создать справочную таблицу указателей на функции:

struct Table_Entry_t 
{ 
    char const * name; 
    Age_Function_Pointer p_func; 
}; 

struct Table_Entry_t Age_Lookup_Table[] = 
{ 
    { "Cathy", Cathy_age}, 
    { "Kate", Kate_age}, 
    { "Tom", Tom_age}, 
    { "John", John_age}, 
}; 
const unsigned int NUMBER_OF_ENTRIES = 
    sizeof(Age_Lookup_Table)/sizeof(Age_Lookup_Table[0]); 

unsigned int age(char const * person_name) 
{ 
    unsigned int person_age = 0; 
    unsigned int i = 0; 
    for (i = 0; i < NUMBER_OF_ENTRIES; ++i) 
    { 
     if (strcmp(person_name, Age_Lookup_Table[i]) == 0) 
     { 
     person_age = (Age_Lookup_Table[i].p_func)(); 
     break; 
     } 
    } 
    return person_age; 
} 
+1

Тот же комментарий, что и для ответа @ MooingDuck: почему это было приостановлено. Это выглядело хорошо для меня. (Есть что-то немного странное: целая куча ответов, похоже, была забита сразу, я обновлял эту страницу и наблюдал за ее развитием.) – TooTone

+0

Благодарим вас за ответ. но это означает, что я должен сделать таблицу и сохранить в ней все функции, соответствующие этим именам. но если я добавлю что-то позже, то мне придется добавить в ту же таблицу снова, или если числа этих функций много, например, несколько тысяч, тогда это будет большая работа, чтобы поместить их вручную в таблицу, не так ли? :( –

+0

Можно создать функцию 'AddToMap (Lookup_Table name_func_map, std :: string name, Age_Function_Pointer ptr)'. Поэтому каждый раз, когда вы добавляете функцию, она может быть добавлена ​​без массивной нагрузки на ввод. И если ваш компилятор просто произойдет для правильной обработки '__FUNCTION__', вы можете, вероятно, сохранить немного больше ввода путем вырезания n-вставки некоторых частей AddToMap(). –

0

Вы описываете то, что является обычная функция в динамических языках программирования, то C и C++ - нет.

Использование std::unordered_map<void (*)(), std::string>, предложенное H2CO3 и Thomas Matthews, является хорошей идеей на C++.

С минимальными накладными расходами вы можете использовать структуру if-else. Это решение должно работать в С.

void age(char* NAME){ 
    void (*fp)(void); 

    if  (strcmp(NAME, "John") == 0) { fp = John_age; } 
    else if (strcmp(NAME, "Tom") == 0) { fp = Tom_age; } 
    /* ... additional cases ... */ 
    else { /* handle bad input */ } 

    fp(); /* calls the appropriate age function */ 
} 
+0

Спасибо за ваш ответ, но числа операторов if-else будут много, если количество функций больше, чем несколько не так ли? –

+0

Да, будет много операторов if-else для многих функций. Было бы больно использовать нединамический язык. –

2

Теперь, когда я знаю, что вы делаете, я серьезно рекомендую не делать этого. Но если вы действительно этого хотите, полиморфизм может быть более интересным способом пойти на C++, хотя и с сомнительной эффективностью. Используйте Perl/Python скрипт для создания заголовка туманно, как это:

struct pin_type { 
    virtual ~pin_type() {} 
    virtual void name()=0; 
    virtual void age()=0; 
}; 

struct John_type : public pin_type { 
    void name() {John_name();} 
    void age() {John_age();} 
}; 
John_type& John() {static John_type John_; return John_;} 

struct Tom_type : public pin_type { 
    void name() {Tom_name();} 
    void age() {Tom_age();} 
} 
Tom_type & Tom() {static Tom_type Tom_; return Tom_;} 

... thousands you say? 

, а затем ваш нормальный код здесь:

pin* get_pin_by_name(const char* name) { 
    //look up table of some sort, described by other answers 
} 

int main() { 
    pin_type * pin = John(); //instant, and probably allows inlining next calls 
    pin->age(); //same speed and size as function pointer 
    pin->name(); //but we avoid multiple lookups 
    pin = get_pin_by_name("Tom"); //runtime names can be ok too 
    pin->name(); //calls Tom_name(); 
} 
0

Никто не использовал механизм constexpr еще. Вот оно:

inline unsigned hash(char const* p) 
{ 
    int h(0); 

    for (; *p; ++p) 
    { 
    h = 31 * h + static_cast<unsigned char>(*p); 
    } 

    return h; 
} 

constexpr unsigned constHash(char const* in, uint const h) 
{ 
    return *in ? constHash(in + 1, 31 * h + static_cast<unsigned char>(*in)) : h; 
} 

constexpr unsigned constHash(char const* in) 
{ 
    return constHash(in, 0); 
} 

void process(char const* const name) 
{ 
    switch (hash(name)) 
    { 
    case constHash("John"): 
     //... 
     break; 

    case constHash("Tom"): 
     //... 
     break; 

    //... 
    default:; 
    } 
} 
+0

Я бы *highly_ рекомендую использовать тот же хеш для 'const' и non-const , Конечно, const может быть медленнее, но если вы сделаете опечатку в одном, вы никогда не найдете эту ошибку. –

+0

@MooingDuck Это зависит от того, насколько велик ваш набор данных. Если вы работаете с миллионами записей, вы можете рискнуть этим. – user1095108

+0

На самом деле, чем больше я смотрю на ваш хэш ... это не похоже на хороший хеш. Это похоже на ужасный хэш. –

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