compiling against a modified header file

时光总嘲笑我的痴心妄想 提交于 2020-01-06 07:42:31

问题


I have a header file with a class definition and some includes. The class contains some public functions and some private variables. The class gets compiled into an executable.

Lets say that somebody takes this header file and creates a "public" copy. He removes all the includes and private variables (uses forward declarations for the undefined symbols). Then he compiles his own code (which calls the public functions of the class in question) against the "public" header file and creates an .so file.

Would this library work properly

  • if it is linked with the executable?
  • if it is dynamically loaded during runtime?

回答1:


As explained in the comments, what you've literally described won't work but the goal is reasonable. My understanding is that you want to hide the implementation details of a class while providing a fixed interface for plugins so that plugin code development can be decoupled from the rest of the program.

You can't just literally hide the private member data and functions by giving a false header. First of all, ODR violation as Igor Tandetnik points out. This isn't just an arbitrary rule. Private data affects the memory required to store an object and hence how code should handle that object. The relative addresses of both public and private functions must be known in the common vtable implementation of polymorphism.

We need indirection. Our interface will tell client code what its public functions are and simply that it needs space to store a pointer to an implementation class. The details of the implementation class need not be known. This is the pimpl idiom. Here's an outline of how it can be used with dynamic loading.

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

Compile and link. I happen to be running OS X. For Linux remove "-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 

Note that we compile and link separately. Specifically, plugin.cpp has no idea what's in interface.cpp.

$ ./main
The value from the plugin is 55

You can change interface.cpp as you please without need to recompile plugin. The dynamic loading isn't absolutely necessary here. It would also work with dynamic linking.

Caveat: The C++ Standard makes few demands of how user defined classes must be laid out in memory. If you try to pass user class instances between the plugin and the main program you might not get what you expect, especially if the main program and plugin were compiled with different compilers.



来源:https://stackoverflow.com/questions/25696776/compiling-against-a-modified-header-file

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!