【动态库.so | 头文件.hpp】基于CMake与CMakeList编写C++自定义库
前言
- 最近比较忙,其他系列教程得等到年后一起更!请大家多多包涵!!
- 相信各位在配置C++环境和各类库的时候一定经常看到如下小连招
git clone https://github.com/opencv/opencv.git
cd opencv
mkdir build && cd build
cmake .. && make -j4
sudo make install
sudo ldconfig
- 今天我们就来谈一谈上述每一行指令的具体含义,并尝试构造我们自己的示例库。
- 之后我们就可以使用编译生成的动态库文件
.so
和我们提供的头文件hpp
给用户使用。 - 通过配置我们可以在
cmake
直接包含链接我们的自定义库find_package(MyTime REQUIRED)
1 分布讲解
1-1 拉去仓库
git clone https://github.com/opencv/opencv.git
- 用于从远程仓库复制一个完整的版本库到本地。
https://github.com/opencv/opencv.git
: 指定了要克隆的远程仓库的URL。此处是OpenCV的官方GitHub仓库地址。
- 执行此命令后,会在当前目录下生成一个名为
opencv
的子文件夹,其中包含OpenCV的完整源代码。
cd opencv
1-2 编译
mkdir build && cd build
- 创建一个名为
build
的子目录,并切换到该目录。通常在此目录中进行编译过程。
cmake .. && make -j4
- 使用CMake生成构建配置并启动构建过程。
cmake ..
: 运行CMake命令,..
表示CMake应该在父目录(即源代码目录)中查找CMakeLists.txt
文件。CMake会根据这个文件生成构建配置文件(如Makefile或Visual Studio工程文件等)。make -j4
: 使用make
工具来开始构建过程。-j4
指定了并行构建的线程数,这里是4个线程,可以加速编译过程。
1-3 安装
sudo make install
make install
: 这个命令会将构建完成的库、头文件和可执行文件拷贝到系统的标准位置(例如/usr/local/lib
、/usr/local/include
等)。具体的安装路径和内容是由CMakeLists.txt
文件中指定的。sudo
: 由于make install
通常需要系统级的权限,所以需要sudo
来提权执行命令。
sudo ldconfig
ldconfig
: 是一个用于更新动态链接库缓存的命令。操作系统会根据/etc/ld.so.conf
和/lib
、/usr/lib
等目录中的库文件来更新缓存。ldconfig
命令的目的是让操作系统能够找到新安装的库文件。sudo
: 因为ldconfig
需要修改系统级配置,所以需要sudo
权限。
1-4 使用
- 通过上述过程,如果你在其他项目中需要使用到 OpenCV,你只需要在项目中正确配置 OpenCV 的头文件和库文件路径,并在编译时链接 OpenCV 库即可。
cmake_minimum_required(VERSION 3.10)project(MyProject)# 查找 OpenCV 包
find_package(OpenCV REQUIRED)# 将 OpenCV 的 include 目录添加到编译器的头文件路径
include_directories(${OpenCV_INCLUDE_DIRS})# 定义源文件
add_executable(my_program main.cpp)# 将 OpenCV 库链接到项目中
target_link_libraries(my_program ${OpenCV_LIBS})
2 构建自己的库
- 通过上述说明,你也许还是懵懵懂懂,下面我们创建自己的一个库,并逐步细致的说明。
2-1 创建文件夹
- 我们打开终端,创建一个文件夹用来存放我们的自定义库,我们给库取名为
MyTime
,并在这个文件夹下创建src
和include
目录。
mkdir -p MyTime/src MyTime/include
-p
:表示递归创建目录,若父目录不存在,也会一并创建。MyTime/src
:存放源代码文件。
MyTime/include
:存放头文件。
2-2 编写头文件
- 我们实现一个简单的打印当前时间库,这个库将提供几个函数
printCurrentDate
:打印当前日期(年月日格式)。getFormattedTime
:返回当前时间的格式化字符串(比如 “YYYY-MM-DD HH:MM:SS”)。calculateTimeDifference
:计算两个时间点之间的差异,返回秒数。
- 然后根据上述旨意,我们编写
include/MyTime.hpp
// MyTime/include/MyTime.h
#ifndef __MY_TIME_HPP__
#define __MY_TIME_HPP__#include <string>namespace mytime{class MyTime{public:// 打印当前的时间void printCurrentTime();// 打印当前的日期(年月日)void printCurrentDate();// 获取当前时间的格式化字符串(YYYY-MM-DD HH:MM:SS)std::string getFormattedTime();// 计算两个时间点之间的差异,返回秒数double calculateTimeDifference(const std::string& time1, const std::string& time2);};};#endif // __MY_TIME_HPP__
- 头文件保护机制:
#ifndef
,#define
和#endif
这一组预处理指令保证了头文件只会被包含一次,从而防止重复定义。__MY_TIME_HPP__
是一个宏定义,它会在头文件被第一次包含时被定义,从而防止第二次包含。- 除此之外我们还可以使用
pragma once
防止头文件被重复编译
- 命名空间
mytime
:为了避免与其他库中的类名冲突,我们将MyTime
类放在mytime
命名空间中。这样你就可以通过mytime::MyTime
来访问MyTime
类。 MyTime
类声明:printCurrentTime
:用于打印当前系统时间。printCurrentDate
:用于打印当前系统日期,格式为YYYY-MM-DD
。getFormattedTime
:返回当前时间的格式化字符串,格式为YYYY-MM-DD HH:MM:SS
。calculateTimeDifference
:计算两个给定时间字符串之间的差异,单位为秒。该函数接受两个字符串形式的时间(格式为YYYY-MM-DD HH:MM:SS
),并返回它们之间的差异(秒)。
2-3 编写源代码
src\Mytime.cpp
// MyTime/src/MyTime.cpp
#include "../include/MyTime.hpp"
#include <iostream>
#include <chrono>
#include <ctime>
#include <sstream>
#include <iomanip>namespace mytime {// 打印当前的时间void MyTime::printCurrentTime() {// 获取当前时间auto now = std::chrono::system_clock::now();std::time_t now_time = std::chrono::system_clock::to_time_t(now);// 打印时间std::cout << "Current time: " << std::ctime(&now_time);}// 打印当前的日期(年月日)void MyTime::printCurrentDate() {// 获取当前时间auto now = std::chrono::system_clock::now();std::time_t now_time = std::chrono::system_clock::to_time_t(now);std::tm *tm = std::localtime(&now_time);// 打印日期(格式为 YYYY-MM-DD)std::cout << "Current date: "<< std::put_time(tm, "%Y-%m-%d") // 格式化为 YYYY-MM-DD<< std::endl;}// 获取当前时间的格式化字符串(YYYY-MM-DD HH:MM:SS)std::string MyTime::getFormattedTime() {// 获取当前时间auto now = std::chrono::system_clock::now();std::time_t now_time = std::chrono::system_clock::to_time_t(now);std::tm *tm = std::localtime(&now_time);// 使用 stringstream 格式化时间为字符串std::ostringstream oss;oss << std::put_time(tm, "%Y-%m-%d %H:%M:%S"); // 格式化为 YYYY-MM-DD HH:MM:SSreturn oss.str();}// 计算两个时间点之间的差异,返回秒数double MyTime::calculateTimeDifference(const std::string& time1, const std::string& time2) {std::tm tm1 = {}, tm2 = {};std::istringstream ss1(time1), ss2(time2);// 解析时间字符串(格式: YYYY-MM-DD HH:MM:SS)ss1 >> std::get_time(&tm1, "%Y-%m-%d %H:%M:%S");ss2 >> std::get_time(&tm2, "%Y-%m-%d %H:%M:%S");// 将 std::tm 转换为 time_tstd::time_t t1 = std::mktime(&tm1);std::time_t t2 = std::mktime(&tm2);// 计算两个时间点的时间差,单位为秒double diff = std::difftime(t2, t1);return diff;}} // namespace mytime
2-4 Cmake
2-4-1 Cmake介绍
CMake
是一个跨平台的自动化构建工具,它能够生成不同平台(如 Linux、Windows、macOS)上的构建文件,并支持多种构建系统(如Make
,Ninja
,Visual Studio
,Xcode
等)。CMake 主要用于管理大型软件项目的构建过程,通过配置项目的构建步骤来生成适合目标平台的构建文件。
2-4-2 CMake 的核心概念
- CMakeLists.txt: 这是 CMake 项目的配置文件,通常在每个子目录中都有一个,CMake 会根据这些文件来决定如何构建整个项目。
CMakeLists.txt
文件包含了 CMake 的指令和设置,它们告诉 CMake 如何配置、编译和链接项目。 - CMake 构建过程: CMake 的构建过程通常包括两个步骤:
- 配置步骤:通过执行
cmake
命令,CMake 会根据CMakeLists.txt
文件的内容生成构建系统的配置文件(如Makefile
、Visual Studio
项目文件等)。 - 构建步骤:通过执行构建命令(如
make
、ninja
等),CMake 会使用生成的构建文件来编译项目。
- 配置步骤:通过执行
- CMake 变量: CMake 允许在
CMakeLists.txt
文件中定义变量,这些变量可以在整个项目中使用。变量可以用来指定路径、设置编译选项或管理构建参数。 - 目标(Target): 在 CMake 中,“目标” 是你要构建的最终产物(比如库文件、可执行文件等)。通过 CMake 指令如
add_executable
、add_library
来定义目标。
2-4-3 CMake 的主要指令
cmake_minimum_required(VERSION <version>)
: 这个指令设置项目所需的最低 CMake 版本。如果当前的 CMake 版本低于这个版本,CMake 会发出警告。
cmake_minimum_required(VERSION 3.10)
project(<project-name>)
: 设置当前项目的名称。
project(MyProject)
set(<variable> <value>)
: 设置变量的值。CMake 中的变量可以用来存储路径、文件名、编译选项等。
set(CMAKE_CXX_STANDARD 11)
add_executable(<target> <source-files>)
: 用于创建一个可执行文件目标,指定源文件列表。
add_executable(MyApp main.cpp)
add_library(<target> <source-files>)
: 用于创建一个库目标,可以是静态库或动态库。
add_library(MyLib STATIC mylib.cpp)
target_link_libraries(<target> <libraries>)
: 指定可执行文件或库所依赖的其他库。
target_link_libraries(MyApp MyLib)
include_directories(<dir>)
: 用于添加头文件的搜索路径。
include_directories(${CMAKE_SOURCE_DIR}/include)
find_package(<package-name>)
: 查找已安装的外部库(如 Boost、OpenGL、Qt 等)。
find_package(Boost REQUIRED)
install(<target>)
: 指定安装规则,用于将构建好的目标安装到指定目录。
install(TARGETS MyApp DESTINATION /usr/local/bin)
2-5 配置Cmake
- 我们为我们的
MyTime
库进行cmake
的配置 - 我们在
MyTime
文件夹下新建文件CMakeLists.txt
# 设置最低 CMake 版本
cmake_minimum_required(VERSION 3.10)# 设置工程名称
project(MyTime)# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 11)# 设置包含头文件的目录
include_directories(${CMAKE_SOURCE_DIR}/include)# 添加源文件目录
add_library(MyTime SHAREDsrc/MyTime.cpp
)# 如果你要构建可执行文件(例如测试程序),可以添加以下内容
# add_executable(MyTimeTest src/test.cpp)
# target_link_libraries(MyTimeTest MyTime)
2-5-1 静态库和动态库
- 静态库(Static Library)和动态库(Dynamic Library)是两种常见的库类型,它们主要在 编译时 和 运行时 的行为上有很大的不同。
特性 | 静态库(Static Library) | 动态库(Dynamic Library) |
---|---|---|
链接时机 | 编译时链接,生成的程序包含库的代码 | 运行时链接,程序在运行时加载库 |
文件格式 | .a (Linux/macOS)或 .lib (Windows) | .so (Linux)、.dll (Windows)、.dylib (macOS) |
文件大小 | 可执行文件较大,因为库代码包含在内 | 可执行文件较小,库共享但需单独管理 |
程序启动时间 | 启动较快,不需要加载外部库文件 | 启动时需要加载外部库文件,可能稍慢 |
依赖性 | 不依赖外部库,完全独立 | 依赖外部库,必须保证库文件存在 |
更新和维护 | 更新库时必须重新编译所有依赖该库的程序 | 更新库时无需重新编译程序,只要库接口不变 |
内存使用 | 每个程序有一份库的副本 | 多个程序共享一份库的副本 |
兼容性 | 编译后不易出现兼容性问题 | 可能出现库版本不匹配或丢失的问题 |
- 在
CMakeLists.txt
中,我们可以制定参数SHARED
用于配置动态库
add_library(MyTime SHAREDsrc/MyTime.cpp
)
- 如果你希望配置静态库,可以把
SHARED
替换为STATIC
add_library(MyTime STATICsrc/MyTime.cpp
)
2-6 cmake …
- 在配置好
CMakeLists
以后,我们就可以进行编译了,我们需要创建一个build
文件夹,build
文件夹通常用于存储编译中间和结果文件
mkdir build && cd build
-
自此我们的库一共有如下结构
-
-
紧接着我们进行编译,再
cd build
进入build
目录下以后,我们执行
cmake ..
- 执行完上述指令后,我们可以看到
build
目录下多出了如下文件
CMakeCache.txt
- 作用:
CMakeCache.txt
是 CMake 配置过程的缓存文件,保存了配置过程中产生的一些变量值(例如编译器路径、编译选项等)。当你重新运行 CMake 时,它会使用这个文件中的信息来加速配置过程。 - 内容:文件中包含了各种设置项,如编译器路径、标志、包含的头文件路径、库路径等。
- 解释:CMake 使用这个文件存储了用户和 CMake 检测到的配置结果,因此如果没有做任何修改,下一次执行
cmake
时会直接读取此文件,加速配置过程。
- 作用:
CMakeFiles
目录- 作用:
CMakeFiles
目录包含 CMake 使用的内部文件,这些文件是 CMake 在生成构建系统时创建的。 - 内容:该目录中包含了一些用于构建的中间文件和子目录,CMake 会根据它们来管理依赖关系和生成最终的构建文件。
- 解释:通常你不需要直接操作这个目录,它是 CMake 在后台处理过程中需要用到的。
- 作用:
cmake_install.cmake
- 作用:
cmake_install.cmake
是 CMake 在配置时生成的安装脚本文件。当你执行make install
或其他安装命令时,CMake 会使用这个文件来执行安装过程。 - 内容:该文件定义了安装目标,如如何将生成的可执行文件、库文件、头文件等复制到指定的系统目录。
- 解释:通常你不需要手动编辑这个文件,除非你有特定的安装需求。它是构建完成后安装过程的关键部分。
- 作用:
Makefile
- 作用:
Makefile
是一个标准的构建脚本文件,CMake 在执行配置时生成它。它包含了如何编译项目的详细信息和规则。 - 内容:这个文件列出了所有的源文件、编译选项、依赖关系以及如何将它们编译和链接成最终的可执行文件。
- 解释:当你运行
make
命令时,它会读取Makefile
,并根据其中的规则来执行编译任务。你可以使用make
来编译项目,或者使用make install
来安装项目。
- 作用:
- 实际上我们使用
cmake ..
就是借助CMakeLists.txt
的配置项去生成编译最关键的Makefile
,用于辅助一会要进行的make
,值得一提的是makefile
是可以直接手写的,因此cmake ..
这一步骤不是必要的,但是当构建大型项目的时候,手写makefile
不方便,因此我们通常借助CMakeLists.txt
和cmake ..
进行makefile
的生成。
2-7 make -j4
- 在
CMakeLists.txt
的辅助下,我们已经生成了makefile
,因此我们正式开始编译
make -j4
- **
[ 50%] Building CXX object CMakeFiles/MyTime.dir/src/MyTime.cpp.o
**这行表示正在编译src/MyTime.cpp
这个源文件。Building CXX object
:说明正在编译 C++ 源文件,目标是生成对应的目标文件(.o
文件)。此时,CMake 会将src/MyTime.cpp
文件编译成一个名为MyTime.cpp.o
的目标文件(即中间文件)。
[100%] Linking CXX shared library libMyTime.so
- 这行表示链接阶段,正在将之前生成的目标文件(如
.o
文件)链接成一个共享库(.so
文件)。
- 这行表示链接阶段,正在将之前生成的目标文件(如
- 编译完成后,我们看到
build
文件夹下生成了libMyTime.so
的动态库文件 - 有了编译生成的
libMyTime.so
和头文件MyTime.hpp
,用户就可以在没有源文件的情况下使用你的代码了。
2-8 sudo make install 安装
- 在用户完成编译后,手动链接动态库其实就可以直接使用你的库了,但是一般我们会使用
sudo make install
将编译生成的动态库和静态库移动到usr/local/lib
中,这样方便我们直接调用。
sudo make install
-
然后不出意外你会抱错
-
那是因为如果你在项目的
Makefile
或CMakeLists.txt
中没有显式定义install
规则,make install
命令就无法找到要执行的目标。install
是一种标准的构建目标,但它不是自动存在的,必须在CMakeLists.txt
或Makefile
中显式声明。 -
我们在
CMakeLists.txt
中添加
install(TARGETS MyTime DESTINATION /usr/local/lib)
install(FILES include/MyTime.hpp DESTINATION /usr/local/include)
- 然我们需要重新生成构建文件并编译
cmake .. && make -j4
- 然后执行
sudo make install
- 我们可以在上述路径中找到这两个文件。这样用户在使用代码的时候,就不需要手动进行库链接了。
2-9 sudo ldconfig
sudo ldconfig
sudo ldconfig
是一个在 Linux 系统中用于更新和管理动态链接库缓存的命令。它会扫描系统上指定的库目录,并更新缓存,以便程序能够找到新的共享库(.so
文件)。通常在安装新库(尤其是共享库)时,执行sudo ldconfig
以确保系统能够正确地识别和链接新库。
- 更新共享库缓存:
- Linux 系统使用动态链接库(shared libraries)来支持程序的运行。
ldconfig
会扫描/etc/ld.so.conf
文件中列出的目录以及LD_LIBRARY_PATH
环境变量指定的路径,找到所有共享库,并将它们的信息保存到缓存中。这个缓存文件通常位于/etc/ld.so.cache
。 - 执行
ldconfig
后,程序在运行时就可以更快地查找需要的库文件,而不必每次都重新搜索整个文件系统。
- Linux 系统使用动态链接库(shared libraries)来支持程序的运行。
- 让新安装的库生效:
- 当你使用
make install
或通过包管理工具(如apt
、yum
等)安装共享库时,ldconfig
会帮助系统更新库的路径,使得程序能够找到新安装的库。特别是在安装自定义库时,执行ldconfig
是很有必要的。
- 当你使用
- 让
ld
和ld.so
正确工作:- 在使用动态链接器加载共享库时(例如,运行一个依赖于共享库的程序),
ldconfig
会确保这些库的路径信息被正确记录,并且链接器能够正确地找到并加载这些库。
- 在使用动态链接器加载共享库时(例如,运行一个依赖于共享库的程序),
3 使用自定义库
- 通过上述过程,我们已经成功把
MyTime
写入下属路径了
-- Up-to-date: /usr/local/lib/MyTime.so
-- Installing: /usr/local/include/MyTime.hpp
- 我们分别讲讲手动链接和
Cmake
链接
3-1 调用头文件<MyTime.hpp>
- 我们创建一个新的
cpp
文件并调用我们的自定义库,testMyTime.cpp
#include <iostream>
#include <MyTime.hpp>int main() {mytime::MyTime timeObj;// 打印当前时间timeObj.printCurrentTime();// 打印当前日期(年月日)timeObj.printCurrentDate();// 获取当前时间的格式化字符串std::string formattedTime = timeObj.getFormattedTime();std::cout << "Formatted time: " << formattedTime << std::endl;// 计算两个时间点之间的差异double timeDiff = timeObj.calculateTimeDifference("2024-12-09 10:00:00", "2024-12-09 12:00:00");std::cout << "Time difference: " << timeDiff << " seconds" << std::endl;return 0;
}
3-2 手动链接
- 由于
MyTime
是一个动态链接库(.so
文件),你需要确保在编译时链接该库。可以通过以下两种方式之一来链接:- 编译时指定库路径:在编译时,使用
-L
选项指定库文件的路径(例如:/usr/local/lib/
),并使用-l
选项指定库名(不包括lib
前缀和文件扩展名)。例如:
g++ -o testMyTime testMyTime.cpp -L/usr/local/lib -lMyTime
- 设置环境变量:你还可以通过设置
LD_LIBRARY_PATH
环境变量来告诉系统动态库的路径:
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
- 编译时指定库路径:在编译时,使用
3-3 编译并测试
- 编译
g++ -o testMyTime testMyTime.cpp -L/usr/local/bin -lMyTime
- 可以看到我们中途在手动链接的时候出现了错误
./testMyTime: error while loading shared libraries: libMyTime.so: cannot open shared object file: No such file or directory
- 顾名思义是由于找不到动态库
.so
导致的,我们只需要手动进行制定即可
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
3-4 CMake配置
- 上述手动配置对于小的项目还好,大的项目还是要借助
CMake
比较方便 - 同理我们创建
src
目录,并把testMyTime.cpp
移动到底下,并创建CMakeLists.txt
# 设置最低 CMake 版本
cmake_minimum_required(VERSION 3.10)# 设置工程名称
project(MyTime)# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 11)set(CMAKE_INSTALL_PREFIX "/usr/local")
# 设置包含头文件的目录
include_directories(${CMAKE_SOURCE_DIR}/include)# 添加源文件目录
add_library(MyTime SHAREDsrc/MyTime.cpp
)
install(TARGETS MyTime DESTINATION /usr/local/lib)
install(FILES include/MyTime.hpp DESTINATION /usr/local/include)
# 安装 CMake 配置文件
install(FILES cmake/MyTimeConfig.cmake DESTINATION /usr/local/lib/cmake/myTime)# 如果你要构建可执行文件(例如测试程序),可以添加以下内容
# add_executable(MyTimeTest src/test.cpp)
# target_link_libraries(MyTimeTest MyTime)
- 然后我们
mkdir build && cd build cmake .. && make -j4
- 可以看到我们的
so
被找到,程序正确执行了。
4 如何让 find_package(myTime REQUIRED)
可用
- 果你希望通过
find_package(myTime REQUIRED)
来查找你的自定义库MyTime
,你需要为它提供一个合适的 CMake 配置文件或者模块。
4-1 配置myTimeConfig.cmake
- 我们让
MyTime
库提供一个 CMake 配置文件myTimeConfig.cmake
,这样你可以直接使用find_package(myTime REQUIRED)
。 - 我们回到刚刚的库
MyTime
文件夹中,创建文件夹cmake
,并在底下创建myTimeConfig.cmake
# myTimeConfig.cmake# 设置 MyTime 库的位置
set(MYTIME_LIBRARY /usr/local/lib/libMyTime.so)# 设置 MyTime 头文件的路径
set(MYTIME_INCLUDE_DIR /usr/local/include)# 提供 MyTime 库和头文件路径
set(MyTime_INCLUDE_DIRS ${MYTIME_INCLUDE_DIR})
set(MyTime_LIBRARIES ${MYTIME_LIBRARY})# 设置 MyTime_FOUND 标志
set(MyTime_FOUND TRUE)
- 然后我们需要在
CMakeLists.txt
中设置安装规则
# 安装 CMake 配置文件
install(FILES cmake/myTimeConfig.cmake DESTINATION /usr/local/lib/cmake/myTime)
- 然后在
build
目录中执行丝滑小连招
cd build
cmake .. && make -j4
sudo make install
sudo ldconfig
-
注意取名必须是
MyTimeConfig.cmake
或者mytime-config.cmake
-
否则你在进行
find_package(myTime REQUIRED)
会出现报错
4-2 find_package(myTime REQUIRED)
- 我们打开
TestMyTime
文件夹下的CMakeLists.txt
# 设置最低的 CMake 版本要求
cmake_minimum_required(VERSION 3.10)# 设置项目名称
project(MyTimeProject)# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 11)# 查找 MyTime 库和其配置
find_package(MyTime REQUIRED)# 添加源文件
add_executable(testMyTime src/testMyTime.cpp)# 使用 MyTime 库链接目标
target_include_directories(testMyTime PRIVATE ${MyTime_INCLUDE_DIRS})
target_link_libraries(testMyTime PRIVATE ${MyTime_LIBRARIES})# 如果 MyTime 库是动态库,还需要设置运行时库路径
# 这样在运行时可以找到动态库
# 如果动态库不在默认的路径中,可以通过以下方式指定:
# set(CMAKE_INSTALL_RPATH /usr/local/lib)
- 重新进行连招,我们就有
5总结
- 本文讲述了如何借助
CMake
和CMakeLists
去创建自定义C++库,如何链接到.so
并包含hpp
去运行自定义库 - 如有错误欢迎指出!!!
- 感谢支持!!!
相关文章:
【动态库.so | 头文件.hpp】基于CMake与CMakeList编写C++自定义库
前言 最近比较忙,其他系列教程得等到年后一起更!请大家多多包涵!!相信各位在配置C环境和各类库的时候一定经常看到如下小连招 git clone https://github.com/opencv/opencv.git cd opencv mkdir build && cd build cma…...
三、nginx实现lnmp+discuz论坛
lnmp l:linux操作系统 n:nginx前端页面 m:mysql数据库,账号密码,数据库等等都保存在这个数据库里面 p:php——nginx擅长处理的是静态页面,页面登录账户,需要请求到数据库&#…...
OpenCV相机标定与3D重建(15)计算给定图像点对应的极线(epipolar lines)函数computeCorrespondEpilines()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 计算给定图像点对应的极线(epipolar lines)。 对于立体图像对中一个图像的点,计算这些点在另一个图像中对应的…...
【MySQL 进阶之路】了解 性能优化 与 设计原则
1.B树的优势 “矮胖”结构: 矮:B树的每个节点存储更多的关键字,从而减少了树的层级(最多三层),减少了磁盘I/O操作,提高了查询效率。胖:叶子节点存储实际的数据,并使用双…...
【银河麒麟高级服务器操作系统】修改容器中journal服务日志存储位置无效—分析及解决方案
了解更多银河麒麟操作系统全新产品,请点击访问 麒麟软件产品专区:https://product.kylinos.cn 开发者专区:https://developer.kylinos.cn 文档中心:https://documentkylinos.cn 服务器环境以及配置 【机型】 整机类型/架构&am…...
轻量级的 HTML 模板引擎
Mustache 简介:Mustache 是一个非常简单的逻辑少的模板引擎,支持 HTML 文件中的占位符替换。它不会执行复杂的逻辑,只支持简单的变量替换。 安装: npm install mustache示例: const Mustache require(mustache);c…...
SQL 查询秘籍:提升你数据库技能的实用指南
目录 一、常用表表达式(CTEs) (一)快速了解 (二)上手演练 练习一:使用 CTE 来分解一般查询逻辑 练习二:使用 CTE 来分解复杂的查询逻辑 二、递归CTEs (一…...
Unity背包道具拖拽(极简版实现)
(感觉Csdn代码页面可以再大一点或者加个放大功能 不然得划着看不太舒服) 1.关键接口,三个拖拽相关的 2.关键参数,PointerEventData 一直没仔细看过,其实有包含鼠标相关的很多参数,鼠标点击次数ÿ…...
Vins_Fusion_gpu中source setup.bash
文章目录 source setup.bashsetup.bashsetup.sh脚本的主要功能脚本的详细解释1. **初始化和检查**2. **检测操作系统**3. **设置环境变量**4. **记住 shell 类型**5. **调用 Python 脚本生成环境变量**6. **加载环境钩子**7. **清理** 总结 _setup_util.py_setup_util.py 的完整…...
appium学习之二:adb命令
1、查看设备 adb devices 2、连接 adb connect IP:端口 3、安装 adb install xxx.apk 4、卸载 adb uninstall 【包名】 5、把对应目录下的1.txt文件传到手机sdcard下 adb push 1.txt /sdcard 6、进入对应的设备里 adb shell 7、切入sdcard目录 cd /sdcard 8、ls 查…...
Docker 安装 sentinel
Docker 安装系列 1、拉取 [rootTseng ~]# docker pull bladex/sentinel-dashboard Using default tag: latest latest: Pulling from bladex/sentinel-dashboard 4abcf2066143: Pull complete 1ec1e81da383: Pull complete 56bccb36a894: Pull complete 7cc80011dc6f: Pull…...
selinux
项目使用: neverallow 报错: 改法:typeattribute system_server system_writes_mnt_vendor_violators; selinux目录: Android/lagvm/LINUX/android/device/qcom/sepolicy_vndr Android/lagvm/LINUX/android/vendor/jidu/sepoli…...
C++11新特性之线程std::atomic
C std::atomic C 中 std::atomic 的定义及功能 std::atomic 是 C 标准库中提供的一种模板类,主要用于实现 原子操作,以保证多线程环境下对共享变量的安全操作。它提供了一种无需使用互斥锁即可安全访问共享数据的机制,避免了竞争条件。 特…...
MySQL中的通配符
1. 百分号 % 内部工作原理: 代表零个、一个或多个任意字符。LIKE A%:MySQL会从索引(如果存在)中查找所有以 "A" 开头的记录。如果没有索引,则需要扫描整个表。MySQL解析器将模式转换为可执行的形式&#x…...
【ETCD】【源码阅读】stepWithWaitOption方法解析
在分布式系统中,ETCD 作为一个强一致性、高可用的 key-value 存储系统,广泛应用于服务发现、配置管理等场景。ETCD 在内部采用了 Raft 协议来保证集群的一致性,而日志预提案(log proposal)是 Raft 协议中至关重要的一部…...
F12抓包01:启动、面板功能介绍、语言设置、前端样式调试
浏览器检查工具通常用来作为浏览器web服务测试过程中,辅助测试、排查问题、定位缺陷的工具。 本文以mac系统下,当前比较常用的Chrome浏览器为例,讲解“检查”工具的常用功能操作方法。 一、打开方式 **1、****鼠标操作:**浏览器…...
用前端html如何实现2024烟花效果
用HTML、CSS和JavaScript编写的网页,主要用于展示“2024新年快乐!”的文字形式烟花效果。下面是对代码主要部分的分析: HTML结构 包含三个<canvas>元素,用于绘制动画。引入百度统计的脚本。 CSS样式 设置body的背景为黑…...
Maven学习(传统Jar包管理、Maven依赖管理(导入坐标)、快速下载指定jar包)
目录 一、传统Jar包管理。 (1)基本介绍。 (2)传统的Jar包导入方法。 1、手动寻找Jar包。并放置到指定目录下。 2、使用IDEA的库管理功能。 3、配置环境变量。 (3)传统的Jar包管理缺点。 二、Maven。 &#…...
mac电脑吧iso文件制作成u盘启动
1 查看u盘的具体路径 diskutil list 根据容量确认路径 /dev/disk2 2、卸载u盘 diskutil unmountDisk /dev/disk2 3、把iso文件写入u盘 sudo dd if/Users/dengjinshan/Downloads/Win11_24H2_Chinese_Simplified_x64.iso of/dev/disk2 bs1m 4、弹出u盘 diskutil eject /dev/dis…...
MySQL数据库的数据类型
个人主页:C忠实粉丝 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C忠实粉丝 原创 MySQL数据库的数据类型 收录于专栏[MySQL] 本专栏旨在分享学习MySQL的一点学习笔记,欢迎大家在评论区交流讨论💌 目录 数据类型分类 …...
【golang】匿名内部协程,值传递与参数传递
代码例子 下面代码的区别是直接调用循环变量,这里使用的就是这个变量的引用,而不是将参数的副本传递给协程执行 for task : range taskChan {wg.Add(1)go func() {defer wg.Done()task.Do() // 使用外部循环变量}() }func DistributeTasks(taskChan &…...
ByteByteGo-Top 9 HTTP Request Methods 9种HTTP请求方法
更详细准确的描述直接参考 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods/CONNECT 本片文章出自 EP85: Top 9 HTTP Request Methods - ByteByteGo Newsletter http request methods 1,GET 从服务器获取资源。 具有幂等性(idempo…...
vue3实际案例分析:展示Vue拖拽功能的实现和效果
在现代前端开发中,交互式的用户界面是提升用户体验的关键因素之一。Vue.js,作为一个渐进式JavaScript框架,提供了灵活的组件系统和响应式数据绑定,使得实现复杂的用户交互变得简单。拖拽功能是其中一个常见的交互模式,…...
EasyExcel使用管道流连接InputStream和OutputStream
前言 Java中的InputSteam 是程序从其中读取数据, OutputSteam是程序可以往里面写入数据。 如果我们有在项目中读取数据库的记录, 在转存成Excel文件, 再把文件转存到OSS中。 生成Excel使用的是阿里的EasyExcel 。 他支持Output的方式写出文件内容。 而…...
源码分析之Openlayers中的ZoomToExtent控件
概述 在 Openlayers 中,ZoomToExtent控件可能用的不是很多,它主要用于将地图视图缩放到指定范围,应用场景就是重置地图、恢复初始状态。但是一般情况下,重置地图可能还会有其它操作,比如清除地图上绘制的点线面或者显…...
Acwing 算法基础课 数学知识 线性筛
线性筛素数 也叫欧拉筛。 int pr[maxn]; bool flg[maxn]; int main() {for (int i 2; i < maxn; i) {if (!flg[i]) pr[pr[0]] i;for (int j 1; i * pr[j] < n && j < pr[0]; j) {flg[i * pr[j]] 1;if (i % pr[j] 0) break; // 重点}} }这样筛的话&…...
K8s驱逐阈值调整
要在 Kubernetes 中调整 kubelet 驱逐阈值,特别是针对 imagefs 和 nodefs 的大小阈值,你可以通过修改 kubelet 的启动参数来实现。这些参数定义了在触发 Pod 驱逐之前,节点上的资源使用情况必须满足的硬性条件。 根据你提供的文件内容&#…...
【密码学】BUUCTF Crypto 1 - 12 题 WriteUp
今天,我在 BUUCTF 网站的 crypto section 开启了一场充满挑战的密码学之旅。 这次我一口气完成了 12 个板块的任务,虽然耗时较长,但每一次解密成功的瞬间都让我无比满足,那种沉浸在密码世界里的感觉真的很棒。 接下来࿰…...
【文献阅读】使用深度语音后验改进独立于说话者的构音障碍可懂度分类
原文名称: IMPROVED SPEAKER INDEPENDENT DYSARTHRIA INTELLIGIBILITY CLASSIFICATION USING DEEPSPEECH POSTERIORS 本文探讨了利用DeepSpeech后验概率改进说话人无关的构音障碍可理解性分类方法。作者提出了一个基于DeepSpeech(一种端到端的语音转文本引擎)输出的新特征集…...
Image Stitching using OpenCV
文章目录 简介图像拼接管道特征检测和提取特征检测特征提取 特征匹配强力匹配FLANN(近似最近邻快速库)匹配 单应性估计扭曲和混合结论 使用opencv进行图像拼接 原为url: https://medium.com/paulsonpremsingh7/image-stitching-using-opencv-a-step-by-s…...
如何将CSDN的文章保存为PDF?
目录 1、打开CSDN文章2、按F12或者鼠标右键选择检查并进入控制台3、在控制台输入以下代码4、然后回车(Enter)如果纵向显示不全就横向 1、打开CSDN文章 2、按F12或者鼠标右键选择检查并进入控制台 3、在控制台输入以下代码 (function(){ $("#side&q…...
设计模式之工厂模式:从汽车工厂到代码工厂
~犬📰余~ “我欲贱而贵,愚而智,贫而富,可乎? 曰:其唯学乎” 工厂模式概述 想象一下你走进一家4S店准备买车。作为顾客,你不需要知道汽车是如何被制造出来的,你只需要告诉销售顾问&a…...
在 Ubuntu 20.04 上离线安装和配置 Redis
下面是从零开始配置 Redis 的完整步骤,包括从安装 Redis 到离线安装 Redis 包的步骤。本文将覆盖如何从下载 Redis 安装包、手动安装 Redis、创建所需目录、配置 Redis、以及如何配置 Redis 为系统服务,确保服务可以在启动时自动运行。 步骤 1: 准备环境…...
Redis的哨兵机制
目录 1. 文章前言2. 基本概念2.1 主从复制的问题2.2 人工恢复主节点故障2.3 哨兵机制自动恢复主节点故障 3. 安装部署哨兵(基于docker)3.1 安装docker3.2 编排redis主从节点3.3 编排redis-sentinel节点 4. 重新选举5. 选举原理6. 总结 1. 文章前言 &…...
服务器数据恢复—热备盘上线过程中硬盘离线导致raid5阵列崩溃的数据恢复案例
服务器数据恢复环境: 两组分别由4块SAS接口硬盘组建的raid5阵列,两组raid5阵列划分LUN并由LVM管理,格式化为EXT3文件系统。 服务器故障: RAID5阵列中一块硬盘未知原因离线,热备盘自动激活上线替换离线硬盘。在热备盘上…...
Android12 设置无home属性的apk为launcher
目标apk的包类名 QSSI.12/device/qcom/qssi/system.prop// add start target_app_package_name=com.mangechargespot.app target_app_class_name=com.mangechargespot.app.ui.activity.SplashActivity // add end...
STM32F407+LAN8720A +LWIP +FreeRTOS ping通
使用STM32CUBEIDE自带的 LWIP和FreeROTS 版本说明STM32CUBEIDE 操作如下1. 配置RCC/SYS2. 配置ETH/USART3. 配置EHT_RESET/LED4. 配置FreeRTOS5. 配置LWIP6. 配置时钟7. 生成单独的源文件和头文件,并生成代码8. printf重定义9. ethernetif.c添加lan8720a复位10. MY_LWIP_Init …...
构建虚幻引擎中的HUD与UI
在游戏开发中,用户界面(UI)和头部显示(HUD)是玩家体验的重要组成部分。它们提供了关键信息,并增强了游戏的互动性。虚幻引擎(Unreal Engine, UE)以其强大的可视化脚本系统Blueprint和C++ API支持,为开发者提供了一个灵活且功能丰富的环境来创建复杂的UI和HUD元素。 本…...
EXCEL数据清洗的几个功能总结备忘
目录 0 参考教材 1 用EXCEL进行数据清洗的几个功能 2 删除重复值: 3 找到缺失值等 4 大小写转换 5 类型转化 6 识别空格 0 参考教材 精通EXCEL数据统计与分析,中国,李宗璋用EXCEL学统计学,日EXCEL统计分析与决策&#x…...
【CSS in Depth 2 精译_076】12.4 @font-face 的工作原理
当前内容所在位置(可进入专栏查看其他译好的章节内容) 第四部分 视觉增强技术 ✔️【第 12 章 CSS 排版与间距】 ✔️ 12.1 间距设置 12.1.1 使用 em 还是 px12.1.2 对行高的深入思考12.1.3 行内元素的间距设置 12.2 Web 字体12.3 谷歌字体12.4 font-fac…...
深入源码解析:Spring Boot 如何加载 Servlet 、Filter 与 Listener
我们知道,Spring Boot 是在 Spring MVC 的基础上进行了封装,以简化开发者的工作量。尽管如此,Spring Boot 的底层架构依然离不开 Spring MVC 的核心组件,如 Servlet、Filter、Listener,以及RequestMappingHandlerMappi…...
Vue 让视图区域滑到指定位置、回到顶部
滑倒指定位置:获取指定的dom,然后用scrollIntoView使dom出现在视图区域 回到顶部:操作父级dom的scrollTop 0,让该父级下的列表回到顶部 代码如下 <template><div class"testDemo"><div><el-bu…...
C# 属性(Property)
C# 属性(Property) C# 中的属性(Property)是一种用于访问和设置类或结构成员的值的特殊类型的方法。属性允许开发者以字段的形式访问方法,提供了更为安全和灵活的数据封装。在本文中,我们将深入探讨 C# 属性的概念、用途、以及如何有效地使用它们。 属性的定义 属性由…...
Ubuntu20.04调整swap分区大小笔记
Ubuntu20.04调整swap分区大小笔记 参考:https://blog.csdn.net/sunyuhua_keyboard/article/details/142485764 第一步 禁用swap $ cat /etc/fstab # /etc/fstab: static file system information. # # Use blkid to print the universally unique identifier fo…...
【 JAVA中常见的集合操作】
JAVA中常见的集合操作 Java 提供了丰富的集合框架(Java Collections Framework),包括多种集合接口和实现类。集合操作可以分为基本操作和高级操作。下面介绍一些常见的集合操作,并给出相应的代码示例。 基本集合操作 创建集合 …...
Kudu 源码编译-aarch架构 1.17.1版本
跟着官方文档编译 第一个问题:在make阶段时会报的问题: kudu/src/kudu/util/block_bloom_filter.cc:210:3: error: ‘vst1q_u32_x2’ was not declared in this scope kudu/src/kudu/util/block_bloom_filter.cc:436:5: error: ‘vst1q_u8_x2’ was no…...
JavaEE多线程案例之阻塞队列
上文我们了解了多线程案例中的单例模式,此文我们来探讨多线程案例之阻塞队列吧 1. 阻塞队列是什么? 阻塞队列是⼀种特殊的队列.也遵守"先进先出"的原则. 阻塞队列是⼀种线程安全的数据结构,并且具有以下特性: 当队列满的时候,继续⼊队列就会…...
Java Web 开发学习中:过滤器与 Ajax 异步请求
一、过滤器 Filter: 过滤器的概念与用途 在一个庞大的 Web 应用中,有许多资源需要受到保护或进行特定的预处理。过滤器就像是一位智能的守卫,站在资源的入口处,根据预先设定的规则,决定哪些请求可以顺利访问资源&…...
git SSL certificate problem: unable to get local issuer certificate
Git 客户端将会验证服务器端提供的SSL证书是否由受信任的证书颁发机构(Certification Authority,CA)签发。如果Git客户端无法找到或验证本地签发者证书,就会出现 unable to get local issuer certificate 或类似的错误。 该问题一…...
Vue.js 响应接口
Vue.js 响应接口 Vue.js,作为一个流行的前端JavaScript框架,以其响应式数据绑定和组件化开发而闻名。响应式系统是Vue.js的核心特性之一,它允许开发者创建高度动态和交互式的用户界面。本文将深入探讨Vue.js的响应接口,包括其工作原理、使用方法和最佳实践。 响应式原理 …...