问题
Say I have a abstract base class defined like so:
interface.hpp
#ifndef INTERFACE_HPP
#define INTERFACE_HPP 1
class interface{
public:
virtual void func() = 0;
};
#endif // INTERFACE_HPP
Then I compile a translation unit test.cpp
into a shared object test.so
:
test.cpp
#include "interface.hpp"
#include <iostream>
class test_interface: public interface{
public:
void func(){std::cout << "test_interface::func() called\n";}
};
extern "C"
interface &get_interface(){
static test_interface test;
return test;
}
If I open that shared object in an executable and try to call get_interface
like this:
#include <dlfcn.h>
#include "interface.hpp"
int main(){
void *handle = dlopen("test.so", RTLD_LAZY);
void *func = dlsym(handle, "get_interface");
interface &i = reinterpret_cast<interface &(*)()>(func)();
i.func(); // print "test_interface::func() called"
dlclose(handle);
}
(just pretend I did error checking)
Is the behaviour well defined? Or am I stepping on my own toes by assuming this will always work?
Keep in mind I will only ever be using clang and gcc
回答1:
One gotcha is that you want protected: ~interface()
to discourage clients from deleting interface
.
A second, practical issue is that if you modify interface
, remember to add the methods at the end of the class only, and do not add new virtual overrides (functions with the same name). (In practice, I have seen overrides be clustered together, even if they are not clustered in the header file).
If you want more than just a single interface (say, your interface inherits from 2 other interfaces), use virtual
inheritance. Adding new virtual
parents after the fact has in my experience proved problematic as well.
None of this is defined by the C++ standard, which is agnostic on the subject of binary interfaces and run time loading of code. However, the above is my experience using a similar technique (admittedly, with pointers instead of references, and using MSVC instead of gcc/clang).
You do have to keep track of what the ABI is on the compilers you use. If you pass std
structures over such an interface, be aware that they sometimes change layout (std::string
in gcc going from reference counted to not, for example, or std::list
getting O(1) size
), and they are not all that likely to be layout-compatible between compilers (well, standard libraries, which different compilers tend to use different ones by default).
来源:https://stackoverflow.com/questions/31407129/defining-interface-of-abstract-class-in-shared-library