Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Import std module #46

Closed
wants to merge 4 commits into from
Closed

Conversation

davidhunter22
Copy link
Contributor

These changes optionally all the library to be built and consumed using import std; rather than classic old style #include <meow>. This currently works for Visual Studio and Clang using libc++. It required the latest CMake 3.30 and Clang versions.
This is not an attempt to create a module from the library, which would be nice to have, only to use the std module.

In CMake everything is guarded by a STRONG_TYPE_IMPORT_STD_LIBRARY variable. In code stuff is guarded by a macro of the same name. By default STRONG_TYPE_IMPORT_STD_LIBRARY is OFF is CMake so by default everything works exactly as now.

Many of the code changes are of the form

#if defined(STRONG_TYPE_IMPORT_STD_LIBRARY)
    import std;
#else
    #include <type_traits>
    #include <initializer_list>
    #include <utility>
#endif

Note both MSVC and clang can do an old style #include before an import std; but both give error when you do the reverse. For this reason in the tests the #include <catch2.hpp> had to be moved to be the first include as it does unguarded #include <meow>.

So as as user I can now #include strong_type.hpp with STRONG_TYPE_IMPORT_STD_LIBRARY defined and use import std;

One advantage of doing this is that the std module doesn't leak macros and global namespace things like uint32_t so you now know that you don't rely on things like that. I did fix a couple of cases where the library used ptrdiff_t from the global namespace.
Note MSVC does have a bug where it does leak stuff into the GMF, see microsoft/STL#1694 for details.

Anyway hope this is of interest, let me know if you want changes or documentation.

Macro guard out all #includes of std headers
Reorder catch2.hpp to be included first as it includes std headers
Note the option command had the off in the wrong place, it should be at the end so I fixed that
@davidhunter22
Copy link
Contributor Author

Oh i did change on other thing. You had

option(STRONG_TYPE_UNIT_TEST off "Decide whether to build unit tests or not")

I believe that the ordering is wrong here, see https://cmake.org/cmake/help/latest/command/option.html#option so I changed it to

option(STRONG_TYPE_UNIT_TEST "Decide whether to build unit tests or not" OFF)

Note the default is OFF so you could omit that but I like it being explicit

Copy link

codecov bot commented Sep 1, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 100.00%. Comparing base (d6be4b9) to head (6f43b6b).
Report is 7 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main       #46   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           23        23           
  Lines          338       356   +18     
=========================================
+ Hits           338       356   +18     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@rollbear
Copy link
Owner

rollbear commented Sep 1, 2024

Wow. Thanks. I've been wanting to do this for some time.

It's a large change, so it'll take a bit of time for me to go through all the details. However, there are two issues that I see at a short glance:

No change to the CI build scripts, so none of your changes are tested. Please fix that.

You changed the include order in the tests so that Catch2 is included before the feature being tested. That is, IMO, totally wrong. Don't do that. You always want to include the functionality being tested first, to ensure you don't rely on any accidentally included dependency.

A related, but different thing. I want to make this library itself a module, but I lack the knowledge and time to do so. Do you have the time (you appear to have the knowledge)? It'd be awesome if you could assist with that. (as a separate PR).

@davidhunter22
Copy link
Contributor Author

1) Change to CI build scripts

This was mainly because I have zero experience in these sort of scripts. Is this a separate appveyor.yml file or should it be added to the current one? I'm also not sure how to test them, can I run them on my branch?
I can have a go if you like but it might need someone who knows what there doing to review it.

2) Reorder of <catch2.h>

I completely agree with you that this is the wrong thing to do. However it was necessary to get the tests to build. Catch2 does not have a similar mechanism to build optionally with import std;. I did try, see catchorg/Catch2#2575, but they rejected it because there view is that random mixtures of import and #include should be fine.

Therefore is you include it second you get the sequence

import std;
#include <string>

This fails on both MSVC and clang. Note that the reverse works on both. According to the standard this should work.
I have seen comments from MSVC that this ordering will be very hard to get to work, in fact they didn't seem to even have a good idea of how to do it.I suspect clang has run into the same issue. I believe this is because in the ordering

#include <string>
import std;

The machinery that does the import can check to see if there is a definition already present and ignore it if there is one.
The other ordering is just the preprocessor, which is dumb, and things clash with stuff already bought in by the import.
Note I completely disagree with allowing random mixtures of import and #include. I think it was the worst decision in the whole standardization of modules. I think it was pushed in by various mega corps who think it will make it easier to migrate to modules. Personally I think it will do the opposite and slow thing down, for instance the rejection of my PR to catch2.

Anyway the upshot is that I could do some macro thing like

#if defined(STRONG_TYPE_IMPORT_STD_LIBRARY)
#include "catch2.hpp"
#endif
#include <strong_type/affine_point.hpp>
#if !defined(STRONG_TYPE_IMPORT_STD_LIBRARY)
#include "catch2.hpp"
#endif

Although I think this is ugly

Another possibility is to have file equivalent to empty.cpp to check the idempotence of the headers so affine_point.cpp would be

#include <strong_type/affine_point.hpp>

So there would be one of these cpp files per header.
Note these cpp files could be in the test directory of the source directory. In the source directory they would only get built if tests were being build.

3) Making a module of strong_type

I have done much less of this. In fact a year or so ago I immediately ran into the issue of including other libraries hence my quest to "fix" them. However I'm willing to have a go. I had a couple of comments/questions

a) There is no such thing as a "interface" or header only module I'm assuming you are OK with this
b) I assume you are fine with only one module
c) I would follow a method similar to STL, see https://github.com/microsoft/STL/blob/main/stl/modules/std.ixx

One sad thing is that I believe MSVC, Clang and GCC all use different file extensions :-(
I have only created modules on MSVC but I am assuming the magic of CMake hides all these things for me. I would probably try to get MSVC work first mainly because I'm mostly a Visual Studio developer.

BTW are you going to CPPCon this year, in a week in fact, if so hope to see you there.

@rollbear
Copy link
Owner

rollbear commented Sep 7, 2024

1 & 2. Hmm. I see. I'll try to do something. A separate .cpp file for each header is a bit of an abomination, but maybe it's OK to have as something that is only used by CI.

  1. No. I've limited myself to "only" four conferences this year, and none of them are in North America. Heading for NDC TechTown in Norway next week, and then a one day conference "C++ under the Sea" in the Netherlands in October.

@davidhunter22
Copy link
Contributor Author

Note there is an ugly way to work around the ordering issue of the catch header. The problem is that no compiler currently supports

import std;
#include <vector>

and they probably won't for a while. The work arounds I know of are

  1. Every standard library header file has macro guards. So create a header file that defines all these macro guards, that way when you #include <vector> the preprocessor macros out all the contents. I attached my version of this which is how I've been using strong_type up to now.
    MacroGuards.txt
  2. Create an empty file for every standard library header, so an empty <vector> and change you build so these are included before the real ones.

Both of these mechanisms were discussed here microsoft/STL#3195

Note MS are still doing a lot of work on modules in fact microsoft/STL#4375 was got me looking at strong_type again. It removed another bunch of ugliness

@rollbear
Copy link
Owner

Hmm, odd. I handled this manually because I wanted to ensure the CI was running with support for "import std;" before merging, and apparently github lost track of where the change came from. Anyway, the support just landed in main. Huge thank you for your contribution, and apologies for taking so long.

@rollbear rollbear closed this Dec 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants