Определение интерфейса абстрактного класса в разделяемой библиотеке
Скажем, у меня есть абстрактный базовый класс, определенный так:
interface.hpp
#ifndef INTERFACE_HPP
#define INTERFACE_HPP 1
class interface{
public:
virtual void func() = 0;
};
#endif // INTERFACE_HPP
Затем я собираю модуль перевода test.cpp
в общий объект test.so
:
test.cpp
#include "interface.hpp"
#include <iostream>
class test_interface: public interface{
public:
void func(){std::cout << "test_interface::func() called\n";}
};
extern "C"
interface &get_interface(){
static test_interface test;
return test;
}
Если я открою этот общий объект в исполняемом файле и попытаюсь вызвать get_interface
как это:
#include <dlfcn.h>
#include "interface.hpp"
int main(){
void *handle = dlopen("test.so", RTLD_LAZY);
void *func = dlsym(handle, "get_interface");
interface &i = reinterpret_cast<interface &(*)()>(func)();
i.func(); // print "test_interface::func() called"
dlclose(handle);
}
(просто сделайте вид, что я сделал проверку ошибок)
Правильно ли определено поведение? Или я наступаю на собственные пальцы, полагая, что это всегда будет работать?
Имейте в виду, что я буду использовать только clang и gcc
1 ответ
Одна ошибка в том, что вы хотите protected: ~interface()
отговорить клиентов от удаления interface
,
Второй практический вопрос заключается в том, что если вы измените interface
не забудьте добавлять методы только в конце класса и не добавлять новые виртуальные переопределения (функции с тем же именем). (На практике я видел кластеризацию переопределений, даже если они не кластеризованы в заголовочном файле).
Если вы хотите больше, чем просто один интерфейс (скажем, ваш интерфейс наследуется от 2 других интерфейсов), используйте virtual
наследование. Добавление нового virtual
Родители после того, как факт, по моему опыту оказался проблематичным, а также.
Ничто из этого не определяется стандартом C++, который не зависит от бинарных интерфейсов и загрузки кода во время выполнения. Тем не менее, вышеизложенное является моим опытом использования подобной техники (по общему признанию, с указателями вместо ссылок и использованием MSVC вместо gcc/clang).
Вы должны отслеживать, что такое ABI на используемых вами компиляторах. Если вы пройдете std
структуры над таким интерфейсом, помните, что они иногда меняют расположение (std::string
в gcc переходя от ссылки, подсчитанной к нет, например, или std::list
получение O (1) size
), и они вряд ли будут совместимы с компоновщиками между компиляторами (ну, стандартные библиотеки, которые разные компиляторы по умолчанию используют по-разному).