针对修改的头文件进行编译

时间:2014-09-06 04:28:05

标签: c++ gcc linker header-files dynamic-loading

我有一个带有类定义的头文件,其中包含一些。该类包含一些公共函数和一些私有变量。该类被编译成可执行文件。

让我们说有人拿这个头文件并创建一个“公共”副本。他删除了所有包含和私有变量(使用未定义符号的前向声明)。然后,他针对“public”头文件编译自己的代码(调用相关类的公共函数)并创建.so文件。

此库是否正常工作

  • 如果它与可执行文件链接?
  • 是否在运行时动态加载?

1 个答案:

答案 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 ++标准对如何在内存中布局用户定义的类几乎没有要求。如果你试图在插件和主程序之间传递用户类实例,你可能得不到你期望的,特别是如果主程序和插件是用不同的编译器编译的。