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

VS 2022 17.5 Preview 1 and std module #3195

Closed
matbech opened this issue Nov 8, 2022 · 23 comments
Closed

VS 2022 17.5 Preview 1 and std module #3195

matbech opened this issue Nov 8, 2022 · 23 comments
Labels
modules C++23 modules, C++20 header units question Further information is requested resolved Successfully resolved without a commit

Comments

@matbech
Copy link

matbech commented Nov 8, 2022

As per the Changelog, Standard Library Module std is available in VS 2022 17.5 Preview 1. Is there something special that needs to be done as it doesn't seem to be working out of the box. I'm getting the following compile error when importing std:
error C2230: could not find module 'std'
with /experimental:module
The "V143 modules (experimental)" individual feature is installed as well.
Importing the legacy std.core module works without any errors though.

I got it working by creating a new C++ static library project, then I added the std.ixx module file from the modules folder:
C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32019\modules
$(VCToolsInstallDir)modules\std.ixx
and then I referenced this new project.

Is there another way?

@matbech matbech added the question Further information is requested label Nov 8, 2022
@davidhunter22
Copy link

davidhunter22 commented Nov 9, 2022

I just added the std.ixx file directly to my project, I didn't need a separate library.

I'll cheekily add my question here. If i do

#include <string>
import std;

should I expect it to work?

@matbech
Copy link
Author

matbech commented Nov 9, 2022

@davidhunter22 Assuming you meant import std; and not using std; that was not working with the legacy modules (std.core) either because the #include comes after the import. However, the following where the #include comes before the import was working:

#include <type_traits>
import std;
std::vector<int> items;

which now fails to compile:
C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32019\include\xtr1common(42,47): error C2572: 'std::enable_if': redefinition of default argument: parameter 1

@davidhunter22
Copy link

Thanks @matbech I updated my question

@fsb4000
Copy link
Contributor

fsb4000 commented Nov 9, 2022

Is there another way?

I think you don't need another static library project. Just add std.ixx to your Source Files and it works.

(Add -> Existing Item... -> C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32019\modules\std.ixx)

изображение

@fsb4000
Copy link
Contributor

fsb4000 commented Nov 9, 2022

should I expect it to work?

@davidhunter22 , probably it should work, but the compiler has bugs.

I have found in the Discord channel:
изображение

@davidhunter22
Copy link

One sad thing I noticed with adding the .ixx file to a project. If you have a solution with 200 projects and all of them add the .ixx file then you compile the standard library module 200 times. This does only happen on the first build but :-(
Hopefully MS will tell us a better way than this so that you only build it once per solution

@davidhunter22
Copy link

Not being able to mix "import std" with classic includes is a real pain. Any large body of code these days probably uses third party libraries, in my case from vcpkg. Almost all of them will #include standard library header in their headers. So you can't migrate your code to use import std until every library you depend on either has a build varaint that uses the std module or has a macro to guard #includes in it's headers. As in

#if defined( DONT_INCLUDE_LEGACY_HEADERS ) #incldue <string> #endif

@StephanTLavavej StephanTLavavej added the modules C++23 modules, C++20 header units label Nov 9, 2022
@matbech
Copy link
Author

matbech commented Nov 10, 2022

One sad thing I noticed with adding the .ixx file to a project. If you have a solution with 200 projects and all of them add the .ixx file then you compile the standard library module 200 times. This does only happen on the first build but :-( Hopefully MS will tell us a better way than this so that you only build it once per solution

I believe using a static library (add the std.ixx) which is then referenced by the other projects in your solution is the right approach.

For the mixed headers issue, I hope @StephanTLavavej can provide some guidance.

@StephanTLavavej
Copy link
Member

Thanks for trying to use the Standard Library Modules as soon as possible! 😻

We're still working on adding build system support so import std; will work automatically. (I believe I'll need to set an environment variable indicating where the modules directory is, and add a JSON file indicating how std.compat depends on std.) My apologies for not getting to this yet - it was important to merge the library and test code as early as possible to prevent regressions from ongoing compiler work, but then I got busy with other tasks (e.g. the big /clr PR #3194). At this point, I'm not sure if we can add build system support in time for the production release of VS 2022 17.5, but I hope everything will be ready for VS 2022 17.6 (at which point the compiler should also be working much more robustly).

Answering specific questions/topics (please let me know if I missed anything; thanks @fsb4000 for quoting an earlier reply of mine from Discord):

  • The /experimental:module compiler option and the "C++ Modules for v143 build tools (x64/x86 - experimental)" VS installer component are not needed for the C++23 Standard modules.
  • Mixing #include <meow> and import std; in any order should work, according to the Standard. However, this is an extremely stressful scenario for the compiler (as it involves noticing that the classic header and the named module are providing the same machinery, and that they should be considered identical instead of duplicate definitions). I've been told by our modules dev @cdacamar to expect that this will totally fail until major compiler work happens, so I have not added any test coverage for this scenario.
    • This is indeed a headache for migrating large codebases with third-party libraries. I don't have any further suggestions, other than my sympathies - the compiler team is aware that this scenario is important and they're working towards it, but it's going to take a while.
  • Until automatic build system support is added, you are responsible for building std.ixx into std.ifc and std.obj. Note that, as long as the command lines match, you can build it once, and then consume it from a hundred projects. Centralizing this depends on what build system you are using, but there should be no fundamental challenge to making this work. Arranging for a static library to be produced seems reasonable. (You can build both std.ifc and std.compat.ifc, and create a .lib containing std.obj and the near-empty std.compat.obj, so that projects can consume either import std; or import std.compat; as they see fit.)
  • Unlike the experimental modules, we are intentionally not distributing prebuilt IFCs/OBJs for the Standard modules, and have no plans to ever do so. Although this requires build systems to do a bit more work, it means that the Standard modules will fully respect all compiler options (release/debug, static/dynamic, Standard modes in the future, warning suppressions, re-enabling removed machinery, etc.).

@davidhunter22
Copy link

I have a work around for including third party libraries that then do old style #includes of standard library headers. If you have the tiniest shred of dignity you may want to look away now.

So the MS standard library headers still use old style macro include guards not #pragma once. So you can simply define all these macro guards yourself and then include the header file that does things like "#include ". Attached is a header that defines all the ones I think you need. Oddly you can't upload .h files so I renamed it to .txt

MacroGuards.txt

@matbech
Copy link
Author

matbech commented Nov 11, 2022

I have a work around for including third party libraries that then do old style #includes of standard library headers. If you have the tiniest shred of dignity you may want to look away now.

So the MS standard library headers still use old style macro include guards not #pragma once. So you can simply define all these macro guards yourself and then include the header file that does things like "#include ". Attached is a header that defines all the ones I think you need. Oddly you can't upload .h files so I renamed it to .txt

MacroGuards.txt

This does not work at all. If you define the guards then any C++ headers won't get included and the 3rd party headers which require those will not compile. E.g.

#include "MacroGuards.h"
#include <atlbase.h>

atlbase.h includes <type_traits> which won't get included because of the define in MacroGuards.h.

@davidhunter22
Copy link

davidhunter22 commented Nov 11, 2022

I am including the headers in my code which has an "import std;" so all the standard library types are aready declared before I include the third party header. I should have mentioned you need to do

import std;
#include "MacroGuards.h"
#include <atlbase.h>

I am already using this successfully for a number of libraries, such as type_safe, pugxml and catch2 that I use via vcpkg without modfiying them in any way.
Sometimes the third party header you include assumes some things are in the global namespace like uint8_t. I also add a using std::uint8_t before I do the include to fix those. You could also "import std.compat" but I am keen to avoid that as not getting all the legacy junk is one of the reasons I am doing this.

@matbech
Copy link
Author

matbech commented Nov 11, 2022

It doesn't work for me when importing the std module in a precompiled header (e.g. pch.h) and I do include the 3rd party libraries there.

@davidhunter22
Copy link

davidhunter22 commented Nov 11, 2022

Ah good point. I switched off pch stuff in my build before trying this out mostly because I feel the standard library module is sort of a replacement for a PCH containing the standard library stuff. If you have PCH files including third party library stuff that then include standard library stuff I am well out of my depth! I am sure there are other corner cases where this hackery does not work but as I said it does work for reasonably complex headers like those from catch2

@StephanTLavavej
Copy link
Member

Instead of defining our preprocessor guard macros to suppress classic includes, there's a less-wacky (still fairly wacky) technique that may work: create a bunch of fake headers named algorithm, bitset, etc. (one for every Standard header) whose content is simply #pragma once followed by import std;. You should then be able to use /I pointed to that directory, which will be looked up before the toolset include directories (this is a little-known but useful property of /I). This should non-intrusively "overlay" the import std; declarations for third-party libraries to pick up, and should interact reasonably with PCHes (although I haven't tested any of this). Good luck! 😸

@matbech
Copy link
Author

matbech commented Nov 17, 2022

I have given this a try, however without success. The compiler gets confused after it encounters an import std; statement in the PCH and it will start throwing errors like:

1>C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.35.32019\include\vector(2212,36): error C2440: 'static_cast': cannot convert from 'std::_Iterator_base12' to 'std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<_Ty>>> &'
1>        with
1>        [
1>            _Ty=int
1>        ]

for code which compiles fine otherwise:

#include "pch.h"

import std;

int main()
{
  std::vector<int> test;
  test.resize(5);
}

pch.h

#pragma once

// All CRT headers must be included before the first import statement
#include <crtheaders.h>

import std;

@davidhunter22
Copy link

This problem was found in trying to build the google_test library with the std module

I found a specific issue of mixing the std module and header files that I am finding hard to work around. In most cases I can just not include the old header file and things are fine however I can't do this with crtdbg.h.
crtdbg.h eventually inludes vcruntime_new.h This contains the following

namespace std
{
    enum class align_val_t : size_t {};
}

which then causes a type redefinition error.
I could try to do some hideous macro stuff to work around this but am wondering if anyone has a good solution.

Ideally the vcruntime_new.h would use the _BUILD_STD_MODULE macro to guard this but I guess it's a VC runtime header so maybe that isn't allowed

@StephanTLavavej
Copy link
Member

Automatic MSBuild suuport was implemented with "Scan Sources for Module Dependencies".

@StephanTLavavej StephanTLavavej added the resolved Successfully resolved without a commit label Sep 26, 2023
@MikeGitb
Copy link

Great!
Any idea when this will land?

@jbrezina
Copy link

Great! Any idea when this will land?

@MikeGitb If I get it right it's already there - Project properties -> C/C++ -> General -> Scan Sources for Module Dependencies

@MikeGitb
Copy link

MikeGitb commented Sep 28, 2023

Thx, will have a look during the weekend.

Edit: Can confirm with 17.7.4 - great stuff. Thanks.
Now, if we get mix and match with standard library headers somehow, I could start using it in my hobby projects.

@isudfv
Copy link

isudfv commented Oct 18, 2023

Great! Any idea when this will land?

@MikeGitb If I get it right it's already there - Project properties -> C/C++ -> General -> Scan Sources for Module Dependencies

What should I do to use this in CMake?

@Perl99
Copy link

Perl99 commented Oct 19, 2023

Something like this worked for me:

std/CMakeLists.txt

cmake_minimum_required(VERSION 3.26)

project(std)

add_library(${PROJECT_NAME} STATIC)
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23)

# hacky :/
string(REGEX REPLACE "\/bin\/Hostx(64|86)\/x(64|86)\/cl\.exe" "" MSVC_ROOT ${CMAKE_CXX_COMPILER})

# std.ixx
configure_file("${MSVC_ROOT}/modules/std.ixx" "${PROJECT_BINARY_DIR}/std.ixx" COPYONLY)
string(JOIN " " STD_REF_PARAM /reference "${PROJECT_BINARY_DIR}/CMakeFiles/${PROJECT_NAME}.dir/std.ifc")
set(STD_REFERENCE ${STD_REF_PARAM} CACHE INTERNAL "std.ifc compiler parameter")

# std.compat.ixx
configure_file("${MSVC_ROOT}/modules/std.compat.ixx" "${PROJECT_BINARY_DIR}/std.compat.ixx" COPYONLY)
set(STD_COMPAT_REFERENCE /reference "${PROJECT_BINARY_DIR}/CMakeFiles/${PROJECT_NAME}.dir/std.compat.ifc" CACHE INTERNAL "std.compat.ifc compiler parameter")

target_sources(${PROJECT_NAME}
		PUBLIC
		FILE_SET cxx_modules TYPE CXX_MODULES BASE_DIRS ${PROJECT_BINARY_DIR}
		FILES ${PROJECT_BINARY_DIR}/std.ixx ${PROJECT_BINARY_DIR}/std.compat.ixx
)

target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR})

myproject/CMakeLists.txt

cmake_minimum_required(VERSION 3.26)

project(myproject)

add_library(${PROJECT_NAME} STATIC)

target_sources(${PROJECT_NAME} ...)

target_compile_options(${PROJECT_NAME} PUBLIC ${STD_REFERENCE})
target_link_libraries(${PROJECT_NAME} PUBLIC std)

target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
modules C++23 modules, C++20 header units question Further information is requested resolved Successfully resolved without a commit
Projects
None yet
Development

No branches or pull requests

8 participants