1

I want to load libraries at run time. I have a base class "Base" and two derived classes "Derived1" and "Derived2" which should be loaded at runtime. I am using the approach from this answer with some small modifications. The code compiles perfectly when I define only one derived class, but it fails to compile when several derived classes are defined. The compilation error is the following:

multiple definitions of 'create'

I think that the problem is "C" doesn't allow overloading of function names. How would one solve this problem?

The important point is that since I don't know how many .so files exist, I want to have one handler for all .so files. The details of the code are given below:

base.hpp:

class Base {
public:
    virtual ~Base() {}
    virtual void foo() const = 0;
};

using Base_creator_t = Base *(*)();

derived1.hpp:

#include "Interface.hpp"

class Derived1: public Base {
public:
    void foo() const override {}
};

extern "C" {
Base * create() {
    return new Derived1;
}
}

derived2.hpp:

#include "Interface.hpp"

class Derived2: public Base {
public:
    void foo() const override {}
};

extern "C" {
Base * create() {
    return new Derived2;
}
}

Dynamic shared library handler: Derived_factory.hpp:

#include <dlfcn.h>

class Derived_factory {
public:
    Derived_factory(char* path) {
        handler = dlopen(path, RTLD_NOW);
        if (! handler) {
            throw std::runtime_error(dlerror());
        }
        Reset_dlerror();
        creator = reinterpret_cast<Base_creator_t>(dlsym(handler, "create"));
        Check_dlerror();
    }

    std::unique_ptr<Base> create() const {
        return std::unique_ptr<Base>(creator());
    }

    ~Derived_factory() {
        if (handler) {
            dlclose(handler);
        }
    }

private:
    void * handler = nullptr;
    Base_creator_t creator = nullptr;

    static void Reset_dlerror() {
        dlerror();
    }

    static void Check_dlerror() {
        const char * dlsym_error = dlerror();
        if (dlsym_error) {
            throw std::runtime_error(dlsym_error);
        }
    }
};

main.cpp:

#include "Derived_factory.hpp"
int main(){
   Derived_factory factoryOne("Derived1.so");
      std::unique_ptr<Base> baseOne = factoryOne.create();
      baseOne->foo();
   Derived_factory factoryTwo("Derived2.so");
     std::unique_ptr<Base> baseTwo = factoryTwo.create();
     baseTwo->foo();
   return 0;
}
fva
  • 33
  • 4
  • You *define* (implement) the `create` function in a header file. Each source-file where this header-file is included in will have that definition. If you include the header file in two different source files that are both linked together into a single library or executable, you will have multiple definition errors. *Declare* the function in the header files, and *define* in a single source file (one for each library). – Some programmer dude Oct 01 '20 at 10:52
  • `derived1.hpp` includes `interface.hpp`. Do you mean `base.hpp`? Post your _actual_ code please. – Ted Lyngmo Oct 01 '20 at 10:54
  • As you say, C does not support function overloading. You will need to use distinct names for each variant of the function (`create_Derived1()`, `create_Derived2()`, etc). – Peter Oct 01 '20 at 10:57

2 Answers2

3

The problem is not extern "C". The problem is you have multiple definitions of the same function.

Base * create() is indistinguishable from Base * create()

Caleth
  • 52,200
  • 2
  • 44
  • 75
1

What you're trying to do is to have the same function in two different loadable modules. But what you're doing instead is putting both implementations (with the same name and signature!) of this function into the main module, which of course results in multiple definition error.

What you should do instead is put the create functions into the *.cpp files, i.e. derived1.cpp and derived2.cpp, omit their definitions from the *.hpp files, and compile the shared objects from these *.cpp files. I've modified your project to achieve this, see the live demo.

Ruslan
  • 18,162
  • 8
  • 67
  • 136