Using legacy header files as c++20 modules
Asked Answered
C

3

15

I am in the process of converting my old code to something that is importable as c++-modules. The problem is that i would want it to still work and be easily maintained as the old header/source version. How do I do this (if possible).

Is it possible to create a module that export the content of a header? (Any other solution that lets you maintain old .cpp/.h files and module files is also accepted)

Toy example:

// In vector.h
template <typename T>
struct Vector {
    T x, y;
}

// In .cppm
export module vector;
// #include "vector.h"
// Export struct/class Vector from header

What i have tried is just export Vector in different versions, with and without templates etc.

Bonus question: Can you do this for the std lib? (for exmaple iostream, or string)

Crichton answered 17/2, 2021 at 10:17 Comment(7)
You can use import "header"; if "header" is a standard library header or meets some implementation-defined criteria.Craner
How does that work with precompilation? And do you have a link that i could read more from?Semantic
vector-of-bool.github.io/2019/10/07/… Note that part of it might be outdated.Craner
@Craner BTW, I've tried doing import <iostream>; in CLang for Windows and it issued error: header file <iostream> (aka '<PATH_TO_MSVC>\include\iostream') cannot be imported because it is not known to be a header unit. Using latest CLang and MSVC 2019 headers.Gaylordgaylussac
Do you want the header and module versions to be able to coexist in the same program?Reduplication
If I understand the question correctly. No, in the same program, the user will only use one or the other (headers or modules). But i guess that some of the headers internally might include other headers.Semantic
Very interesting how to solve this question! I need same task for my project.Gaylordgaylussac
C
4

I will take examples from one of my own library I have converted to support both old style headers and modules.

This example is for a header only library, I will add an example of a library that is not header only when I have one.

Header only/module lib

Make export optional. In my case I have defined a file with macros that adds export if the project is used with modules.

export.h

#pragma once

// Macros for handling compatability with/without modules

#ifdef matmath_use_modules

#ifndef matmath_export
#define matmath_export export
#endif

#else

#ifndef matmath_export
#define matmath_export
#endif

#endif

Then the headers for the library will be defined almost as usual, but with the minor tweak that the export macro is used instead of the export keyword for symbols that you want, and all includes are removed.

vec.h

// vec.h
#pragma once

#include "export.h"

#ifndef matmath_use_modules

#include <cmath>
#include <ostream>
#if __cplusplus >= 201103L
#include <tuple>
#endif

#include "pi.h"

#endif

matmath_export template <typename T>
class VecT {
public:
    T x = 0, y = 0, z = 0;

    constexpr VecT() = default;

   ...
};

Then you can use your header in a module of choice and activate the right macros.

vec.cppm

module;

#include <cmath>
#include <ostream>

import matmath.pi;

export module matmath.vec;

#define matmath_use_modules
#include "matmath/vec.h"

// If your file has a regular cpp-file you could include that
// here in the same fashion
// #include "path/to/vec.cpp"

And finally: When using the class you can choose to use the project through your module files, or through the header files (combining headers and modules for the lib yields a lot of pain though).

Enabling a small project to be built with and without modules

If you just want to build a small project with and without modules, you can just remove all export related statements and convert import statements to includes. This approach requires you to do changes to your code, or at least have a separate step that creates a copy of your code without the module related code. https://github.com/mls-m5/rym/blob/master/non-module-build.sh

Crichton answered 29/3, 2021 at 7:20 Comment(0)
G
2

Recently I created two answers first one regarding how to create module (in CLang) out of any old-style header. Second one regarding how to create modules out of STD headers.

Using these two answers now you can just code as before, in old .hpp-based style. Then in usage code you can do following:

#if USE_MODULES
    import std_mod; // Module created out of all STD headers.
    import my_lib; // Module created out of my_lib.h.
#else
    #include <iostream>
    #include <vector>
    // ... All other tons of STD headers ...

    #include "my_lib.h"
#endif

int main() { use_my_lib(); }

Using headers as modules or as includes are totally interchangable, in both cases you'll have even macros, because CLang's header modules export macros as well.

Also you don't need any special syntax of creating my_lib library. You don't need to use export or module keywords at all, you just code as in old-style C++ without modules and then your headers are easily converted to modules.

I did such thing in my quite huge C++ project. It was coded totally without modules and then automatically in my build system I converted all headers to modules, which greatly improved compilation speed. Of cause I implemented my own build system that is aware of includes and modules and automatically does conversion on flight without changing original files.

Gaylordgaylussac answered 29/4, 2021 at 19:20 Comment(1)
Hey, do you have a link to your build system. I have also started creating my own to support modules, and it would be nice to exchange ideas github.com/Laserskold/matmake2 (writing here because there is no other obvious ways to send messages)Semantic
C
1

a) You want to keep your code (mostly) as-is, but put wrap it inside modules.

That would result in something similar to PCH, but standardized on a language level. Under the assumption that no code changes are neccessary, this has the following advantages:

  • Users can use your library both as header files as well as modules (if they themselves update to C++20)
  • If the library is consumed as modules, you might see benefits in compilation time (depends on the project though)

The most simple way to achieve this is putting all your public headers in a single module:

// your_lib.cppm
module;
// global module fragment here
#include "your_header1.hpp"
#include "your_header2.hpp"
// ...

export module your_lib;

You should read about importable headers (the syntax already shown in the comments, e.g. import <iostream>). As far as I know, what is an importable header is implementation defined. So in general, you would not have to use the global module fragment.

b) You want to modularize your code base, but allow others to still consume header files.

The only reason (which is a pretty good reason) to want something like this is to support the usage of your library for users that still use an older standard of C++20.

The solution you were hoping for is not really possible. Sorry. There is no way back from module to header. Everything internal to your library can be completely modularized. Then you have to make a decision. Keep the public interface as headers, or modularize it as well. In the first case, see a) on how you would consume your own public headers in your internal modules. In the second case you have to provide a second implementation of your interface as header files. As I said, you only choose b) to support older standard versions. So you'd also have to make sure to not to use any C++20 features in your (header based) public interface.

Castillo answered 28/3, 2021 at 20:3 Comment(1)
Solution (1) is incomplete. Merely including headers in the global module of some module interface does not cause the contents of those headers to be members of the module interface.Itinerary

© 2022 - 2024 — McMap. All rights reserved.