How does a traditional third-party library play with the `module`?
Asked Answered
P

1

0

CMake already supports the most basic use of module, which is good, but the module itself doesn't seem to be ready, or rather the large number of libraries consisting of header files + source files is not ready.

Here is a minimal example for MSVC:

cmake_minimum_required(VERSION 3.25)

if (CMAKE_VERSION VERSION_LESS "3.26")
    # 3.25
    set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "3c375311-a3c9-4396-a187-3227ef642046")
elseif (CMAKE_VERSION VERSION_LESS "3.27")
    # 3.26
    set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "2182bf5c-ef0d-489a-91da-49dbc3090d2a")
else ()
    message(FATAL_ERROR "See `https://github.com/Kitware/CMake/blob/v${CMAKE_VERSION}/Help/dev/experimental.rst`.")
endif (CMAKE_VERSION VERSION_LESS "3.26")
# turn on the dynamic depends for ninja
set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP ON)

project(test)

set(TEMP_CXX_MODULE_PATH ${CMAKE_BINARY_DIR}/temp_module)
if (NOT DEFINED ENV{VCToolsInstallDir})
    message(FATAL_ERROR "Unable to find path to VC Tools in environment variables!")
endif (NOT DEFINED ENV{VCToolsInstallDir})

if (NOT EXISTS $ENV{VCToolsInstallDir})
    message(FATAL_ERROR "Invalid VC Tools installation path!")
else ()
    message(STATUS "Found VC Tools in: [$ENV{VCToolsInstallDir}]")
endif (NOT EXISTS $ENV{VCToolsInstallDir})

if (NOT EXISTS $ENV{VCToolsInstallDir}modules/std.ixx)
    message(FATAL_ERROR "Cannot find std.ixx! Please check MSVC version(${CMAKE_CXX_COMPILER_VERSION})!")
else()
    message(STATUS "Found module std.ixx in: [$ENV{VCToolsInstallDir}modules\\std.ixx]")
endif (NOT EXISTS $ENV{VCToolsInstallDir}modules/std.ixx)

add_executable(${PROJECT_NAME} main.cpp)

target_compile_features(
        ${PROJECT_NAME}
        PUBLIC
        cxx_std_23
)

set_target_properties(
        ${PROJECT_NAME}
        PROPERTIES
        CXX_STANDARD_REQUIRED ON
)

configure_file(
        $ENV{VCToolsInstallDir}modules/std.ixx
        ${TEMP_CXX_MODULE_PATH}/std.ixx
        COPYONLY
)

target_compile_options(
        ${PROJECT_NAME}
        PRIVATE
        /Wv:18
)

target_sources(
        ${PROJECT_NAME}
        PRIVATE
        FILE_SET module_files
        TYPE CXX_MODULES
        BASE_DIRS "${PROJECT_SOURCE_DIR}"
        FILES

        ${TEMP_CXX_MODULE_PATH}/std.ixx

        ${PROJECT_SOURCE_DIR}/src/m1.ixx
        ${PROJECT_SOURCE_DIR}/src/m2.ixx
)

target_sources(
        ${PROJECT_NAME}
        PUBLIC
        FILE_SET header_files
        TYPE HEADERS
        BASE_DIRS "${PROJECT_SOURCE_DIR}"
        FILES

        ${PROJECT_SOURCE_DIR}/src/test_header.hpp
)

File test_header.hpp:

#include <concepts>

namespace test {
    template<typename T>
    concept no_matter_what_is = std::move_constructible<T>;
}

File m1.ixx:

module;

#include <src/test_header.hpp>

export module m1;

export namespace test_m1 {
    // Additionally, when a module-import-declaration in a module unit of some module M imports another module unit U of M,
    // it also imports all translation units imported by non-exported module-import-declarations in the module unit purview of U.
    template<test::no_matter_what_is T>
    class M1 {};

//  template<typename T>
//  class M1_OK {};
//  template<typename T>
//  using M1 = M1_OK<T>;
}// namespace test_m1

File m2.ixx:

export module m2;

import m1;
// for `std::move_constructible`
import std;

export namespace test_m2 {
    // Error: error C7570: 'std::move_constructible': variable template has already been defined
    template<std::move_constructible T>
    class M2 : public test_m1::M1<T> {
    };
}// namespace test_m2

File main.cpp:

import std;
import m1;
import m2;

int main() {

test_m1::M1<int> m1{};
    test_m2::M2<int> m2{};

return 0;
}

Output:

C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Tools\MSVC\14.36.32323\include\concepts(116): error C7570: 'std::move_constructible': variable template has already been defined

The redefinition may be caused by a conflict between #include <concepts> in test_header.hpp and in import std;, but I pessimistically think that this form of implicit export (I mean std::move_constructible been exported due to the test_m1::M1 in module m1) is inevitable, which means that to use the module, all unready third-party libraries must be discarded.

See also: https://mcmap.net/q/1175247/-why-does-exporting-a-type-alias-such-as-std-vector-lt-std-string-gt-in-a-module-allow-use-of-both-std-vector-and-std-string-in-some-internal-partition

Pampuch answered 9/3, 2023 at 8:29 Comment(2)
I can't speak to your situation because I can't get import std; to work. However with 17.5 I have noticed that however you include/import something you need to be consistant about it. That said you may be able to get away with using import <concepts>; or the flags that does that for you automagicallyLowpitched
It's not about using import <concepts>, I mean, there are countless third-party libraries here, and you want to use them, but once you include their files, you will face this problem.Pampuch
C
0

Could be related to the header inclusion guards inside "concepts" file not being triggered when including it in the global module fragment, because the import <concepts> does not perform any inclusion guard checking. I must double on the suggestion by @Mgetz - take any dependencies and compile them to header units and then import those header units instead.

Module migration is meant to be bottom-up.

Chiekochien answered 22/4, 2023 at 13:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.