CMake函数和宏(function和macro):使用函数和宏提高代码可读性

简介: CMake函数和宏(function和macro):使用函数和宏提高代码可读性

介绍

CMake简介

CMake是一个开源的跨平台构建工具,它可用于生成可定制的构建过程,如Makefiles、Visual Studio项目或Xcode项目。CMake使用CMakeLists.txt文件来描述构建过程,这些文件包含了构建项目所需的信息,包括源代码、库、头文件和可执行文件等。


function和macro的概念

在CMake中,function和macro都是用来实现代码重用的工具。它们的主要区别在于参数传递和作用域。

function和macro都可以在CMakeLists.txt文件中定义和调用。在调用时,function和macro的参数传递方式不同。function的参数传递采用传值方式,而macro的参数传递采用文本替换方式。

另外,function和macro的作用域也有所不同。在CMake中,function的作用域是局部作用域,而macro的作用域是全局作用域。这意味着在function中定义的变量只在function中有效,而在macro中定义的变量则在整个CMakeLists.txt文件中有效。

使用function和macro,可以将一些常用的操作封装起来,比如生成安装目录、编译选项等。这样可以减少代码的重复性,提高代码的可维护性和可读性。


function

function的语法和用法

在CMake中,function是用来实现代码重用的工具。function的语法如下:

function(function_name arg1 arg2 ...)
  # function body
endfunction()

其中,function_name是函数名,arg1、arg2等是函数的参数。在function的body中,可以使用CMake语句来实现具体的操作。

function的参数传递

function的参数传递采用传值方式。这意味着在调用function时,实参的值会被传递到形参中。例如:

function(add_numbers num1 num2)
  math(EXPR result "${num1} + ${num2}")
  message("The result is ${result}")
endfunction()
add_numbers(1 2)

在上面的例子中,add_numbers函数有两个参数num1和num2。在调用函数时,传递了实参1和2,这些实参的值会被传递到num1和num2中。

function的返回值

function可以有返回值,返回值可以通过set命令设置。例如:

function(add_numbers num1 num2)
  math(EXPR result "${num1} + ${num2}")
  set(${result_var} ${result} PARENT_SCOPE)
endfunction()
add_numbers(1 2 RESULT)
message("The result is ${RESULT}")

在上面的例子中,add_numbers函数计算了num1和num2的和,并将结果通过set命令设置到了result_var变量中。在调用函数时,传递了实参1和2,并将返回值保存到了RESULT变量中。


示例代码

以下是一个示例函数,在CMakeLists.txt中定义了一个函数,用于生成一个静态库:

function(create_static_library LIBRARY_NAME SOURCE_FILES)
  add_library(${LIBRARY_NAME} STATIC ${SOURCE_FILES})
  set_target_properties(${LIBRARY_NAME} PROPERTIES OUTPUT_NAME ${LIBRARY_NAME})
  set_target_properties(${LIBRARY_NAME} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
endfunction()
set(SOURCE_FILES foo.cpp bar.cpp)
create_static_library(my_library ${SOURCE_FILES})

在上面的示例代码中,create_static_library函数有两个参数LIBRARY_NAME和SOURCE_FILES。函数的作用是创建一个名为LIBRARY_NAME的静态库,其中包含了SOURCE_FILES中指定的文件。函数通过add_library命令创建了一个静态库,并通过set_target_properties命令设置了输出名称和输出路径。在调用函数时,传递了实参my_library和${SOURCE_FILES}变量。最终,create_static_library函数会生成一个名为my_library的静态库。


macro

Macro是CMake中的一种宏定义,可以将一些常用的代码片段定义为一个宏,当需要使用时直接调用宏即可,可以减少代码的重复性,提高代码的可读性和可维护性。

macro的语法和用法

宏定义的基本格式为:

macro(宏名 参数列表) 宏体 endmacro()

其中,宏名为标识符,参数列表为用括号括起来的形式参数列表,宏体为用endmacro()结束的宏定义体,宏名和宏体之间用空格隔开。

示例:

macro(PRINT_HELLO_WORLD) message("Hello World!") endmacro()

macro的参数传递

宏定义可以接受参数,参数传递方式类似于函数的参数传递,可以传递常量、变量或表达式等。参数传递的格式为:$(参数名)。

示例:

macro(PRINT_MESSAGE message) message(${message}) endmacro()

macro的变量作用域

宏定义中的变量作用域与函数中的变量作用域不同,宏定义中的变量作用域是在宏定义体内,即在宏定义体中定义的变量在宏体外无法访问。

示例:

cmake_minimum_required(VERSION 3.5)
#定义一个宏,用于打印传入的消息和变量值
macro(PRINT_MESSAGE_AND_VALUE message value)
#在当前宏中定义的变量,只在当前宏中有效
set(local_var "This is a local variable") message("${message}: ${value}") message("local_var: ${local_var}") endmacro()
#定义一个全局变量
set(global_var "This is a global variable")
#打印全局变量的值
message("global_var: ${global_var}")
#调用宏,打印传入的消息和变量值
PRINT_MESSAGE_AND_VALUE("Print global_var" ${global_var})
#如果在宏中使用了外部定义的变量,需要在宏调用时将其传入
#调用宏,打印传入的消息和变量值,以及宏中定义的局部变量
PRINT_MESSAGE_AND_VALUE("Print local_var" "This is a parameter")
#此处打印的local_var为空,因为它只在上面的宏中定义,在此处无法访问

示例代码

cmake_minimum_required(VERSION 3.5)
#定义一个宏,用于打印"Hello world!"
macro(PRINT_HELLO_WORLD) message("Hello World!") endmacro()
#定义一个宏,用于打印传入的消息
macro(PRINT_MESSAGE message) message(${message}) endmacro()
#定义一个宏,用于计算传入的两个数的最大值并打印
macro(PRINT_MAX a b)
#定义一个变量max,并将其初值设置为a
set(max ${a})
#如果b比max大,则将max的值设为b
if(${b} GREATER ${a}) set(max ${b}) endif()
#打印最大值
message("max=${max}") endmacro()
#调用宏,打印"Hello world!"
PRINT_HELLO_WORLD()
#定义一个变量msg,并将其值设置为"Hello CMake!"
set(msg "Hello CMake!")
#调用宏,打印变量msg的值
PRINT_MESSAGE(${msg})
#调用宏,计算传入的两个数的最大值并打印
PRINT_MAX(10 20)

function和macro的区别

参数传递

function和macro在参数传递方面有所不同。在function中,参数传递是通过参数列表来完成的,参数有类型和顺序,类似于C语言函数的参数传递方式。而在macro中,参数传递是通过文本替换来完成的,因此参数没有类型和顺序限制,可以传递任何文本。

返回值

function可以有返回值,而macro没有返回值。在function中,可以使用return语句返回值,返回值的类型可以是任何CMake支持的类型。而在macro中,不能使用return语句返回值,因为macro的返回值是通过文本替换来实现的。

变量作用域

变量作用域: function和macro在变量作用域方面也有所不同。在function中,变量的作用域是函数内部,函数外部无法访问函数内部定义的变量。而在macro中,变量的作用域是整个CMake文件,因此可以在文件中的任何地方访问和修改宏中定义的变量。

代码示例

以下是一个使用function和macro实现同样功能的示例代码:

#使用function实现
function(ADD_ONE num) math(EXPR result "${num} + 1") return(${result}) endfunction()
#调用function,将10加1,并将结果赋值给变量res
ADD_ONE(10 res) message("Result: ${res}")
#使用macro实现
macro(ADD_ONE num) math(EXPR result "${num} + 1") set(${ARGV1} ${result}) endmacro()
#调用macro,将10加1,并将结果赋值给变量res
ADD_ONE(10 res) message("Result: ${res}")

在这个示例中,我们实现了将一个数加1的功能,使用function和macro两种方式分别实现。可以看出,function使用return语句返回结果,而macro使用set命令将结果赋值给一个参数。另外,调用方式也有所不同,function使用函数调用的方式,而macro使用文本替换的方式。


function和macro的优缺点以及使用场景

  • function的优点:

参数传递和返回值更加灵活,可以实现复杂的逻辑处理;

变量作用域仅限于函数内部,避免了变量命名冲突的问题;

可以使用递归调用实现复杂的算法;

在CMake文件中调用function更加直观,易于理解。

  • function的缺点:

由于使用return语句返回结果,因此会创建新的变量,占用额外的内存空间;

递归调用会导致堆栈溢出的问题;

由于函数调用需要额外的资源开销,因此在处理大量数据时可能会影响性能。

  • macro的优点:

参数传递更加灵活,可以传递任意文本;

可以直接修改CMake文件中的变量,更加方便;

在处理大量数据时性能更好,因为不需要额外的资源开销。

  • macro的缺点:

由于使用文本替换,因此不能实现复杂的逻辑处理;

变量作用域是整个CMake文件,容易出现变量命名冲突的问题;

不能使用递归调用。

  • function的使用场景:

实现复杂的逻辑处理;

需要递归调用的场景;

在CMake文件中调用function更加直观,易于理解。

  • macro的使用场景:

实现简单的文本替换;

需要直接修改CMake文件中的变量的场景;

在处理大量数据时性能更好的场景。


综上所述,function和macro各有优缺点,在不同的场景下选择适合的方法可以使CMake的使用更加高效和方便。


总结

在这篇博客中,我们将详细介绍CMake中function和macro的概念、语法和用法,并通过示例代码演示它们的使用。我们还将比较function和macro之间的区别,并讨论它们在实际应用中的使用场景和最佳实践。

通过本文的学习,读者将能更好地理解和应用CMake中的function和macro,提高代码的可重用性和可维护性。

目录
相关文章
|
29天前
|
JavaScript
箭头函数与普通函数(function)的区别
箭头函数是ES6引入的新特性,与传统函数相比,它有更简洁的语法,且没有自己的this、arguments、super或new.target绑定,而是继承自外层作用域。箭头函数不适用于构造函数,不能使用new关键字调用。
|
1月前
|
数据可视化 开发者 索引
详解Wireshark LUA插件函数:function p_myproto.dissector(buffer, pinfo, tree)
在 Wireshark 中,LUA 插件通过 `function p_myproto.dissector(buffer, pinfo, tree)` 扩展协议解析能力,解析自定义应用层协议。参数 `buffer` 是 `PacketBuffer` 类型,表示原始数据包内容;`pinfo` 是 `ProtoInfo` 类型,包含数据包元信息(如 IP 地址、协议类型等);`tree` 是
57 1
|
28天前
|
JavaScript
箭头函数与普通函数(function)的区别
箭头函数是ES6引入的新语法,相比传统函数表达式更简洁,且没有自己的this、arguments、super或new.target绑定,而是继承自外层作用域。这使得箭头函数在处理回调和闭包时更加灵活方便。
|
1月前
|
C++ 容器
函数对象包装器function和bind机制
函数对象包装器function和bind机制
18 0
|
3月前
【Azure Durable Function】PowerShell Activity 函数遇见 Newtonsoft.Json.JsonReaderException: The reader's MaxDepth of 64 has been exceeded.
【Azure Durable Function】PowerShell Activity 函数遇见 Newtonsoft.Json.JsonReaderException: The reader's MaxDepth of 64 has been exceeded.
|
3月前
|
安全 JavaScript 应用服务中间件
【Azure Function App】如何修改Azure函数应用的默认页面呢?
【Azure Function App】如何修改Azure函数应用的默认页面呢?
|
3月前
|
缓存
【Azure Function】Function App代码中使用Managed Identity认证获取Blob数据时遇见400报错
【Azure Function】Function App代码中使用Managed Identity认证获取Blob数据时遇见400报错
【Azure Function】Function App代码中使用Managed Identity认证获取Blob数据时遇见400报错
|
3月前
|
SQL JavaScript 前端开发
【Azure 应用服务】Azure JS Function 异步方法中执行SQL查询后,Callback函数中日志无法输出问题
【Azure 应用服务】Azure JS Function 异步方法中执行SQL查询后,Callback函数中日志无法输出问题
|
3月前
|
JSON 数据格式 Python
【Azure 应用服务】Azure Function Python函数中,如何获取Event Hub Trigger的消息Event所属于的PartitionID呢?
【Azure 应用服务】Azure Function Python函数中,如何获取Event Hub Trigger的消息Event所属于的PartitionID呢?
|
3月前
|
C++ Python
【Azure 应用服务】Azure Function Python函数部署到Azure后遇见 Value cannot be null. (Parameter 'receiverConnectionString') 错误
【Azure 应用服务】Azure Function Python函数部署到Azure后遇见 Value cannot be null. (Parameter 'receiverConnectionString') 错误

热门文章

最新文章