在共享库

时间:2015-07-14 12:51:56

标签: 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

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 个答案:

答案 0 :(得分:0)

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

第二个实际问题是,如果您修改interface,请记住仅在类的 end 添加方法,并且不要添加新的虚拟覆盖(使用一样的名字)。 (实际上,我已经看到覆盖被聚集在一起,即使它们没有聚集在头文件中)。

如果您不仅需要单个接口(例如,您的接口继承自其他2个接口),请使用virtual继承。事实证明,在我的经验中添加新的virtual父母也证明是有问题的。

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

您必须跟踪您使用的编译器上的ABI。如果您通过此类界面传递std个结构,请注意它们有时会更改布局(例如,gcc中的std::string从引用计数到非,或std::list获取O(1) size),并且它们在编译器之间并不是那么可能是布局兼容的(好吧,标准库,不同的编译器默认使用不同的编译器)。

相关问题