【问题标题】:dynamic function to call a function动态函数调用函数
【发布时间】:2013-07-08 17:10:57
【问题描述】:

假设有人以下列方式分别为每个人创建了单独的函数:

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 为每个单独的引脚 X 生成格式为 void X_functionName(void); 的可执行函数。所以我一直在寻找一种更通用的方法来使用 void customName(const char* X) 等函数轻松调用它们。

【问题讨论】:

  • 名称到年龄而不是函数的映射。当您不修改它时,为什么要采用非常量char *?这样做真的很痛苦。
  • 您可以使用具有字段名称和函数指针的相同结构。从这个question and its answers得到一个想法
  • 哪种语言,C 或 C++?根据语言的不同,有不同的可能性。
  • 您是否可以将其设为虚函数并使其签名相同,例如 getAge()
  • @chris 感谢您指出这一点。 :) 我还是个初学者,所以确实会发生错误。 :p

标签: c++ c


【解决方案1】:

这很容易。将名称映射到年龄函数。
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;
}

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

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

【讨论】:

  • 而在 C++11 中,你可以内联初始化它。
  • 为什么这被否决了。我觉得很好。 (发生了什么有点奇怪吗?一大堆答案似乎一次被否决了。我一直在刷新这个页面并观察它的发展。)
  • 对,我完全忘记了 C++11 可以内联
  • 感谢您的回答。但这意味着我必须制作一个表格并将与该名称对应的所有函数保留在其中。但是如果我稍后添加一些东西,那么我必须再次添加到同一个表中,或者如果这些函数的数量很多,比如几千个,那么手动将它们放入表中将是很多工作,对吧? :(
  • @ZulkarnineMahmud:是的,如果你以后添加一个函数,你也必须将它添加到表中。如果有数千个函数,(A) 你可能在做坏事,(B) 编写一个 perl/python 程序来编写将它们放入表中的文件可能会更容易。
【解决方案2】:

最简单但不可扩展的解决方案是这样的(在 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);

【讨论】:

  • 您确定宏有效吗? OP 将名称作为 参数
  • 名字被读入内存,而不是源代码中,比如char name[80]; fgets (name,80,stdin);,我输入“John”
  • -1 表示宏,+1 表示第一个解决方案(我更喜欢简单、可读的代码,而不是花哨、不可读的代码)。根据我对 OP 问题的解释,他需要一个运行时解决方案,这个宏将在编译时被评估并转换为内联代码。
  • @Prashant 注意AGE(John); 没有引号。 ## 是一个宏操作符,它只是在预处理阶段连接任何东西。编译只得到John_age()
  • 实际上,我用于微控制器的 IDE 以 void PinNumber_functionName(void); 格式生成可执行函数;自动为每个单独的引脚。所以我正在寻找一种更通用的方法来使用 void customName(PinNumber); 等函数轻松调用它们。谢谢您的建议。我认为在这种情况下,宏解决方案可以正常工作。 :) 我会尝试所有可能的解决方案。谢谢
【解决方案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;
}

【讨论】:

  • 与@MooingDuck 的回答相同的评论:为什么这被否决了。我觉得很好。 (发生了什么有点奇怪吗?一大堆答案似乎一次被否决了。我一直在刷新这个页面并观察它的发展。)
  • 感谢您的回答。但这意味着我必须制作一个表格并将与该名称对应的所有函数保留在其中。但是如果我稍后添加一些东西,那么我必须再次添加到同一个表中,或者如果这些函数的数量很多,比如几千个,那么手动将它们放入表中将是很多工作,对吧? :(
  • 可以创建AddToMap( Lookup_Table name_func_map, std::string name, Age_Function_Pointer ptr ) 函数。因此,每次您添加一个功能时,都可以在没有大量打字负担的情况下添加它。如果你的编译器恰好正确处理了__FUNCTION__,你可以通过剪切粘贴一些 AddToMap() 部分来节省更多的输入。
  • @WesMiller:要使__FUNCTION__ 有任何用处,您必须调用每个函数一次才能启动,这有点不合时宜。不过有趣的想法。
【解决方案4】:

在 C++ 或 C 中有什么简单的方法可以做到这一点

在 C++ 中,不,因为可怕的名称重整(除非您在所有可能的函数中创建一个 std::unordered_map&lt;void (*)(), std::string&gt;)。但在 C 中,你可以这样做:

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

【讨论】:

  • 仅限POSIX,并依赖于函数具有defaultvisibility的假设。
  • -1 因为在 C 中使用函数指针表有任何简单的方法。在 C++ 中,函数指针的映射。
  • @ThomasMatthews 嗯?那么1.您需要手动实现某种键值映射(不在C标准库中),2.它不会自动找到所有可见的功能。所以我的解决方案在 C 语言中优于你的解决方案。
  • @H2CO3:正如 Fanel 指出的那样,您的解决方案仅适用于 POSIX。我的解决方案适用于任何支持 C 或 C++ 语言的平台。我只是指出您的评论,“在 C++ 中,不,由于可怕的名称修改......”是不正确的。经典且可移植的解决方案(也在汇编语言中)是查表,并且在 C++ 成为一种语言之前就已使用。
  • @ThomasMatthews 它需要一大堆样板代码(尤其是在 C 中,正如我已经提到的,您还必须实现表),并且本质上它复制了动态链接器的功能/操作系统的加载程序没有充分的理由,甚至没有做好,即。 e.还是要手动添加函数,容易出错。
【解决方案5】:

既然我知道你在做什么,我强烈建议不要这样做。但是,如果您真的想这样做,多态性可能是 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();
}

【讨论】:

    【解决方案6】:

    您描述的是动态编程语言中常见的特性,而 C 和 C++ 不是。

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

    以最小的开销,您可以使用 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 */
    }
    

    【讨论】:

    • 感谢您的回复,但是如果函数数量多于几个,if-else 语句的数量会很多。不是吗?
    • 是的,很多函数都会有很多 if-else 语句。那将是使用非动态语言的祸患。
    【解决方案7】:

    目前还没有人利用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:;
      }
    }
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-04
    • 2018-02-02
    • 2012-07-17
    • 2011-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多