boost::typeIndex 的相关探究

boost::typeIndex 的相关探究 Effective Modern C++ 的 Item 4: Know how to view deduced types. 中提到了 Boost::typeindex 的使用,但并没有讲到其实现原理。 1. typeid 操作符 typeid 是 C++ 中的一个操作符,可以用于获取类型的信息,常常用在必须知道多态对象的动态类型,或是识别静态类型的地方。 我们可以写一个简单的 demo 用于获取对象类型相关的信息,需要包含 tepyinfo 头文件: #include <iostream> #include <typeinfo> using namespace std; class Foo {}; int main() { cout << "1: " << typeid(1).name() << endl; cout << "int: " << typeid(int).name() << endl; // 和 sizeof 操作符类似,typeid 也可以直接对数据类型(比如 int)进行操作 cout << "typeid: " << typeid(typeid(int)).name() << endl; cout << "typeid: " << typeid(const type_info &)....

July 31, 2020 · 6 min

CMake 入门

CMake 入门 0. 序 CMake 是一个跨平台的开源构建工具,使用 CMake 能够方便地管理依赖多个库的目录层次结构并生成 makefile 和使用 GNU make 来编译和连接程序。 1. 构建单个文件 1.1 使用 GCC 编译 假设现在我们希望编写一个函数来实现安全的 int 类型加法防止数据溢出,这个源文件没有任何依赖的源码或静态库: // safe_add.cpp #include <iostream> #include <memory> #define INT_MAX 2147483647 #define ERROR_DATA_OVERFLOW 2 int SafeIntAdd(std::unique_ptr<int> &sum, int a, int b) { if (a > INT_MAX - b) { *sum = INT_MAX; return ERROR_DATA_OVERFLOW; } *sum = a + b; return EXIT_SUCCESS; } int main() { int a, b; std::cin >> a >> b; std::unique_ptr<int> sum(new int(1)); int res = SafeIntAdd(sum, a, b); std::cout << *sum << std::endl; return res; } 我们可以直接使用一句简单的 gcc 命令来编译这个文件并执行:...

June 21, 2020 · 11 min

GDB 调试入门

GDB 调试入门 0. 序 调试程序是开发过程中必不可少的一环,在 Windows 或 MacOS 上开发时,可以使用 VS 和 CLion 等 IDE 上自带的调试功能来打断点或查看变量和堆栈,但 Linux 并没有图形化的操作界面,而如果只通过打 log 的方式来查找问题的话效率将会非常低下,此时我们可以利用 GDB 来提升我们的开发效率。 GDB 是 GNU Debugger 的简写,是 GNU 软件系统中的标准调试器。GDB 具备各种调试功能,包括但不限于打断点、单步执行、打印变量、查看寄存器、查看函数调用堆栈等,能够有效地针对函数的运行进行追踪和警告;使用 GDB 调试时,可以监督和修改程序的变量,并且这些修改是独立于主程序之外的。GDB 主要用于调试编译型语言,对 C,C++,Go,Fortran 等语言有内置的支持,但它不支持解释型语言。 1. 环境搭建 1.1 编写程序 为了进行调试,我们需要准备一个简单的 C++ 程序: $ cat test.cpp #include <iostream> void Func(const char *s) { int *p = nullptr; int &r = static_cast<int&>(*p); int num = std::atoi(s); r = num; printf("%d\n", r); } int main (int argc, char *argv[]) { if (argc !...

April 22, 2020 · 5 min

coverity 的 WRAPPER_ESCAPE 告警

coverity 的 WRAPPER_ESCAPE 告警 const char* Foo() { std::string str_msg("test"); return str_msg.c_str(); } int main() { const char *p_msg = Foo(); printf("%s\n", p_msg); return 0; } // output:(为空,或乱码) D? 上面代码中的 Foo 函数会被 coverity 报告 WRAPPER_ESCAPE,详细说明是: Wrapper object use after free (WRAPPER_ESCAPE) 1. escape: The internal representation of local strMsg escapes, but is destroyed when it exits scope 大意是局部变量 str_msg 在离开函数 Foo 的时候会被释放(因为 str_msg 是分配在栈上的变量),而通过函数 std::string::c_str() 获取的指向 str_msg 头部的指针会因此变为一个悬空指针,将这个悬空指针返回给函数调用者使用将会发生不可预知的行为。 而 c_str() 本身返回的是一个 const char *p,虽然我们无法直接修改指针 p 所指向的数据,但我们可以通过修改 str_msg 来达到修改 p 所指向内存的效果,例如如下的代码:...

March 15, 2020 · 1 min

C++ 协程(1):函数和协程

C++ 协程(1):函数和协程 这篇文章的目的是探究 C++ 中协程的机制和用法,以及怎样利用协程的特性来构建上层的库和应用。 1. 栈帧和函数 栈帧是一个函数执行的环境,包括函数参数、函数返回地址、局部变量等信息。操作系统每次调用一个函数,都会为其分配一个新的栈帧,相关的概念有: ESP:栈指针寄存器(Extended Stack Pointer),其内存中存放一个始终指向系统栈最顶部栈帧栈顶的指针 EBP:基址指针寄存器(Extended Base Pointer),其内存中存放一个始终指向系统最顶部栈帧栈底的指针 函数栈帧:ESP和EBP之间的内存空间为当前栈帧,EBP标识了当前栈帧的底部,ESP标识了当前栈帧的顶部 对于普通的函数来说,一般我们可以对其进行两种操作:call(调用)和 return(返回)。为了方便对比,此处不讨论 throw exception 的情况。在运行一个 C++ 程序时,编译器会先执行 C++ runtime,然后会调用 main 函数,再由 main 函数调用其他的函数。 call 操作一般包含以下几个步骤: 参数入栈:参数从右向左依次入栈 返回地址入栈:将当前代码区的下一条待执行的指令入栈,以便在函数 return 之后执行 代码区跳转:处理器跳转到被调函数的入口 栈帧调整,包括: 保存当前栈帧状态值,EBP 入栈 从当前栈帧切换到新的栈帧,更新 EBP,将 EBP 的值设置为 ESP 的值 给新的栈帧分配内存空间,更新 ESP,将 ESP 的值减去所需空间的大小 当一个函数通过 return 语句返回时,执行的步骤与调用时相反: 2. 协程 协程由程序所控制,即在用户态执行,而不是像线程一样由操作系统内核管理,使用协程时,不需要如线程一般频繁地进行上下文切换,性能能够得到很大的提升,因此协程的开销远远小于线程的开销。一般来说协程有三种特性: suspend 悬停:暂停当前协程的执行,将执行权交还给调用者,但是保留当前栈帧。和函数的 return 类似,协程的 suspend 只能由协程自身发起 resume 恢复:继续执行已经 suspend 的协程,重新激活协程的栈帧 destroy 销毁:销毁协程的栈帧和其对应的内存 可以看到,协程可以在不清除栈帧的情况下被挂起而不被销毁,因此我们不能够使用调用栈这样的数据结构来严格保证活动栈帧的生命周期,我们可以把协程存储在堆中。我们可以把协程的栈帧分为两部分,一部分是执行栈帧,这部分仅在当前协程执行期间存在,在执行结束,即协程 suspend 的时候被释放;另一部分是数据栈帧,这部分即使在协程 suspend 的时候依然存在。...

January 20, 2020 · 2 min