CMAKE实践读书笔记(7)P43-P47

目录

FindCURL 模块

libcurl库

size_t

回调函数

fwrite()

const char *

fopen()

curl_global_init(CURL_GLOBAL_ALL);

CURLcode

curl_easy_init()


FindCURL 模块

FindCURL模块是CMake中的一个模块,用于查找和配置libcurl库,这是一个用于传输文件和数据的常用C语言库。使用该模块可以在CMake中轻松地配置和链接Curl库,而无需手动设置所有必要的编译器标志和链接器标志。

使用该模块的示例:

find_package(CURL REQUIRED)
include_directories(${CURL_INCLUDE_DIR})
target_link_libraries(my_executable ${CURL_LIBRARIES})

在这里,find_package命令将查找Curl库并设置CURL_INCLUDE_DIRCURL_LIBRARIES变量,其中前者是包含文件路径,后者是库文件路径。然后,可以使用include_directories命令将包含路径添加到可执行文件的编译器标志中,并使用target_link_libraries命令将库文件链接到可执行文件中。


libcurl库

libcurl是一个使用C语言编写的、支持多种协议的开源网络库,可以用来进行HTTP、FTP、SMTP等协议的数据传输。它提供了简单的API接口,方便地进行HTTP请求和数据的发送与接收。libcurl被广泛应用于各种语言的网络编程中,例如C++、Python、Java等。在使用libcurl时,需要先安装libcurl库,并在代码中包含相应的头文件。


#include <curl/curl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

FILE *fp;

int write_data(void *ptr, size_t size, size_t nmemb, void *stream)
{
    int written = fwrite(ptr, size, nmemb, (FILE *)fp);
    return written;
}

int main()
{
    const char *path = "/tmp/curl-test";
    const char *mode = "w";
    fp = fopen(path, mode);

    curl_global_init(CURL_GLOBAL_ALL);

    CURLcode res;
    CURL *curl = curl_easy_init();
    curl_easy_setopt(curl, CURLOPT_URL, "http://www.linux-ren.org");
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);

    res = curl_easy_perform(curl);

    curl_easy_cleanup(curl);
}

#include <curl/curl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
  • curl/curl.h:这是libcurl的主头文件,定义了libcurl的API函数和数据类型。
  • stdio.h:这是标准输入输出函数库的头文件,其中包含了输入输出函数所需的函数原型、宏和数据类型等定义。
  • stdlib.h:这是C标准库中的头文件,其中包含了许多通用工具函数的函数原型、宏和数据类型等定义。
  • unistd.h:这是Unix标准中定义的头文件,其中包含了一些Unix系统调用函数的函数原型和常量定义。在这个例子中,它用来引用文件操作函数中的一些常量定义。

FILE *fp;

FILE *fp;是在C语言中定义一个指向FILE类型的指针变量,用于指向后面打开的文件。它是一个文件指针变量,类似于指向数组的指针变量。

在上述代码中,fp是一个指向打开的文件的指针,用于将curl下载的数据写入到该文件中。在函数write_data中,通过fwrite函数将指针ptr指向的数据写入到文件中,参数(FILE *)fp将指针fp强制转换为FILE类型,确保将数据写入到正确的文件中。


size_t

size_t是一种无符号整型数据类型,通常用于表示内存大小、数组下标等非负整数。其大小是由具体平台决定,通常为4或8个字节。在C语言中,通常使用size_t来表示数组的长度或者内存分配的大小。


int write_data(void *ptr, size_t size, size_t nmemb, void *stream)
{
    int written = fwrite(ptr, size, nmemb, (FILE *)fp);
    return written;
}

当你使用 curl 请求某个网站时,curl 在接收到网站响应后会将响应内容传递给你指定的回调函数(callback function)进行处理。这个回调函数就是 write_data(),它的作用是将接收到的数据写入文件中。在回调函数的参数中,ptr 代表从服务器接收到的数据的指针,size 代表接收到的每个数据块的大小,nmemb 代表接收到的数据块的数量,stream 则是你传入 curl_easy_setopt() 函数中的第三个参数,通常情况下用于传递写入数据的文件句柄。函数的返回值则是写入文件的数据块数量。

在这里,stream 是一个指向 FILE 结构体的指针,它表示一个文件流对象。在 C 语言中,文件 I/O 操作需要使用 FILE 结构体来表示文件对象,其中包含了文件的状态信息,例如文件的位置、读写状态等等。指针变量 stream 就是用来指向这个 FILE 结构体的,通过它可以对文件进行读写操作。在这段代码中,stream 实际上指向了一个文件对象 fp,表示将写入的数据写入到这个文件中。


回调函数

回调函数(Callback function)是指由调用者(或客户端)传递给另外一个函数(或服务提供者)的可执行代码的引用,通常作为参数传递给函数。在执行过程中,函数通过调用这个可执行代码来实现一定的功能。

在上面的代码中,write_data就是一个回调函数,它被传递给了libcurl中的CURLOPT_WRITEFUNCTION选项。当libcurl需要写入数据时,就会调用这个回调函数来完成数据写入操作。


fwrite()

fwrite() 是 C 语言标准库中的一个函数,用于向文件中写入数据。它的函数原型如下:

size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);

ptr 是一个指向要写入数据的指针,size 是每个数据项的字节数,count 是要写入的数据项数目,stream 是指向文件的指针。函数的返回值是实际写入的数据项数目。

在上面的代码中,fwrite() 函数被用于将 curl 库获取的数据写入到文件中。ptr 参数是 curl 库返回的数据指针,size 是每个数据项的字节数,nmemb 是要写入的数据项数目,stream 是指向文件的指针。函数的返回值是实际写入的数据项数目,赋值给 written 变量,最终作为 write_data() 函数的返回值。


int written = fwrite(ptr, size, nmemb, (FILE *)fp);
return written;

这行代码是将获取到的数据写入到本地文件中,通过fwrite()函数实现。其中,参数ptr是指向数据块的指针,size是每个数据块的大小,nmemb是数据块的数量,fp是文件指针。函数返回值written是fwrite()实际写入的数据块数量,它应该等于nmemb,如果返回值小于nmemb,说明写入文件失败。最后,将written作为write_data()函数的返回值。

该回调函数只使用了一个文件指针(fp)来将下载的数据写入文件中。因此,此处将void指针(stream)转换为FILE指针(fp),以便在后续的文件写入操作中使用。


const char *path = "/tmp/curl-test";
const char *mode = "w";
fp = fopen(path, mode);

const char *path 定义了要写入的文件路径为 /tmp/curl-test。这是一个字符串常量,用 const char * 类型的指针来表示。

const char *mode 定义了打开文件的模式为 "w",即写入模式。这也是一个字符串常量,用 const char * 类型的指针来表示。

在下面的代码中,fopen() 函数将使用这两个参数打开一个文件,并返回一个文件指针,用于后续的文件操作。


const char *

const char * 是指向字符常量的指针类型。它指向的字符常量是不可修改的,而指针本身是可以修改的。在C语言中,字符串就是一个字符数组,以空字符'\0'作为结尾,所以用指向第一个字符的指针表示字符串是很方便的。


fopen()

fopen() 是 C 标准库中的一个函数,用于打开一个文件,并返回一个指向该文件的指针。它的原型如下:

FILE *fopen(const char *filename, const char *mode);

其中,filename 参数表示要打开的文件名,mode 参数表示打开文件的模式。在这段代码中,path 存储了文件名,mode 存储了打开文件的模式,它们被传递给 fopen() 函数来打开文件,并将返回的指针赋值给变量 fp

在这个例子中,文件名为 "/tmp/curl-test",模式为 "w",表示以写入模式打开文件,如果文件不存在则创建该文件,如果文件已经存在则截断文件长度为 0。


curl_global_init(CURL_GLOBAL_ALL);

curl_global_init 函数用于全局初始化 libcurl 库。在使用 libcurl 库的其他函数之前,需要调用该函数进行初始化。CURL_GLOBAL_ALL 参数表示对 SSL、文件协议以及其他协议进行初始化。


CURLcode

CURLcode是一个枚举类型,用于表示libcurl库函数调用的返回值。这个类型包括许多不同的返回值,例如CURLE_OK表示操作成功完成,而CURLE_FAILED_INIT则表示libcurl库初始化失败。在这个例子中,CURLcode res是一个变量,用于存储curl_easy_perform函数的返回值。


curl_easy_init()

curl_easy_init()是一个CURL库函数,它用于初始化CURL句柄,即创建一个新的CURL对象。这个句柄将被用来设置CURL选项,执行请求以及获取响应等。在使用CURL之前,需要先调用此函数来初始化CURL。如果初始化成功,则会返回一个指向CURL对象的指针,否则返回NULL。在本代码中,将curl_easy_init()的返回值赋值给指针变量curl。


curl_easy_setopt(curl, CURLOPT_URL, "http://www.linux-ren.org");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);

这三行代码用于设置 CURL 句柄(handle)的选项。

  • CURLOPT_URL 选项用于设置请求的 URL,这里设置为 "http://www.linux-ren.org" ,即向该URL 发送请求。
  • CURLOPT_WRITEFUNCTION 选项用于设置写入数据的回调函数,这里设置为 write_data,即当有数据返回时,将数据写入到文件中。
  • CURLOPT_VERBOSE 选项用于设置是否启用详细输出,这里设置为 1,即启用详细输出,将在标准错误输出中打印请求的详细信息,如请求头、响应头等。

res = curl_easy_perform(curl);

这行代码执行了实际的HTTP请求,并且将结果传回给应用程序。它将curl_easy_setopt函数中设置的所有选项应用于传输,并执行HTTP GET请求。HTTP响应的内容通过之前设置的回调函数write_data()写入到指定的文件中。执行成功时,该函数返回CURLE_OK(0),否则返回另一个错误码。

curl_easy_setopt是用于设置CURL请求选项的函数,而curl_easy_perform则是用于执行请求的函数。在设置完选项后,调用curl_easy_perform就可以发送HTTP请求并接收响应。


curl_easy_cleanup(curl);

curl_easy_cleanup函数是用来释放使用curl_easy_init函数初始化的CURL句柄。在使用完CURL相关操作后,需要调用该函数进行资源释放。


write_data函数并不是由main函数直接调用的,而是通过设置curl_easy_setopt中的CURLOPT_WRITEFUNCTION参数来告诉CURL库,当它下载数据时要使用write_data函数来处理这些数据。

在执行curl_easy_perform时,CURL库会自动调用设置的CURLOPT_WRITEFUNCTION函数,将下载的数据传递给该函数进行处理,这就是为什么我们看不到在main函数中显式调用write_data函数的原因。


FIND_PACKAGE(CURL)
IF(CURL_FOUND)
    INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR})
    TARGET_LINK_LIBRARIES(curltest ${CURL_LIBRARY})
ELSE(CURL_FOUND)
    MESSAGE(FATAL_ERROR "CURL library not found")
ENDIF(CURL_FOUND)

在这个代码片段中,首先使用FIND_PACKAGE命令查找CURL库,如果找到,则使用INCLUDE_DIRECTORIES命令添加CURL库的头文件路径,并使用TARGET_LINK_LIBRARIES命令将CURL库链接到名为“curltest”的目标可执行文件中。如果没有找到CURL库,则使用MESSAGE命令输出错误信息并终止构建。


SET(mySources viewer.c)
SET(optionalSources)
SET(optionalLibs)
FIND_PACKAGE(JPEG)
IF(JPEG_FOUND)
    SET(optionalSources ${optionalSources} jpegview.c)
    INCLUDE_DIRECTORIES(${JPEG_INCLUDE_DIR})
    SET(optionalLibs ${optionalLibs} ${JPEG_LIBRARIES})
    ADD_DEFINITIONS(-DENABLE_JPEG_SUPPORT)
ENDIF(JPEG_FOUND)
IF(PNG_FOUND)
    SET(optionalSources ${optionalSources} pngview.c)
    INCLUDE_DIRECTORIES(${PNG_INCLUDE_DIR})
    SET(optionalLibs ${optionalLibs} ${PNG_LIBRARIES})
    ADD_DEFINITIONS(-DENABLE_PNG_SUPPORT)
ENDIF(PNG_FOUND)
ADD_EXECUTABLE(viewer ${mySources} ${optionalSources})
TARGET_LINK_LIBRARIES(viewer ${optionalLibs})

 

SET(mySources viewer.c)
SET(optionalSources)
SET(optionalLibs)

这三行代码定义了三个变量:

  • mySources:包含主要源文件 viewer.c
  • optionalSources:包含可选源文件(暂时为空)
  • optionalLibs:包含可选库(暂时为空)

SET(optionalSources ${optionalSources} jpegview.c)

这行代码的作用是将 jpegview.c 文件加入到一个名为 optionalSources 的 CMake 变量中。SET 命令的语法为:

SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])

其中,VAR 为变量名,VALUE 为变量的值,CACHE, TYPE, DOCSTRING, FORCE 为可选参数。在这行代码中,VARoptionalSourcesVALUE${optionalSources} jpegview.c,即在原有的 optionalSources 变量中加入 jpegview.c 文件。${} 语法是 CMake 中的变量引用方式,表示获取变量的值。

第一个 optionalSources 是一个变量名,而第二个 optionalSources 是一个键值对中的键(key),表示要设置的变量。这个键的值是 ${optionalSources},表示获取变量 optionalSources 当前的值。

在 CMake 中,变量名前不加 $ 符号,而变量的值在使用时需要使用 ${} 将其括起来。因此,第二个 optionalSources 是为了获取变量 optionalSources 当前的值。


ADD_DEFINITIONS(-DENABLE_JPEG_SUPPORT)

ADD_DEFINITIONS 是一个 CMake 指令,用于向编译器添加自定义的编译选项。该指令会向 CMakeLists.txt 文件中的所有子目录和源文件添加预处理器宏定义。

在这个代码段中,ADD_DEFINITIONS(-DENABLE_JPEG_SUPPORT) 添加了一个名为 ENABLE_JPEG_SUPPORT 的预处理器宏定义,以启用 JPEG 支持。在 C++ 源文件中,可以通过 #ifdef ENABLE_JPEG_SUPPORT 这样的条件编译语句来判断是否启用了 JPEG 支持。


optionalSources初始值为一个空列表,即没有任何元素。在这里,SET(optionalSources)实际上是在声明一个变量optionalSources并初始化为空列表。如果JPEG和PNG都存在,那么 optionalSources 最后会包含 jpegview.cpngview.c


FIND_PATH(HELLO_INCLUDE_DIR hello.h /usr/include/hello /usr/local/include/hello)
FIND_LIBRARY(HELLO_LIBRARY NAMES hello PATH /usr/lib /usr/local/lib) 

IF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY)
  SET(HELLO_FOUND TRUE)
ENDIF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY)

IF (HELLO_FOUND)
  IF (NOT HELLO_FIND_QUIETLY)
    MESSAGE(STATUS "Found Hello: ${HELLO_LIBRARY}")
  ENDIF (NOT HELLO_FIND_QUIETLY)
ELSE (HELLO_FOUND)
  IF (HELLO_FIND_REQUIRED)
    MESSAGE(FATAL_ERROR "Could not find hello library")
  ENDIF (HELLO_FIND_REQUIRED)
ENDIF (HELLO_FOUND)

这段CMake代码主要是用于查找并配置hello库,包括其头文件路径和库文件路径。具体解释如下:

  • FIND_PATH宏用于查找hello.h头文件所在路径,查找路径分别为/usr/include/hello/usr/local/include/hello。找到后将路径存储在变量HELLO_INCLUDE_DIR中。
  • FIND_LIBRARY宏用于查找hello库文件所在路径,查找路径分别为/usr/lib/usr/local/lib,库文件名为hello。找到后将路径存储在变量HELLO_LIBRARY中。
  • IF条件判断语句用于判断头文件路径和库文件路径是否都存在,如果都存在,则将变量HELLO_FOUND设为TRUE
  • IF条件判断语句用于判断是否找到hello库。如果找到,则输出信息Found Hello: ${HELLO_LIBRARY},否则根据HELLO_FIND_REQUIRED变量的值来确定是输出错误信息还是跳过。
  • ELSE语句用于处理未找到hello库的情况,根据HELLO_FIND_REQUIRED变量的值来确定是输出错误信息还是跳过。

 HELLO_FIND_QUIETLY 是一个可选参数,它通常在调用 FIND_LIBRARYFIND_PATH 等函数时使用,用于指示这些函数在查找库或头文件时是否输出诊断信息。如果将 HELLO_FIND_QUIETLY 设置为 TRUE,则函数会在找不到指定库或头文件时仍然返回成功,但不会输出错误信息。在示例代码中,HELLO_FIND_QUIETLY 没有显式设置,因此默认为 FALSE。


FIND_PACKAGE(HELLO)

IF(HELLO_FOUND)
  ADD_EXECUTABLE(hello main.c)
  INCLUDE_DIRECTORIES(${HELLO_INCLUDE_DIR})
  TARGET_LINK_LIBRARIES(hello ${HELLO_LIBRARY})
ENDIF(HELLO_FOUND)

这段代码是使用CMake的命令来查找HELLO库并链接到一个可执行文件中。

  • FIND_PACKAGE命令会在系统中查找名为HELLO的包,该包应该提供了可用于构建目标的头文件和库文件。
  • IF(HELLO_FOUND)检查是否找到了HELLO包,如果找到了,则添加一个名为hello的可执行文件,包括main.c源文件,然后使用INCLUDE_DIRECTORIES命令将HELLO_INCLUDE_DIR目录添加到包含路径中,最后使用TARGET_LINK_LIBRARIESHELLO_LIBRARY链接到hello可执行文件中。

${HELLO_INCLUDE_DIR}是一个变量,它表示 FIND_PACKAGE 命令查找到的 HELLO 库的头文件目录路径。${HELLO_LIBRARY}也是一个变量,表示 FIND_PACKAGE 命令查找到的 HELLO 库的路径。在这段代码中,INCLUDE_DIRECTORIES命令将 HELLO 库的头文件目录路径添加到项目中,TARGET_LINK_LIBRARIES命令将 HELLO 库链接到可执行文件 hello 中。


通常情况下,${PROJECT_SOURCE_DIR}是在顶层CMakeLists.txt文件中通过PROJECT()命令设置的,它表示项目的根目录路径。因此,如果使用${PROJECT_SOURCE_DIR},则需要在项目中使用PROJECT()命令设置项目名称。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值