【问题标题】:How to portably load dynamic libraries and run their initialization code?如何可移植地加载动态库并运行它们的初始化代码?
【发布时间】:2020-08-07 07:18:26
【问题描述】:

使用 GCC 并在 Linux 上,或者可能在任何glibc 可用的地方,我可以使用dl_open() 库函数来动态加载共享对象/DLL:

void *dlopen(const char *filename, int flags);

... 这也运行所有在 ELF 格式中标有 .init 的函数;或者在 C/C++ 代码中,用__attribute__((constructor)) 标记:

How exactly does __attribute__((constructor)) work?

我的问题是:我怎样才能以更便携的方式做同样的事情?我对向其他编译器和其他平台的可移植性感兴趣。

注意:我标记了这个 C++,因为这是我正在使用的,但显然 C-ish 解决方案是可以接受的 - 因为我上面描述的是 C-ish 解决方案。

【问题讨论】:

  • boost 有 dll module 但我不确定它是否适合您的“便携性”。
  • @LouisGo:有点意思。想把它变成答案吗?如果您能描述它在不同平台上的工作原理,那就更好了。

标签: c++ dll shared-libraries portability


【解决方案1】:

由于 Boost 在许多平台上都可用,它的_dll module 可能被视为跨平台。虽然需要针对不同平台进行编译。

以下是 Boost.Dll 的 basic example,通过在插件中导入单个变量。

标题

#include <boost/config.hpp>
#include <string>

class BOOST_SYMBOL_VISIBLE my_plugin_api {
public:
   virtual std::string name() const = 0;
   virtual float calculate(float x, float y) = 0;

   virtual ~my_plugin_api() {}
};

来源

#include <boost/config.hpp> // for BOOST_SYMBOL_EXPORT
#include "../tutorial_common/my_plugin_api.hpp"

namespace my_namespace {

class my_plugin_sum : public my_plugin_api {
public:
    my_plugin_sum() {
        std::cout << "Constructing my_plugin_sum" << std::endl;
    }

    std::string name() const {
        return "sum";
    }

    float calculate(float x, float y) {
        return x + y;
    }

    ~my_plugin_sum() {
        std::cout << "Destructing my_plugin_sum ;o)" << std::endl;
    }
};

// Exporting `my_namespace::plugin` variable with alias name `plugin`
// (Has the same effect as `BOOST_DLL_ALIAS(my_namespace::plugin, plugin)`)
extern "C" BOOST_SYMBOL_EXPORT my_plugin_sum plugin;
my_plugin_sum plugin;

} // namespace my_namespace

用法:注意append_decorations是寻求平台特定命名约定的方式。

例如:linux 上的 libplugin.so 或 windows 上的 plugin.dll。

#include <boost/dll/import.hpp> // for import_alias
#include <iostream>
#include "../tutorial_common/my_plugin_api.hpp"

namespace dll = boost::dll;

int main(int argc, char* argv[]) {

    boost::dll::fs::path lib_path(argv[1]);             // argv[1] contains path to directory with our plugin library
    boost::shared_ptr<my_plugin_api> plugin;            // variable to hold a pointer to plugin variable
    std::cout << "Loading the plugin" << std::endl;

    plugin = dll::import<my_plugin_api>(          // type of imported symbol is located between `<` and `>`
        lib_path / "my_plugin_sum",                     // path to the library and library name
        "plugin",                                       // name of the symbol to import
        dll::load_mode::append_decorations              // makes `libmy_plugin_sum.so` or `my_plugin_sum.dll` from `my_plugin_sum`
    );

    std::cout << "plugin->calculate(1.5, 1.5) call:  " << plugin->calculate(1.5, 1.5) << std::endl;
}


要从插件创建对象,这里是factory example。 首先,做一个工厂方法返回boost::shared_ptr&lt;my_plugin_aggregator&gt;

#include <boost/dll/alias.hpp> // for BOOST_DLL_ALIAS   
#include "../tutorial_common/my_plugin_api.hpp"

namespace my_namespace {

class my_plugin_aggregator : public my_plugin_api {
    float aggr_;
    my_plugin_aggregator() : aggr_(0) {}

public:
    std::string name() const {
        return "aggregator";
    }

    float calculate(float x, float y) {
        aggr_ += x + y;
        return aggr_;
    }

    // Factory method
    static boost::shared_ptr<my_plugin_aggregator> create() {
        return boost::shared_ptr<my_plugin_aggregator>(
            new my_plugin_aggregator()
        );
    }
};


BOOST_DLL_ALIAS(
    my_namespace::my_plugin_aggregator::create, // <-- this function is exported with...
    create_plugin                               // <-- ...this alias name
)

} // namespace my_namespace

加载创建者方法并创建对象。

#include <boost/dll/import.hpp> // for import_alias
#include <boost/function.hpp>
#include <iostream>
#include "../tutorial_common/my_plugin_api.hpp"

namespace dll = boost::dll;

int main(int argc, char* argv[]) {

    boost::dll::fs::path shared_library_path(argv[1]);                  // argv[1] contains path to directory with our plugin library
    shared_library_path /= "my_plugin_aggregator";
    typedef boost::shared_ptr<my_plugin_api> (pluginapi_create_t)();
    boost::function<pluginapi_create_t> creator;

    creator = boost::dll::import_alias<pluginapi_create_t>(             // type of imported symbol must be explicitly specified
        shared_library_path,                                            // path to library
        "create_plugin",                                                // symbol to import
        dll::load_mode::append_decorations                              // do append extensions and prefixes
    );

    boost::shared_ptr<my_plugin_api> plugin = creator();
    std::cout << "plugin->calculate(1.5, 1.5) call:  " << plugin->calculate(1.5, 1.5) << std::endl;
    std::cout << "plugin->calculate(1.5, 1.5) second call:  " << plugin->calculate(1.5, 1.5) << std::endl;
    std::cout << "Plugin Name:  " << plugin->name() << std::endl;
}

注意:当creator 被销毁时,动态库也会被卸载。在库卸载后取消引用plugin未定义的行为

【讨论】:

  • 运行初始化代码的机制怎么样?
  • @einpoklum 感谢您的建议。它使答案更完整。虽然我希望加载跨平台动态库除了 boost 之外还有另一个答案。
  • 建议将my_plugin_aggregator 重命名为adderadder_plugin。当前名称表示聚合器聚合插件。
  • 另外,我还是没看到你运行初始化代码...
  • @einpoklum 示例代码是从 boost 的文档中复制的,所以我保持原样。您能否详细说明“运行初始化代码的机制”?我以为您的意思是如何从动态库创建对象,但似乎我理解错了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-27
  • 2011-06-11
  • 1970-01-01
  • 2016-12-17
  • 1970-01-01
  • 2017-09-24
  • 2013-04-23
相关资源
最近更新 更多