Multiple shared libs, alternative implementations of common interface, *minimal* cmake/c++ example?

Hi all,

new user here, my name is Jörg, but I usually go as “dunc” online… this is my first post here, and it’s really more of a very generic/basic cmake tutorial question, so you gents might consider it “too basic”. That’s all fine, but I’m struggling to find a good example, so I thought I ask as close to the source as possible.

Upfront: I am absolutely fine with answers like “RTFM!” or “plenty of examples available, just check them out”… however, I am unable to find the correct stuff online, so in case your answers sway that way, I would appreciate at least some pointers, if possible.

Here’s what I’d like to achieve with cmake (using C/C++):

a) have a very basic common INTERFACE header file, defining API functionality to be exported by a shared library

b) have a very basic SHARED library implementation which implements above API (this shared lib might be replaced later on by a similar shared lib that implements tha same API)

c) have a consumer APPLICATION that uses the shared library (and in case s/o replaces shared lib implementation 1 with implementation 2 later on, it is transparent to the consumer app)

So, as you can tell, extremely basic stuff. And yes, I know how to code and implement the C/C++ side of things.

My questions are obviously regarding “how to properly create a cmake config for this, which takes care of all the import export handling, as well as is universal enough to work for both DLLs on Windows and so on Linux”?

I know about Wiki examples like BuildWindowsDLL, which is targeted at cmake 2.8.something. But even here, there never is a complete example roundtrip availabe which combines everything, instead the reader is forced (well, at least I am) to be able to work with snippets and deduce the actual correct combination of cmake options. Given the fact that cmake offers SO MUCH options for special cases, I fail to specifiy what the minimum working base config for such a task would be.

Like “the interface library should be configured like this, we will use a minimal interface with only one exposed method as an example” … “the shared lib should be configured like this, we implement only the single exposed method from the interface” … “this is what you need to add as macros to the code, this is what cmake handles for you” … “this is where you need to handle Windows DLL and Unix SO seperately, this is where cmake handles it for you in a unified way” … “this is how to configre the app via cmake to consume the shared library, and this is the corresponding app code that YOU need to take care of, while THESE parts will be handled by cmake”.

I hope you understand what I’m asking for, and I also hope this is not considered too trivial or too basic for this forum. If it is, no hard feelings, I will try to look around elsewhere.

Thanks upfront and kind regards,
dunc

Not sure that I understood this part:

in case s/o replaces shared lib implementation 1 with implementation 2 later on, it is transparent to the consumer app

But let’s start with at least something, and then it will probably get clearer. So what I got from your description is that you’d like to have two shared libraries with the same interface (but obviously with different implementations). And then you’d like to be able to just replace one variant with another without rebuilding the consumer application (hence the libraries being shared)?

Based on that understanding, I’ve made the following example project. The README.md contains detailed instructions, but in short, first you build variant #1 of the dependency, then build and run the application against it, and then you build variant #2 of the dependency and run the application against this one (no need to rebuild the application, obviously).

The interface header file is naturally the same for both variants of the dependency. Generating the export config is quite involved, but probably can be simplified.

Is that what you wanted to achieve?

Thank you,

and yes, that’s kind of what I wanted to achieve indeed. However, maybe my actual concerns were not pointed out correctly. I understand the results that the cmake configuration needs to produce. And there are multiple ways to achive the desired result.

What I am struggling with is “what is currently considered the best practice, now that we are in the cmake 4.x age”. For example:

  • Should I really opt for adding all the required dllimport/export stuff myself? Or is it easier/cleaner/preferred to e.g use GENERATE_EXPORT_HEADER functionality? If so, how would a minimal exampe for the preferred way look like?

  • Should I move the common interface header to its own cmake INTERFACE library, and have the implementation shared libraries reference/consume that INTERFACE library? Or is this not best practise to decouple things in such a way from a cmake POV?

Overall: with “modern” cmake 4.x where can cmake help me or ease the pain of having to take care of things manualy, and how to keep the config “clean and readable” at the same time.

So, that was basically what I meant by minimal example. It was not about the desired outcome per se, but about the “proper” or “most convenient” or “most structured cmake” way to achieve it.

Sorry that I did not made myself clear enough.

As for your question: the idea is to have a public interface of something that is provided to some external developers in order they can implement their own desired version of something and all they need to do is place their DLL in the correct spot (using the correct name) to “enable” their implementation.

Of course, their impl will not be part of the cmake config at all. But for our impl, we simply provide different example, hence we have two (or more) shared libs that will be “candidates” for usage at runtime. They will not link at the same time, but the .lib part of those shared libraries that is required for linking needs to be identical, so that in the end, the consumer app is built agnostic to the implementation.

Anyway, that’s probably not even necessary for the cmake config questions.

Thanks,’
dunc

Okay, I got confused by your way too humble introduction :slight_smile:
It seems that you are quite above the entry level, for which I was targeting my example.
Then it’s better to wait for a response from more experienced CMake users/maintainers, as I am still a sort of beginner myself.

Thank you for your kind words :blush:

I try not to presume anything about my knowlege when asking questions, because I know it’s mostly dangerous half-knowledge. In this particular case, I guess I just failed to describe my targets properly, so thank you for helping me pointing them out again more clearly.