c++language-lawyerc++20one-definition-rulec++-modules

C++ Modules, the Standard Library, Third-Party Libraries, and the One Definition Rule


Every year or two I decide to try out using C++ modules, but every time I run into issues, usually with the compiler. But this time my issue seems to be in the specification itself, and I can't seem to find a way around it.

Consider a third-party library with a header foo.hpp. In foo.hpp, it includes <string>. Assume that, for some reason (maybe because of weird macro usage), foo.hpp can't be compiled as a header unit, so that it must be #included. My issue comes from trying to use both this library and <string> at the same time.

Idea #1:

module;

#include <foo.hpp>

export module A;

import <string>;

...

The issue here, as I understand it, is that <string> is included in foo.hpp, but then the header guards from including <string> aren't applied when importing <string>, causing odr violations.

Idea #2:

export module A;

import <string>;
#include <foo.hpp>

...

Because header units do define macros, this fixes the odr violation, with the header guards kicking in when foo.hpp includes <string>. However, this ends up defining all of the contents of foo.hpp as entities in the module A, which is just incorrect and causes all sorts of issues.

Idea #3:

module;

#include <foo.hpp>

export module A;

...

The idea behind this one is that if foo.hpp is already including <string>, why bother trying to get it again? The issue is that this doesn't work in more complicated situations. Say that rather than importing <string>, I was actually importing another module that export imported <string>? Or maybe something more complicated? In the end this solution doesn't scale.

Idea #4:

module;

#include <string>
#include <foo.hpp>

export module A;

...

This works, but is less than ideal for obvious reasons.

My question is, is there any way around this issue? Or are C++ modules just impossible to use with third-party libraries that are not modular, which basically makes C++ modules useless?

Note that I'm looking for what the C++ standard says, and not what any particular implementation does, because implementations are still figuring out exactly what they're doing when it comes to modules. However, I'll accept another answer if it's of the form "The C++ standard doesn't allow this, but all implementations are working together to make a workaround work."

This question ran into the same issue in a slightly different fashion, but the question was about identifying the issue, not solving it. The asker said they would post another question about how to solve it but never did. This question asks basically the same thing, but it was unfocused and it seems that everybody misinterpreted what they were asking.


Solution

  • The standard says ([std.modules]/5):

    A declaration in the standard library denotes the same entity regardless of whether it was made reachable through including a header, importing a header unit, or importing a C++ library module.

    So it's an implementation bug if mixing #include and import does not work well (a hard-to-fix bug, AFAIK).