2

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

RamblingMad
  • 5,332
  • 2
  • 24
  • 48
  • If you're using the same compiler then I see no reason this could go wrong. I've just ported your sample to msvc (2015rc) and it works there, too. – tamas.kenez Jul 14 '15 at 14:32
  • You should be fine as long as you maintain binary compatibility. See these... http://stackoverflow.com/questions/1774911/how-to-design-a-c-api-for-binary-compatible-extensibility https://techbase.kde.org/Policies/Binary_Compatibility_Issues_With_C++ http://stackoverflow.com/questions/3217513/are-llvm-gcc-and-clang-binary-compatible-with-gcc-particularly-mingw-gcc-on-w – QuestionC Jul 14 '15 at 17:37

1 Answers1

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).

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524