Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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?
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
149 views
Welcome To Ask or Share your Answers For Others

1 Answer

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...