'Error with C++ module fragments and namespaces?

I want to build a single module with several implementation files. One file with structs is used by two implementation files.

When I compile I get the following error in gcc trunk (12) and also 11:

$HOME/bin/bin/g++ -std=c++20 -fmodules-ts -g -o test_optimization test_optimization.cpp optimization.cpp opt_structs.cpp golden.cpp brent.cpp
test_optimization.cpp: In function ‘int main()’:
test_optimization.cpp:13:3: error: ‘emsr’ has not been declared
   13 |   emsr::MinBracket<double> mb;
      |   ^~~~
test_optimization.cpp:13:20: error: expected primary-expression before ‘double’
   13 |   emsr::MinBracket<double> mb;
      |                    ^~~~~~

There could be several non-exclusive things going on from my not understanding modules at all to incomplete g++ implementation and on and on...

Main tester (test_optimization.cpp):

#include <cmath>

import emsr.opt;

int
main()
{
  emsr::MinBracket<double> mb;
}

Module interface (optimization.cpp):

export module emsr.opt;
export import :golden;
export import :brent;

One implementation module (golden.cpp):

module;

#include <cmath>
#include <numbers>

export module emsr.opt:golden;

export import :structs;

namespace emsr
{
  template<typename Func, typename Real>
    void
    mean_bracket(Func func, MinBracket<Real>& mb)
    { }

  template<typename Func, typename Real>
    FuncPt<Real>
    golden(Func func, MinBracket<Real>& mb, Real tol)
    { return FuncPt<Real>(); }
} // namespace emsr

Another implementation module (brent.cpp):

module;

#include <cmath>
#include <numbers>

export module emsr.opt:brent;

export import :structs;

namespace emsr
{
  template<typename Func, typename Real>
    FuncPt<Real>
    brent(Func func, Real ax, Real bx, Real cx, Real tol)
    { return FuncPt<Real>(); }

  template<typename Func, typename Deriv, typename Real>
    FuncPt<Real>
    brent_deriv(Func func, Deriv deriv, Real ax, Real bx, Real cx, Real tol)
    { return FuncPt<Real>(); }
} // namespace emsr

Structs module fragment (opt_structs.cpp):

export module emsr.opt:structs;

namespace emsr
{
  template<typename Real>
    struct FuncPt
    {
      Real arg;
      Real val;

      FuncPt() = default;
    };

  template<typename Real>
    struct MinBracket
    {
      FuncPt<Real> a;
      FuncPt<Real> b;
      FuncPt<Real> c;
    };

  template<typename Real>
    struct DerivPt
    {
      Real arg;
      Real val;
      Real der;

      DerivPt() = default;
    };
}


Solution 1:[1]

I'm trying to figure out modules with gcc as well, I can't comment so I'll post here.

The gcc documentation says:

Standard Library Header Units:

The Standard Library is not provided as importable header units. If you want to import such units, you must explicitly build them first. If you do not do this with care, you may have multiple declarations, which the module machinery must merge—compiler resource usage can be affected by how you partition header files into header units.

No new source file suffixes are required or supported. If you wish to use a non-standard suffix (see Overall Options), you also need to provide a -x c++ option too.2

According to this blog post, with gcc you need to compile headers separately from what I understand. They use iostream from the standard library as an example.

While Clang comes with a mapping of standard headers to modules. Using GCC, we need to compile iostream manually.

> g++ -std=c++20 -fmodules-ts -xc++-system-header iostream
> g++ -std=c++20 -fmodules-ts hello_modular_world.cc

It also looks like they use import <header>; syntax instead of include.

> cat hello_modular_world.cc
import <iostream>;
int main() {
    std::cout << "Hello Modular World!\n";
}

I'm not sure if this is acceptable but it works, this is what I did:

I compiled the system headers:

> g++-12 -std=c++20 -fmodules-ts -xc++-system-header cmath
> g++-12 -std=c++20 -fmodules-ts -xc++-system-header numbers

I changed the code like this:

opt_structs.cpp

export module emsr.opt:structs;

export namespace emsr {
template <typename Real> struct FuncPt {
  Real arg;
  Real val;

  FuncPt() = default;
};

template <typename Real> struct MinBracket {
  FuncPt<Real> a;
  FuncPt<Real> b;
  FuncPt<Real> c;
};

template <typename Real> struct DerivPt {
  Real arg;
  Real val;
  Real der;

  DerivPt() = default;
};
} // namespace emsr

golden.cpp

module;

import <cmath>;
import <numbers>;

export module emsr.opt:golden;

export import :structs;

export namespace emsr {
template <typename Func, typename Real>
void mean_bracket(Func func, MinBracket<Real> &mb) {}

template <typename Func, typename Real>
FuncPt<Real> golden(Func func, MinBracket<Real> &mb, Real tol) {
  return FuncPt<Real>();
}
} // namespace emsr

brent.cpp

module;

import <cmath>;
import <numbers>;

export module emsr.opt:brent;

export import :structs;

export namespace emsr {
template <typename Func, typename Real>
FuncPt<Real> brent(Func func, Real ax, Real bx, Real cx, Real tol) {
  return FuncPt<Real>();
}

template <typename Func, typename Deriv, typename Real>
FuncPt<Real> brent_deriv(Func func, Deriv deriv, Real ax, Real bx, Real cx,
                         Real tol) {
  return FuncPt<Real>();
}
} // namespace emsr

optimization.cpp (the same)

export module emsr.opt;
export import :golden;
export import :brent;

test_optimization.cpp

import <cmath>;
import emsr.opt;

int main() {
  emsr::MinBracket<double> mb;
  return 0;
}

All I did was change #includes to import <header>; and exported the namespace emsr.

Then I compiled like this:

> g++-12 -std=c++20 -fmodules-ts -g -c opt_structs.cpp
> g++-12 -std=c++20 -fmodules-ts -g -c brent.cpp
> g++-12 -std=c++20 -fmodules-ts -g -c golden.cpp
> g++-12 -std=c++20 -fmodules-ts -g -c optimization.cpp
> g++-12 -std=c++20 -fmodules-ts -g -c test_optimization.cpp
> g++-12 -std=c++20 test_optimization.o opt_structs.o brent.o golden.o optimization.o -o test_optimization

> ./test_optimization

Note that I had to compile opt_structs.cpp first, then golden.cpp and brent.cpp, then optimization.cpp and then the main file.

Again, I don't know if this is the best or right way, I'm still learning, but this is what I did trying to figure this out.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Todd Fulton