You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从共享库导出符号并获取派生类实例?Boost.DLL场景

动态加载共享库获取派生类实例的链接错误解决方法

问题场景

需要实现插件机制,通过Boost.DLL加载共享库并获取库中派生类实例,现有代码及链接报错如下:

共享库头文件(test_lib.h)

#pragma once

#include <iostream>
#include <memory>
#include <boost/dll/config.hpp>


class BOOST_SYMBOL_EXPORT BaseA
{
public:
    virtual BOOST_SYMBOL_EXPORT ~BaseA() = default;
};


class BOOST_SYMBOL_EXPORT DerivedA : public BaseA
{
public:
    BOOST_SYMBOL_EXPORT virtual ~DerivedA();
    BOOST_SYMBOL_EXPORT void method2();
};


// 获取派生类实例的函数
extern "C" BOOST_SYMBOL_EXPORT BaseA* get_derived_instance();

共享库实现文件(lib.cpp)

#include <test_lib.h>
#include <iostream>
#include <boost/dll/alias.hpp>


void DerivedA::method2()
{
    std::cout << "M2" << std::endl;
};


DerivedA::~DerivedA()
{
    std::cout << "DDestr" << std::endl;
}

BaseA* get_derived_instance() { return new DerivedA(); }

测试主程序(main.cpp)

#include <memory>
#include <boost/dll/import.hpp>
#include <test_lib.h>


int main()
{
    typedef BaseA* (*get_derived_instance_t)();

    void* thandle = dlopen("build/deblib/libisolated_proj.so", RTLD_LAZY);
    if (!thandle) { std::cerr << "load" << std::endl; exit(1); }
    auto creator = reinterpret_cast<get_derived_instance_t>(dlsym(thandle, "get_derived_instance"));
    if (!creator) { std::cerr << "fc" << std::endl; exit(1); }

    auto new_obj = creator();

    DerivedA *da = dynamic_cast<DerivedA*>(new_obj);
    da->method2();
}

链接错误信息

[ 50%] Building CXX object deblib/CMakeFiles/isolated_proj.dir/lib.cpp.o
[ 50%] Building CXX object CMakeFiles/testss.dir/main.o
[ 75%] Linking CXX shared library libisolated_proj.so
[100%] Linking CXX executable testss
[100%] Built target isolated_proj
/usr/bin/ld: CMakeFiles/testss.dir/main.o: warning: relocation against `_ZTI8DerivedA' in read-only section `.text'
/usr/bin/ld: CMakeFiles/testss.dir/main.o: in function `main':
main.cpp:(.text+0xcd): undefined reference to `typeinfo for DerivedA'
/usr/bin/ld: main.cpp:(.text+0xf3): undefined reference to `DerivedA::method2()'
/usr/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/testss.dir/build.make:102: testss] Error 1
make[1]: *** [CMakeFiles/Makefile2:109: CMakeFiles/testss.dir/all] Error 2
make: *** [Makefile:91: all] Error 2

错误原因

链接器报错的核心原因是:主程序编译时直接引用了DerivedA的类型信息和成员函数符号,但这些符号仅存在于共享库中,主程序链接阶段无法找到。即便使用了动态加载,代码中dynamic_cast<DerivedA*>和da->method2()的写法会让编译器认为这些符号需要在链接时解析,而非运行时动态获取。

解决方法

方法1:面向基类编程(推荐)

插件机制的核心原则是依赖抽象而非具体实现,通过基类提供统一接口,避免主程序直接引用派生类。

  1. 修改基类,添加纯虚函数替代派生类的method2:
// test_lib.h
class BOOST_SYMBOL_EXPORT BaseA
{
public:
    virtual BOOST_SYMBOL_EXPORT ~BaseA() = default;
    virtual BOOST_SYMBOL_EXPORT void method2() = 0; // 添加纯虚函数接口
};

class BOOST_SYMBOL_EXPORT DerivedA : public BaseA
{
public:
    BOOST_SYMBOL_EXPORT virtual ~DerivedA();
    BOOST_SYMBOL_EXPORT void method2() override; // 重写基类接口
};
  1. 主程序直接通过基类指针调用方法,无需dynamic_cast:
// main.cpp
int main()
{
    typedef BaseA* (*get_derived_instance_t)();

    void* thandle = dlopen("build/deblib/libisolated_proj.so", RTLD_LAZY);
    if (!thandle) { std::cerr << "load" << std::endl; exit(1); }
    auto creator = reinterpret_cast<get_derived_instance_t>(dlsym(thandle, "get_derived_instance"));
    if (!creator) { std::cerr << "fc" << std::endl; exit(1); }

    std::unique_ptr<BaseA> new_obj(creator());
    new_obj->method2(); // 直接通过基类接口调用
}

方法2:动态解析派生类符号(按需使用)

如果必须在主程序中使用派生类的特定功能,需将派生类的相关符号也通过动态加载获取,避免编译时直接引用:

  1. 在共享库中导出派生类成员函数的获取接口:
// test_lib.h
extern "C" BOOST_SYMBOL_EXPORT void (*get_derived_method2())();
// lib.cpp
void (*get_derived_method2())() {
    return &DerivedA::method2;
}
  1. 主程序中动态获取符号并调用:
// main.cpp
int main()
{
    typedef BaseA* (*get_derived_instance_t)();
    typedef void (*method2_t)(DerivedA*);

    void* thandle = dlopen("build/deblib/libisolated_proj.so", RTLD_LAZY);
    if (!thandle) { std::cerr << "load" << std::endl; exit(1); }
    
    auto creator = reinterpret_cast<get_derived_instance_t>(dlsym(thandle, "get_derived_instance"));
    if (!creator) { std::cerr << "fc" << std::endl; exit(1); }
    
    auto get_method2 = reinterpret_cast<void (*)()>(dlsym(thandle, "get_derived_method2"));
    if (!get_method2) { std::cerr << "get method2 failed" << std::endl; exit(1); }
    method2_t method2 = reinterpret_cast<method2_t>(get_method2());

    auto new_obj = creator();
    // 此处用static_cast,因dynamic_cast需类型信息(需额外动态加载typeinfo符号,不推荐)
    DerivedA* da = static_cast<DerivedA*>(new_obj);
    method2(da);
}

方法3:链接时显式依赖共享库(不推荐)

若主程序与共享库属于同一项目,可在链接主程序时直接指定共享库依赖,让链接器找到符号。但此方式会失去动态加载的灵活性,程序启动时就会加载共享库,不符合插件按需加载的设计初衷。

在CMake中添加链接依赖:

target_link_libraries(testss isolated_proj)

内容的提问来源于stack exchange,提问作者A.N.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 02:39:54