定义共享库中抽象类的接口

Defining interface of abstract class in shared library

本文关键字:接口 抽象类 共享 定义      更新时间:2023-10-16

假设我有一个抽象基类,定义如下:

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() calledn";}
};
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

一个问题是,您希望protected: ~interface()阻止客户端删除interface

第二个实际问题是,如果修改interface,请记住只在类的末尾添加方法,而不要添加新的虚拟覆盖(具有相同名称的函数)。(在实践中,我见过重写被聚集在一起,即使它们没有聚集在头文件中)。

如果你想要不止一个接口(比如,你的接口继承了另外两个接口),使用virtual继承。根据我的经验,事后添加新的virtual父母也是有问题的。

这些都不是c++标准定义的,它与二进制接口和运行时加载代码的主题无关。然而,以上是我使用类似技术的经验(不可否认,使用指针而不是引用,使用MSVC而不是gcc/clang)。

您必须跟踪您使用的编译器上的ABI。如果您在这样的接口上传递std结构,请注意它们有时会改变布局(例如,gcc中的std::string从引用计数变为不引用计数,或者std::list变为0 (1)size),并且它们不太可能在编译器之间实现布局兼容(好吧,标准库,不同的编译器倾向于默认使用不同的库)。