C++单元测试GoogleTest和GoogleMock十分钟快速上手(gtestgmock)
C++单元测试GoogleTest和GoogleMock(gtest&gmock)
环境准备
下载
git clone https://github.com/google/googletest.git
# 或者
wget https://github.com/google/googletest/releases/tag/release-1.11.0
安装
cd googletest
cmake CMakeLists.txt
make
sudo make install
重要文件
googletest
- gtest/gtest.h
- libgtest.a
- libgtest_main.a
当不想写 main 函数的时候,可以直接引入 libgtest_main.a;
g++ sample.cc -o sample -lgtest -lgtest_main -lpthread
g++ sample.cc -o sample -lgmock -lgmock_main -lpthread
否则
g++ sample.cc -o sample -lgtest -lpthread
googlemock
- gmock/gmock.h
- libgmock.a
- libgmock_main.a
GoogleTest
一 .断言
gtest中的断言分成两大类:
-
ASSERT_\*系列:如果检测失败就直接退出当前函数 -
EXPECT_\*系列:如果检测失败发出提示,并继续往下执行
通常情况应该首选使用EXPECT_,因为ASSERT_在报告完错误后不会进行清理工作,有可能导致内存泄露问题。
gtest有很多类似的宏用来判断数值的关系、判断条件的真假、判断字符串的关系。
条件判断
ASSERT_TRUE(condition); // 判断条件是否为真
ASSERT_FALSE(condition); // 判断条件是否为假EXPECT_TRUE(condition); // 判断条件是否为真
EXPECT_FALSE(condition); // 判断条件是否为假
数值比较
ASSERT_EQ(val1, val2); // 判断是否相等
ASSERT_NE(val1, val2); // 判断是否不相等
ASSERT_LT(val1, val2); // 判断是否小于
ASSERT_LE(val1, val2); // 判断是否小于等于
ASSERT_GT(val1, val2); // 判断是否大于
ASSERT_GE(val1, val2); // 判断是否大于等于EXPECT_EQ(val1, val2); // 判断是否相等
EXPECT_NE(val1, val2); // 判断是否不相等
EXPECT_LT(val1, val2); // 判断是否小于
EXPECT_LE(val1, val2); // 判断是否小于等于
EXPECT_GT(val1, val2); // 判断是否大于
EXPECT_GE(val1, val2); // 判断是否大于等于
字符串比较
ASSERT_STREQ(str1,str2); // 判断字符串是否相等
ASSERT_STRNE(str1,str2); // 判断字符串是否不相等
ASSERT_STRCASEEQ(str1,str2); // 判断字符串是否相等,忽视大小写
ASSERT_STRCASENE(str1,str2); // 判断字符串是否不相等,忽视大小写EXPECT_STREQ(str1,str2); // 判断字符串是否相等
EXPECT_STRNE(str1,str2); // 判断字符串是否不相等
EXPECT_STRCASEEQ(str1,str2); // 判断字符串是否相等,忽视大小写
EXPECT_STRCASENE(str1,str2); // 判断字符串是否不相等,忽视大小写
谓词断言
谓词断言能比 EXPECT_TRUE 提供更详细的错误消息;
EXPECT_PRED1(pred,val1);
EXPECT_PRED2(pred,val1,val2);
EXPECT_PRED3(pred,val1,val2,val3);
EXPECT_PRED4(pred,val1,val2,val3,val4);
EXPECT_PRED5(pred,val1,val2,val3,val4,val5);ASSERT_PRED1(pred,val1);
ASSERT_PRED2(pred,val1,val2);
ASSERT_PRED3(pred,val1,val2,val3);
ASSERT_PRED4(pred,val1,val2,val3,val4);
ASSERT_PRED5(pred,val1,val2,val3,val4,val5);
// Returns true if m and n have no common divisors except 1.
bool MutuallyPrime(int m, int n) { ... }
...
const int a = 3;
const int b = 4;
const int c = 10;
...
EXPECT_PRED2(MutuallyPrime, a, b); // Succeeds
EXPECT_PRED2(MutuallyPrime, b, c); // Fails
能得到错误信息:
MutuallyPrime(b, c) is false, where
b is 4
c is 10
二 .宏测试
如果自己编写mian函数,那么需要调用testing::InitGoogleTest函数进行初始化然后调用RUN_ALL_TESTS(); 函数执行所有的测试集
TEST
进一步,为了更好的组织test cases,比如针对Factorial函数,输入是负数的cases为一组,输入是0的case为一组,正数cases为一组。gtest提供了一个宏TEST(TestSuiteName, TestName),用于组织不同场景的cases,这个功能在gtest中称为test suite
原型
#define TEST(test_suite_name,test_name)
代码示例
TEST_F()宏的第一个参数(即test_suite_name的名称)必须是测试装置类的类名。
TEST(test_suite_name,test_name)
{//可以像普通函数一样定义变量之类的行为。EXPECT_TRUE(condition);EXPECT_EQ(val1, val2);EXPECT_PRED1(pred,val1);
}
TEST_F
我们想让多个Test使用同一套数据配置时,就需要用到测试装置,创建测试装置的具体方法如下:
- 派生一个继承 ::testing::Test 的类,并将该类中的一些内容声明为 protected 类型,以便在子类中进行访问;
- 根据实际情况,编写默认的构造函数或SetUp()函数,来为每个 test 准备所需内容;
- 根据实际情况,编写默认的析构函数或TearDown()函数,来释放SetUp()中分配的资源;
- 根据实际情况,定义 test 共享的子程序。
TEST_F()宏的第一个参数(即Test Case的名称)必须是测试装置类的类名。
它继承testing::Test类,然后根据我们的需要实现下面这两个虚函数:
virtual void SetUp()类似于构造函数,总是在测试用例开始时被调用virtual void TearDown()类似于析构函数,总是在测试用例结束后被调用
此外,testing::Test还提供了两个static函数:
static void SetUpTestSuite():在第一个TEST之前运行static void TearDownTestSuite():在最后一个TEST之后运行
代码示例
class QueueTestSmpl3 : public testing::Test { // 继承了 testing::Test
protected: static void SetUpTestSuite() {std::cout<<"run before first case..."<<std::endl;} static void TearDownTestSuite() {std::cout<<"run after last case..."<<std::endl;}virtual void SetUp() override {std::cout<<"enter into SetUp()" <<std::endl;q1_.Enqueue(1);q2_.Enqueue(2);q2_.Enqueue(3);}virtual void TearDown() override {std::cout<<"exit from TearDown" <<std::endl;}static int Double(int n) {return 2*n;}void MapTester(const Queue<int> * q) {const Queue<int> * const new_q = q->Map(Double);ASSERT_EQ(q->Size(), new_q->Size());for (const QueueNode<int>*n1 = q->Head(), *n2 = new_q->Head();n1 != nullptr; n1 = n1->next(), n2 = n2->next()) {EXPECT_EQ(2 * n1->element(), n2->element());}delete new_q;}Queue<int> q0_;Queue<int> q1_;Queue<int> q2_;
};
测试集代码
// in sample3_unittest.cc// Tests the default c'tor.
TEST_F(QueueTestSmpl3, DefaultConstructor) {// !!! 在 TEST_F 中可以使用 QueueTestSmpl3 的成员变量、成员函数 EXPECT_EQ(0u, q0_.Size());
}// Tests Dequeue().
TEST_F(QueueTestSmpl3, Dequeue) {int * n = q0_.Dequeue();EXPECT_TRUE(n == nullptr);n = q1_.Dequeue();ASSERT_TRUE(n != nullptr);EXPECT_EQ(1, *n);EXPECT_EQ(0u, q1_.Size());delete n;n = q2_.Dequeue();ASSERT_TRUE(n != nullptr);EXPECT_EQ(2, *n);EXPECT_EQ(1u, q2_.Size());delete n;
}// Tests the Queue::Map() function.
TEST_F(QueueTestSmpl3, Map) {MapTester(&q0_);MapTester(&q1_);MapTester(&q2_);
}
运行结果
% ./sample3_unittest
Running main() from /Users/self_study/Cpp/OpenSource/demo/include/googletest/googletest/samples/sample3_unittest.cc
[==========] Running 3 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 3 tests from QueueTestSmpl3
run before first case... # 所有的test case 之前运行
[ RUN ] QueueTestSmpl3.DefaultConstructor
enter into SetUp() # 每次都会运行
exit from TearDown
[ OK ] QueueTestSmpl3.DefaultConstructor (0 ms)
[ RUN ] QueueTestSmpl3.Dequeue
enter into SetUp() # 每次都会运行
exit from TearDown
[ OK ] QueueTestSmpl3.Dequeue (0 ms)
[ RUN ] QueueTestSmpl3.Map
enter into SetUp() # 每次都会运行
exit from TearDown
[ OK ] QueueTestSmpl3.Map (0 ms)
run after last case... # 所有test case结束之后运行
[----------] 3 tests from QueueTestSmpl3 (0 ms total)[----------] Global test environment tear-down
[==========] 3 tests from 1 test suite ran. (0 ms total)
[ PASSED ] 3 tests.
GoogleMock
当你写一个原型或测试,往往不能完全的依赖真实对象。一个 mock 对象实现与一个真实对象相同的接口,但让你在运行时指定它时,如何使用?它应该做什么?(哪些方法将被调用?什么顺序?多少次?有什么参数?会返回什么?等)
可以模拟检查它自己和调用者之间的交互;
mock 用于创建模拟类和使用它们;
- 使用一些简单的宏描述你想要模拟的接口,他们将扩展到你的 mock 类的实现;
- 创建一些模拟对象,并使用直观的语法指定其期望和行为;
- 练习使用模拟对象的代码。 Google Mock会在出现任何违反期望的情况时立即处理。
注意
googlemock 依赖 googletest;调用 InitGoogleMock 时会自动调用 InitGoogleTest ;
头文件 #include “gmock/gmock.h”
什么时候使用?
- 测试很慢,依赖于太多的库或使用昂贵的资源;
- 测试脆弱,使用的一些资源是不可靠的(例如网络);
- 测试代码如何处理失败(例如,文件校验和错误),但不容易造成;
- 确保模块以正确的方式与其他模块交互,但是很难观察到交互;因此你希望看到观察行动结束时的副作用;
- 想模拟出复杂的依赖;
使用方法
我们假设一个支付场景逻辑开发业务。我们开发复杂的业务模块,而团队其他成员开发用户行为模块。他们和我们约定了如下接口
class User {
public:User() {};~User() {};
public:// 登录virtual bool Login(const std::string& username, const std::string& password) = 0;// 支付virtual bool Pay(int money) = 0;// 是否登录virtual bool Online() = 0;
};
我们的业务模块要让用户登录,并发起支付行为。于是我们的代码如下
class Biz {
public:void SetUser(User* user) {_user = user;}std::string pay(const std::string& username, const std::string& password, int money) {std::string ret;if (!_user) {ret = "pointer is null.";return ret;}if (!_user->Online()) {ret = "logout status.";// 尚未登录,要求登录if (!_user->Login(username, password)) {// 登录失败ret += "login error.";return ret;} else {// 登录成功ret += "login success.";}} else {// 已登录ret = "login.status";}if (!_user->Pay(money)) {ret += "pay error.";} else {ret += "pay success.";}return ret;}private:User* _user;
};
第一步我们需要Mock接口类
MOCK_METHOD0(FUNC, TYPE);第一个参数填写函数名,第二个参数填写函数类型MOCK_METHOD()后面的数字表示需要几个参数- const成员方法使用
MOCK_CONST_METHOD系列
class TestUser : public User {
public:MOCK_METHOD2(Login, bool(const std::string&, const std::string&));MOCK_METHOD1(Pay, bool(int));MOCK_METHOD0(Online, bool());
};
第二步,我们就可以设计测试场景了。在设计场景之前,我们先看一些Gmock的方法
// EXPECT_CALL(mock_object, Method(argument-matchers))
// .With(multi-argument-matchers)
// .Times(cardinality)
// .InSequence(sequences)
// .After(expectations)
// .WillOnce(action)
// .WillRepeatedly(action)
// .RetiresOnSaturation();
//
// where all clauses are optional, and .InSequence()/.After()/
// .WillOnce() can appear any number of times.
- EXPECT_CALL声明一个调用期待,就是我们期待这个对象的这个方法按什么样的逻辑去执行。
- mock_object是我们mock的对象,上例中就是TestUser的一个对象。
- Method是mock对象中的mock方法,它的参数可以通过argument-matchers规则去匹配。
- With是多个参数的匹配方式指定。
- Times表示这个方法可以被执行多少次。如果超过这个次数,则按默认值返回了。
- InSequence用于指定函数执行的顺序。它是通过同一序列中声明期待的顺序确定的。
- After方法用于指定某个方法只能在另一个方法之后执行。
- WillOnce表示执行一次方法时,将执行其参数action的方法。一般我们使用Return方法,用于指定一次调用的输出。
- WillRepeatedly表示一直调用一个方法时,将执行其参数action的方法。需要注意下它和WillOnce的区别,WillOnce是一次,WillRepeatedly是一直。
- RetiresOnSaturation用于保证期待调用不会被相同的函数的期待所覆盖。
先举一个例子,我们要求Online在第一调用时返回true,之后都返回false。Login一直返回false。Pay一直返回true。也就是说用户第一次支付前处于在线状态,并可以支付成功。而第二次将因为不处于在线状态,要触发登录行为,而登录行为将失败。我们看下这个逻辑该怎么写
{TestUser test_user;EXPECT_CALL(test_user, Online()).WillOnce(testing::Return(true));EXPECT_CALL(test_user, Login(_,_)).WillRepeatedly(testing::Return(false));EXPECT_CALL(test_user, Pay(_)).WillRepeatedly(testing::Return(true));Biz biz;biz.SetUser(&test_user);std::string admin_ret = biz.pay("user", "", 1);admin_ret = biz.pay("user", "", 1);}
第4行的意思是Online在调用一次后返回true,之后的调用返回默认的false。第5行意思是Login操作一直返回false,其中Login的参数是两个下划线(_),它是通配符,就是对任何输入参数都按之后要求执行。第6行意思是Pay操作总是返回true。那么我们在第10行和第11行分别得到如下输出
login status.pay success.
logout status.login error.
可以见得输出符合我们的预期。
我们再看一种场景,这个场景我们使用了函数参数的过滤。比如我们不允许admin的用户通过我们方法登录并支付,则可以这么写
{TestUser test_user;EXPECT_CALL(test_user, Online()).WillOnce(testing::Return(false));EXPECT_CALL(test_user, Login("admin",_)).WillRepeatedly(testing::Return(false));Biz biz;biz.SetUser(&test_user);std::string admin_ret = biz.pay("admin", "", 1);}
第3行表示,如果Login的第一个参数是admin,则总是返回false。于是07行返回是
logout status.login error.
那么如果不是admin的用户登录,则返回成功,这个案例要怎么写呢?
{TestUser test_user;EXPECT_CALL(test_user, Online()).WillOnce(testing::Return(false));EXPECT_CALL(test_user, Login(StrNe("admin"),_)).WillRepeatedly(testing::Return(true));EXPECT_CALL(test_user, Pay(_)).WillRepeatedly(testing::Return(true));Biz biz;biz.SetUser(&test_user);std::string user_ret = biz.pay("user", "", 1);}
03行使用了StrNe的比较函数,即Login的第一个参数不等于admin时,总是返回true。08行的输出是
logout status.login success.pay success.
我们再看一个例子,我们要求非admin用户登录成功后,只能成功支付2次,之后的支付都失败。这个案例可以这么写
{TestUser test_user;EXPECT_CALL(test_user, Online()).WillOnce(testing::Return(false));EXPECT_CALL(test_user, Login(StrNe("admin"),_)).WillRepeatedly(testing::Return(true));EXPECT_CALL(test_user, Pay(_)).Times(5).WillOnce(testing::Return(true)).WillOnce(testing::Return(true)).WillRepeatedly(testing::Return(false));Biz biz;biz.SetUser(&test_user);std::string user_ret = biz.pay("user", "", 1);user_ret = biz.pay("user", "", 1);user_ret = biz.pay("user", "", 1);}
第4行我们使用Times函数,它的参数5表示该函数期待被调用5次,从第6次的调用开始,返回默认值。Times函数后面跟着两个WillOnce,其行为都是返回true。这个可以解读为第一次和第二次调用Pay方法时,返回成功。最后的WillRepeatedly表示之后的对Pay的调用都返回false。我们看下执行的结果
logout status.login success.pay success.
logout status.login success.pay success.
logout status.login success.pay error.
从结果上看,前两次都支付成功了,而第三次失败。符合我们的期待。
相关文章:
C++单元测试GoogleTest和GoogleMock十分钟快速上手(gtestgmock)
C单元测试GoogleTest和GoogleMock(gtest&gmock) 环境准备 下载 git clone https://github.com/google/googletest.git # 或者 wget https://github.com/google/googletest/releases/tag/release-1.11.0安装 cd googletest cmake CMakeLists.txt make sudo make instal…...
Starknet的去中心化路线图
1. 引言 StarkWare正以2条路线在迈向去中心化: planningimplementation 以让Starknet协议 走向 去中心化proof-of-stake协议。 Starknet向以太坊发送STARK proofs来验证其状态变更。 一年前Starknet就在做去中心化规划,相关提案见: Sim…...
python基础语法(十二)
目录 标准库认识标准库使用 import 导入模块代码示例: 字符串操作剑指offer 58, 翻转单词顺序题目题目做法代码 leetcode 796, 旋转字符串题目题目做法 leetcode 2255, 统计是给定字符串前缀的字符串数目题目题目做法 代码示例: 文件查找工具 感谢各位大佬对我的支持,如果我的文…...
【开源】基于SpringBoot的农村物流配送系统的设计和实现
目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统登录、注册界面2.2 系统功能2.2.1 快递信息管理:2.2.2 位置信息管理:2.2.3 配送人员分配:2.2.4 路线规划:2.2.5 个人中心:2.2.6 退换快递处理:…...
【2024秋招】2023-9-16 贝壳后端开发一面
1 秒杀系统 1.1 如何抗住高并发 1.2 数据一致性你是怎么处理,根据场景来说明你的设计思路 1.3 你们当时系统的架构是怎么样的 秒杀表做节点隔离, 1.4 为了保证数据一致性,引入了redission的锁,你是为了抗住高并发而去为了引入…...
BI是什么?想要了解BI需要从哪些方面入手?
企业为了执行数字化战略,实行数字化转型,实现数据价值,除了需要相关数字化技术及理念、人才等,还需要借助数字化相关应用,例如商业世界中广受企业欢迎的ERP、OA、CRM等业务信息系统,以及上升势头非常迅猛的…...
软件测试---等价类划分(功能测试)
能对穷举场景设计测试点-----等价类划分 等价类划分 说明:在所有测试数据中,具有某种共同特征的数据集合进行划分分类: 1)有效等价类 2)无效等价类步骤:1)明确需求 2)确定有效和无…...
javascript原生态xhr上传多个图片,可预览和修改上传图片为固定尺寸比例,防恶意代码,加后端php处理图片
//前端上传文件 <!DOCTYPE html> <html xmlns"http://www.w3.org/1999/xhtml" lang"UTF-8"></html> <html><head><meta http-equiv"Content-Type" content"text/html;charsetUTF-8;"/><title…...
【Java】Map集合中常用方法
Map集合的常用方法 方法名称作用V put(Key k,V value)添加元素V remove(K key, V value)根据键值删除对应的值void clear()清除所有键值元素boolean containsKey(Object key)判断集合中是否包含指定的键boolean containsValue(Object value)判断集合中是否包含指定的值boolean …...
方太描画未来厨房的模样
作者 | 辰纹 来源 | 洞见新研社 不知不觉中,iPhone已经更新到15代了,家里的电视变成了越来越轻薄的液晶屏,过去被称为“老三样”的富康,捷达、桑塔纳,如今也被以特斯拉为代表的新能源智能汽车们所取代…… 类似以上的…...
ELASTICO-A Secure Sharding Protocol For Open Blockchains
INTRO 在中本聪共识中,通过POW机制来公平的选举leader,不仅非常消耗power,并且拓展性也不好。现在比特币中是7 TPS,和其他的支付系统相比效率相差甚远。 当前的许多拜占庭共识协议,并不支持在一个开放的环境中使用&a…...
【数据结构】Map和Set
⭐ 作者:小胡_不糊涂 🌱 作者主页:小胡_不糊涂的个人主页 📀 收录专栏:浅谈数据结构 💖 持续更文,关注博主少走弯路,谢谢大家支持 💖 Map、Set 1. 搜索树1.1 概念1.2 性能…...
Python Flask
Python Flask是一个轻量级的web开发框架,用于快速地构建web应用程序。以下是Python Flask的基本使用步骤: 安装Flask:使用pip安装Flask包。在命令行中输入以下命令: pip install flask创建Flask对象:在Python文件中&am…...
时序预测 | Python实现ARIMA-LSTM差分自回归移动平均模型结合长短期记忆神经网络时间序列预测
时序预测 | Python实现ARIMA-LSTM差分自回归移动平均模型结合长短期记忆神经网络时间序列预测 目录 时序预测 | Python实现ARIMA-LSTM差分自回归移动平均模型结合长短期记忆神经网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 时序预测 | Python实现ARIM…...
Redis快速上手篇八(redission完善分布式锁)
Redisson Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务,其中就包含了各种分布式锁的实现。简单说就是redis在分布式系统上工…...
Dataset文件下载以及使用,以nuswide为例
文章目录 文件夹结构如何使用继承torch.utils.data.Dataset构建新的class构建新的Dataloader 数据集要求以文章 multi-label learning from single positive label为例; 文件夹结构 我是这么放置的,其中含有obs的文件是通过运行文件夹preproc下的genera…...
ZYNQ连载02-开发环境
ZYNQ连载02-开发环境 1. 官方文档 ZYNQ开发使用的软件为Vivado/Vitis/PetaLinux,软件体积比较大,硬盘保留100G以上的空间,赛灵思提供详细的文档,链接如下: ZYNQ文档 2. Vivido和Vitis安装 赛灵思统一安装程序 3. PetaLinux安装…...
前端 :用HTML和css制作一个小米官网的静态页面
1.HTML: <body><div id "content"><div id "box"><div id "top"><div id "top-left"><span id "logo">MI</span><span id "text-logo">小米账…...
modelsim仿真报错:vlog-2388 ‘scl‘ already declared in this scope
问题背景: 1、使用vivado直接仿真的时候没有报错。 2、在vivado中调用modelsim的时候报错。 报错的代码: module iic_write(input clk,input rst,output scl,input en,inout sda);reg scl;……报错的意思是scl已经声明过了,mode…...
C#中通过BeginInvoke()和EndInvoke()来实现异步
.NET Framework允许异步调用任何方法。定义与需要调用的方法具有相同签名的委托;公共语言运行库将自动为该委托定义具有适当签名的 BeginInvoke 和 EndInvoke 方法。以下介绍C#中,通过BeginInvoke()和EndInvoke()来实现异步。 1、异步编程 调用BeginInv…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
