c++ - 在共享库中定义抽象类的接口(interface)

标签 c++ c++14 dlopen dlsym

假设我有一个像这样定义的抽象基类:

接口(interface).hpp

#ifndef INTERFACE_HPP
#define INTERFACE_HPP 1

class interface{
    public:
        virtual void func() = 0;
};

#endif // INTERFACE_HPP

然后我将一个翻译单元test.cpp编译成一个共享对象test.so:

测试.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

最佳答案

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

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

如果您不仅需要一个接口(interface)(例如,您的接口(interface)继承自 2 个其他接口(interface)),请使用 virtual 继承。根据我的经验,事后添加新的 virtual parents 也被证明是有问题的。

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

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

关于c++ - 在共享库中定义抽象类的接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31407129/

相关文章:

c++ - 对象生命周期结束和它何时不复存在之间有什么关系?

c++ - 删除调用析构函数

c++ - 给定相同数据结构的两个迭代器,如何检查哪个迭代器在另一个之前?

c++ - 自动扣除别名模板和模板类的模板参数

C++1z 使用 std::initializer_list<int> 处理 == 测试,在自动函数中使用和不使用 const

c++ - 为什么 constexpr 的标准库函数不是常量?

c++ - dlopen 在 Rhel 7.2 上失败,64 位 libjvm.so

c++ - c++ for循环中的 bool 运算符

static-linking - 在静态二进制文件上使用 dlsym

c - 可见性、Fortran 公共(public)变量、共享库的运行时加载