序
本文档是基于linux开发环境进行编写,也同样适用于window开发环境。
本文仅是介绍 vscode、xmake 的一种使用方法。 xmake本身也是支持远程编译,相比ssh更加稳定、高效。
学习源码 : https://gitcode.com/xiaofeio/stu_linux/overview
介绍
-
vscode、xmake、git 使用
-
通过 xmake 配置项目工程(目标)依赖
-
配置 vscode,实现快捷构建任务(习惯了vs2013的快捷方式)、构建依赖(非xmake的配置)、IntelliSense功能(代码分析、代码补全),调试程序的参数配置,附加进程调试,调试器自定义结构的Visualizers
系统环境
操作系统 CentOS 9 Stream
工作环境 vscode、gcc、xmake、git
项目版本管理
创建git版本库
-
点击右侧 新建 按钮创建一个 stu_linux 项目(可自行命名)
-
创建完成进程项目代码后点击 Clone 按钮
-
弹出的页面中选择 SSH ,点击标记2复制url路径
克隆git仓储
请参考《Linux C/C++ 编程环境配置》配置版本管理部分,配置 ssh 参考 第4条、第5条, 克隆仓储参考 第6条,完成后如下图所示
创建项目工程
先 不要创建 代码工程,规划不会为每个学习用例都创建一个仓储,而是都放在这一个仓储下(根据个人规划可跳过下面这几步,通过 vscodeXMake:CreateProject 命令创建工程也会失败,提示不是一个空文件夹)。
方法一
-
首先创建一个 eg_prjs 的用例,在终端中输入
mkdir eg_prjs
-
在vscode中选择打开 eg_prjs 文件夹
-
通过 vscode XMak:CreateProject 创建项目工程
需要注意的是vscode XMake:CreateProject 创建了 .vscode 文件夹
方法二
-
通过 xmak 命令行的方式创建 ex_prjs 共享库,进入到 ex_prjs 文件夹下,通过命令行的方式可以正常编译
注: 为了区分方法一命名为 ex_prjs
但是不能使用vscode中 XMake:Build 命令构建
提示如下没有找到 xmake.lua,这是因为 vscode xmake 插件的命令会直接作用在顶级目录上,还是需要切换vs code的工作目录
-
在vscode中通过打开 ex_prjs 文件夹方式切换vscode的工作空间
需要注意的是没有 .vscode 文件夹,这并不影响后面的配置,可以添加回来。
相比 方法一 缺少了 compile_commands.json 文件,通过输入下列命令
xmake project -k compile_commands
会生成 compile_commands.json 文件
在根目录下创建 .vscode 文件夹,并把 compile_commands.json 文件移动(vscode中直接拖拽)到 .vscode 文件夹下就和方法一样了
.xmake 文件夹下内容随着后期使用自然会明白的,虽然和 方法一 有些不一样,但这不影响本次实验,后面的操作都是相同的。
编译调试项目
提交代码
在进行下列演练之前,先提交代码并上传到 远端仓储
- 点击导航中 Source Control,在切换 SOURCE CONTROL界面上点击提交,查看变更信息,书写日志,提交代码
点击”提交“按钮弹出确认对话框,点击”Save“
只是完成了本地的提交
-
同步推送到远端仓储,点击”Sync Changes“
在弹出的确认对话中点击”OK“或"OK, Don’t Show Again"
提交成功后, Source Control 界面更新控件状态
查看远端仓储,如果一直打开了远端仓储界面,按下浏览器上的 F5刷新一下
查看到提交信息
编译工程
编译前先了解下各个文件和文件内容
-
xmake.lua,这是本节中的一个重要文件
这个文件是 xmake 的配置文件,含义参考图中的描述,创建默认是没有这些说明。 xmake是基于 lua 开发的,注释可以用 – ,为了保留原样没加注释。
重要的一句是 add_deps(“foo”) 表示 eg_prjs 目标(或称为应用程序)依赖了 foo 目标(或称为模块)。
-
foo.h
这里重要的一句是导出 add(int a, int b) 函数
__export int add(int a, int b);
C/C++编程人员都懂的,foo.cpp 的内容不想就知道是什么了,这里就不在说明了。
-
main.cpp
很好,foo.h没有红色的波浪线等情况,这是因为foo.h、foo.cpp、main.cpp都在一个src的文件夹下
-
使用 vscode xmake 插件中的命令构建编译项目工程。注没有编译无法启动调试(后面会讲解如何配置满足个人习惯)
执行 XMake:Build
命令,终端一共执行两条 xmak 命令,分别是xmake f -p linux a x86_64 -m debug
这句用于配置平台、架构、debug模式
xmake
这句用于构建编译。
出现了 build ok 表示构建成功,花费 1.077 秒。
运行和调试
运行
通过点击 xmake 工具栏上 运行 按钮就可以启动运行,效果如下
首先会执行 xmake run eg_prjs 命令启动运行, 程序运行后main.cpp中调用了foo.h中的 add()函数进行计算并打印计算结果。
既然已经知道了 xmake 插件在vscode中的执行也是基于 xmake 本身提供的命令集进行操作,那么自然也可以通过输入命令行的方式进行 运行 同样也适用 调试
在命令终端中直接输入 xmake run 即可运行, 与上一个比少输入了一个eg_prjs(因为这个下面下就一个可执行目标可以忽略,关于xmake的用法请参考官网说明)
调试
简单调试
首先在main.cpp中第5行按下快捷键 F9 打一个断点
然后点击上图的标记2运行 debug 启动调试
调试程序启动后,会停在断点处,并且显示标记2的 调试操作命令(可执行单步调试,进入、跳出、停止、重启),在标记3可以看到变量信息,调用堆栈。
按下 F11 进入到 foo.cpp 的 add()函数
接下来修改一下main.cpp的函数
#include "foo.h"
#include <iostream>
#include <string>
#include <vector>
int main(int argc, char** argv) {
std::cout << "add(1, 2) = " << add(1, 2) << std::endl;
std::vector<std::string> stdvec = { "Hello", "vscode", "xmake", "C/C++", "!!!" };
for (auto &&i : stdvec)
{
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
在第7行再次按下快捷键 F9 去掉第7断点, 在main.cpp中第10行按下快捷键 F9 打一个断点,然后再启动调试(可以直接按下 F5 弹出的命令面板中选择 xmake)
当程序停止在断点处,在右侧变量查看区,点击 stdvec 右侧展开按钮,可以看到 vector 容器内容的各个元素值。
项目工程部署
在创建项目工程中,可以看到通过 xmake 命令创建的工程是一个简单框架目录部署结构,而在实际开发中会根据不同的应用场景进行更复杂的目录部署。
调整文件部署结构
接下来以foo、app两个文件夹分别作为独立模块的存储文件夹为例。将目录调整如下结构。
foo文件夹下的inc文件夹用于对外提供的头文件,而src文件作为foo模块内部的源码文件。
app文件夹用于存放应用程序的源文件,由于app并不对外提供头文件,因此没有部署inc、src等相应功能的文件夹。
修改 xmake 配置、编译和调试
接下来打开 xmake.lua 文件,修改内容如下
add_rules("mode.debug", "mode.release")
target("foo")
set_kind("shared")
add_files("src/foo/src/**.cpp") -- ** 表示添加 src/foo/src文件夹及子文件夹夹下的所有.cpp文件
target("eg_prjs")
set_kind("binary")
add_deps("foo")
add_files("src/app/*.cpp") -- * 表示添加 src/app文件夹下的所有cpp文件
修改完成后编译一下看看会是什么情况
结果显示 没有找到 foo.h 这个文件,并且是因为编译 foo.cpp 在第1行第10列报的错误。
因为编译是基于 xmake, 因此只需要继续修改 xmake.lua 文件即可, 更多的信息请参考 xmake教程 ,修改内容如下。
add_rules("mode.debug", "mode.release")
target("foo")
set_kind("shared")
add_includedirs("src/foo/inc") -- 添加头文件的目录
add_files("src/foo/src/**.cpp") -- ** 表示添加 src/foo/src文件夹及子文件夹夹下的所有.cpp文件
target("eg_prjs")
set_kind("binary")
add_deps("foo")
add_files("src/app/*.cpp") -- * 表示添加 src/app文件夹下的所有cpp文件
再次构建看看效果
还是有错误,只不过报告的错误是 main.cpp 在第1行第10列报的错误。那么继续修改 xmake.lua 文件,内容如下
add_rules("mode.debug", "mode.release")
target("foo")
set_kind("shared")
add_includedirs("src/foo/inc", {public = true }) -- 添加头文件的目录, public表示把头文件暴露出来提供给通过 add_deps() 添加代模块, 如下面的 add_deps(fool)
add_files("src/foo/src/**.cpp") -- ** 表示添加 src/foo/src文件夹及子文件夹夹下的所有.cpp文件
target("eg_prjs")
set_kind("binary")
add_deps("foo")
add_files("src/app/*.cpp") -- * 表示添加 src/app文件夹下的所有cpp文件
再次构建,可以看到编译成功
启动调试
查看调试效果
和之前的调试效果是一样的
构建调试运行
配置构建任务
习惯了使用Vistual Studio 2013等系列的IDE,按下 Ctrl + Shift + B 进行构建程序,而vscode 会弹出命令面板, 并且根据当前打开的文件显示的命令也不同,打开的是cpp文件显示效果如下
如果当前文件是 .json 格式,显示如下
虽然可以通过设置 xmake 的快捷键是 Ctrl + Shift + B,但这样也会丢失了vscode 的Ctrl + Shift + B灵活行。可以参考vscode官网 Get Started with C++ on Linux in Visual Studio Code配置tasks.json方法。
下面来通过配置tasks.json达到使用 Ctrl + Shift + B 快捷键的方法
关闭 所有打开的文档,点击 Terminal - Configure Tasks…
弹出的命令面板中 点击 Create tasks.json file from template
点击 Others Example to run an arbitrary extemal command
会在 .vscode 文件夹下自动创建 tasks.json 文件,并自动打开
修改配置文件的内容如下然后保存
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "xmake-b", // 显示的名称
"type": "shell", // 脚本任务
"command": "xmake", // 指定运行的命令
"args": [ // xmake 命令行的参数
"-v" // 打印详细, 根据个人习惯是否添加. 详细说明请参考xmake官方文档说明
],
"group": {
"kind": "build", // 添加到构建分组中, 用于在命令面板中显示
"isDefault": false // 默认是 false, 设置为true后, 按下快捷键 Ctrl + Shift + B 后, 不弹出命令面板直接运行该任务
}
}
]
}
避免xmake 构建的缓存影响查看的效果,先在终端中运行下面的命令,清空构建缓存
xmake clean
clear
清理完构建的缓存后,按下 Ctrl + Shift + B 快捷键,弹出的命令面板中显示tasks的任务列表,可以看到刚刚配置的 xmake-b 任务
点击 xmake-b 任务,
可以看到执行任务的构建命令 xmake -v,然后是编译的详细信息,如果不设置 -v 参数就和前面的构建输出信息一致。关于参数请查看 xmake 教程。
有时希望在构建任务前先执行一些其他事情,类似vs2013的构建前事件。比如在构建前先自动清理后再构建,那么可以在 task.json 文件中通过添加其他任务(也就是配置多任务都在 task.json 文件中), 然后通过 dependsOn 配置依赖的其他任务。 拷贝下面代码并粘贴 task.json 文件中
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "xmake-b", // 显示的任务名称
"type": "shell", // 脚本任务
"command": "xmake", // 指定运行的命令
"args": [ // xmake 命令行的参数
"-v" // 打印详细, 根据个人习惯是否添加. 详细说明请参考xmake官方文档说明
],
"group": {
"kind": "build", // 添加到构建分组中, 用于在命令面板中显示
"isDefault": false // 默认是 false, 设置为true后, 按下快捷键 Ctrl + Shift + B 后, 不弹出命令面板直接运行该任务
},
"dependsOn":[ // 构建前先执行其他依赖任务集.
"stu-echo", // stu-echo 显示 Hello task for echo!!!
"xmake-c" // 执行 xmake-c 任务清理构建缓存
],
},
{
"label": "xmake-c", // 显示的任务名称
"type": "shell", // 脚本任务
"command": "xmake", // 指定运行的命令
"args": [ // xmake 命令行的参数
"c" // 清理构建缓存
],
"group": "build", // 添加到构建分组中, 用于在命令面板中显示
},
{
"label": "stu-echo", // 显示的任务名称
"detail": "显示输出字符", // 详细信息
"type": "shell", // 脚本任务
"command": "echo", // 指定运行的命令
"args": [ // echo 命令行的参数
"Hello task for echo!!!" // 显示 Hello task for echo!!!
],
//"group": "build", // 去掉分组, 表示这个任务不会出现在命令面板的构建列表中
}
]
}
粘贴保存后再次 按下 Ctrl + Shift + B 快捷键,在命令面板的任务列中可以看到 xmake-b、 xmake-c 两个任务
选择 xmake-b 并执行
这里并没有看到显示 Hello task for echo!!! 信息,原因是执行 xmake 的命令创建了一个新的输出面板,原文的翻译 终端将被任务重新使用,按任意键将其关闭。按任意键后,可以看到执行了 stu-echo 并输出 Hello task for echo!!! 信息
配置运行调试
虽然通过按 F5快捷键或 xmake 工具栏 上的 debug 按钮可以启动调试,但是不能解决代码改变后自动编译后再运行调试。可以通过配置运行任务增加前置任务来解决。
点击 Run - Add Configuration
在弹出的命令面板中选择并点击 xmake
在 .vscode 文件夹下自动创建 launch.json 文件,并自动打开
打开 main.cpp 并增加输出 Hello launch.json
#include "foo.h"
#include <iostream>
#include <string>
#include <vector>
int main(int argc, char** argv) {
std::cout << "add(1, 2) = " << add(1, 2) << std::endl;
std::vector<std::string> stdvec = { "Hello", "vscode", "xmake", "C/C++", "!!!" };
for (auto &&i : stdvec)
{
std::cout << i << " ";
}
std::cout << std::endl;
std::cout << "Hello launch.json" << std::endl; // 增加输出 Hello launch.json
return 0;
}
保存后直接 按 F5 启动调试,发现并没有输出 Hello launch.json,原因是因为没有编译
在使用vs2013习惯了自动编译,通过在 launch.json 文件配置 preLaunchTask 执行前置的构建任务,内容修改如下
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "xmake", // 调试类型 xmake
"request": "launch", // 请求方式是运行
"name": "xmake-d", // 名称, 修改一个好识别的
"target": "target name", // 目标名
"cwd": "${workspaceFolder}", // 工作目录
"stopAtEntry": false, // 默认是true, 设为true时程序将暂停在程序入口处,相当于在main上打断点
"preLaunchTask": "xmake-b" // 运行前执行 xmake-b 任务
}
]
}
再次按 F5 启动调试,发现并输出 Hello launch.json
配置调试程序的参数
修改 main.cpp, 增加 输出 argv 的值
#include "foo.h"
#include <iostream>
#include <string>
#include <vector>
int main(int argc, char** argv) {
std::cout << "开始打印程序的参数!" << std::endl;
for (int i = 0; i < argc; ++i)
{
std::cout << "参数" << i << " " << argv[i] << std::endl;
}
std::cout << "结束打印程序的参数!" << std::endl;
std::cout << "add(1, 2) = " << add(1, 2) << std::endl;
std::vector<std::string> stdvec = { "Hello", "vscode", "xmake", "C/C++", "!!!" };
for (auto &&i : stdvec)
{
std::cout << i << " ";
}
std::cout << std::endl;
std::cout << "Hello launch.json" << std::endl;
return 0;
}
修改 launch.json 文件, 增加设置 argv 值,
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "xmake", // 调试类型 xmake
"request": "launch", // 请求方式是运行
"name": "xmake-d", // 名称, 修改一个好识别的
"target": "target name", // 目标名
"cwd": "${workspaceFolder}", // 工作目录
"args": [ // 配置调试程序的运行参数
"ABC",
"BCD",
"123",
"Hello launch param"
],
"stopAtEntry": false, // 默认是true, 设为true时程序将暂停在程序入口处,相当于在main上打断点
"preLaunchTask": "xmake-b" // 运行前执行 xmake-b 任务
}
]
}
按 F5 运行程序,程序打印输出了程序参数
附加进程调试
调试程序还有一种经常遇到的情况,就是程序已经运行了,无法用 启动调试 的方法进行调试,在vs2013中都是通过 附加进程 方式进行调试
Linux
修改 main.cpp, 新增加 std::cin >> waitAttach; 用于程序运行起来后等待输入字符,这是好通过 vscode进行附加进程操作, 内容如下
#include "foo.h"
#include <iostream>
#include <string>
#include <vector>
int main(int argc, char** argv) {
std::cout << "开始打印程序的参数!" << std::endl;
for (int i = 0; i < argc; ++i)
{
std::cout << "参数" << i << " " << argv[i] << std::endl;
}
std::cout << "结束打印程序的参数!" << std::endl;
std::cout << "验证Attach功能" << std::endl;
std::cout << "第一步, 在std::cin >> waitAttach;下一句打断点" << std::endl;
std::cout << "第二步, 启动eg_prjs程序" << std::endl;
std::cout << "第三步, 在调试界面 选择 cppvsdbg attech" << std::endl;
std::cout << "第四步, 点击绿色执行按钮" << std::endl;
std::cout << "第五步, 附加 eg_prjs 进程. 然后在 eg_prjs 控制台界面中输入字符回车后, 会自动停止打断点处"<< std::endl;
std::cout << "等待输入:" << std::endl;
std::string waitAttach;
std::cin >> waitAttach;
std::cout << "输入值是:" << waitAttach << std::endl;
std::cout << "add(1, 2) = " << add(1, 2) << std::endl;
std::vector<std::string> stdvec = { "Hello", "vscode", "xmake", "C/C++", "!!!" };
for (auto &&i : stdvec)
{
std::cout << i << " ";
}
std::cout << std::endl;
std::cout << "Hello launch.json" << std::endl;
return 0;
}
修改完一定要 重新编译 下
打开 launch.json 文件,点击 Add Configuration… 文件
在弹出的窗口界面中选择 C/C++: (gdb) Attach
注:本篇文档也适用 vscode + xmake + msvc开发环境的配置,需要选择 C/C++: (Windows) Attach
自动把 (gdb) Attach 配置增加到 launch.json 文件中
"program": "enter program name, for example ${workspaceFolder}/a.out",
修改为
"program": "${workspaceFolder}/build/linux/x86_64/debug/eg_prjs",
launch.json 文件内容如下
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Attach",
"type": "cppdbg",
"request": "attach",
"program": "${workspaceFolder}/build/linux/x86_64/debug/eg_prjs", // 修改为程序运行的路径
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
{
"type": "xmake", // 调试类型 xmake
"request": "launch", // 请求方式是运行
"name": "xmake-d", // 名称, 修改一个好识别的
"target": "target name", // 目标名
"cwd": "${workspaceFolder}", // 工作目录
"args": [ // 配置调试程序的运行参数
"ABC",
"BCD",
"123",
"Hello launch param"
],
"stopAtEntry": false, // 默认是true, 设为true时程序将暂停在程序入口处,相当于在main上打断点
"preLaunchTask": "xmake-b" // 运行前执行 xmake-b 任务
}
]
}
在命令终端中输入
xmake run
先运行起程序
程序运行到等待输入操作,接下来切换到调试界面,如下图所示,启动项中选择 (gdb) Attach,然后点击旁边的绿色按钮(标记3)启动附加进程
在弹出命令面板的搜索框张输入 eg_prjs,查找到运行的进程
双击 eg_prjs 进程,发现弹出错误提示窗(请忽略进程的id,因为是多次启动, 进程的id发生变化,这并不影响本文档操作流程步骤)
出现这个原因是应该是没有权限,也许在截图的过程中超时导致的。可以尝试开启 linux 系统上的 xmake的远程编译环境(该操作会创建token),然后按下快捷键 Ctrl + C 终止这个服务。如果还不起作用,在window上启动cmd命令窗口,也同样启动一下 xmake 的远程编译环境,在关闭。
暂时还不明白什么原因,待学习研究中。
这样操作后可以附加进程调试了,附上 linux 指令
-
查看下运行的进程
ps aux
-
kill进程
kill 进程id
确保当前没有 eg_prjs 进行在运行。
这次多开一个终端窗口方便查看,通过终端 分割 按钮分割成两个终端窗口
分割后如下图所示,并在第2个终端窗口输入 xmake run 启动 程序,并进入等待输入操作
切换到调试模式再次选择 (gdb) Attach,然后点击旁边的绿色按钮(标记3)启动附加进程
选择要附加的进程 eg_prjs
启动附加进程后,终端界面被自动切换了,需要把刚才 xmake run 界面显示出来,在最右侧的终端列表中点击 xmake eg_prjs
在下图的标记1处,输入字符串后回车
可以看到,附加调试停止在断点处
按下 F5 键,终端输出窗口打印的消息是在 24 行之后执行的。(注: 虽然输入的是 Hello Test Attach 但打印输出的是 Hello 这是C++输入缓冲区问题,有关C/C++编程请自行学习)
Window
本文也适用window、 xmake、msvc。
添加附加进程任务选择 C/C++: (Windows) Attach
遇到的问题是有win11可以正常查询进程,有的报错 wmic 错误,是因为win11系统中没有 wmic 程序。而vscode 1.96.2 还在使用 wmic 这个命令获取系统进程列表,可以自行安装或等待微软官网后期升级改进。报错截图如下
报错解释
Visual Studio Code (VS Code) 在尝试通过Windows Management Instrumentation Command-line (WMIC) 工具进行附加调试时,未能在系统上找到WMIC。WMIC是一个命令行界面,用于访问Windows系统的WMI信息。VS Code需要WMIC来启动附加进程的调试会话。
微软官网资料: WMI 命令行 (WMIC) 实用程序 - Win32 apps | Microsoft Learn
可以参考《 真是奇哉怪也,Windows11居然不支持WMIC?(附离线安装方法及安装包) 》安装WMIC
数据结构Visualizers
说明
在远端主机(linux)上用户目录下没有找到 .vscode 文件夹,这是在远端主机没有安装过 vscode,一直都是通过vscode ssh插件远程访问,vscode在远端主机上仅安装了 .vscode-server
进入到扩展文件夹下查看到 ms-vscode.cpptools-1.22.11-linux-x64
cd .vscode-server/extensions/
查看 debugAdapters 文件下并没有 vsdbg 文件夹。从 git hub 下载 cpptools 的cpptools-linux-x64_1.22.11.vsix和cpptools-windows-x64_1.22.11.vsix后,通过压缩软件(vsix实际是zip格式)打开 debugAdapters 文件夹对比
可以看到,微软没有将C/C++插件Visualizer功能支持l到inux版本的插件上,期望以后添加。
对应linux上自定义数据结构的Visualizers需要在学习其他方法(本次先停一段落)。
window上Visualizer
下面以 window 平台基于 msvc 编译环境(vs2022)为例。
系统环境
操作系统 Windows 11
工作环境 vscode、vs2022、xmake、git
关闭远程
关闭linux的远程链接,如下图所示,先点击 SSH: 192.168.1.82,在弹出的命令面板中点击 Close Remote Connection
下载代码
与linux保持一致的文件夹(除根目录)结构,创建 D:/stu 文件夹并按照前面的记录将代码克隆到 D:/stu 文件夹下
通过 File - Open Folder…切换到 eg_prjs 文件夹
实现举例
打开 foo/inc/fool.h 文件,在文件后面添加如下代码,其中 FXXDateTime 自定义数据类,演示在调试时如何显示类信息
/**
* brief 日期时间. 为了避免与常用的名称区分, 定义名称为 FXXDateTime
* 成员变量就是 年/月/日 小时:分钟:秒
*/
class __export FXXDateTime
{
public:
FXXDateTime(int year, int month, int day, int hour, int minute, int second);
~FXXDateTime();
/// 头文件除了提供API外, 更重要的是让使用者通过查看头文件就知道怎么使用(谁还看文档[文档该写还是的得写], 除非必要, 大家用微信有看过微信说明吗),
/// 那么使用者最关心的就是public部分, 根本就不看private部分, protected部分根据情况才看
/// 私有的放在前面, 在阅读的时候还要把代码拉到下面, 不觉的麻烦么.
/// 我的理念是private部分放在类的最后面, 其次是protected部分, public放在类的最前面. 因为实际开发过程中我也从不看私有(除非必要).
/// 况且类似Qt库等做法, 把私有数据完全封装到一个data的结构体中, 类中只有一个data的指针, 继承类连data的指针都看不见.
/// 这里只是演示, 定义成员变量如下
/// @cond
private:
int m_year;
int m_month;
int m_day;
int m_hour;
int m_minute;
int m_second;
/// @endcond
};
打开 foo/inc/fool.cpp 文件,在文件后面添加如下代码
FXXDateTime::FXXDateTime(int year, int month, int day, int hour, int minute, int second)
: m_year(year)
, m_month(month)
, m_day(day)
, m_hour(hour)
, m_minute(minute)
, m_second(second)
{}
FXXDateTime::~FXXDateTime()
{
}
在 app/main.cpp 文件,添加在 main() 入口函数(为了好打断点)
#include "foo.h"
#include <iostream>
#include <string>
#include <vector>
int main(int argc, char** argv) {
// 里 main 远点是为了腾出空间好查看 fdatetime 变量的调试信息
FXXDateTime fdatetime(2024, 12, 22, 05, 53, 29); // 添加到这里, 在下面一句打断点, 调试运行后将鼠标悬停在fdatetime变量上
std::cout << "开始打印程序的参数!" << std::endl; // 打断点
for (int i = 0; i < argc; ++i)
{
std::cout << "参数" << i << " " << argv[i] << std::endl;
}
std::cout << "结束打印程序的参数!" << std::endl;
std::cout << "验证Attach功能" << std::endl;
std::cout << "第一步, 在std::cin >> waitAttach;下一句打断点" << std::endl;
std::cout << "第二步, 启动eg_prjs程序" << std::endl;
std::cout << "第三步, 在调试界面 选择 cppvsdbg attech" << std::endl;
std::cout << "第四步, 点击绿色执行按钮" << std::endl;
std::cout << "第五步, 附加 eg_prjs 进程. 然后在 eg_prjs 控制台界面中输入字符回车后, 会自动停止打断点处"<< std::endl;
std::cout << "等待输入:" << std::endl;
std::string waitAttach;
std::cin >> waitAttach;
std::cout << "输入值是:" << waitAttach << std::endl;
std::cout << "add(1, 2) = " << add(1, 2) << std::endl;
std::vector<std::string> stdvec = { "Hello", "vscode", "xmake", "C/C++", "!!!" };
for (auto &&i : stdvec)
{
std::cout << i << " ";
}
std::cout << std::endl;
std::cout << "Hello launch.json" << std::endl;
return 0;
}
按下 F5 启动调试看看,需要注意的调试 快捷键 是否配置正确,检测是否和下图一致。也可手动编译、调试(没有直接按下 F5 编译和调试一起执行的方便,使用vs2022遗留的习惯)。请参考 配置运行调试 确保配置正确。
查看一下调试运行效果,记得鼠标悬停在 fdatatime 变量上才能看到标记2的信息
标记3的显示效果还是不能像 argc = 5 那样正常显示,接下来要做的就是如何让fdatetime像 argc = 5 那样显示
微软官方资料 创建 C++ 对象的自定义视图 - Visual Studio (Windows) | Microsoft Learn 使用 Natvis 框架在调试器中创建 C++ 对象的自定义视图。
[C++][调试技巧] VS 中的 Debugger Visualizers 使用指南 (.natvis文件)_vs debugger-优快云博客
本着开发程序管理一切的变更都要有版本控制和记录,那么首先在 eg_prjs 工程下创建一个文件夹,用于管理其他文件。
举例创建文件夹名为 natvis,如下图所示
在 natvis 文件夹下新建一个 eg_prjs.natvis 文件,内容如下
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="FXXDateTime">
<DisplayString>{{ {m_year}/{m_month}/{m_day} {m_hour}:{m_minute}:{m_second} }}</DisplayString>
<Expand>
<Item Name="[year]">m_year</Item>
<Item Name="[month]">m_month</Item>
<Item Name="[day]">m_day</Item>
<Item Name="[hour]">m_hour</Item>
<Item Name="[minute]">m_minute</Item>
<Item Name="[second]">m_second</Item>
</Expand>
</Type>
</AutoVisualizer>
确保调试快捷键切换回 xmake-d
按下 快捷键 Ctrl + Shift + B,命令面板中选择设置过的 xmake-b 启动编译
可以看到,正确执行设置的 xmake c,xmake -v 命令。从编译信息看编译失败的原因是源文件的代码页问题,UTF-8 格式的文件需要 BOM 标签msvc才能较好的支持,linux 是不需要的,可能是为了兼容微软的规则,目前linux也支持编译带有 BOM 标签的文件,因此代码的文本格式建议都修改为带有 BOM 标签的 UTF-8。
下面切换一下文本的编码格式。打开源码文件, 点击右下角 UTF-8,弹出的界面选择 Save with Encoding
选择 UTF-8 with BOM utf8bom
再次快捷键 Ctrl + Shift + B,命令面板中选择设置过的 xmake-b 启动编译
编译成功,打开 main.cpp 文件,在第15行打断点,启动调试。
注: xmake插件和vscode 协调性需要注意一下,通过快捷键 F5 启动自定义编译、调试运行任务有时失效,需要在命令面板中 通过 >XMake:Build或>XMake:ReBuild 编译后在按 快捷键 F5 就会生效。
看到的效果还不是预期的,原因是还没有使用 eg_prjs.natvis 文件,把 eg_prjs.natvis 文件拷贝到
C:\Users\xxx\.vscode\extensions\ms-vscode.cpptools-1.22.11-win32-x64\debugAdapters\vsdbg\bin\Visualizers
文件夹下,xxx 是实际用户名, ms-vscode.cpptools-1.22.11-win32-x64 根据当前安装的版本决定
再次启动调试
可以看到,调试信息中关于 fdatetime 变量显示的信息是符合预期。
配置intellisense
创建工程
把eg_prjs中的foo模块作为一个外部库使用。在 stu_linux 文件夹下创建一个 eg_app 文件夹,如下图所示
在vscode中打开 eg_app 文件夹,并通过 >XMake:CreateProject - c++ - console 创建一个C++的控制台程序,目录结构如下图所示
引用foo模块
引用前面的 eg_prjs 工程下已经生成了 foo 模块并使用 foo 模块的 add 函数和 FXXDatetime 类。
修改 xmake.lua 配置文件
add_rules("mode.debug", "mode.release")
target("eg_app")
add_includedirs("../eg_prjs/src/foo/inc") -- 增加头文件夹的路径
if is_plat("linux") then -- 是否是linux平台
if is_mode("debug") then -- 是否是调试模式
add_linkdirs("../eg_prjs/build/linux/x86_64/debug") -- 增加链接库的路径
add_rpathdirs("/home/xx/stu/stu_linux/eg_prjs/build/linux/x86_64/debug") -- 添加程序运行时动态库的加载搜索目录. 遇坑指南, 必须是绝对路径
else
add_linkdirs("../eg_prjs/build/linux/x86_64/release")
add_rpathdirs("/home/xx/stu/stu_linux/eg_prjs/build/linux/x86_64/release")
end
add_links("libfoo.so") -- 引用 foo 模块链接库
end
set_kind("binary")
add_files("src/*.cpp")
修改 main.cpp 文件
#include <foo.h>
#include <iostream>
int main(int argc, char** argv) {
FXXDateTime fdatetime(2024, 12, 22, 05, 53, 29); // 添加到这里, 在下面一句打断点, 调试运行后将鼠标悬停在fdatetime变量上
std::cout << "add(1, 2) = " << add(1, 2) << std::endl;
return 0;
}
点击构建
可以看到构建成功,但是标记3处, #include <foo.h> 未能正确解析(有红色波浪线提示),FXXDatetime 显示颜色提示也不正确
打开控制面板,选择 C/C++: Edit Configurations(JSON) ,也可以选择 C/C++: Edit Configurations(UI) 根据个人习惯来定
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/home/xx/stu/stu_linux/eg_prjs/src/foo/inc/**" // 增加 foo模块的头文件路径
],
"defines": [],
"compilerPath": "/usr/bin/clang",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "linux-clang-x64"
}
],
"version": 4
}
在打开查看 main.cpp 文件,#include <foo.h> 、FXXDatetime 正确解析
运行程序,看到调用 foo 模块的 add 函数正常运行并输出结果3