1

Suppose someone has made individual functions in the following way separately for each person:

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

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

to determine their age.

Now I want to make such a function to call those functions by using just the person's names, like:

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

to call the specific function for that person "NAME".

void NAME_age(void);

is there any easy way to do that in C++ or C? I would really appreciate your help. Thanks.

The IDE i am using for a microcontrolles makes executable functions in the format void X_functionName(void); for every individual pin X. so I was looking for a more general approach to call them easily using functions like void customName(const char* X).

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • 8
    A map of names to ages instead of functions. And why would you want to take a non-const `char *` when you're not modifying it? It's a real pain when things do that. – chris Jul 08 '13 at 17:14
  • You can use same structure with field name and function pointer. get an idea from this [question and its answers](http://stackoverflow.com/questions/12971995/is-pointers-to-function-as-struct-member-useful-in-c) – Grijesh Chauhan Jul 08 '13 at 17:20
  • 2
    Which language, C or C++? There are different possibilities depending on the language. – Thomas Matthews Jul 08 '13 at 17:23
  • Is it possible for you to just make it a virtual function and make the signature of the function the same, something like `getAge()` ? – dchhetri Jul 08 '13 at 17:43
  • @chris thank you for pointing that out. :) i'm still a beginner so mistakes do happen. :p – Zulkarnine Mahmud Jul 08 '13 at 20:14

7 Answers7

6

It's pretty easy. Make a map of names to age functions.
typedefs make function pointers easier, and a static local map causes it to be initialized only once and then "cache" the results. Unordered maps are perfect for this sort of thing.

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: (Here I use a linear map instead of a hash like the C++, because I'm lazy)

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;
}

All this code is off the top of my head and probably doesn't quite compile as is.

In reality, I highly recommend not having a function per person, use data instead. If absolutely needed, use a enumeration instead of a string to identify them.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • And in C++11, you can initialize it inline. – chris Jul 08 '13 at 17:39
  • 1
    why did this get downvoted. It looked good to me. (Is something a bit odd going on? A whole bunch of answers seem to have been downvoted at once. I have been refreshing this page and watching it develop.) – TooTone Jul 08 '13 at 17:46
  • Right, I totally forgot C++11 could do it inline – Mooing Duck Jul 08 '13 at 17:47
  • Thank you for your answer. but that means I have to make a table and keep all the functions corresponding to that names in that. but if I add something later on then I have to add in the same table again or if the numbers of those functions are a lot, like few thousands then it's going to be a lot of work to put them manually in the table, right? :( – Zulkarnine Mahmud Jul 08 '13 at 17:53
  • 2
    @ZulkarnineMahmud: yes, if you add a function later, you also have to add it to the table. If there's thousands of functions, (A) you're probably doing a bad thing, and (B) it might be easier to write a perl/python program to write the file that puts them in the table. – Mooing Duck Jul 08 '13 at 17:55
  • Again, I _highly_ recommend not having a function for each user. Instead, have _one_ function that uses data to do the right thing (which is effectively what this answer does). The data can be hardcoded or loaded at runtime, either one. – Mooing Duck Jul 08 '13 at 18:07
  • actually the IDE i am using for a microcontrolles makes executable functions in the format void X_functionName(void); for every individual elements X. so I was looking for a more general approach to call them easily using functions like void customName(const char* X); thank you for your suggestion.:) – Zulkarnine Mahmud Jul 08 '13 at 18:13
  • And you have _thousands_ of X for each functionName? Especailly for a microcontroller though, I wouldn't do this unless absolutely needed, it may slow your code down quite a bit. – Mooing Duck Jul 08 '13 at 18:50
  • As a C++ programmer who's currently learning Haskell, the `It's pretty easy` start followed by dozens of lines of code made me go _sigh_. :-/ – Frerich Raabe Jul 08 '13 at 19:20
  • Easy != short. The C++11 is 2 lines of setup, 5 lines of data, and 7 lines of code. As far as C++ goes, that sounds fairly good. But yeah, C++ is more verbose than some languages. – Mooing Duck Jul 08 '13 at 20:30
  • @FrerichRaabe: I realized I coded it the long way. Silly me. Fixed. – Mooing Duck Jul 08 '13 at 20:36
3

In C++, you create a std::map of function pointers:

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;
}

Similarly in C you can create a look up table of function pointers:

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;
}
Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
  • 1
    Same comment as for @MooingDuck's answer: why did this get downvoted. It looked good to me. (Is something a bit odd going on? A whole bunch of answers seem to have been downvoted at once. I have been refreshing this page and watching it develop.) – TooTone Jul 08 '13 at 17:52
  • Thank you for your answer. but that means I have to make a table and keep all the functions corresponding to that names in that. but if I add something later on then I have to add in the same table again or if the numbers of those functions are a lot, like few thousands then it's going to be a lot of work to put them manually in the table, right? :( – Zulkarnine Mahmud Jul 08 '13 at 17:53
  • It is possible to make an `AddToMap( Lookup_Table name_func_map, std::string name, Age_Function_Pointer ptr )` function. So each time you add a function it can be added without the massive typing burden. And if your compiler just happens to handle `__FUNCTION__` correctly, you can probably save a bit more typing by cut-n-pasting some of the AddToMap() parts. – Wes Miller Jul 08 '13 at 20:57
  • @WesMiller: For `__FUNCTION__` to be of any use, you'd have to call each function once for it to kick in, which sort of defeats the point. Interesting thought though. – Mooing Duck Jul 09 '13 at 00:00
3

Simpliest but not scalable solution is something like this(in 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
}

Simple, scalable, but messy solution is to use macroses:

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

and call without quotes:

AGE(John);
Wlodzislav K.
  • 404
  • 2
  • 9
  • are you sure the macro works? The OP had the name as a _parameter_. – TooTone Jul 08 '13 at 17:44
  • 1
    The name is read into memory, not in the source, like `char name[80]; fgets (name,80,stdin);` where I type in "John" – Prashant Kumar Jul 08 '13 at 17:48
  • 2
    -1 for macro, +1 for first solution (I like simple, readable code much much more than fancy, unreadable code). From what I interpreted from the OP's question, he requires a runtime solution, this macro will be evaluated and converted into inline code at compile-time. – George Mitchell Jul 08 '13 at 17:56
  • @Prashant Note `AGE(John);` has no quotes. ## is the macro operator which just concatenates anything in preprocessing phase. Compile gets only `John_age()`. – Wlodzislav K. Jul 08 '13 at 17:59
  • @WlodzislavK.: Did you read what Prashant wrote? `AGE(name_entered_by_user)` isn't going to work as well. – Mooing Duck Jul 08 '13 at 18:05
  • 2
    actually the IDE i am using for a microcontrolles makes executable functions in the format void PinNumber_functionName(void); for every individual pins automatically. so I was looking for a more general approach to call them easily using functions like void customName(PinNumber); thank you for your suggestion. i think in this case the macro solution will work fine. :) I will try all the possible solutions. Thanks – Zulkarnine Mahmud Jul 08 '13 at 18:18
  • @ZulkarnineMahmud: Wow, the `fgets(name,80,stdin)` comment really made me think macros wouldn't work. Way to mislead us all :P – Mooing Duck Jul 08 '13 at 18:48
  • @WlodzislavK, Oops... well then, +1! I totally misunderstood what was going on... *Slowly backs away* – George Mitchell Jul 08 '13 at 19:24
  • @WlodzislavK.one limitation with the macro is that I cannot use another macro inside the parentheses, anyway it saved a lot of time for me. thanks once again..:) – Zulkarnine Mahmud Jul 08 '13 at 20:23
  • @ZulkarnineMahmud: [There's a trick to get around that](http://ideone.com/TQJAok) – Mooing Duck Jul 08 '13 at 23:57
2

is there any easy way to do that in C++ or C

In C++, no, due to the horrible name mangling (unless you make an std::unordered_map<void (*)(), std::string> of all the possible functions). But in C, you can do this:

void *hndl = dlopen(NULL, RTLD_NOW); // or dlopen(RTLD_DEFAULT, RTLD_NOW)
void (*fptr)(void) = dlsym(hndl, "func_name");
fptr();
  • 4
    POSIX only, and relies on the assumption that the function have `default` [visibility](http://gcc.gnu.org/wiki/Visibility). –  Jul 08 '13 at 17:28
  • @Fanael Yup. I know. But what else, really? (If OP wants to do so, he can make the equivalent Windows example by reading the documentation. And if the functions are `static`, there's still the possibility of a hash table.) –  Jul 08 '13 at 17:29
  • thank you for your replies. Actually I'm not familiar with RTLD_NOW and its not visible i guess. :( I'm using Xcode in mac for compilation. – Zulkarnine Mahmud Jul 08 '13 at 17:40
  • 3
    -1 Since there is any easy way using table of function pointers in C. In C++, a map of function pointers. – Thomas Matthews Jul 08 '13 at 17:41
  • @ZulkarnineMahmud `#include ` –  Jul 08 '13 at 17:43
  • 2
    @ThomasMatthews Huh? Then 1. you need to manually implement some sort of key-value map (which is not in the C standard library), 2. it won't automatically find all the visible functions. So my solution is superior to yours in C. –  Jul 08 '13 at 17:44
  • @H2CO3: This can work in C++ too if the age functions are marked as `C` functions in the source. – Mooing Duck Jul 08 '13 at 18:05
  • @MooingDuck Yes, technically. –  Jul 08 '13 at 18:26
  • 2
    @H2CO3: As Fanel points out, your solution is POSIX only. My solution works on any platform supporting the C or C++ languages. I'm just pointing out that your comment, "In C++, no, due to horrible name mangling.." is incorrect. The classic and portable solution (also in assembly language) is a table look up, and has been in use before C++ became a language. – Thomas Matthews Jul 08 '13 at 19:23
  • 2
    @ThomasMatthews And it requires a whole bunch of boilerplate code (especially in C when, as I already mentioned, you have to implement the table as well), and essentially it duplicates functionality of the dynamic linker/loader of the OS without good reason, and even fails to do it well, i. e. you still have to manually add functions, which is error prone. –  Jul 08 '13 at 19:26
2

Now that I know what your doing, I seriously recommend not doing that. But if you really really want to, polymorphism might be a more interesting way to go for C++, though of questionable effectiveness. Use a perl/python script to generate a header vaguely like this:

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?

and then your normal code here:

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();
}
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
0

You are describing something that is a commonplace feature in dynamic programming languages, something C and C++ are not.

Using std::unordered_map<void (*)(), std::string> as suggested by H2CO3 and Thomas Matthews is a good idea in C++.

With minimal overhead, you can use an if-else structure. This solution should work in C.

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 */
}
Prashant Kumar
  • 20,069
  • 14
  • 47
  • 63
0

No one exploited the constexpr mechanism yet. Here it goes:

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:;
  }
}
user1095108
  • 14,119
  • 9
  • 58
  • 116
  • I would _highly_ recommend using the same hash for both the `const` and non-const. Sure the const might be slower, but if you make a typo in one, you'll never find that bug. – Mooing Duck Jul 09 '13 at 00:03
  • @MooingDuck This depends on how large your dataset is. If you're working with millions of records, you might be willing to risk it. – user1095108 Jul 09 '13 at 06:21
  • Actually, the more I look at your hash... that doesn't look like a good hash. That looks like a terrible hash. – Mooing Duck Jul 09 '13 at 16:51
  • [Here, I made a `constexpr` impelementation of Jenkin's One-At-A-Time hash function](http://coliru.stacked-crooked.com/view?id=759a3623c7db9c359fe05e66a932cae4-f674c1a6d04c632b71a62362c0ccfc51) – Mooing Duck Jul 09 '13 at 17:27
  • [And here's the assembly showing that MSVC11 correctly inlined and unrolled the non-constexpr version](http://coliru.stacked-crooked.com/view?id=5e721fb6d81e969a28a4630e4c9925ae-f674c1a6d04c632b71a62362c0ccfc51) – Mooing Duck Jul 09 '13 at 17:40
  • @MooingDuck The hash was taken from Qt 5. Perhaps your comment could be made into a bug report for Qt? Otherwise, thanks for your hash implementation. What do you think about the Jesteress hash function? – user1095108 Jul 09 '13 at 17:49
  • Qt uses it? How strange. It's possible I'm wrong about the quality of that hash I guess, but... it still doesn't look very good. Especially for strings <5 characters... – Mooing Duck Jul 09 '13 at 18:40