我正在编写一个库,它将使用dlopen加载动态库并调用给定的函数。
我的函数指针需要一个返回类型为int的函数指针。
typedef int (*function_handle)(int);
共享对象中一个函数的返回类型为void。
void some_function_ret_void(int b)
但如果我将此指针指定给fn,则dlsym不会抛出任何错误。
typedef int (*function_handle)(int);
function_handle fn;
有没有办法检查从dlsym获得的函数指针的返回值?
#include <dlfcn.h>
#include <stdio.h>
typedef int (*function_handle)(int);
int main()
{
void* handle=dlopen("./libtest.so",RTLD_LAZY);
function_handle fn;
int retval =0;
char *err;
/**** How to check the return type of fn *********/
fn=dlsym(handle, "some_function_ret_void");
if ((err = dlerror()) != NULL) {
printf("Could not invoke the handler %s",err);
dlclose(handle);
return 0;
}
/*
if(fn return type is void don't print anything)
if(fn return type is char* , print the charecter)
free(str);
*/
retval = fn(4);
printf("%d",retval);
dlclose(handle);
return 0;
}
libtest.c(libtest.so)中的int some_function_ret_int( int a) {
return a+10;
}
char* some_function_ret_str(int b) {
//allocate memory for string and return;
}
void some_function_ret_void(int b) {
//allocate memory for string and return;
}
答案 0 :(得分:1)
不,无法检查dlsym()
的返回值。
dlsym()
中没有返回值智能 - dlsym()
甚至不知道您的符号是函数指针还是数据指针。您应该查看实现设计的其他方法,而不是取决于dlsym()
的返回值/类型。
dlsym()函数将在加载handle引用的对象时自动加载的所有对象中搜索命名符号。
http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html
答案 1 :(得分:1)
基本问题 - 如何实现提供不同类型功能的插件 - 非常有趣。
例如,考虑一个简单的Reverse Polish calculator,带有一个添加新运算符的插件接口。
不要让主程序使用dlsym()
来查找每个符号,而是让插件只导出一个 - 比如plugin_init()
- 将注册函数作为函数指针参数。然后,每个插件会根据其希望添加的每个功能调用一次注册函数。
RPN计算器基于堆栈。如果我们假设每个操作符都可以调整堆栈大小,那么操作符函数原型基本上就是
int operation(double **stackptr, int *countptr, int *maxcountptr);
其中*stackptr
是指向当前堆栈的指针,*countptr
是堆栈中double
的数量,*maxcountptr
告诉分配的大小(double
1)} s)用于堆栈。如果操作成功执行,它将返回0
,否则返回非零errno
错误代码。
考虑这个 application.c :
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <dlfcn.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
struct operator {
struct operator *next;
int (*func)(double **, int *, int *);
char name[];
};
struct operator *operators = NULL;
static int register_operator(const char *name,
int (*func)(double **, int *, int *))
{
const size_t namelen = (name) ? strlen(name) : 0;
struct operator *curr;
/* Make sure name and func are valid. */
if (!namelen || !func)
return EINVAL;
/* See if name is already used. */
for (curr = operators; curr != NULL; curr = curr->next)
if (!strcmp(name, curr->name))
return EEXIST;
/* Allocate memory for this operator. */
curr = malloc(namelen + 1 + sizeof (struct operator));
if (!curr)
return ENOMEM;
/* Copy function pointer and name. */
curr->func = func;
memcpy(curr->name, name, namelen + 1); /* Include terminating '\0'. */
/* Prepend to list. */
curr->next = operators;
operators = curr;
/* Success. */
return 0;
}
static int list_operators(double **stack, int *count, int *maxcount)
{
struct operator *curr;
fprintf(stderr, "Known operators:\n");
for (curr = operators; curr != NULL; curr = curr->next)
fprintf(stderr, "\t'%s'\n", curr->name);
return 0;
}
int main(int argc, char *argv[])
{
double *stack = NULL;
int count = 0;
int maxcount = 0;
int arg;
if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s ./plugin ... NUMBER [ OPERATOR | NUMBER ] ...\n", argv[0]);
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
if (register_operator("list", list_operators)) {
fprintf(stderr, "Failed to register built-in 'list' operator.\n");
return EXIT_FAILURE;
}
for (arg = 1; arg < argc; arg++) {
struct operator *op;
double val;
char dummy;
/* Check if argument is a plugin path, starting with "./". */
if (argv[arg][0] == '.' && argv[arg][1] == '/') {
void *handle = dlopen(argv[arg], RTLD_NOW);
if (handle) {
int (*func)(int (*)(const char *, int (*)(double **, int *, int *))) = dlsym(handle, "plugin_init");
if (func) {
int failure = func(register_operator);
if (failure) {
fprintf(stderr, "%s: Operator registration failed: %s.\n", argv[arg], strerror(failure));
return EXIT_FAILURE;
}
} else
dlclose(handle);
continue;
}
}
/* Check if argument is a known operator. */
for (op = operators; op != NULL; op = op->next)
if (!strcmp(op->name, argv[arg]))
break;
if (op) {
int failure = op->func(&stack, &count, &maxcount);
if (failure) {
fprintf(stderr, "%s: Cannot apply operator: %s.\n", argv[arg], strerror(failure));
return EXIT_FAILURE;
}
continue;
}
/* Parse as a number. */
if (sscanf(argv[arg], " %lf %c", &val, &dummy) != 1) {
fprintf(stderr, "%s: Unknown operator.\n", argv[arg]);
return EXIT_FAILURE;
}
/* Make sure stack has enough room for an additional number. */
if (count >= maxcount) {
double *temp;
maxcount = (count | 255) + 257;
temp = realloc(stack, maxcount * sizeof *stack);
if (!temp) {
fprintf(stderr, "%s.\n", strerror(ENOMEM));
return EXIT_FAILURE;
}
stack = temp;
}
/* Push val to top of stack. */
stack[count++] = val;
}
for (arg = 0; arg < count; arg++)
printf("[%d] = %g\n", arg + 1, stack[arg]);
return (count == 1) ? EXIT_SUCCESS : EXIT_FAILURE;
}
struct operator *operators
是已知运营商的全球单链表。除非已经采用了名称,否则register_operator()
函数会将新运算符添加到列表中。
唯一的内置运算符是list
,因此您可以列出已知的运算符。
让我们看看几个不同的插件实现。首先, plugin_basic.c :
#include <errno.h>
static int op_add(double **stack, int *count, int *maxcount)
{
if (*count < 2)
return EINVAL;
(*stack)[*count - 2] = (*stack)[*count - 1] + (*stack)[*count - 2];
(*count)--;
return 0;
}
static int op_sub(double **stack, int *count, int *maxcount)
{
if (*count < 2)
return EINVAL;
(*stack)[*count - 2] = (*stack)[*count - 1] - (*stack)[*count - 2];
(*count)--;
return 0;
}
static int op_mul(double **stack, int *count, int *maxcount)
{
if (*count < 2)
return EINVAL;
(*stack)[*count - 2] = (*stack)[*count - 1] * (*stack)[*count - 2];
(*count)--;
return 0;
}
static int op_div(double **stack, int *count, int *maxcount)
{
if (*count < 2)
return EINVAL;
(*stack)[*count - 2] = (*stack)[*count - 1] / (*stack)[*count - 2];
(*count)--;
return 0;
}
int plugin_init(int (*register_operator)(const char *name,
int (*func)(double **, int *, int *)))
{
int failure;
if ((failure = register_operator("+", op_add)))
return failure;
if ((failure = register_operator("-", op_sub)))
return failure;
if ((failure = register_operator("x", op_mul)))
return failure;
if ((failure = register_operator("/", op_div)))
return failure;
return 0;
}
提供四个基本操作符+
,-
,x
和/
;和 plugin_sincos.c :
#include <math.h>
#include <errno.h>
static int op_sin(double **stack, int *count, int *maxcount)
{
if (*count < 1)
return EINVAL;
(*stack)[*count - 1] = sin((*stack)[*count - 1]);
return 0;
}
static int op_cos(double **stack, int *count, int *maxcount)
{
if (*count < 1)
return EINVAL;
(*stack)[*count - 1] = sin((*stack)[*count - 1]);
return 0;
}
int plugin_init(int (*register_operator)(const char *name,
int (*func)(double **, int *, int *)))
{
int failure;
if ((failure = register_operator("sin", op_sin)))
return failure;
if ((failure = register_operator("cos", op_cos)))
return failure;
return 0;
}
提供sin
和cos
个功能。
因为只需要动态导出plugin_init()
函数,所以让我们添加一个公共符号文件 plugin.syms :
{
plugin_init;
};
请注意,我已明确标记了许多函数static
。这是为了避免命名空间污染:确保它们对其他编译单元不可见,否则可能会导致冲突。 (虽然符号文件应该确保只动态导出plugin_init()
,但static
提醒我作为程序员,在任何情况下都不应该导出函数。)
最后, Makefile 将它们全部绑定在一起:
CC := gcc
CFLAGS := -Wall -O2
LD := $(CC)
LDFLAGS := -lm -ldl
.PHONY: clean all
all: rpcalc basic.plugin sincos.plugin
clean:
rm -f rpcalc basic.plugin sincos.plugin
rpcalc: application.c
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@
basic.plugin: plugin_basic.c
$(CC) $(CFLAGS) -fPIC -shared $^ $(LDFLAGS) -Wl,-dynamic-list,plugin.syms -Wl,-soname,$@ -o $@
sincos.plugin: plugin_sincos.c
$(CC) $(CFLAGS) -fPIC -shared $^ $(LDFLAGS) -Wl,-dynamic-list,plugin.syms -Wl,-soname,$@ -o $@
请注意,预期的行必须以 Tab 开头,而不是以8个空格开头。如果您不确定,请运行sed -e 's|^ *|\t|' -i Makefile
进行修复。
编译计算器及其插件:
make clean all
如果你跑
./rpcalc list
它会告诉您唯一支持的运算符是list
本身。但是,如果你运行例如。
./rpcalc ./basic.plugin list
./rpcalc ./*.plugin list
它将显示插件实现的运算符。
这也是一个有效的计算器。如果你想计算,比如sin(0.785398) x cos(0.785398)
,请运行
./rpcalc ./*.plugin 0.785398 sin 0.785398 cos x
,程序将输出[1] = 0.5
,正如您所期望的那样。