I'm writing a program (macOS, clang++ compiler, only AppleSilicon at the moment) that I can extend later by providing custom plugins (dynamic library, loaded at runtime) which use main program's public interface.
test.hpp - public interface:
#if defined(MAGIC_PLUGIN)
# define MAGIC_EXPORT /* nothing */
#else
# define MAGIC_EXPORT __attribute__((visibility("default")))
#endif
MAGIC_EXPORT
void testCall();
test.cpp - main programm:
#include <stdio.h>
#include <dlfcn.h>
#include "test.hpp"
// Declare a function to call from a loaded plugin
typedef void (* plugin_call_func)(void);
int main(int argc, char** argv) {
// Load library
const char* libraryName = "plugin.dylib";
void* library = dlopen(libraryName, RTLD_NOW);
if (library == nullptr) {
printf("Cannot open library\n");
return 1;
}
// Get function from loaded library
plugin_call_func pluginCall = reinterpret_cast<plugin_call_func>(
dlsym(library, "pluginCall"));
if (pluginCall == nullptr) {
printf("Cannot find the pluginCall function\n");
return 2;
}
// Execute loaded function
pluginCall();
// Forget function and close library
pluginCall = nullptr;
auto libraryCloseResult = dlclose(library);
if (libraryCloseResult != 0) {
printf("Cannot close library\n");
return 3;
}
return 0;
}
// Public function, should be called from a plugin
void testCall() {
printf("Test call\n");
}
plugin.cpp - plugin's source:
#define MAGIC_PLUGIN
#include <stdio.h>
#include "test.hpp"
__attribute__((visibility("default")))
extern "C" void pluginCall(void) {
printf("Plugin call\n");
testCall();
}
First, I compile main app:
clang++ -std=c++20 -fvisibility=hidden -target arm64-apple-macos12 test.cpp -o test
The nm --defined-only test shows these symbols:
0000000100003ee4 T __Z8testCallv
0000000100000000 T __mh_execute_header
0000000100003dcc t _main
Mangled __Z8testCallv is what I need. Everything looks good so far. But then I try to compile the plugin as dynamic library...
clang++ -std=c++20 -fvisibility=hidden -dynamiclib -g -current_version 0.1 -target arm64-apple-macos12 plugin.cpp -o plugin.dylib
and get this error:
Undefined symbols for architecture arm64:
"testCall()", referenced from:
_pluginCall in plugin-38422c.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Well, it's kind of fair, I can understand this, because the dynamic library does not know that testCall is somewhere implemented. So I want to say it that it does not have to worry about testCall's existence.
I tried to research how to do this, looked up man pages, read tons of stack overflow answers, and what I only found that works is adding these flags to linker:
-Wl,-undefined,dynamic_lookup
It works, the library compiles and the app works as expected. But I don't really want to use dynamic_lookup because it will mark every undefined symbol in the library as resolved, which may lead to some bad consequences. I want to tell the linker only about existence of the main program's public symbols.
What am I missing? Is there any better solution than dynamic_lookup?
Your best bet is to manually do the work that's done by the library loader. That is: populating function pointers. After all, the plugin->main binding is already done manually, so doing the same thing the other way around makes sense.
You can make this process essentially transparent by carefully crafting the header shared by the plugin and main application. The only tricky part is handling ODR for plugins that are composed of multiple source files.
Since this is a C++ question, here's what it could look like with a RAII wrapper. The ODR conundrum is handled via the PLUGIN_MAIN macro that should only be defined in one of a plugin's sources.
test_plugin.hpp
using pluginCall_fn = void(*)();
using testCall_fn = void(*)();
#if !defined(MAIN_APPLICATION)
#if defined(PLUGIN_MAIN)
#define EXPORTED_FROM_MAIN __attribute__((visibility("default")))
#else
#define EXPORTED_FROM_MAIN __attribute__((visibility("default"))) extern
#endif
extern "C" {
// Declare symbols provided by the plugin
__attribute__((visibility("default"))) void pluginCall();
// etc...
// Declare/define pointers that will be populated by the main application
EXPORTED_FROM_MAIN testCall_fn testCall;
// etc...
}
#undef EXPORTED_FROM_MAIN
#else // In the main app.
#include <stdexcept>
// Declare "symbols" provided by the main application
void testCall();
// Utility class to load/unload a dynamic library.
// Probably belongs in its own header...
struct loaded_library final {
loaded_library(const char* libName)
: handle_(dlopen(libName, RTLD_NOW)) {
if(!handle_) {
throw std::runtime_error("failed to load plugin");
}
}
loaded_library(const loaded_library&) = delete;
loaded_library& operator=(const loaded_library&) = delete;
loaded_library(loaded_library&& rhs) : handle_(rhs.handle_) {
rhs.handle_ = nullptr;
}
loaded_library& operator=(loaded_library&& rhs) {
handle_ = rhs.handle_;
rhs.handle_ = nullptr;
return *this;
}
~loaded_library() {
if(handle_) {
dlclose(handle_);
}
}
template<typename T>
T get_symbol(const char* symbol) {
T result = reinterpret_cast<T>(dlsym(handle_, symbol));
if(!result) {
throw std::runtime_error("missing symbol");
}
return result;
}
private:
void* handle_;
};
// Plugin interface.
struct loaded_plugin final {
loaded_plugin(const char* libName)
: lib_(libName) {
// Load functions from plugin
pluginCall = lib_.get_symbol<pluginCall_fn>("pluginCall");
// ...
// Assign callbacks to plugin
*lib_.get_symbol<testCall_fn*>("testCall") = &testCall;
// ...
// Call the plugin's init function here if applicable.
}
pluginCall_fn pluginCall;
private:
loaded_library lib_;
};
#endif
plugin.cpp
#define PLUGIN_MAIN
#include "test_plugin.hpp"
#include <stdio.h>
void pluginCall() {
printf("Plugin call\n");
testCall();
}
test.cpp
#define MAIN_APPLICATION
#include "test_plugin.hpp"
int main(int argc, char** argv) {
const char* libraryName = "plugin.dylib";
loaded_plugin plugin(libraryName);
plugin.pluginCall();
}
// Public function, should be called from a plugin
void testCall() {
printf("Test call\n");
}
You may find that this code is a bit on the fragile side of things, since a few different portions of test_plugin.hpp need to be kept in sync.
This can be worked around with the use of X-Macros, at the cost of confusing IDEs and hurting code legibility. I wouldn't go down that road until the APIs in question become unwieldingly large.
Related
I am trying to use the prebuilt tensorflow c-api with a cpp-wrapper in my package. Unfortunately I am getting a segfault error. After searching I found out that there is a Git Issue about it: Linking to both tensorflow and protobuf causes segmentation fault during static initializers.
So I could resolve the Issues by doing:
The workarounds sound like (1) only load the second copy of protobuf in a .so that does not use TensorFlow, and you can use both that .so and TensorFlow's .so from your main program, (2) instead of linking normally, dlopen() TensorFlow with RTLD_DEEPBIND set so TensorFlow prefers its own symbols.
I want to try to load the library using dlopen(), unfortunately I have never used it and I cannot find a good example of its use. How would I use it in my case and where?
My initial understanding:
Load it into the header of my cpp-wrapper since they use the tensorflow functions/header?
But than do I really need to change every single function of the cpp-wrapper header, and put a reference to the loaded handler?
A little dlopen example:
Some lib written in C, we call it foobar.so
#include <stdio.h>
void foo() { printf("foo\n"); }
void bar() { printf("bar\n"); }
gcc -o foobar.so foobar.c -shared -fPIC
A (foobar) wrapper in C++
#include <dlfcn.h>
struct FooBar {
typedef void (*foo_handle)(); //same signature as in the lib
typedef void (*bar_handle)(); //same signature as in the lib
foo_handle foo;
bar_handle bar;
void *foobar_lib;
FooBar() {
//probably best not in the constructor, but we're lazy (for now)
//and of course no error checking (so don't)
foobar_lib = dlopen("./foobar.so", RTLD_LAZY | RTLD_DEEPBIND);
foo = reinterpret_cast<foo_handle>(dlsym(foobar_lib, "foo"));
bar = reinterpret_cast<bar_handle>(dlsym(foobar_lib, "bar"));
}
~FooBar() {
dlclose(foobar_lib);
}
};
int main()
{
FooBar foobar;
foobar.foo();
foobar.bar();
return 0;
}
or
#include <dlfcn.h>
typedef void (*foo_handle)(); //same signature as in the lib
typedef void (*bar_handle)(); //same signature as in the lib
foo_handle foo;
bar_handle bar;
void *foobar_lib;
int main()
{
foobar_lib = dlopen("./foobar.so", RTLD_LAZY | RTLD_DEEPBIND);
foo = reinterpret_cast<foo_handle>(dlsym(foobar_lib, "foo"));
bar = reinterpret_cast<bar_handle>(dlsym(foobar_lib, "bar"));
foo();
bar();
dlclose(foobar_lib);
return 0;
}
g++ -ldl -o foobar_test foobar_test.cpp
For each and every symbol you use from the original lib, you will have to call dlsym to obtain its address.
So yes, it is a tedious work, since you're implementing a wrapper to provide the full functionality of the underlying library.
For example, opengl developers knows very well what that means. Luckily, over the years there are now many tools available, which helps to load the myriads of symbols in runtime with no/less effort. Maybe there is something similar to tensorflow.
I wanted to test what happens if executable and library share different versions of a library,
i.e. different classes with the same name. Idea: make a test function which
is called once from the executable directly, and once with the code from the library:
MWE:
base.h
defines an abstract plugin class, which can generate a port object (of type base)
struct base
{
virtual void accept(struct visitor& ) {}
virtual void test() = 0;
virtual ~base() {}
};
struct visitor
{
virtual void visit(struct base& ) {}
};
struct plugin
{
virtual ~plugin() {}
virtual base& port() = 0;
virtual void test() = 0;
};
typedef plugin* (*loader_t) (void);
plugin.cpp
defines a derived plugin class, which can return a derived port (mport)
#include <iostream>
#include "base.h"
struct mport : public base
{
void accept(struct visitor& ) override {}
void test() override { std::cout << "plugin:test" << std::endl; }
virtual ~mport() = default;
};
struct port_failure_plugin : public plugin
{
void test() override final { inp.test(); }
virtual ~port_failure_plugin() {}
private:
mport inp;
base& port() override { return inp; }
};
extern "C" {
const plugin* get_plugin() { return new port_failure_plugin; }
}
host.cpp
defines a derived port class with the same name (mport)
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <dlfcn.h>
#include "base.h"
struct mport : public base
{
#ifdef ACCEPT_EXTERN
void accept(struct visitor& ) override;
#else
void accept(struct visitor& ) override {}
#endif
void test() override { std::cout << "host:test" << std::endl; }
};
#ifdef ACCEPT_EXTERN
void mport::accept(struct visitor& ) {}
#endif
int main(int argc, char** argv)
{
assert(argc > 1);
const char* library_name = argv[1];
loader_t loader;
void* lib = dlopen(library_name, RTLD_LAZY | RTLD_LOCAL);
assert(lib);
*(void **) (&loader) = dlsym(lib, "get_plugin");
assert(loader);
plugin* plugin = (*loader)();
base& host_ref = plugin->port();
host_ref.test(); // expected output: "host:test"
plugin->test(); // expected output: "plugin:test"
return EXIT_SUCCESS;
}
Compile e.g.:
g++ -std=c++11 -DACCEPT_EXTERN -shared -fPIC plugin.cpp -o libplugin.so
g++ -std=c++11 -DACCEPT_EXTERN -ldl -rdynamic host.cpp -o host
The complete code is on github (try make help)
In order to let the host run test "like the plugin does",
it calls a virtual function, which is implemented in the plugin. So I expect that test is called
once from the object code of the host executable (expectation: "host:test")
once from the object code of the plugin library (expectation: "plugin:test")
The reality looks different:
In all (of the following) cases, both outputs are equal (2x"host:test" or 2x"plugin:test")
Compile host.cpp with -rdynamic, and without -DACCEPT_EXTERN the test calls output "plugin:test"
Compile host.cpp with -rdynamic, and with -DACCEPT_EXTERN (see Makefile), and the test calls call "host:test"
Compile host.cpp without -rdynamic, and the test calls output plugin:test (both intern and extern)
Questions:
Is it even possible to call both versions of mport::test (e.g. executable and library)?
Why does -rdynamic change the behavior?
Why does -DACCEPT_EXTERN affect the behavior?
The thing here is you are violating the one definition rule.
Your two versions of mport::test have the same declaration, yet they do not have the same definition.
But you are doing it a dynamic linking time. Now, the C++ standard does not concern itself with dynamic loading. We have to turn to the x86 ELF ABI for further detail.
Long story short, the ABI supports a technique known as symbol interposition, which allows substituting symbols dynamically and still see consistent behavior. This is what you are doing here, though inadvertently.
You can check it manually:
spectras#etherhop$ objdump -R libplugin.so |grep test
0000000000202cf0 R_X86_64_64 _ZN19port_failure_plugin4testEv##Base
0000000000202d10 R_X86_64_64 _ZN5mport4testEv##Base
0000000000203028 R_X86_64_JUMP_SLOT _ZN5mport4testEv##Base
Here you see that, in the shared object, all uses of mport::test have a relocation entry. All calls go through the PLT.
spectras#etherhop$ objdump -t host |grep test
0000000000001328 w F .text 0000000000000037 _ZN5mport4testEv
Here you see that host does export the symbol (because of -rdynamic). So when dynamic linking libplugin.so, the dynamic linker will use the mport::test of your main program.
That's the basic mechanism. That's also why you don't see this without -rdynamic: the host no longer exports its own version, so the plugin uses its own.
How to fix?
You can avoid all this leakage by hiding symbols (a good practice in general, it makes for faster loading and avoids name clashes).
add -fvisibility=hidden when compiling.
manually export your get_plugin function by prepending __attribute__ ((visibility("default"))) to the line. Tip: that's compiler-specific stuff, it's a good idea to make it a macro somewhere.
#define EXPORT __attribute__((visibility("default")))
// Later:
EXPORT const plugin* get_plugin() { /* stuff here */ }
Thus:
spectras#etherhop$ g++ -std=c++11 -fvisibility=hidden -shared -fPIC plugin.cpp -o libplugin.so
spectras#etherhop$ g++ -std=c++11 -fvisibility=hidden -rdynamic host.cpp -ldl -o host
spectras#etherhop$ ./host ./libplugin.so
plugin:test
plugin:test
Another option would be to simply make the class static by enclosing it within an anonymous namespace. This will work in your simple case, but it is not good if your plugin is made of multiple translation units.
As for your expectations of having a different result on your two lines, you're getting a base-reference to a derived class, why would you expect anything other than the appropriate virtual override to be called?
I have a dll and I even have the header files for the dll, but I don't have the implementation neither the lib file for the dll. I try to load up the dll with the QLibrary class and get class instance from it. I successfully retrieved the class after 2hours, but when I try to call a function on the object I get unresolved external symbol which tells me that the dll did not exported properly. For simplicity I re-created the issue with the following sources:
DLL-Project (testlibrary_global.hpp):
#ifndef TESTLIBRARY_GLOBAL_HPP
#define TESTLIBRARY_GLOBAL_HPP
#include <QtCore/qglobal.h>
#if defined(TESTLIBRARY_LIBRARY)
# define TESTLIBRARYSHARED_EXPORT Q_DECL_EXPORT
#else
# define TESTLIBRARYSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // TESTLIBRARY_GLOBAL_HPP
DLL-Project (testlibrary.hpp):
#ifndef TESTLIBRARY_HPP
#define TESTLIBRARY_HPP
#include "testlibrary_global.hpp"
#include <QDebug>
class TESTLIBRARYSHARED_EXPORT TestLibrary {
public:
TestLibrary();
~TestLibrary();
void Test();
};
extern "C" TESTLIBRARYSHARED_EXPORT TestLibrary* getInstance();
#endif // TESTLIBRARY_HPP
DLL-Project (testlibrary.cpp):
#include "testlibrary.hpp"
TestLibrary::TestLibrary() {
qDebug() << "Constructor called!";
}
TestLibrary::~TestLibrary() {
qDebug() << "Destructor called!";
}
void Test() {
qDebug() << "Hello from library!";
}
TestLibrary *getInstance() {
return new TestLibrary();
}
This is very straight forward, does not contain anything fancy really. As you can see I kept the class default as the QtCreator does did not change anything, except added another function with extern "C" and the export defined in global. the purpose of this would be to get an object from the dll itself, (since I have the .h and .dll nothing else). Now for the loader application, again dirty yet simple basic stuff:
#include <QCoreApplication>
#include <QLibrary>
#include <QDebug>
#include "testlibrary.hpp"
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
QString libPath = QString("C:/Users/johorvat/Documents/QTProjects/build-TestLibrary-Desktop_Qt_5_2_0_MSVC2010_32bit_OpenGL-Debug/debug/TestLibrary.dll");
QLibrary lib(libPath);
bool loaded = lib.load();
QString error = lib.errorString();
qDebug() << "Loaded: " << loaded;
typedef TestLibrary* (*Prototype)();
Prototype Func = (Prototype) lib.resolve("getInstance");
if (Func) {
TestLibrary* tl = Func();
if (tl) {
qDebug() << "Yey, I gotta clazz!";
}
}
return a.exec();
}
I added the header file to the project because i have it anyway. I used QLibrary to load up the dll and retrieved the getInstance method from it with which I could get an instance of the TestLibrary class. However if I try to call the Test() method of TestLibrary within the if(tl) { ... } i get an unresolved external symbol error message that tells me it can't find the definition of the Test method.
What am I missing in here?
P.S.: I won't get lib files so let's focus on the problem with the dll loading :).
Regards,
Joey
Well since you've written void Test() { in your .cpp file and not void TestLibrary::Test { your function isn't being defined and so it isn't exported at all.
EDIT:
After this code like that works fine and prints "Hello" in qDebug (dll should be compiled in debug, I failed on that the first time)
QFunctionPointer raw = lib.resolve("?Test#TestLibrary##QEAAXXZ");
TestPrototype testFunc;
*(QFunctionPointer*) &testFunc = raw;
(tl->*testFunc) ();
Decorated function name is not very nice but I don't know what exactly can be done about it :) And also you'll get differently mangled names with different compilers so using Qt in this case will not be cross-platform anyway.
I have a noob question here.
I'm getting my head around the C++ structure and syntax and I've hit a bit of a wall.
I know I am missing something from my concept. So first a little code to help describe the situation.
Control.h
#pragma once
#ifndef CONTROL_H
#define CONTROL_H
class Control
{
public:
Control();
~Control();
private:
public:
};
#endif /*CONTROL_H*/
Control.cpp
#include "Control.h"
#include "Hello.h"
Hello helloObj;
Control::Control()
{
}
Control::~Control()
{
}
int main()
{
int a = helloObj.HelloWorld();
return 0;
}
Hello.h
#pragma once
#ifndef HELLO_H
#define HELLO_H
class Hello
{
public:
Hello();
~Hello();
private:
public:
int HelloWorld(void);
};
#endif /*HELLO_H*/
Hello.cpp
#include "Hello.h"
Hello::Hello()
{
}
Hello::~Hello()
{
}
int HelloWorld()
{
return 5;
}
I try and compile control.cpp with g++ on OSX 10.7 and get
Undefined symbols for architecture x86_64:
"Hello::Hello()", referenced from:
__static_initialization_and_destruction_0(int, int)in cccZHWtd.o
"Hello::~Hello()", referenced from:
___tcf_1 in cccZHWtd.o
"Hello::HelloWorld()", referenced from:
_main in cccZHWtd.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
Is it the compiler, my code or my concept of whats going on?
Am I not instantiating something correctly?
Any links describing this in more detail would be appreciated.
Ultimately I want to be able to run a function in another class and return the result...normal OO, keeping your program modular stuff....
The errors you are getting are Linking errors not compilation errors.
The linker is not able to find definitions of the said functions & hence it reports the errors. It seems You have not included the Hello.cpp file containing the function definitions in your project.
Make sure Hello.cpp is included in your project and is a part of your project or
If you are using command line for compilation and linking make sure you have specified Hello.cpp in the file names on the command line.
Most of the issue is me not being familiar as I should be with g++ (Thanks Als).
There were are few syntax issues as well (Thanks Brain).
Here is the corrected (albiet slightly bloated for an overview of stucture) code and g++ command
Control.h
#pragma once
#ifndef CONTROL_H
#define CONTROL_H
class CONTROL
{
private:
//nothing defined yet...
public:
Control(); //default constructor
~Control(); //default destructor
};
#endif /*CONTROL_H*/
Control.cpp
#include "Hello.h"
#include "Control.h"
Hello helloTest; //instantiates the Hello Object
Control::Control()
{
}
Control::~Control()
{
}
int main()
{
helloTest.HelloWorld();
return 0;
}
Hello.h
#pragma once
#ifndef HELLO_H
#define HELLO_H
class Hello
{
private:
//nothing defined yet
public:
Hello(); //default constructor
~Hello(); //default destructor
void HelloWorld();
};
#endif /*HELLO_H*/
Hello.cpp
#include "Hello.h"
#include <iostream> //so we can use 'cout'
using namespace std;
Hello::Hello()
{
}
Hello::~Hello()
{
}
void Hello::HelloWorld()
{
std::cout << "Hello lovelies!\n"; //The magic word.
}
Then we run g++ like so
g++ -o Hello ./Control.cpp ./Hello.cpp
g++ [option] [output file name] [input files]
First of all:
public:
Hello();
~Hello();
private:
public:
is pointless, a class defaults to private, and there is no need to make it
public twice nor do I know if you can do that furthermore if you have no private members private should not be in there (not trying to be mean just some advice :-) )
Now to answer the question (with a guess DISCLAIMER: I AM NOT 100% FAMILIAR WITH GCC):
This is a linker error, it may be there because
the compiler can not find the definition of
HelloWorld(void);.
Let me explain:
In your header file you wrote:
int HelloWorld(void);
However in your .cpp you write:
int HelloWorld()
{
return 5;
}
The function's (or in this case method because it is inside a class)
arguments need to be exactly the same in the header and source, you
can not even change the names (or at least you cant with VC++ which is
what I use; I have little experience with gcc) so this may be resolvable
by typing
int HelloWorld(void)
{
return 5;
}
Next (DISCLAIMER I AM NOT 100% familiar with the pre-proccsor):
You also use the #pragma once pre-proccsor tag, I dont use it but
I believe that means you can only include a file once and you have included Hello.h and Control.h twice, like I said I am no expert in the pre-proccsor but you commented out
HELLO_H
and
CONTROL_H
This is a follow-up to Dynamic Shared Library compilation with g++.
I'm trying to create a shared class library in C++ on Linux. I'm able to get the library to compile, and I can call some of the (non-class) functions using the tutorials that I found here and here. My problems start when I try to use the classes that are defined in the library. The second tutorial that I linked to shows how to load the symbols for creating objects of the classes defined in the library, but stops short of using those objects to get any work done.
Does anyone know of a more complete tutorial for creating shared C++ class libraries that also shows how to use those classes in a separate executable? A very simple tutorial that shows object creation, use (simple getters and setters would be fine), and deletion would be fantastic. A link or a reference to some open source code that illustrates the use of a shared class library would be equally good.
Although the answers from codelogic and nimrodm do work, I just wanted to add that I picked up a copy of Beginning Linux Programming since asking this question, and its first chapter has example C code and good explanations for creating and using both static and shared libraries. These examples are available through Google Book Search in an older edition of that book.
myclass.h
#ifndef __MYCLASS_H__
#define __MYCLASS_H__
class MyClass
{
public:
MyClass();
/* use virtual otherwise linker will try to perform static linkage */
virtual void DoSomething();
private:
int x;
};
#endif
myclass.cc
#include "myclass.h"
#include <iostream>
using namespace std;
extern "C" MyClass* create_object()
{
return new MyClass;
}
extern "C" void destroy_object( MyClass* object )
{
delete object;
}
MyClass::MyClass()
{
x = 20;
}
void MyClass::DoSomething()
{
cout<<x<<endl;
}
class_user.cc
#include <dlfcn.h>
#include <iostream>
#include "myclass.h"
using namespace std;
int main(int argc, char **argv)
{
/* on Linux, use "./myclass.so" */
void* handle = dlopen("myclass.so", RTLD_LAZY);
MyClass* (*create)();
void (*destroy)(MyClass*);
create = (MyClass* (*)())dlsym(handle, "create_object");
destroy = (void (*)(MyClass*))dlsym(handle, "destroy_object");
MyClass* myClass = (MyClass*)create();
myClass->DoSomething();
destroy( myClass );
}
On Mac OS X, compile with:
g++ -dynamiclib -flat_namespace myclass.cc -o myclass.so
g++ class_user.cc -o class_user
On Linux, compile with:
g++ -fPIC -shared myclass.cc -o myclass.so
g++ class_user.cc -ldl -o class_user
If this were for a plugin system, you would use MyClass as a base class and define all the required functions virtual. The plugin author would then derive from MyClass, override the virtuals and implement create_object and destroy_object. Your main application would not need to be changed in any way.
The following shows an example of a shared class library shared.[h,cpp] and a main.cpp module using the library. It's a very simple example and the makefile could be made much better. But it works and may help you:
shared.h defines the class:
class myclass {
int myx;
public:
myclass() { myx=0; }
void setx(int newx);
int getx();
};
shared.cpp defines the getx/setx functions:
#include "shared.h"
void myclass::setx(int newx) { myx = newx; }
int myclass::getx() { return myx; }
main.cpp uses the class,
#include <iostream>
#include "shared.h"
using namespace std;
int main(int argc, char *argv[])
{
myclass m;
cout << m.getx() << endl;
m.setx(10);
cout << m.getx() << endl;
}
and the makefile that generates libshared.so and links main with the shared library:
main: libshared.so main.o
$(CXX) -o main main.o -L. -lshared
libshared.so: shared.cpp
$(CXX) -fPIC -c shared.cpp -o shared.o
$(CXX) -shared -Wl,-soname,libshared.so -o libshared.so shared.o
clean:
$rm *.o *.so
To actual run 'main' and link with libshared.so you will probably need to specify the load path (or put it in /usr/local/lib or similar).
The following specifies the current directory as the search path for libraries and runs main (bash syntax):
export LD_LIBRARY_PATH=.
./main
To see that the program is linked with libshared.so you can try ldd:
LD_LIBRARY_PATH=. ldd main
Prints on my machine:
~/prj/test/shared$ LD_LIBRARY_PATH=. ldd main
linux-gate.so.1 => (0xb7f88000)
libshared.so => ./libshared.so (0xb7f85000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0xb7e74000)
libm.so.6 => /lib/libm.so.6 (0xb7e4e000)
libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0xb7e41000)
libc.so.6 => /lib/libc.so.6 (0xb7cfa000)
/lib/ld-linux.so.2 (0xb7f89000)
On top of previous answers, I'd like to raise awareness about the fact that you should use the RAII (Resource Acquisition Is Initialisation) idiom to be safe about handler destruction.
Here is a complete working example:
Interface declaration: Interface.hpp:
class Base {
public:
virtual ~Base() {}
virtual void foo() const = 0;
};
using Base_creator_t = Base *(*)();
Shared library content:
#include "Interface.hpp"
class Derived: public Base {
public:
void foo() const override {}
};
extern "C" {
Base * create() {
return new Derived;
}
}
Dynamic shared library handler: Derived_factory.hpp:
#include "Interface.hpp"
#include <dlfcn.h>
class Derived_factory {
public:
Derived_factory() {
handler = dlopen("libderived.so", 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);
}
}
};
Client code:
#include "Derived_factory.hpp"
{
Derived_factory factory;
std::unique_ptr<Base> base = factory.create();
base->foo();
}
Note:
I put everything in header files for conciseness. In real life you should of course split your code between .hpp and .cpp files.
To simplify, I ignored the case where you want to handle a new/delete overload.
Two clear articles to get more details:
C++ dlopen mini how-to
C++ Dynamic Loading of Shared Objects at Runtime
Basically, you should include the class' header file in the code where you want to use the class in the shared library. Then, when you link, use the '-l' flag to link your code with the shared library. Of course, this requires the .so to be where the OS can find it. See 3.5. Installing and Using a Shared Library
Using dlsym is for when you don't know at compile time which library you want to use. That doesn't sound like it's the case here. Maybe the confusion is that Windows calls the dynamically loaded libraries whether you do the linking at compile or run-time (with analogous methods)? If so, then you can think of dlsym as the equivalent of LoadLibrary.
If you really do need to dynamically load the libraries (i.e., they're plug-ins), then this FAQ should help.