Reputation: 3343
Say I have three libraries, all written in C++:
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
).libmainproject.so
: the main library. It contains a class, unknown to the outside, (CustomLoggerImpl
) which extends CustomLogger
and implements the custom logging method.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
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