microsoft / STL

MSVC's implementation of the C++ Standard Library.
Other
10.11k stars 1.49k forks source link

VS 2022 17.5 Preview 1 and std module #3195

Closed matbech closed 1 year ago

matbech commented 1 year ago

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?

davidhunter22 commented 1 year ago

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 commented 1 year ago

@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 commented 1 year ago

Thanks @matbech I updated my question

fsb4000 commented 1 year ago

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 commented 1 year ago

should I expect it to work?

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

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

davidhunter22 commented 1 year ago

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 commented 1 year ago

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

`

matbech commented 1 year ago

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 commented 1 year ago

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

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):

davidhunter22 commented 1 year ago

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 commented 1 year ago

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 which won't get included because of the define in MacroGuards.h.

davidhunter22 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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! :smile_cat:

matbech commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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

MikeGitb commented 1 year ago

Great! Any idea when this will land?

jbrezina commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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)