C++ how to move templated headers into cxx modules

236 views Asked by At

after some first tests with CXX Modules I decided to migrate some legacy code. Templated Header seamed like an easy choice since everything is already located in the header.

Since old compilers do not understand modules, we have to keep the classical headers and use the new modules in our project.

I set up my project using this example from kitware blog.

To illustrate my question/problem, let's work with this simple template from TemplateHeader.hpp

#pragma once

#include <iostream>

namespace some_ns
{
  template<typename String_T>
  class TemplateHeader
  {
  public:
    explicit TemplateHeader(const String_T& str);
    void print() const;
  private:
    const String_T m_str;
  };


  template<typename String_T>
  TemplateHeader<String_T>::TemplateHeader(const String_T& str):
    m_str(str)
  {
  }

  template<typename String_T>
  void TemplateHeader<String_T>::print() const
  {
    std::cout << "Value of str is " << m_str << std::endl;
  }
}

My module (module.cxx) basically looks almost the same

// Global module fragment where #includes can happen
module;
#include <iostream>

// first thing after the Global module fragment must be a module command
export module TemplateHeaderFromModule;

export namespace some_ns
{
  export 
  template<typename String_T>
  class TemplateHeaderFromModule
  {
  public:
    explicit TemplateHeaderFromModule(const String_T& str);
    void print() const;
  private:
    const String_T m_str;
  };


  template<typename String_T>
  TemplateHeaderFromModule<String_T>::TemplateHeaderFromModule(const String_T& str) :
    m_str(str)
  {
  }

  template<typename String_T>
  void TemplateHeaderFromModule<String_T>::print() const
  {
    std::cout << "Value of str is " << m_str << std::endl;
  }
}

As you can see I had to copy the implementation (to avoid name collisions I renamed the class though)

Now my question is, how can a avoid the coping, if I'd like to use templated headers and templates in modules together (for old and modern compilers)

My first idea was to include the header in my module.

// Global module fragment where #includes can happen
module;
#include <iostream>

// first thing after the Global module fragment must be a module command
export module TemplateHeader;
#include "TemplateHeader.hpp"

It turns out that includes are only allowed BEFORE the export module

As a second step I tried to add my header as a module to my CMakeLists

target_sources(${MODULE_LIB_NAME}
  PUBLIC
    FILE_SET CXX_MODULES FILES
      module.cxx TemplateHeader.hpp
)

This results in a cmake error TemplateHeader.hpp in a "FILE_SET TYPE CXX_MODULES" but it is not scheduled for compilation. Which makes sense, since a header is not being compiled.

I can only think of the following other options

  1. use cmake to copy TemplateHeader.hpp to a new file ModuleTemplateHeader.cxx and add the export stuff to this new file

  2. somehow tell the compiler to treat *.hpp files as sources (altough this sounds really nasty to me)

Do you have any other suggestions to combine modules with legacy code?

1

There are 1 answers

1
WeededMuffin385 On BEST ANSWER

First thing that I remembered was fork of GLM library that migrated from headers to modules, where types were imported to module via "using" statement: https://github.com/YaaZ/glm/blob/master/glm/glm.cppm ; Then I found, that you can use "alias declarations" with templates, so in the end we have something like this in module file:

module;
#include "TemplateHeader.hpp"
export module TemplateHeaderFromModule;

export namespace another_namespace {
    template <typename T>
    using TemplateHeader = some_ns::TemplateHeader<T>;
}

The problem is that I can't figure out how to export type in some_ns:: namespace (name conflict occurs).