如何使用动态加载函数(dlopen)操作/返回数据?
我花了几天时间阅读和重新阅读我找到的关于该主题的每个教程,并花了几个小时(甚至几天)在 SO 浏览相关问题,但我仍然无法让以下内容发挥作用。如果这是重复的,请接受我的歉意:很可能我已经多次看到并重新阅读了重复的问题,但无法理解答案与我的问题的相关性。有了这个,
我正在尝试为我的应用程序实现一个插件架构。插件被编译并安装为库。在运行时,应用程序使用 dlopen() / dlsym() 加载并链接到插件的函数。
这个想法是插件(库)将实现一组函数以将数据返回到主应用程序,或操作从应用程序传递的数据。
为了测试这个想法,我尝试实现一个函数(在插件内部),该函数将返回插件本身的(人类可读的)名称(作为 std::string )。我认为这将是一个简单的开始...... :-/
这是我到目前为止所得到的:
// Plugin.cpp
extern "C" void plugin_name(std::string *name) {
name = new std::string("Example plugin name");
}
// Application.cpp
void* handle = dlopen("libplugin.so", RTLD_LAZY);
typedef void (*plugin_t)(std::string*);
dlerror(); // Reset errors.
plugin_t call_plugin_name = (plugin_t) dlsym(handle, "plugin_name");
// ... Some error handling code.
std::string my_plugin_name;
call_plugin_name(&my_plugin_name);
dlclose(handle);
// More code that displays my_plugin_name.
我已经尝试了许多不同的组合,包括一个看起来更直接的组合(但没有更好的工作),其中返回插件名称:
// Plugin.cpp
extern "C" std::string plugin_name(void) {
return std::string("Example plugin name");
}
我知道我已经接近了:代码编译并且应用程序停止崩溃;)
但是,我有一个空白区域,我希望在其中看到实际的插件名称。
到目前为止,我读过的所有教程都非常快速地介绍了数据双向传递的机制:plugin <=>应用。我试图用“简单”的 std::string 做的事情,我希望稍后用更复杂的对象做(即插件函数将通过引用获取对象并更改其一些属性)。这些教程或多或少都停留在使用 dlsym() 创建指针这一点上,并且没有给出太多关于如何使用该指针的示例。
那么,如何做到这一切呢?
另一个相关问题:我是否使用与应用程序和插件一起使用的通用标头以及在哪里定义函数调用签名?我该怎么做,这会有什么帮助?
I've spent days reading and re-reading every tutorials I've found on the subject, and spent hours (and even days) browsing related questions here at SO, but I still can't get the following to work. Accept my apologies if this is a duplicate: chances are that I've seen and re-read many times the duplicate questions but couldn't understand the relevance of the answers to my problem. With that out of the way...
I'm trying to implement a plugin architecture for my Application. Plugins are compiled and installed as libraries. At run time, the Application then uses dlopen() / dlsym() to load and link to the plugin's functions.
The idea is that plugins (libraries) will implement a set of functions to return data to the main Application, or manipulate data passed from the Application.
In order to test this idea, I tried to implement a function (inside the plugin) that would return the (human readable) name of the plugin itself (as a std::string). I thought that would be something simple to start with.... :-/
Here is what I got so far:
// Plugin.cpp
extern "C" void plugin_name(std::string *name) {
name = new std::string("Example plugin name");
}
// Application.cpp
void* handle = dlopen("libplugin.so", RTLD_LAZY);
typedef void (*plugin_t)(std::string*);
dlerror(); // Reset errors.
plugin_t call_plugin_name = (plugin_t) dlsym(handle, "plugin_name");
// ... Some error handling code.
std::string my_plugin_name;
call_plugin_name(&my_plugin_name);
dlclose(handle);
// More code that displays my_plugin_name.
I've tried many different combinations, including one that seemed more straigtforward (but didn't work any better) where the plugin name is returned:
// Plugin.cpp
extern "C" std::string plugin_name(void) {
return std::string("Example plugin name");
}
I know I'm close: the code compiles and the Application stopped crashing ;)
However, I've got an empty space where I'd expect seeing the actual plugin name.
All the tutorials I've read so far go very quickly over the mechanism by which data is passed both ways: plugin <=> Application. What I'm trying to do with a "simple" std::string, I wish to do later with much more complex objects (i.e. a plugin function would take an object by reference and change some of its properties). The tutorials more or less all stop at the point of creating a pointer with dlsym() and do not give much examples on how to use this pointer.
So, how to do all that?
Another pertinent question: do I use a common header that I'd use both with the Application and with the plugin and where I'd define the function calls signature? How would I do this and how would that help?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
函数的签名是根据其名称和参数类型生成的(返回值类型无关紧要)。当您使用 extern "C" 声明函数时,将使用 C 符号命名方案,该方案显然无法处理 std::string 等 C++ 类型。这就是为什么将 std::string 作为参数传递不起作用。
我无法解释为什么返回 std::string 不起作用。也许使用了不同的调用约定。
无论如何,从共享库导入 C++ 代码的正确方法是从入口点返回指向 C++ 类型的指针。并且此入口点必须具有 C 中可用类型的参数。(入口点是从共享库导出的记录函数)
这里是一篇关于从共享库加载 C++ 类的基本方面的好文章。本文将彻底解答您的疑问。
请注意,使用从共享库向主应用程序抛出的异常时存在陷阱。并使用在库内创建的对象的dynamic_cast。我提到这个主题是为了让您在遇到这些问题时可以有所准备。
[编辑]
为了让我的答案更清楚,我将添加几个示例。
要获取插件名称,您可以使用:
要真正使用插件功能,您应该在标头中声明一个基类:
The signature of a function is generated from its name and argument types (return value type doesn't matter). When you declare function with extern "C", C symbol naming scheme is used which apparently can't handle C++ types like std::string. That's why passing std::string as an arguments doesn't work.
I can't explain why returning std::string doesn't work. Maybe different calling conventions are used.
Anyway the correct way of importing C++ code from a shared library is to return pointers to C++ types from entry points. And this entry points have to have arguments with types available in C. (Entry point is a documented function exported from a shared library)
Here is a good article on basic aspects of loading C++ classes from shared libraries. This article will answer your question throughly.
Please note that there are pitfalls when using exceptions thrown from a shared library to the main applications. And with dynamic_cast of objects created inside a library. I've mentioned this topics so that you could be somewhat prepared when you face this problems.
[edit]
To make my answer more clear I'll add a couple of examples.
To get the plugin name you can use:
To really use the plugin functionality you should declare a base class in a header:
尝试:
当您分配作为参数传递的指针的副本时,而不是您打算分配的指针的副本。
编辑给你:
文件main.cpp
文件plugin.cpp
只是一句警告。尽管可以编译并运行,但跨 dll 边界传递 C++ 类型是有风险的,并且上面的代码只是足够固定以编译和运行的代码,它不安全并且具有非常显式的内存处理。您可能想以不同的方式解决问题。
Try:
As you are assigning a copy of the pointer you passed as the argument, not the one you intended to assign.
EDIT Here you go:
File main.cpp
File plugin.cpp
Just a word of warning. Although this compiles and runs, passing C++ types across the dll boundry is risky and the above code is just your code fixed enough to compile and run, it is not safe and has very explicit memory handling. You may want to attack the problem in a different way.
请阅读此问题并它的答案。 C++ 中的共享库边界有很多不兼容的机会。
Please have a read of this question and its answers. There are many opportunities for incompatibilities across the shared lib boundaries in C++.