Search code examples
c++linuxfusedlopendlsym

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


Suppose I have the following:

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

and

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

Resulting in lib1.so and lib2.so.

So, my question is:

#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);
}

My real-world issue is - how do I load at run-time libfuse2 or libfuse3 and execute fuse_main_real, given the fact it has fuse_operations* parameter, which is a struct with function pointers, but with different types for the two versions?

Edit: I don't believe this breaks the one-definition rule - only one of them will be linked at the same time, never both of them. (I'm also not sure if ODR is broken if both are loaded, as they are manually used, but that's another topic)


Solution

  • To the extent that the standard can be said to apply to dynamic objects, you’re right that there’s no ODR violation between multiple objects that are never loaded together (because there is no program that contains both definitions). The same cannot be said for your program if it contains a definition of each class type (obviously) or contains a definition of one and (the runtime choice is that it) loads the dynamic object that uses the other. (With DLLs the situation is stranger: types are shared but not their member functions, static data members, or RTTI symbols.)

    The answer is to keep those definitions separate: wrap each dynamic object in one of your own (linked against it with a simple -l) that exposes a common interface (which must be possible for the question to make sense). Load one or the other of those in your main program and call it using the unique type(s) #included from that interface.