我有一个主要功能如下:
#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的方法?
答案 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)
};
<小时/> 顺便说一下,
// 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;
}