如何动态加载和调用具有库特定类型作为函数参数的符号

How to dynamically load and call a symbol which has a library-specific type as a function parameter

假设我有以下内容:

// lib1.h
struct S 
{
    int x;
};
void f(S* s);  // defined in .cpp

// lib2.h
struct S 
{
    int x;
    int y;
};
void f(S* s);  // defined in .cpp

导致 lib1.solib2.so

那么,我的问题是:

#include <dlfcn.h>

int main()
{
    const auto lib_to_use = <some application level logic, resulting in lib1.so or lib2.so>;
    const auto lib = dlopen(lib_to_use, RTLD_LAZY);
    assert(lib);

    const auto func = dlsym(lib, "f");
    assert(func);

    // how do I call `func`, given the fact it takes different, library-specific type `S`?

    dlclose(lib);
}

我的现实问题 是 - 如何在 运行 时间 libfuse2libfuse3 加载并执行 fuse_main_real,鉴于它有 fuse_operations* 参数,这是一个带有函数指针的 struct,但两个版本的类型不同?

编辑:我不认为这会打破单一定义规则——只有其中一个会同时链接,绝不会同时链接。 (我也不确定如果两者都加载,ODR​​ 是否被破坏,因为它们是手动使用的,但这是另一个话题)

就可以说该标准适用于动态对象而言,您是对的,在从未一起加载的多个对象之间没有 ODR 违规(因为没有 程序 包含两个定义)。如果您的程序包含每个 class 类型的定义(显然)或包含一个定义并且(运行时选择是它)加载使用另一个的动态对象,则不能对您的程序说同样的话。 (对于 DLL,情况比较奇怪:共享类型但不共享它们的成员函数、静态数据成员或 RTTI 符号。)

答案是将这些定义分开:将每个动态对象包装在你自己的一个对象中(用一个简单的-l链接到它)公开一个common interface(这个问题必须是可行的)。在主程序中加载 those 中的一个,并使用该接口中的唯一类型 #included 调用它。

how do I call func, given the fact it takes different, library-specific type S?

显然,您不能在主程序中输入 S 同时匹配 lib1 Slib2 S。所以你必须声明两个不同的类型:S1S2.

其余的都是微不足道的:

int version = which_version_should_I_use();
if (version == V1) {
  void *h = dlopen("lib1.so", RTLD_LAZY);
  void (*fn)(S1*) = (void (*)(S1*))dlsym(h, "f");
  assert(fn != NULL);
  S1 s = ...;
  fn(&s);
} else {
  // must be V2
  void *h = dlopen("lib2.so", RTLD_LAZY);
  void (*fn)(S2*) = (void (*)(S2*))dlsym(h, "f");
  assert(fn != NULL);
  S2 s = ...;
  fn(&s);
}