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 中有没有简单的方法来做到这一点?我将衷心感谢您的帮助。谢谢。

我用于微控制器的 IDEvoid X_functionName(void);以每个单独引脚的格式生成可执行功能X。所以我一直在寻找一种更通用的方法来使用void customName(const char* X).

4

7 回答 7

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

所有这些代码都在我的脑海中,并且可能无法按原样编译。

实际上,我强烈建议不要每个人都拥有一个功能,而是使用数据。如果绝对需要,请使用枚举而不是字符串来识别它们。

于 2013-07-08T17:36:33.357 回答
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;
}
于 2013-07-08T17:39:18.600 回答
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);
于 2013-07-08T17:41:26.873 回答
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();
于 2013-07-08T17:21:44.600 回答
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();
}
于 2013-07-08T18:59:16.340 回答
0

您正在描述动态编程语言中常见的功能,而 C 和 C++ 不是。

按照 H2CO3 和 Thomas Matthews 的建议在 C++ 中使用std::unordered_map<void (*)(), std::string>是一个好主意。

以最小的开销,您可以使用 if-else 结构。这个解决方案应该在 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 */
}
于 2013-07-08T17:39:29.380 回答
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:;
  }
}
于 2013-07-08T20:44:10.533 回答