0

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

每个学生都应该使用相同的 API 编写自己的函数。并且将创建一个文件,其中包含所有函数和 main 调用它们。

可以说:int studentname(int a, int b)是功能模式。

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

我想知道,有没有一种方法可以通过它的名字来调用一个函数?

像:int student1(int a , int b),,student2()等。

不知何故,main我们可以打电话给sscanf(funcname,"student%d",i); funcname();.

你还有别的想法吗?也许

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

任何有创意的都可以!:)

谢谢!贝科

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

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

这样我就可以给每个学生一个数字,然后瞧……但没办法。不过这很有趣。


编辑:我正在使用linux。

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

4

7 回答 7

2

也许有点过于复杂,但自发的想法:

  • 将所有学生源文件编译到一个共享库中,导出学生函数。
  • 然后枚举所有暴露的函数,调用并测试它们。

作为备选:

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

还有一个想法:

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

dlopen()使用and的方法示例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)
于 2012-11-08T00:11:11.023 回答
2

类似于@Jamey-Sharp 提出的建议:

  • 要求每个学生提供.c具有给定姓名/签名的输入功能的文件
  • 将每个文件编译.c成一个共享库,以学生姓名命名,或赋予任何唯一名称。此步骤可以使用make简单的脚本轻松自动化。
  • 制作一个简单的主机应用程序,它枚举.so给定目录中的所有文件,并使用dlopen()dlsym()访问入口点函数。
  • 现在您可以简单地调用每个学生的实现。

BTW,插件通常是这样实现的,不是吗?

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

这是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
于 2012-11-08T00:26:25.937 回答
1

我会采取不同的方法:

  1. 要求每个学生使用相同的函数名,并将每个学生的代码放在单独的源文件中。
  2. 再编写一个源文件,其中的 amain调用标准名称。
  3. 生成一个单独的可执行文件,从链接main.cstudent1.c,然后main.cstudent2.c,依此类推。您可以在 makefile 或 shell 脚本中使用通配符来自动执行此操作。

也就是说,至少在类 Unix 操作系统上,您可以按照您的要求进行操作。

  1. 调用dlopen(NULL)以获取主程序中符号的句柄。
  2. 传递那个句柄和你想要的函数名dlsym。将结果指针强制指向正确类型的函数指针,然后调用它。
于 2012-11-08T00:10:31.430 回答
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的)

于 2012-11-08T01:19:47.533 回答
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;
}
于 2012-11-08T01:37:21.743 回答
0

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

于 2012-11-08T00:08:57.167 回答
0

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

于 2012-11-08T00:10:30.253 回答