На исходный вопрос был дан ответ, но в комментариях содержался этот вопрос, который, я думаю, тоже нуждается в ответе.
как клиентская программа, использующая dlopen()
, откроет общий объектнайти правильный символ, если он вызывает setName
в таком возвращаемом экземпляре?
Вы создаете методы virtual
.Вот пример, в котором создается libfoobar.so
.Он предоставляет одну фабричную функцию (make_Foo
) для создания Foo
.Bar
может быть создан из экземпляра Foo
.Все методы могут быть использованы через указатели экземпляра.Я смешал, используя необработанные указатели и unique_ptr
s, чтобы показать некоторые опции.
Сначала foobar.hpp
и foobar.cpp
, которые будут помещены в libfoobar.so
:
foobar.hpp
#pragma once
#include <string>
#include <memory>
class Bar;
class Foo {
public:
Foo();
virtual ~Foo();
virtual void set_name(const std::string& name);
virtual std::string const& get_name() const;
virtual Bar* get_Bar() const;
private:
std::string m_name;
};
class Bar {
public:
Bar(const Foo&);
virtual ~Bar();
virtual std::string const& get_value() const;
private:
std::string m_value;
};
// a Foo factory
extern "C" {
std::unique_ptr<Foo> make_Foo();
}
foobar.cpp
#include "foobar.hpp"
// You can also use the library constructor and destructor
// void __attribute__((constructor)) init(void) {}
// void __attribute__((destructor)) finalize(void) {}
// Foo - impl
Foo::Foo() : m_name{} {}
Foo::~Foo() {}
void Foo::set_name(const std::string& name) {
m_name = name;
}
std::string const& Foo::get_name() const {
return m_name;
}
Bar* Foo::get_Bar() const {
return new Bar(*this);
}
// Bar - impl
Bar::Bar(const Foo& f) : m_value(f.get_name()) {}
Bar::~Bar() {}
std::string const& Bar::get_value() const { return m_value; }
// a factory function that can be loaded with dlsym()
extern "C" {
std::unique_ptr<Foo> make_Foo() {
return std::make_unique<Foo>();
}
}
Затем универсальный помощник динамической библиотеки:
dynlib.hpp
#pragma once
#include <dlfcn.h> // dlload, dlsym, dlclose
#include <stdexcept>
using dynlib_error = std::runtime_error;
class dynlib {
public:
dynlib(const char* filename);
dynlib(const dynlib&) = delete;
dynlib(dynlib&&);
dynlib& operator=(const dynlib&) = delete;
dynlib& operator=(dynlib&&);
virtual ~dynlib();
protected:
template<typename T>
T load(const char* symbol) const {
static_cast<void>(dlerror()); // clear errors
return reinterpret_cast<T>(dlsym(handle, symbol));
}
private:
void* handle;
};
dynlib.cpp
#include "dynlib.hpp"
#include <utility>
dynlib::dynlib(const char* filename) : handle(dlopen(filename, RTLD_NOW | RTLD_LOCAL)) {
if(handle == nullptr) throw dynlib_error(std::string(dlerror()));
}
dynlib::dynlib(dynlib&& o) : handle(std::exchange(o.handle, nullptr)) {}
dynlib& dynlib::operator=(dynlib&& o) {
if(handle) dlclose(handle);
handle = std::exchange(o.handle, nullptr);
return *this;
}
dynlib::~dynlib() {
if(handle) dlclose(handle);
}
и потомок dynlib
для загрузки libfoobar.so
:
foobarloader.hpp
#pragma once
#include "foobar.hpp"
#include "dynlib.hpp"
#include <memory>
class foobarloader : public dynlib {
public:
using foo_t = std::unique_ptr<Foo> (*)();
const foo_t make_Foo; // a factory function to load
// add more if needed
foobarloader();
};
foobarloader.cpp
#include "foobarloader.hpp"
foobarloader::foobarloader() :
dynlib("./libfoobar.so"),
make_Foo(load<foo_t>("make_Foo")) // load function
{
if(make_Foo == NULL) throw dynlib_error(std::string(dlerror()));
}
И, наконец, приложение, которое никак не связано с libfoobar.so
:
test_app.cpp
#include "foobarloader.hpp"
#include <iostream>
#include <stdexcept>
#include <utility>
#include <memory>
int main() {
try {
foobarloader fbl;
auto f = fbl.make_Foo(); // std::unique_ptr<Foo> example
f->set_name("Howdy");
std::cout << "Foo name: " << f->get_name() << "\n";
Bar* b = f->get_Bar(); // raw Bar* example
std::cout << "Bar value: " << b->get_value() << "\n";
delete b;
} catch(const std::exception& ex) {
std::clog << "Exception: " << ex.what() << "\n";
return 1;
}
}
building
Если вы используете clang++
, добавьте -Wno-return-type-c-linkage
для подавления предупреждений о заводском методе.Я использовал -DNDEBUG -std=c++14 -O3 -Wall -Wextra -Wshadow -Weffc++ -pedantic -pedantic-errors
, но для краткости исключил его ниже.
g++ -fPIC -c -o foobar.o foobar.cpp
g++ -shared -o libfoobar.so foobar.o
g++ -c -o dynlib.o dynlib.cpp
g++ -c -o foobarloader.o foobarloader.cpp
g++ -c -o test_app.o test_app.cpp
g++ -rdynamic -o test_app test_app.o foobarloader.o dynlib.o -ldl
Нет libfoobar.so
связь:
% ldd test_app
linux-vdso.so.1 (0x00007ffcee58c000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007fb264cb3000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007fb264aba000)
libm.so.6 => /lib64/libm.so.6 (0x00007fb264974000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fb26495a000)
libc.so.6 => /lib64/libc.so.6 (0x00007fb264794000)
/lib64/ld-linux-x86-64.so.2 (0x00007fb264cfd000)
Но функции-члены класса работают, как и ожидалось:
% ./test_app
Foo name: Howdy
Bar value: Howdy