I'm authoring a templated header only library. It has no state, no global variables, no .cpp that needs to be compiled.
Is it possible to export/consume this as a module? How? What are the benefits? What are the pitfalls?
There are some convenience macros that I probably want the user to have. What about those?
I have found an example using #ifdef ...
to cater for both module and old-school cases. I think I want to avoid that.
Broadly speaking, that is not the best way to do this. You can do it by just importing the header as a header unit with
import <header_file.hpp>;
but you'll be losing some important aspects of modules. You're going to need to do a limited amount of#define
ing if you want your module version to work well.Just about every header-only library will have some declarations that are considered part of the library, but it will also have some which are not. These are usually put into a
detail
namespace or other attempts are made to hide them from users.Modules have a mechanism for doing that:
export
. Or more to the point, they have a way to not put something in your interface: don'texport
it. But this requires explicitly tagging your interfaces with theexport
keyword... which won't work in non-module builds. So you need a macro to say if the build is a module build or not, so that you can have a#define
that resolves toexport
or to nothing.You could do an
export namespace my_namespace {};
to try to export everything, but that can have... unpleasant side effects.You also may need to explicitly
inline
certain class members of exported classes, as modules do not implicitlyinline
defined members of non-template classes the way a non-module build does. Fortunately, addinginline
will work fine on non-module builds.Writing a header-only library and a module interface file for it, such that a user can include whichever they prefer, isn't too difficult. But it does require some care, and to best take advantage of module features, you should use some macros.
The primary module interface unit would look like this:
The
<external header includes>
is very important. You need to#include
every file that your library explicitly includes. If you include parts of the C++ standard library, then include them here.The point of this is to stop those headers from shoving the contents of those headers into the module's purview. You want your inclusion of your library to be the only code that gets shoved into the module. So you include those headers in the global module fragment, and use their include-guards to prevent later inclusion.
And yes, this does mean you have to keep two separate lists of these headers. You can write a tool to look through all of your headers and build that list for you, but one way or another it's still a thing you need to have.
At the top of your library headers, after any include guards, you need to have this:
This allows you to decorate anything you want to export with
MY_MODULE_NAME_EXPORT
:If you're in a module build, that will export the function. If you aren't, then it won't.