c++ 20 module 模块使用 cmake

全中文网第一个讲清楚了module 与cmake结合使用

1、模块概述

ixx 声明文件
icc 实现文件
async_simple框架用的后缀是cppm

可以在模块中用模板,我是直接export的命名空间!!!
学习可以看看这篇博客学习

2、cmake 如何支持的?

cmake 官网文档链接:
https://cmake.org/cmake/help/latest/prop_tgt/CXX_MODULE_SETS.html

CXX_MODULE_SETS¶
New in version 3.25.

Note Experimental. Gated by CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API
Read-only list of the target's PRIVATE and PUBLIC C++ module sets (i.e. all file sets with the type CXX_MODULES). Files listed in these file sets are treated as source files for the purpose of IDE integration.

C++ module sets may be defined using the target_sources() command FILE_SET option with type CXX_MODULES.

See also CXX_MODULE_SET_<NAME>, CXX_MODULE_SET and INTERFACE_CXX_MODULE_SETS.

1、必须是3.25以上的版本
2、模块文件使用target_sources()导入
3、需要设置CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API参数
4、linux 平台:必须使用ninja 1.11以上编译,github上有二进制包,单个文件的,clang大于15版本,cmake project name 必须等到CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API 设置完后才可以设置
6、windows平台mvsc可以直接使用,忽略以上3、4条,因为他可以自动扫描依赖没这么麻烦,但是必须保持最新版本

3、demo

CMakeLists.txt:

cmake_minimum_required(VERSION 3.25.2)

    
if (UNIX)
   
    set(CMAKE_GENERATOR "/usr/bin/ninja")
    set(CMAKE_C_COMPILER "clang-16")
    set(CMAKE_CXX_COMPILER "clang++-16")

   set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "2182bf5c-ef0d-489a-91da-49dbc3090d2a")
   set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "clang")
   set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG "@<MODULE_MAP_FILE>")
# 必须设置,否则他会去找gcc的头文件
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem /usr/lib/llvm-16/lib/clang/16/include -Wall -Wextra -Wunused-parameter")
 include(cxx_modules_rules_clang.cmake)
endif()

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

if (WIN32)
        add_definitions("/utf-8")
        add_definitions("/wd4828 /wd4267")
        #set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded")
        set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zf /O3 /openmp /MP /std:c++20 /bigobj /await:strict")
        set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Zf /O0 /openmp /MP /std:c++20 /bigobj /await:strict")
        set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Zf /O3 /openmp /MP /std:c20 /bigobj /await:strict")
        set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Zf /O0 /openmp /MP /std:c20 /bigobj /await:strict")
        #[如果确认不需要PDB, 即不需要调试开源库, 完全可以在设置里将/Zi或/ZI去掉]

        # 文件utf-8
        add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
        add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")

    else()
   
        # Wreturn-type 协程是空实现
        #-fmodules-ts 模块
        #-fcoroutines-ts 协程
        #-fconcepts-ts 概念
        #-fsanitize=address 检查内存溢出
        #-freport-bug 打印完整的编译 bug
        # -Wunused-result 忽略返回结果
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20  -fmodules-ts -fsanitize=address -fcoroutines-ts -fconcepts-ts -fpermissive -Wreturn-type -freport-bug -Wunused-result")
        set(CXX_FLAGS "${CXX_FLAGS} -std=c++20 -fmodules-ts -fsanitize=address -fcoroutines-ts -fconcepts-ts -fpermissive -Wreturn-typ -freport-buge -Wunused-result")

        #add_compile_options(-fmodules)
        #add_compile_options(-fbuiltin-module-map)
        #add_compile_options(-fimplicit-module-maps)
        #add_compile_options(-fprebuilt-module-path=${PREBUILT_MODULE_PATH})

    endif ()
# 源文件
file(GLOB_RECURSE SOURCE_FILES
        ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c
        ${CMAKE_CURRENT_SOURCE_DIR}/src/*.ui
        ${CMAKE_CURRENT_SOURCE_DIR}/src/*.qrc
        )

# 头文件
file(GLOB_RECURSE CURRENT_HEADERS
        ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h
        ${CMAKE_CURRENT_SOURCE_DIR}/src/*.hpp
        ${CMAKE_CURRENT_SOURCE_DIR}/src/*.h
        ${CMAKE_CURRENT_SOURCE_DIR}/src/*.hpp)


#!!! 重点递归查找模板文件 FAST_MODULES保存所有的模块文件
file(GLOB_RECURSE FAST_MODULES
        ${CMAKE_CURRENT_SOURCE_DIR}/src/*.ixx
        ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cxx
        ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cppm)

# 构建可执行程序
add_executable(
${APP_NAME}#[[项目名称]] 
${SOURCE_FILES} #[[源文件]]
${CURRENT_HEADERS} #[[头文件]]
${CMAKE_CURRENT_SOURCE_DIR}/run/main.cpp #[[主函数入口]])

#!!!追加模板
target_sources(
        ${APP_NAME}
        PUBLIC
        ${FAST_MODULES}
)


什么是${CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS}

clang-scan-deps-${llvm版本号}

如何使用这个?

apt install clang-tools-16

获取不到这个版本?

https://apt.llvm.org/ 这个网址进去找到对应的扩展源

什么是CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API ?

cmake实验性的version号,github-cmake上可以找到这个东西

下面的string(…) 就是调用这个clang-scan-deps扫描工具

cxx_modules_rules_clang.cmake:

set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE
  "${CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS}"
  " -format=p1689"
  " --"
  " <CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS>"
  " -x c++ <SOURCE> -c -o <OBJECT>"
  " -MT <DYNDEP_FILE>"
  " -MD -MF <DEP_FILE>"
  " > <DYNDEP_FILE>")
set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "clang")
set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG "@<MODULE_MAP_FILE>")

# Default to C++ extensions being off. Clang's modules support have trouble
# with extensions right now.
set(CMAKE_CXX_EXTENSIONS OFF)

4.测试文件

//声明是一个模板
module;

#include <iostream>
#include <tuple>
#include <array>
#include <utility>

// 导出.hpp文件后,从这个模块开始,后面用这个文件智能import <utility>
export import <utility>

// 导出整个命名空间
export module tuple_utils;
namespace tuple_utils{
/***********递归+特化方式遍历打印tuple***************/
    template<typename Tuple, std::size_t N>
    struct tuple_printer {
        static void print(const Tuple &t) {
            tuple_printer<Tuple, N - 1>::print(t);
            std::cout << ", " << std::get<N - 1>(t);
        }
    };

    template<typename Tuple>
    struct tuple_printer<Tuple, 1> {
        static void print(const Tuple &t) {
            std::cout << std::get<0>(t);
        }
    };

    template<typename... Args>
    void print_tuple(const std::tuple<Args...> &t) {
        tuple_printer<decltype(t), sizeof...(Args)>::print(t);
        std::cout << std::endl;
    }

/***********使用流的方式,这里用到了C++14的index_sequence****************/
    template<typename Char, typename Traits, typename Tuple, std::size_t... Index>
    void print_tuple_impl(std::basic_ostream<Char, Traits> &os, const Tuple &t, std::index_sequence<Index...>) {
        using swallow = int[]; // guaranties left to right order
        (void) swallow{0, (void(os << (Index == 0 ? "" : ", ") << std::get<Index>(t)), 0)...};
    }

    template<typename Char, typename Traits, typename... Args>
    decltype(auto) operator<<(std::basic_ostream<Char, Traits> &os, const std::tuple<Args...> &t) {
        os << "(";
        print_tuple_impl(os, t, std::index_sequence_for<Args...>{});
        return os << ")";
    }

/***********将std::array转换成std::tuple,这里用到了C++14的index_sequence****************/
    template<typename Array, std::size_t... Index>
    decltype(auto) array2tuple_impl(const Array &a, std::index_sequence<Index...>) {
        return std::make_tuple(a[Index]...);
    }

    template<typename T, std::size_t N>
    decltype(auto) array2tuple(const std::array<T, N> &a) {
        return array2tuple_impl(a, std::make_index_sequence<N>{});
    }

/***********通过元素类型获取元素的值****************/
    template<typename T, std::size_t N, typename... Args>
    struct index_of;

    template<typename T, std::size_t N, typename... Args>
    struct index_of<T, N, T, Args...> : std::integral_constant<int, N> {
    };

    template<typename T, std::size_t N, typename U, typename... Args>
    struct index_of<T, N, U, Args...> : std::integral_constant<int, index_of<T, N + 1, Args...>::value> {
    };

    template<typename T, std::size_t N>
    struct index_of<T, N> : std::integral_constant<int, -1> {
    };

    template<typename T, typename... Args>
    T get_element_by_type(const std::tuple<Args...> &t) {
        return std::get<index_of<T, 0, Args...>::value>(t);
    };

/***********通过std::tuple作为参数调用函数****************/
    template<typename Function, typename Tuple, std::size_t... Index>
    decltype(auto) invoke_impl(Function &&func, Tuple &&t, std::index_sequence<Index...>) {
        return func(std::get<Index>(std::forward<Tuple>(t))...);
    };

    template<typename Function, typename Tuple>
    decltype(auto) invoke(Function &&func, Tuple &&t) {
        constexpr auto size = std::tuple_size<typename std::decay<Tuple>::type>::value;
        return invoke_impl(std::forward<Function>(func), std::forward<Tuple>(t), std::make_index_sequence<size>{});
    };

/***********make_from_tuple****************/
    template<typename T, typename Tuple, std::size_t... Index>
    decltype(auto) make_from_tuple_impl(Tuple &&t, std::index_sequence<Index...>) {
        return T{std::get<Index>(std::forward<Tuple>(t))...};
    };

    template<typename T, typename Tuple>
    decltype(auto) make_from_tuple(Tuple &&t) {
        constexpr auto size = std::tuple_size<typename std::decay<Tuple>::type>::value;
        return make_from_tuple_impl<T>(std::forward<Tuple>(t), std::make_index_sequence<size>{});
    };
}
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
C++ 多文件项目中,可以通过将代码分成多个模块使用多个 CMakeLists.txt 文件来管理它们。为了使每个模块都可以单独编译运行,我们需要为每个模块创建一个单独的 CMakeLists.txt 文件,并将其设置为可执行文件。 假设我们有以下项目结构: ``` myproject/ ├── CMakeLists.txt ├── module1/ │ ├── CMakeLists.txt │ ├── include/ │ │ └── module1.h │ └── src/ │ └── module1.cpp └── module2/ ├── CMakeLists.txt ├── include/ │ └── module2.h └── src/ └── module2.cpp ``` 其中,`myproject ` 目录下的 `CMakeLists.txt` 文件是主 CMakeLists.txt 文件,它用于管理整个项目。`module1` 目录和 `module2` 目录是两个子模块,每个子模块都有自己的 `CMakeLists.txt` 文件、头文件和源文件。 现在,我们需要使用 CMake 来构建这个项目。我们可以在 `module1` 目录下创建一个名为 `CMakeLists.txt` 的文件,并添加以下内容: ```cmake # 添加头文件搜索路径 include_directories(include) # 添加源文件 add_executable(module1 src/module1.cpp) # 暴露头文件 target_include_directories(module1 PUBLIC include) ``` 该 `CMakeLists.txt` 文件指定了 `module1` 子模块的构建规则。`include_directories` 命令添加了头文件搜索路径,它告诉 CMake 在 `include` 目录中查找头文件。`add_executable` 命令编译 `module1.cpp` 源文件并生成一个可执行文件 `module1`。最后,`target_include_directories` 命令将 `include` 目录暴露给其他模块,以便它们可以包含 `module1.h` 头文件。 然后,在 `module2` 目录下创建一个名为 `CMakeLists.txt` 的文件,并添加以下内容: ```cmake # 添加头文件搜索路径 include_directories(include) # 添加源文件 add_executable(module2 src/module2.cpp) # 暴露头文件 target_include_directories(module2 PUBLIC include) ``` 该 `CMakeLists.txt` 文件指定了 `module2` 子模块的构建规则,与 `module1` 子模块类似。 完成以上步骤后,我们可以使用以下命令来构建每个模块: ```bash cd module1 mkdir build cd build cmake .. make ``` ```bash cd module2 mkdir build cd build cmake .. make ``` 这些命令将进入每个模块的目录,创建一个新的构建目录,并使用相应的 CMakeLists.txt 文件来构建和编译可执行文件。 这就是如何使用多个 CMakeLists.txt 文件来管理 C++ 多文件项目并使每个模块都可以单独编译运行的示例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值