• CMake语法—宏和函数(macro vs function)


    CMake语法—宏和函数(macro vs function)

    1 宏macro定义与应用

    macro(<name> [<arg1> ...])
      <commands>
    endmacro()
    
    • macro:宏关键字
    • name:宏名称
    • arg1:宏参数

    宏的定义与使用方式与函数相同,可参考随笔进行简单理解。本文侧重对比宏与函数区别。

    2 宏与函数区别

    2.1 示例代码结构

    目录结构

    • learn_cmake:为根目录

    • build:为CMake配置输出目录(在此例中即生成sln解决方案的地方)

    • CMakeLists.txt:CMake脚本

    • cmake_config.bat:执行CMake配置过程的脚本(双击直接运行),公用代码如下:

      @echo off
      set currentDir=%~dp0
      set buildDir=%currentDir%
      set cmakeOutputDir=%currentDir%\build
      cmake -S %buildDir% -B %cmakeOutputDir% -G"Visual Studio 16 2019" -T v140 -A x64
      pause
      

    2.2 区别1:函数会产生新作用域;宏是把执行代码替换到调用位置

    2.2.1 示例代码(CMakeLists.txt)
    cmake_minimum_required(VERSION 3.18)
    
    # 设置工程名称
    set(PROJECT_NAME KAIZEN)
    
    # 设置工程版本号
    set(PROJECT_VERSION "1.0.0.10" CACHE STRING "默认版本号")
    
    # 工程定义
    project(${PROJECT_NAME}
        LANGUAGES CXX C
        VERSION ${PROJECT_VERSION}
    )
    
    # 打印开始日志
    message(STATUS "\n########## BEGIN_TEST_MACRO_VS_FUNCTION")
    
    # 定义函数
    function(test_func_argument age)
        # 打印ARGN参数值
        message(STATUS "ARGN: ${ARGN}")
        # 打印ARGC参数值
        message(STATUS "ARGC: ${ARGC}")
        # 打印ARGV参数值
        message(STATUS "ARGV: ${ARGV}")
        # 打印ARGV0参数值
        message(STATUS "ARGV0: ${ARGV0}")
        
        # 打印参数个数
        list(LENGTH ARGV argv_len)
        message(STATUS "length of ARGV: ${argv_len}")
        
        # 遍历打印各参数值
        set(i 0)
        while(i LESS ${argv_len})
            list(GET ARGV ${i} argv_value)
            message(STATUS "argv${i}:${argv_value}")
            math(EXPR i "${i} + 1")
        endwhile()
        
        if (ARGV1) # ARGV1 is a true variable
            message(STATUS "ARGV1: ${ARGV1}")
        endif()
        
        if (DEFINED ARGV2) # ARGV2 is a true variable
            message(STATUS "ARGV2: ${ARGV2}")
        endif()
        
        if (ARGC GREATER 2) # ARGC is a true variable
            message(STATUS "ARGC: ${ARGC}")
        endif()
        
        foreach (loop_var IN LISTS ARGN) # ARGN is a true variable
            message(STATUS "var: ${loop_var}")
        endforeach()
    endfunction()
    
    # 定义宏
    macro(test_macro_argument age)
        # 打印ARGN参数值
        message(STATUS "ARGN: ${ARGN}")
        # 打印ARGC参数值
        message(STATUS "ARGC: ${ARGC}")
        # 打印ARGV参数值
        message(STATUS "ARGV: ${ARGV}")
        # 打印ARGV0参数值
        message(STATUS "ARGV0: ${ARGV0}")
        
        # 打印参数个数
        list(LENGTH ARGV argv_len)
        message(STATUS "length of ARGV: ${argv_len}")
        
        # 遍历打印各参数值
        set(i 0)
        while(i LESS ${argv_len})
            list(GET ARGV ${i} argv_value)
            message(STATUS "argv${i}:${argv_value}")
            math(EXPR i "${i} + 1")
        endwhile()
        
        if (ARGV1) # ARGV1 is not a variable
            message(STATUS "ARGV1: ${ARGV1}")
        endif()
        
        if (DEFINED ARGV2) # ARGV2 is not a variable
            message(STATUS "ARGV2: ${ARGV2}")
        else()
            message(STATUS "not defined ARGV2")
        endif()
        
        if (ARGC GREATER 2) # ARGC is not a variable
            message(STATUS "ARGC: ${ARGC}")
        endif()
        
        foreach (loop_var IN LISTS ARGN) # ARGN is not a variable
            message(STATUS "var: ${loop_var}")
        endforeach()
    endmacro()
    
    test_func_argument(22 33 44)
    message(STATUS "\n")
    test_macro_argument(22 33 44)
    
    # 打印结束日志
    message(STATUS "########## END_TEST_MACRO_VS_FUNCTION\n")
    
    2.2.2 运行结果
    -- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.17763.
    -- The CXX compiler identification is MSVC 19.0.24245.0
    -- The C compiler identification is MSVC 19.0.24245.0
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
    -- Detecting C compile features
    -- Detecting C compile features - done
    --
    ########## BEGIN_TEST_MACRO_VS_FUNCTION
    -- ARGN: 33;44
    -- ARGC: 3
    -- ARGV: 22;33;44
    -- ARGV0: 22
    -- length of ARGV: 3
    -- argv0:22
    -- argv1:33
    -- argv2:44
    -- ARGV1: 33
    -- ARGV2: 44
    -- ARGC: 3
    -- var: 33
    -- var: 44
    --
    
    -- ARGN: 33;44
    -- ARGC: 3
    -- ARGV: 22;33;44
    -- ARGV0: 22
    -- length of ARGV: 0
    -- not defined ARGV2
    -- ########## END_TEST_MACRO_VS_FUNCTION
    
    -- Configuring done
    -- Generating done
    -- Build files have been written to: F:/learn_cmake/build
    请按任意键继续. . .
    
    2.2.3 说明

    从示例及运行结果可知:与函数相比,在宏中ARGV、ARGV1、ARGV2、ARGC、ARGN都不是真实的变量。

    那么,示例程序中第6167行,输出第3134行怎么来的呢?宏替换的作用。如果不理解,需要回去恶补一下C语言的宏。

    2.3 区别2:函数内可以使用return;宏中不建议使用return

    2.3.1 示例代码(CMakeLists.txt)
    cmake_minimum_required(VERSION 3.18)
    
    # 设置工程名称
    set(PROJECT_NAME KAIZEN)
    
    # 设置工程版本号
    set(PROJECT_VERSION "1.0.0.10" CACHE STRING "默认版本号")
    
    # 工程定义
    project(${PROJECT_NAME}
        LANGUAGES CXX C
        VERSION ${PROJECT_VERSION}
    )
    
    # 打印开始日志
    message(STATUS "\n########## BEGIN_TEST_MACRO_VS_FUNCTION")
    
    # 定义函数
    function(test_func_argument age)
        # 打印ARGN参数值
        message(STATUS "ARGN: ${ARGN}")
        # 打印ARGC参数值
        message(STATUS "ARGC: ${ARGC}")
        # 打印ARGV参数值
        message(STATUS "ARGV: ${ARGV}")
        # 打印ARGV0参数值
        message(STATUS "ARGV0: ${ARGV0}")
        
        # 打印参数个数
        list(LENGTH ARGV argv_len)
        message(STATUS "length of ARGV: ${argv_len}")
        
        if (argv_len GREATER 3)
            foreach (loop_var IN LISTS ARGV) # ARGV is a true variable
                message(STATUS "loop_var: ${loop_var}")
            endforeach()
        else()
            message(STATUS "in func exec return")
            return()  ## 从此退出
        endif()
             
        message(STATUS "after return")
    endfunction()
    
    # 定义宏
    macro(test_macro_argument age)
        # 打印ARGN参数值
        message(STATUS "ARGN: ${ARGN}")
        # 打印ARGC参数值
        message(STATUS "ARGC: ${ARGC}")
        # 打印ARGV参数值
        message(STATUS "ARGV: ${ARGV}")
        # 打印ARGV0参数值
        message(STATUS "ARGV0: ${ARGV0}")
        
        # 定义一个变量
        set(list_var "${ARGV}")
        
        # 打印参数个数
        list(LENGTH list_var argv_len)
        message(STATUS "length of ARGV: ${argv_len}")
    
        if (argv_len GREATER 3)
            foreach (loop_var IN LISTS list_var) # list_var is a true variable
                message(STATUS "loop_var: ${loop_var}")
            endforeach()
        else()
            message(STATUS "in macro exec return")
            return()  ## 从此退出
        endif()
        
        message(STATUS "after return")
    endmacro()
    
    test_func_argument(22 44 66 88 100)
    message(STATUS "\n")
    
    test_func_argument(10 11 12)
    message(STATUS "\n")
    
    test_macro_argument(11 33 55 77 99)
    message(STATUS "\n")
    message(STATUS "after exec macro with 5 value to continue\n")
    
    test_macro_argument(20 21 22)
    message(STATUS "\n")
    message(STATUS "after exec macro with 3 value to continue")
    
    # 打印结束日志
    message(STATUS "########## END_TEST_MACRO_VS_FUNCTION\n")
    
    2.3.2 运行结果
    -- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.17763.
    -- The CXX compiler identification is MSVC 19.0.24245.0
    -- The C compiler identification is MSVC 19.0.24245.0
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
    -- Detecting C compile features
    -- Detecting C compile features - done
    --
    ########## BEGIN_TEST_MACRO_VS_FUNCTION
    -- ARGN: 44;66;88;100
    -- ARGC: 5
    -- ARGV: 22;44;66;88;100
    -- ARGV0: 22
    -- length of ARGV: 5
    -- loop_var: 22
    -- loop_var: 44
    -- loop_var: 66
    -- loop_var: 88
    -- loop_var: 100
    -- after return
    --
    
    -- ARGN: 11;12
    -- ARGC: 3
    -- ARGV: 10;11;12
    -- ARGV0: 10
    -- length of ARGV: 3
    -- in func exec return
    --
    
    -- ARGN: 33;55;77;99
    -- ARGC: 5
    -- ARGV: 11;33;55;77;99
    -- ARGV0: 11
    -- length of ARGV: 5
    -- loop_var: 11
    -- loop_var: 33
    -- loop_var: 55
    -- loop_var: 77
    -- loop_var: 99
    -- after return
    --
    
    -- after exec macro with 5 value to continue
    
    -- ARGN: 21;22
    -- ARGC: 3
    -- ARGV: 20;21;22
    -- ARGV0: 20
    -- length of ARGV: 3
    -- in macro exec return
    -- Configuring done
    -- Generating done
    -- Build files have been written to: F:/learn_cmake/build
    请按任意键继续. . .
    
    2.3.3 小结

    从示例及运行结果可知:与函数相比,如果在宏中执行return命令之后,整个CMake进程会退出执行,不再继续执行其他语句。

    因为函数会产生新的作用域,在函数中return只意味着退出函数作用域,父作用域的代码语句会正常执行。

    而宏只是等价替换,当调用宏时,相当于把宏中命令语句替换到相应的位置,所以宏中return,也意味着会退出整个进程。

    另外,在此示例程序中第57行,我们定义了一个普通变量list_var,主要作用:为了解决宏中没有ARGV的”尴尬“,可自行体会这种用法。

    2.4 区别3:在函数中调用宏的精妙

    2.4.1 示例代码(CMakeLists.txt)
    cmake_minimum_required(VERSION 3.18)
    
    # 设置工程名称
    set(PROJECT_NAME KAIZEN)
    
    # 设置工程版本号
    set(PROJECT_VERSION "1.0.0.10" CACHE STRING "默认版本号")
    
    # 工程定义
    project(${PROJECT_NAME}
        LANGUAGES CXX C
        VERSION ${PROJECT_VERSION}
    )
    
    # 打印开始日志
    message(STATUS "\n########## BEGIN_TEST_MACRO_VS_FUNCTION")
    
    macro(bar)
        if (DEFINED ARGN)
            message("defined ARGN")
        endif()
        
        foreach(loop_var IN LISTS ARGN)
            message(STATUS "loop_var: ${loop_var}")
        endforeach()
    endmacro()
    
    function(foo)
        # 打印ARGN参数值
        message(STATUS "ARGN: ${ARGN}")
        # 打印ARGC参数值
        message(STATUS "ARGC: ${ARGC}")
        # 打印ARGV参数值
        message(STATUS "ARGV: ${ARGV}")
        # 打印ARGV0参数值
        message(STATUS "ARGV0: ${ARGV0}")
        ## 调用宏
        bar(x y z)
    endfunction()
    
    foo(a b c)
    
    # 打印结束日志
    message(STATUS "########## END_TEST_MACRO_VS_FUNCTION\n")
    
    2.4.2 运行结果
    -- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.17763.
    -- The CXX compiler identification is MSVC 19.0.24245.0
    -- The C compiler identification is MSVC 19.0.24245.0
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
    -- Detecting C compile features
    -- Detecting C compile features - done
    --
    ########## BEGIN_TEST_MACRO_VS_FUNCTION
    -- ARGN: a;b;c
    -- ARGC: 3
    -- ARGV: a;b;c
    -- ARGV0: a
    defined ARGN
    -- loop_var: a
    -- loop_var: b
    -- loop_var: c
    -- ########## END_TEST_MACRO_VS_FUNCTION
    
    -- Configuring done
    -- Generating done
    -- Build files have been written to: F:/learn_cmake/build
    请按任意键继续. . .
    
    2.4.3 小结

    我们强调过很多次,在CMake语法中,函数会产生新的作用域,同时会默认生成新的变量即ARGN、ARGC、ARGV等等。

    那么,在函数中调用宏时,仅仅只是把宏的实现语句往函数中拷贝了一份(即宏替换),所以,宏中语句的执行都基础函数变量的基础上。

    因此,也就有示例代码23行成立的原因,同时也有了对输出结果第20行的解释或说明。

    2.5 区别4:函数中有一些特有的默认变量

    2.5.1 示例代码(CMakeLists.txt)
    cmake_minimum_required(VERSION 3.18)
    
    # 设置工程名称
    set(PROJECT_NAME KAIZEN)
    
    # 设置工程版本号
    set(PROJECT_VERSION "1.0.0.10" CACHE STRING "默认版本号")
    
    # 工程定义
    project(${PROJECT_NAME}
        LANGUAGES CXX C
        VERSION ${PROJECT_VERSION}
    )
    
    # 打印开始日志
    message(STATUS "\n########## BEGIN_TEST_MACRO_VS_FUNCTION")
    
    function(test_func_default_var)
        # 打印CMAKE_CURRENT_FUNCTION参数值
        message(STATUS "CMAKE_CURRENT_FUNCTION: ${CMAKE_CURRENT_FUNCTION}")
        # 打印CMAKE_CURRENT_FUNCTION_LIST_DIR参数值
        message(STATUS "CMAKE_CURRENT_FUNCTION_LIST_DIR: ${CMAKE_CURRENT_FUNCTION_LIST_DIR}")
        # 打印CMAKE_CURRENT_FUNCTION_LIST_FILE参数值
        message(STATUS "CMAKE_CURRENT_FUNCTION_LIST_FILE: ${CMAKE_CURRENT_FUNCTION_LIST_FILE}")
        # 打印CMAKE_CURRENT_FUNCTION_LIST_FINE参数值
        message(STATUS "CMAKE_CURRENT_FUNCTION_LIST_LINE: ${CMAKE_CURRENT_FUNCTION_LIST_LINE}")
    endfunction()
    
    test_func_default_var()
    
    # 打印结束日志
    message(STATUS "########## END_TEST_MACRO_VS_FUNCTION\n")
    
    2.5.2 运行结果
    -- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.17763.
    -- The CXX compiler identification is MSVC 19.0.24245.0
    -- The C compiler identification is MSVC 19.0.24245.0
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped
    -- Detecting C compile features
    -- Detecting C compile features - done
    --
    ########## BEGIN_TEST_MACRO_VS_FUNCTION
    -- CMAKE_CURRENT_FUNCTION: test_func_default_var
    -- CMAKE_CURRENT_FUNCTION_LIST_DIR: F:/learn_cmake
    -- CMAKE_CURRENT_FUNCTION_LIST_FILE: F:/learn_cmake/CMakeLists.txt
    -- CMAKE_CURRENT_FUNCTION_LIST_LINE: 18
    -- ########## END_TEST_MACRO_VS_FUNCTION
    
    -- Configuring done
    -- Generating done
    -- Build files have been written to: F:/learn_cmake/build
    请按任意键继续. . .
    
    2.5.3 小结
    • CMAKE_CURRENT_FUNCTION:当前函数名称
    • CMAKE_CURRENT_FUNCTION_LIST_DIR: 当前函数路径
    • CMAKE_CURRENT_FUNCTION_LIST_FILE:当前函数所属文件
    • CMAKE_CURRENT_FUNCTION_LIST_LINE:当前函数定义的起始行数
  • 相关阅读:
    WPF入门教程系列六——布局介绍与Canvas(一)
    WPF入门教程系列五——Window 介绍
    WPF入门教程系列四——Dispatcher介绍
    WPF入门教程系列三——Application介绍(续)
    html5 标签
    html5
    sublime汉化教程
    html5 文本格式化
    主键和索引的区别
    响应式布局的开发基础知识
  • 原文地址:https://www.cnblogs.com/Braveliu/p/15621973.html
Copyright © 2020-2023  润新知