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#defineing 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
detailnamespace 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'texportit. But this requires explicitly tagging your interfaces with theexportkeyword... 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#definethat resolves toexportor 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
inlinecertain class members of exported classes, as modules do not implicitlyinlinedefined members of non-template classes the way a non-module build does. Fortunately, addinginlinewill 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#includeevery 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.