我有一个带有类定义的头文件,其中包含一些。该类包含一些公共函数和一些私有变量。该类被编译成可执行文件。
让我们说有人拿这个头文件并创建一个“公共”副本。他删除了所有包含和私有变量(使用未定义符号的前向声明)。然后,他针对“public”头文件编译自己的代码(调用相关类的公共函数)并创建.so文件。
此库是否正常工作
答案 0 :(得分:0)
正如评论中所解释的那样,你所描述的内容不起作用,但目标是合理的。我的理解是,您希望隐藏类的实现细节,同时为插件提供固定的接口,以便插件代码开发可以与程序的其余部分分离。
您不能通过提供false标头来隐藏私有成员数据和函数。首先,Igor Tandetnik指出,ODR违规行为指出。这不仅仅是一项武断的规则。私有数据会影响存储对象所需的内存,从而影响代码应该如何处理该对象。公共和私有函数的相对地址必须在多态的公共vtable实现中已知。
我们需要间接。我们的接口将告诉客户端代码它的公共函数是什么,并且只需要空间来存储指向实现类的指针。无需了解实现类的详细信息。这是pimpl idiom。以下是动态加载如何使用的概述。
的main.cpp
#include <iostream>
#include <dlfcn.h>
#include "interface.h"
typedef int (bar_type)(const Interface&);
int main() {
#ifndef EXE_INPUT
#define EXE_INPUT 5
Interface interface(EXE_INPUT);
#endif
void* plugin = dlopen("plugin.so", RTLD_LAZY);
if (plugin == NULL) {
std::cout << dlerror() << std::endl;
return 1;
}
bar_type* bar_ptr = (bar_type*)dlsym(plugin, "bar");
if (bar_ptr == NULL) {
std::cout << dlerror() << std::endl;
return 1;
}
const int ret = (*bar_ptr)(interface);
std::cout << "The value from the plugin is " << ret << std::endl;
}
interface.h
#ifndef INTERFACE_H
#define INTERFACE_H
class Implementation;
class Interface
{
public:
Interface(const int);
~Interface();
int foo(int) const;
private:
Implementation* imp_ptr;
};
#endif
interface.cpp
#include "interface.h"
struct Implementation {
Implementation(const int v)
: v(v)
{}
int foo(const int w) {
return v * w;
}
int v;
/* this struct is not exposed, do whatever you want here */
};
Interface::Interface(const int v)
: imp_ptr(new Implementation(v))
{}
Interface::~Interface() {
delete imp_ptr;
}
/* if this signature changes or other functions get added
* to Interface, plugin must be recompiled */
int Interface::foo(const int w) const {
return imp_ptr->foo(w);
}
plugin.cpp
#include "interface.h"
#include "plugin.h"
extern "C" int bar(const Interface& i)
{
#ifndef PLUGIN_INPUT
#define PLUGIN_INPUT 11
return i.foo(PLUGIN_INPUT);
#endif
}
plugin.h
#ifndef PLUGIN_H
#define PLUGIN_H
#include "interface.h"
extern "C" int bar(const Interface& i);
#endif
编译和链接。我碰巧运行的是OS X.对于Linux,请删除“-undefined dynamic_lookup”。
g++-4.8 -o main main.cpp interface.cpp
g++-4.8 -shared -fpic -undefined dynamic_lookup -ldl -o plugin.so plugin.cpp
请注意,我们分别编译和链接。具体来说,plugin.cpp不知道interface.cpp中有什么。
$ ./main
The value from the plugin is 55
您可以根据需要更改interface.cpp
,而无需重新编译插件。这里动态加载并非绝对必要。它也适用于动态链接。
警告:C ++标准对如何在内存中布局用户定义的类几乎没有要求。如果你试图在插件和主程序之间传递用户类实例,你可能得不到你期望的,特别是如果主程序和插件是用不同的编译器编译的。