C++的backtrace
开始之前
很多语言的log模块都有一个功能,就是在打log的时候能够追溯调用栈,有的时候对查bug能有点帮助。之前我也想过给我们的log模块加上C++的backtrace的功能,迟迟一直没有做主要是两个原因:一是C++的backtrace在各个平台和编译器上都不太一样,比较冗杂;二是C/C++在编译优化之后,调用行之类的信息和甚至一些函数可能就被优化没了。所以能提供的信息就相当有限。前两天刚好有朋友问有没有提供这个,所以就花了点时间整理了下适配方案。
很多语言的log模块都有一个功能,就是在打log的时候能够追溯调用栈,有的时候对查bug能有点帮助。之前我也想过给我们的log模块加上C++的backtrace的功能,迟迟一直没有做主要是两个原因:一是C++的backtrace在各个平台和编译器上都不太一样,比较冗杂;二是C/C++在编译优化之后,调用行之类的信息和甚至一些函数可能就被优化没了。所以能提供的信息就相当有限。前两天刚好有朋友问有没有提供这个,所以就花了点时间整理了下适配方案。
经过茫茫长时间的编写+过年在家无聊补充和修正单元测试,再加上这两天的整理,终于把以前的这个关于服务器通信中间件的基本功能和相应的单元测试完成啦。还是可以热烈庆祝一下的。
Github地址: https://github.com/owent/libiniloader
OSChina镜像: http://git.oschina.net/owent/libiniloader
项目中经常会碰到需要读取配置的情况,而用ini配置最大的优势就是简单易懂。 但是现在很多的配置读取库都过于庞大了,比如Boost.PropertyTree,功能很强大,但是基本不会用到里面所有的东西。 另外微软的INI读取,功能不是很强不说,还限制在Windows上,更重要的是API设计得实在不优雅。 于是libiniloader就诞生啦。
今年准备安安心心写一个协程库。一方面是觉得协程挺有意思,另一方面也是因为C/C++在这方面没有一个非常权威的解决方案。 按照我自己风格还是喜欢C++,所以协程库定名为 libcopp 。 源码托管在 github: https://github.com/owent/libcopp 镜像托管 http://git.oschina.net/owent/distinctionpp
C++11标准里有动态模板参数已经是众所周知的事儿了。但是当时还有个主流编译器还不支持。 但是现在,主要的编译器。VC(Windows),GCC(Windows,Linux),Clang(Mac,IOS)都已经支持了。所以就可以准备用于生产环境了。 type_traits没啥好说的。主要是一些静态检测。主要还是要看动态模板参数和他们两的结合使用上。 动态模版参数标准文档见: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2242.pdf 和 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2555.pdf 虽然贴出来了。估计是没人看得。所以就直接说重点。
终于到最后的虚继承了。
测试代码如下:
#include <ctime>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <stdint.h>
#include <cstddef>
#include <string>
class foo_a
{
public:
virtual ~foo_a(){}
virtual void info() {
printf("%s:%d\n", __FUNCTION__, __LINE__);
}
void print(){
printf("%s:%d\n", __FUNCTION__, __LINE__);
}
};
class foo_l
{
public:
int l;
foo_l(): l(1){}
virtual void info() {
printf("%s:%d\n", __FUNCTION__, __LINE__);
}
};
class foo_r: virtual public foo_a
{
public:
int r;
foo_r(): r(2){}
};
class foo_c: virtual public foo_l, public foo_r
{
public:
virtual void info() {
printf("%d,%d. %s:%d\n", l, r, __FUNCTION__, __LINE__);
}
};
int main(int argc, char* argv[]) {
void (foo_a::*vptr1)() = &foo_a::info;
void (foo_l::*vptr2)() = &foo_l::info;
void (foo_a::*ptr)() = &foo_a::print;
foo_c c;
foo_r r;
foo_a a;
printf("pword size = %d\n", (int)(sizeof(size_t)));
printf("address of &foo_a::info = 0x%llxH\n", &foo_a::info);
printf("pointer to &foo_a::info = 0x%llxH\n", vptr1);
printf("sizeof vptr = %d\n", (int)(sizeof(vptr1)));
(a.*vptr1)();
(r.*vptr1)();
(c.*vptr1)();
(c.*vptr2)();
c.info();
(c.*ptr)();
return 0;
}
因为是兼容虚继承和非虚继承的,所以赋值的部分的汇编是一样的。这里就不贴了。关键在于执行期它是怎么找到虚基类的。请往下看:
接下来是多重继承,用到的测试代码如下所示:
#include <ctime>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <stdint.h>
#include <cstddef>
#include <string>
class foo_a
{
public:
virtual ~foo_a(){}
virtual void info() {
printf("%s:%d\n", __FUNCTION__, __LINE__);
}
};
class foo_b
{
public:
virtual ~foo_b(){}
// 这个函数用于试foo_b的结构与foo_a不同
// 以防止VC对foo_a和foo_b的`vcall'{8}'的代码合并
virtual void inspect() {
printf("%s:%d\n", __FUNCTION__, __LINE__);
}
virtual void info() {
printf("%s:%d\n", __FUNCTION__, __LINE__);
}
void print() {
printf("%s:%d\n", __FUNCTION__, __LINE__);
}
};
class foo_c: public foo_a, public foo_b
{
public:
virtual void info() {
printf("%s:%d\n", __FUNCTION__, __LINE__);
}
};
int main(int argc, char* argv[]) {
void (foo_a::*vptr1)() = &foo_a::info;
void (foo_b::*vptr2)() = &foo_b::info;
void (foo_b::*ptr)() = &foo_b::print;
foo_c c;
foo_b b;
foo_a a;
printf("word size = %d\n", (int)(sizeof(size_t)));
printf("address of &foo_a::info = 0x%llxH\n", &foo_a::info);
printf("pointer to &foo_a::info = 0x%llxH\n", vptr1);
printf("address of &foo_b::info = 0x%llxH\n", &foo_b::info);
printf("pointer to &foo_b::info = 0x%llxH\n", vptr2);
printf("sizeof vptr = %d\n", (int)(sizeof(vptr1)));
(a.*vptr1)();
(b.*vptr2)();
(c.*vptr1)();
(c.*vptr2)();
(c.*ptr)();
return 0;
}
最近在《C++对象模型》一书里说到的virtual的成员函数指针,低于128的被cfront编译器认为是虚表偏移量(支持子类对父类函数的覆盖)。VC只是提了下单继承、多继承和虚继承的实现方案不同,GCC没有提及,所以就专门稍微深入分析研究下他们的实现机制。