函数名作为C中main的参数

时间:2017-07-26 12:36:11

标签: c function-pointers

我有一个主要功能如下:

#include <stdio.h>

int main(int argc, char *argv[])
{
    int i, sum = 0;
    char *func_user = argv[1];

    // execute func_user function

    return 0;
}

void foo1(void)
{
    printf("I'm foo1.");
}

void foo2(void)
{
    printf("I'm foo2.");
}

void foo3(void)
{
    printf("I'm foo3.");
}

我希望用户将他的函数名作为参数给main,我希望我的程序执行这个给定的函数。有没有办法做这个(比如使用反射)而不使用类似switch / case的方法?

4 个答案:

答案 0 :(得分:14)

你不能直接这样做,因为C既没有introspection也没有reflection。你必须自己一个名字(一个字符串)映射到(指向一个)函数。

创建这样的映射的一种不常见的方法是使用具有该信息的结构,然后对所有函数使用这些结构的数组。然后迭代数组以查找名称及其函数指针。

也许像

struct name_function_map_struct
{
    char *name;             // Name of the function
    void (*function)(void); // Pointer to the function
};

// Declare function prototypes
void foo1(void);
void foo2(void);
void foo3(void);

// Array mapping names to functions
const struct name_function_map_struct name_function_map[] = {
    { "foo1", &foo1 },
    { "foo2", &foo2 },
    { "foo3", &foo3 }
};

int main(int argc, char *argv[])
{
    // Some error checking
    if (argc < 2)
    {
        // Missing argument
        return 1;
    }

    // Calculate the number of elements in the array
    const size_t number_functions = sizeof name_function_map / sizeof name_function_map[0]

    // Find the function pointer
    for (size_t i = 0; i < number_functions; ++i)
    {
        if (strcmp(argv[1], name_function_map[i].name) == 0)
        {
            // Found the function, call it
            name_function_map[i].function();

            // No need to search any more, unless there are duplicates?
            break;
        }
    }
}

// The definitions (implementations) of the functions, as before...
...

答案 1 :(得分:5)

是的,但您可能不希望这样做是出于您的目的(即避免switch声明)。 From another SO answer (compile via gcc -std=c99 -Wall -rdynamic ds.c -o ds -ldl, assuming the filename is ds.c):请记住,您需要知道函数签名,即:返回类型加参数;提前)。

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

void hello ()
{
    printf ("hello world\n");
}

int main (int argc, char **argv)
{
    char *buf = "hello";
    void *hndl = dlopen (NULL, RTLD_LAZY);
    if (!hndl) { fprintf(stderr, "dlopen failed: %s\n", dlerror()); 
        exit (EXIT_FAILURE); };
    void (*fptr) (void) = dlsym (hndl, buf);
    if (fptr != NULL)
        fptr ();
    else
        fprintf(stderr, "dlsym %s failed: %s\n", buf, dlerror());
    dlclose (hndl);
}    

你最好只创建一个专门提供函数/字符串映射的函数。我提供的例子只是为了证明你所要求的东西可以做到。 仅仅因为你可以,并不意味着你应该。

修改示例

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

void foo1(void);
void foo2(void);
void foo3(void);

int main (int argc, char **argv)
{

    /* dlopen() self. */
    void *hndl = dlopen (NULL, RTLD_LAZY);
    if (!hndl) { fprintf(stderr, "dlopen failed: %s\n", dlerror()); 
        exit (EXIT_FAILURE); };

    /* Attempt to find symbols and call them. */
    for (int i = 0; i < argc; i++) {
        void (*fptr) (void) = dlsym(hndl, argv[i]);
        if (fptr != NULL)
            fptr();
        else
            fprintf(stderr, "dlsym %s failed: %s\n", argv[i], dlerror());
    }

    /* Cleanup. */
    dlclose (hndl);

    return 0;
}

void foo1()
{
    printf("hello world\n");
}

void foo2()
{
    printf("Mello world\n");
}

void foo3()
{
    printf("Jello world\n");
}

样本运行

./ds foo1 foo2 foo3 foo1
dlsym ./ds failed: ./ds: undefined symbol: ./ds
hello world
Mello world
Jello world
hello world

答案 2 :(得分:2)

C并不是那样的。它(通常)编译为机器代码,除非它包含调试信息,否则可执行文件不会保留源文件中存在的标识符(函数名,变量名和类型名)。

即使存在调试信息,在执行期间也没有标准的方法来访问该信息。

您唯一的选择是:

if(strcmp(func_user,"foo1)==0){
    foo1();
}else if (strcmp(func_user,"foo2)==0){
    foo2();
}/* and so on... */

[或者当然是数组和/或数据结构相当于......]

假设一个传统的编译和链接过程,你调用foo1()将会进入目标代码的是有效地说&#34;调用foo1&#34;的指令。到现在为止还挺好。但是当链接器出现时它将转向&#34;调用foo1&#34;跳转(通常是汇编程序jmp)到链接器已确定的地址是foo1()的入口点,并且(除非您包含所提到的调试符号)所有对标识符foo1的引用将被删除。执行不需要知道你所谓的函数,它只需要知道它的入口点的地址,这就是(通常)可执行文件中出现的所有内容。

脚注:在任何人提到它之前,C99确实定义了一个与正在编译的函数相同的符号__func__。在main()中,main可用于发出有意义的错误消息,但无法以任何方式调用该函数或以某种方式将其注册到某个查找表。< / p>

答案 3 :(得分:1)

除了Some programmer dude回答:

您可以使用预处理器(stringify)代替两次编写函数名称:

#define FUNC_MAP_ENTRY(name) { #name, &name}

const struct name_function_map_struct name_function_map[] = {
    FUNC_MAP_ENTRY(foo1),
    FUNC_MAP_ENTRY(foo2),
    FUNC_MAP_ENTRY(foo3)
};

<小时/> 顺便说一下,如果你使用C ++,或者你在纯C中有一些动态数组(它不难implement)你可以制作动态表这是工作示例如何实现动态函数表:

// MIT license
// Copyright 2017 "Yet Another John Smith"
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files (the "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial
// portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
// EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
// THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

// ================================
//   Dynamic function names table
// ================================

struct FuncNamesTableEntry
{
    const char* name;
    // You could make it more flexibile (could store any function type)
    // say use (void*), but user have to cast it by hand then
    void(*func_ptr)(void);
};

struct FuncNamesTable
{
    struct FuncNamesTableEntry* entries;
    size_t size;
    size_t capacity;
};


void func_table_init(struct FuncNamesTable * tbl)
{
    tbl->entries = NULL;
    tbl->size = 0;
    tbl->capacity = 0;
}


#define eprintf(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)


void func_table_add_entry(
    struct FuncNamesTable * tbl,
    struct FuncNamesTableEntry entry)
{
    if (tbl->size >= tbl->capacity) {
        struct FuncNamesTableEntry* new_mem;
        size_t new_capacity = tbl->capacity * 1.5 + 1;

        new_mem = realloc(tbl->entries, new_capacity * sizeof(struct FuncNamesTableEntry));
        if (new_mem == NULL) {
            printf("%s\n", "Not enough memory");
            exit(1);
        }

        tbl->entries = new_mem;
        tbl->capacity = new_capacity;
    }

    tbl->entries[tbl->size] = entry;
    tbl->size += 1;
}


struct FuncNamesTable _the_func_table;
// I need this hack to make ADD macro
struct FuncNamesTableEntry _insterting_entry;

#define FUNC_TABLE_INIT() func_table_init(&_the_func_table);
//#define FUNC_TABLE_ENTRY(f_name) struct FuncNamesTableEntry{#f_name, &f_name}
#define FUNC_TABLE_ADD_ENTRY(f_name) do{                        \
    _insterting_entry.name = #f_name;                           \
    _insterting_entry.func_ptr = &f_name;                       \
    func_table_add_entry(&_the_func_table, _insterting_entry);  \
}while(0);

#define FUNC_TABLE_ITERATE(_i)                                     \
    for (struct FuncNamesTableEntry* _i = _the_func_table.entries; \
         _i - _the_func_table.entries < _the_func_table.size;      \
         ++_i)


void foo1(void){}
void foo2(void){}
void foo3(void){}
void foo4(void){}
void foo5(void){}
void foo6(void){}

int main(int argc, char const *argv[])
{
    FUNC_TABLE_INIT();

    FUNC_TABLE_ADD_ENTRY(foo1);
    FUNC_TABLE_ADD_ENTRY(foo2);
    FUNC_TABLE_ADD_ENTRY(foo3);
    FUNC_TABLE_ADD_ENTRY(foo4);

    if (1) {
        FUNC_TABLE_ADD_ENTRY(foo5);
    }
    else {
        FUNC_TABLE_ADD_ENTRY(foo6);
    }

    FUNC_TABLE_ITERATE(i)
    {
        printf("Name: '%s'; pointer: %p\n", i->name, i->func_ptr);
    }

    return 0;
}