按名称处理功能

时间:2012-11-08 00:00:21

标签: c linux function function-pointers

假设您创建了一个main()来处理您向学生询问的练习。

每个学生都应该使用相同的API编写自己的函数。将创建一个文件,包含所有函数和主函数。

让我们说:int studentname(int a, int b)是功能模式。

我处理它的一种方法是使用指向函数int (*func[MAX])()的向量。但是你需要逐个完成向量func[0]=studentname;

我想知道,有某种方法可以通过某种方式调用某个函数吗?

类似于:int student1(int a , int b)student2()

main {@ 1}}我们可以调用sscanf(funcname,"student%d",i); funcname();

你还有其他想法吗?也许

int studentname(int a, int b, char *fname)
{
    strcpy(fname, "studentname");

任何有创意的人都会做! :)

谢谢! 贝乔

PS。我尝试了一个函数向量,但C不允许我! :)

int func[2]()={{;},{;}};

这样我就可以给每个学生一个号码,然后瞧...但是没办法。这很有趣。


编辑:我正在使用linux。

编辑2:谢谢!我接受了一个帮助我的答案,但我还记录了一个完整的例子作为答案。

7 个答案:

答案 0 :(得分:2)

可能有点过于复杂,但是自发的想法:

  • 将所有学生源文件编译成一个共享库,学生的功能为导出。
  • 然后枚举所有公开的函数,调用并测试它们。

作为替代方案:

  • 编写一个小工具,使用预处理器定义编译所有“学生单元”,用唯一名称(“func1”,“func2”等)替换预定义的函数名称。
  • 然后让工具在执行测试等时编写一个调用所有这些函数的小单元。

还有另一个想法:

  • 使用C ++编写一个特殊的类模板,该模板将在对象工厂中注册派生类,并使用extern "C"嵌入学生的代码。根据实施情况,这可能看起来有点混乱和过于复杂。
  • 然后使用工厂创建每个实例并运行代码。

使用dlopen()dlsym()的方法示例(每个库是否只有一个函数或全部 - 无关紧要):

void *pluginlib = dlopen("student1.so", RTLD_NOW); // RTLD_NOW will load the file right away
if (!pluginlib)
    ; // failed to load
studentproc func = (studentproc)dlsym(pluginlib, "student1"); // this loads the function called "student1"
if (!func)
    ; // failed to resolve
func("hello world!"); // call the lib
dlclose(pluginlib); // unloads the dll (this will make all further calls invalid)

答案 1 :(得分:2)

与@ Jamey-Sharp提出的相似:

  • 要求每个学生提供.c文件,其中包含给定名称/签名的入口功能
  • 将每个.c编译成一个共享库,由学生名称命名,或者给出任何唯一名称。使用make或简单脚本可以轻松实现此步骤。
  • 创建一个简单的宿主应用程序,它枚举给定目录中的所有.so个文件,并使用dlopen()dlsym()来获取入口点函数。
  • 现在您可以简单地致电每位学生的实施。
顺便说一句,这就是插件通常是如何实现的,不是吗?

编辑:这是一个有效的概念证明(以及每个学生可以使用相同名称的入口点函数的证据)。

这是student1.c

#include <stdio.h>

void student_task()
{
    printf("Hello, I'm Student #1\n");    
}

这是student2.c

#include <stdio.h>

void student_task()
{
    printf("Hello, I'm Student #2\n");    
}

这是主程序,tester.c

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

/* NOTE: Error handling intentionally skipped for brevity! 
 * It's not a production code!
 */

/* Type of the entry point function implemented by students */
typedef void (*entry_point_t)(void);

/* For each student we have to store... */
typedef struct student_lib_tag {
    /* .. pointer to the entry point function, */
    entry_point_t entry;
    /* and a library handle, so we can play nice and close it eventually */ 
    void* library_handle;
} student_solution_t;

void load(const char* lib_name, student_solution_t* solution)
{
    /* Again - all error handling skipped, I only want to show the idea! */

    /* Open the library. RTLD_LOCAL is quite important, it keeps the libs separated */
    solution->library_handle = dlopen(lib_name, RTLD_NOW | RTLD_LOCAL);

    /* Now we ask for 'student_task' function. Every student uses the same name.
     * strange void** is needed for C99, see dlsym() manual.
     */
    *(void**) (&solution->entry) = dlsym(solution->library_handle, "student_task");

    /* We have to keep the library open */
}

int main()
{
    /* Two entries hardcoded - you need some code here that would scan
     * the directory for .so files, allocate array dynamically and load 
     * them all.
     */
    student_solution_t solutions[2];

    /* Load both solutions */
    load("./student1.so", &solutions[0]);
    load("./student2.so", &solutions[1]);

    /* Now we can call them both, despite the same name of the entry point function! */
    (solutions[0].entry)();
    (solutions[1].entry)();

    /* Eventually it's safe to close the libs */
    dlclose(solutions[0].library_handle);
    dlclose(solutions[1].library_handle);
    return 0;
}

让我们编译所有:

czajnik@czajnik:~/test$ gcc -shared -fPIC student1.c -o student1.so -Wall
czajnik@czajnik:~/test$ gcc -shared -fPIC student2.c -o student2.so -Wall
czajnik@czajnik:~/test$ gcc tester.c -g -O0 -o tester -ldl  -Wall 

看到它有效:

czajnik@czajnik:~/test$ ./tester 
Hello, I'm Student #1
Hello, I'm Student #2

答案 2 :(得分:1)

我采取不同的方法:

  1. 要求每个学生使用相同的功能名称,并将每个学生的代码放在一个单独的源文件中。
  2. 使用调用标准名称的main再写一个源文件。
  3. 生成单独的可执行文件,将main.cstudent1.c关联,然后将main.cstudent2.c关联,依此类推。您可以在makefile或shell脚本中使用通配符来自动执行此操作。
  4. 那就是说,至少在类Unix操作系统上,你可以做你所要求的。

    1. 调用dlopen(NULL)来处理主程序中的符号。
    2. 将该句柄和您想要的函数名称传递给dlsym。将结果指针强制转换为正确类型的函数指针,然后调用它。

答案 3 :(得分:1)

这是一个丑陋的预处理程序黑客:

#Makefile

FILE_NAME=student

${FILE_NAME}: main.c
        cc -Wall -DFILE_NAME=\"${FILE_NAME}.c\" -o $@ main.c -lm

老师的main.c:

#include <math.h>
#include <stdio.h>

#include FILE_NAME

char *my_name(void);
double my_sin(double val);

int main(void)
{
double dd;
dd = my_sin(3.1415923563);

printf("%s: %f\n", my_name(), dd);
return 0;
}

学生的.c文件:

#include <math.h>

char * my_name(void);
double my_sin(double val);

char * my_name(void)
{
return "Wildplasser-1.0";
}

double my_sin(double val)
{
return sin (val);
}

诀窍在于我是否包含了学生的.c文件。

为避免这种情况,您还可以使用不同的make行,例如:

 cc -Wall -o $@ ${FILE_NAME}.c main.c -lm

(并删除丑陋的#include FILENAME当然)

答案 4 :(得分:1)

谢谢大家。我接受了一个答案,给了我解决问题的灵感。在这里,只是为了记录它,是我的完整解决方案:

文件shamain.c

/* Uses shared library shalib.so
 * Compile with:
 *    gcc shamain.c -o shamain -ldl -Wall
 */


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

int main(void)
{
  void *libstud;
  int (*student[2])(int, int);
  char fname[32];
  int i,r;

  libstud = dlopen("./shalib.so", RTLD_NOW);
  if (!libstud)
  {
    fprintf(stderr, "error: %s\n", dlerror());
    exit(EXIT_FAILURE);
  }
  dlerror();    /* Clear any existing error */

  for(i=0; i<2; i++)
  {
    sprintf(fname, "func%d", i);
    *(void **) (&student[i]) = dlsym(libstud, fname); /* c99 crap */
    //student[i] = (int (*)(int, int)) dlsym(libstud, fname); /* c89 format */
  }

  for(i=0; i<2; i++)
  {
    r=student[i](i, i);
    printf("i=%d,r=%d\n", i, r);
  }

  return 0;
}

文件shalib.c

/* Shared library.
 * Compile with:
 *  gcc -shared -fPIC shalib.c -o shalib.so -Wall
 */

#include <stdio.h>

int func0(int one, int jadv)
{
  printf("%d = Smith\n", one);
  return 0;
}

int func1(int one, int jadv)
{
  printf("%d = John\n", one);
  return 0;
}

答案 5 :(得分:0)

我使用共享库已经有一段时间了,但我觉得你可以从DLL / shlib中提取命名函数。你能创建一个包含所有实现的DLL /共享库,然后从主要的名称访问它们吗?

答案 6 :(得分:0)

根据@ william-morris的建议,您可能很幸运使用dlsym()来动态查找函数。 (dlsym()可能是也可能不是您在特定平台上使用的库调用。)