user2891462
user2891462

Reputation: 3343

Does the vtable persist among binaries if using a non-common class?

Say I have three libraries, all written in C++:

  1. logging.a: a static library to assist with logging. It defines an abstract class that other classes can use to customize their logging (by implementing the pure virtual method virtual void CustomLogger::customLogging(std::string) = 0).
  2. libmainproject.so: the main library. It contains a class, unknown to the outside, (CustomLoggerImpl) which extends CustomLogger and implements the custom logging method.
  3. libplugin.so: a plugin library which needs to log stuff and wants to integrate its logging with the one in libmainproject.

The interface between libmainproject and libplugin is a C interface, despite the internals of both libraries being written in C++.

libplugin has no idea of the existence of CustomLoggerImpl inside libmainproject, but does know about the interface CustomLogger from logging.

In order to use the custom logging from libplugin I have passed a pointer to CustomLoggerImpl as void * (because it happens over a C interface, can't use custom objects).

Once I need to log inside the plugin, I cast the void * (which points to a CustomLoggerImpl object) into a CustomLogger * (Liskov substitution principle). Then I invoke customLogger->customLogging("whatever");.

Although nothing crashes, the call doesn't seem to make it to libmainproject. Why is that? I assume the issue is that there is some issue with the vtable, so that it doesn't work properly from a different binary which doesn't have the definition of the object, but would like some more in-depth explanation of why this might be happening.

Upvotes: 0

Views: 91

Answers (1)

MSalters
MSalters

Reputation: 179981

Dynamically-linked (shared) libraries are not defined by Standard C++, so you're in implementation-specific behavior.

Implementations generally do reasonable things; it would be reasonable to assume that CustomLogger has the same vtable layout in all binaries. The Standard doesn't even prescribe a vtable, so this is not a hard statement.

Implementations that support dynamically-linked libraries generally do not assume that all code lives in a single binary. On Windows, MSVC makes less assumptions because it has an __declspec(dllimport) extension that tells the compiler specifically that there's code coming from another binary. But your question makes it clear you're not using MSVC. Your compiler is not going to assume that a customLogging override has to be defined in libplugin.so, just because libplugin.so calls customLogging.

Hence, I'm going to agree with VTT. The biggest issue here seems to be that you're casting a CustomLoggerImpl* to CustomLogger* via void*, where it should have been CustomLoggerImpl* -> CustomLogger* -> void* -> CustomLogger*. The conversion to base class should happen explicitly, in C++ code.

Upvotes: 1

Related Questions