语言:C
操作系统:Red Hat EL
以“例如”开头:
假设我有两个库:libJUMP.so
和libSIT.so
。
跳转包含函数jump()
,同样,sit包含函数sit()
。
我有一个应用程序,我想提供给不同的人;他们可以获得jump()
功能,sit()
功能,或者两者兼得。但是,如果可能的话,我不想使用#ifdef
。libJUMP.so
的标题:
#ifndef JUMP_H_
#define JUMP_H_
#define JUMP_ENABLED
void jump();
#endif /* JUMP_H_ */
libSIT.so
的标题:#ifndef SIT_H_
#define SIT_H_
#define SIT_ENABLED
void sit();
#endif /* SIT_H_ */
我有一个申请:
#include "jump.h"
#include "sit.h"
int main()
{
// #ifdef JUMP_ENABLED
jump();
// #endif /* JUMP_ENABLED */
// #ifdef SIT_ENABLED
sit();
// #endif /* SIT_ENABLED */
}
所以:
有没有办法不使用
#ifdef
?有更好的办法吗?我听说我们可以用这两个so库进行编译,如果在目标系统上运行应用程序时缺少一个so库,它可以自动排除该功能(使用
dlopen()
和dlsym()
?)如果这是真的,有什么简单的例子吗?如果可能的话,我的代码来自上面的一个例子:d?如果这是一个愚蠢的问题,或者根本不可能,请随时告诉我。如果有类似的问题,这将被视为一个副本,让我知道,我将删除这篇文章。
最佳答案
考虑这三个文件。首先,jump.c
:
#include <stdio.h>
int jump(const double height)
{
fflush(stdout);
fprintf(stderr, "Jumping %.3g meters.\n", height);
fflush(stderr);
return 0;
}
其次,
sit.c
:#include <stdio.h>
int sit(void)
{
fflush(stdout);
fprintf(stderr, "Sitting down.\n");
fflush(stderr);
return 0;
}
第三,使用上述的一个或两个,取决于它们是否在当前工作目录中存在(分别为
example.c
或libjump.so
):#include <stdio.h>
#include <dlfcn.h>
static const char *jump_lib_path = "./libjump.so";
static int (*jump)(const double) = NULL;
static const char *sit_lib_path = "./libsit.so";
static int (*sit)(void) = NULL;
static void load_dynamic_libraries(void)
{
void *handle;
handle = dlopen(jump_lib_path, RTLD_NOW | RTLD_LOCAL);
if (handle) {
jump = dlsym(handle, "jump");
/* If no jump symbol, we don't need the library at all. */
if (!jump)
dlclose(handle);
}
handle = dlopen(sit_lib_path, RTLD_NOW | RTLD_LOCAL);
if (handle) {
sit = dlsym(handle, "sit");
/* If no sit symbol, the library is useless. */
if (!sit)
dlclose(handle);
}
}
int main(void)
{
int retval;
load_dynamic_libraries();
if (jump) {
printf("Calling 'jump(2.0)':\n");
retval = jump(2.0);
printf("Returned %d.\n\n", retval);
} else
printf("'jump()' is not available.\n\n");
if (sit) {
printf("Calling 'sit()':\n");
retval = sit();
printf("Returned %d.\n\n", retval);
} else
printf("'sit()' is not available.\n\n");
return 0;
}
让我们首先编译并运行示例程序:
gcc -Wall -O2 example.c -ldl -o example
./example
程序输出的jump()或sit()都不可用。让我们将jump.c编译到动态库libjump.so中,然后再次运行示例:
gcc -Wall -O2 -fPIC -shared jump.c -Wl,-soname,libjump.so -o libjump.so
./example
现在,jump()函数可以工作了。让我们也编译sit.c,最后运行这个示例:
gcc -Wall -O2 -fPIC -shared jump.c -Wl,-soname,libsit.so -o libsit.so
./example
在这里,两个函数都被调用,一切都正常。
在
libsit.so
中,example.c
和jump
是函数指针。我们将它们初始化为空,以便使用sit
检查if (jump)
是否指向有效函数。jump
函数使用load_dynamic_libraries()
和dlopen()
获取函数指针。注意,如果动态库成功打开,并且找到了必要的符号,则不会dlsym()
它,因为我们希望将动态库保存在内存中。(如果它看起来不是我们想要的那种库,我们只会dlclose()
。)如果要避免
dlclose()
和if (jump)
子句,可以使用int unsupported_jump(const double height)
{
return ENOTSUP;
}
int unsupported_sit(void)
{
return ENOTSUP;
}
在
if (sit)
结束时,将函数转移到stub而不是空指针,即。if (!jump)
jump = unsupported_jump;
if (!sit)
sit = unsupported_sit;
注意,类函数接口最容易使用,因为函数指针充当有效的原型。如果需要对象,我建议使用getter函数。只要记住
load_dynamic_libraries()
返回指向对象的指针,对象就可以正常工作;使用getter函数,这在getter函数指针类型中是显式的。插件接口通常只有一个函数(例如,
dlsym()
),用于填充函数和对象指针的结构。应用程序提供它使用的结构的版本,插件函数返回成功或失败,这取决于它是否可以填充结构以适应该版本。由于插件通常存储在单个目录中(
int properties(struct plugin *const props, const int version)
非常常见),您可以使用/usr/lib/yourapp/plugins/
和opendir()
逐个扫描插件目录中的文件名,对每个文件名进行readdir()
扫描,获取dlopen()
函数指针,并调用它以查看插件提供的服务类型,从而轻松加载所有插件;通常创建数组或插件结构的链接列表。如您所见,所有这些在Linux中都非常、非常简单和直接。如果您想要一个特定的插件功能示例,我建议您将其作为一个单独的问题提出,并详细说明接口应该公开什么样的功能——确切的数据结构和函数原型在很大程度上取决于我们手头有什么样的应用程序。
问题评论?