简单C++单元测试框架(支持一键切到GTest或Boost.Test)
简介
众所周知,单元测试对于持续集成和变更的检测是十分重要的。 这个单元测试框架本是用于之前规划的C++协程框架使用的。 虽然已经有比较成熟的单元测试框架GTest、Boost.Test等等,但是要引入它们都会倒入很多文件。而且他们的兼容性也不是很好 GTest在ARM架构下需要额外作一些适配 Boost.Test的话,按Boost的尿性,一旦引入就会涉及上千个文件
众所周知,单元测试对于持续集成和变更的检测是十分重要的。 这个单元测试框架本是用于之前规划的C++协程框架使用的。 虽然已经有比较成熟的单元测试框架GTest、Boost.Test等等,但是要引入它们都会倒入很多文件。而且他们的兼容性也不是很好 GTest在ARM架构下需要额外作一些适配 Boost.Test的话,按Boost的尿性,一旦引入就会涉及上千个文件
今年准备安安心心写一个协程库。一方面是觉得协程挺有意思,另一方面也是因为C/C++在这方面没有一个非常权威的解决方案。 按照我自己风格还是喜欢C++,所以协程库定名为 libcopp 。 源码托管在 github: https://github.com/owent/libcopp 镜像托管 http://git.oschina.net/owent/distinctionpp
回顾一下2013年学习和看到的技术、思路、方案总结:
熟悉了一些sed和awk的使用,看到书上说perl是专门设计来替代awk的,什么时候研究下
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 虽然贴出来了。估计是没人看得。所以就直接说重点。
前几天我们项目的日志系统出现了一点问题,但是一直没有时间去深究。 昨天在同事的帮助下,无意中猜了一种可能性,结果还真被我猜中了,于是今天就特别研究了一下,记录下来。
在某个神奇的下午,收到一个垃圾邮件(至少被邮件系统当成了垃圾邮件)。
结果就一不小心看到了这个充满回忆的ACM模式竞赛,还有咱腾讯的,就忍不住看了一下。
项目里面引入了Lua,就特别学习了一下。
其实对于理解Javascipt的人来说,Lua也很容易理解,因为他们太多的地方相像了。
初步看来,Lua的设计模式和思想很像Javascript,也是原型模型(Javascript里叫prototype,Lua里是metatable)
终于到最后的虚继承了。
测试代码如下:
#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没有提及,所以就专门稍微深入分析研究下他们的实现机制。
今天看到一个小例子,发现了一个小trick。见代码:
#include <cstdio>
#include <cstdlib>
class base_1
{
public:
int a;
};
class base_2
{
public:
int b;
};
class base_3: public base_1, public base_2
{
public:
int c;
};
int main(int argc, char* argv[]) {
printf("&base_1::a = %p\n", &base_1::a);
printf("&base_2::b = %p\n", &base_2::b);
printf("&base_3::a = %p\n", &base_3::a);
printf("&base_3::b = %p\n", &base_3::b);
printf("&base_3::c = %p\n", &base_3::c);
base_3 t;
t.a = 1;
t.b = 2;
t.c = 3;
typedef int (base_3::*tip);
tip pm = NULL;
printf("base_3::a = %d\n", t.base_3::a);
printf("base_3::b = %d\n", t.base_3::b);
printf("base_3::c = %d\n", t.base_3::c);
pm = &base_3::a;
printf("base_3::a(%p) = %d(ptr)\n", pm, t.*pm);
pm = &base_3::b;
printf("base_3::b(%p) = %d(ptr)\n", pm, t.*pm);
pm = &base_3::c;
printf("base_3::c(%p) = %d(ptr)\n", pm, t.*pm);
return 0;
}
猜猜看这个代码输出什么? 答案是:
最近看了glibc的ptmaoolc,Goolge的tcmalloc和jemalloc,顺便做了一点记录。可能有些地方理解地不太对,如有发现还请大神指出。
今天心情好,刷了两到ACM水题,思路很简单都在注释里,所以直接贴代码:
/**
* @file 龟兔赛跑.cpp
* @brief 龟兔赛跑 AC代码 (DP)
* DP方程式: [到第i的充电站的最短时间] = [到最后一个冲了电的充电站的最短时间] + [那个充电站到第i个充电站的时间]
*
* @link http://acm.hdu.edu.cn/showproblem.php?pid=2059
* @version 1.0
* @author OWenT
* @date 2013.07.15
*
* @history
*
*
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#include <numeric>
int pn[128];
double dp[128]; /** dp[i] 表示 到第i个充电站的最小时间(0为开始位置,n+1为终点) **/
double calc_charge_time(int dis, int v1, int v2, int c) {
if (dis <= c)
return 1.0 * dis / v1;
return 1.0 * c / v1 + 1.0 * (dis - c) / v2;
}
int main(int argc, char* argv[]) {
using namespace std;
double eps = std::numeric_limits<double>::epsilon();
int l, n, c, t, vr, v1, v2;
while(cin>> l) {
cin >> n>> c>> t>> vr>> v1>> v2;
pn[0] = 0; /** 0为起点 **/
for (int i = 1; i <= n; ++ i) {
cin>> pn[i];
}
pn[n + 1] = l; /** n+1为终点 **/
memset(dp, 0, sizeof(dp));
for(int i = 0; i <= n + 1; ++ i) {
dp[i] = calc_charge_time(pn[i] - pn[0], v1, v2, c);
}
for(int i = 1; i <= n + 1; ++ i) {
for(int j = 0; j < i; ++ j) {
double tc = calc_charge_time(pn[i] - pn[j], v1, v2, c) + t + dp[j];
dp[i] = std::min(tc, dp[i]);
}
}
double rt = 1.0 * l / vr, tt = dp[n + 1];
if (tt < rt)
puts("What a pity rabbit!");
else
puts("Good job,rabbit!");
}
return 0;
}
/**
* @file Zipper.cpp
* @brief Zipper AC代码 (DP)
* @link http://poj.org/problem?id=2192
* DP方程式: [A串消耗个数i][B串消耗个数j] = min{[A串消耗个数i - 1][B串消耗个数j]|[A串消耗个数i][B串消耗个数j + 1]}
* 以上分支选取条件是 A或B的新选用字符和C串新字符匹配
*
* @version 1.0
* @author OWenT
* @date 2013.07.15
*
* @history
*
*
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#include <numeric>
char strA[256], strB[256], strC[512];
int dp[256][256]; /** dp[i][j] = k 表示A消耗了i个字符,B消耗了j个字符,拼成的C串消耗了k个字符(其实就是i+j) **/
int main(int argc, char* argv[]) {
using namespace std;
int s, n;
scanf("%d", &n);
for( s = 0; s < n; ++ s) {
scanf("%s %s %s", strA, strB, strC);
int lenA = strlen(strA);
int lenB = strlen(strB);
int lenC = strlen(strC);
bool bFlag = false;
if ( lenC != lenA + lenB ) {
printf("Data set %d: no\n", s + 1);
continue;
}
memset(dp, 0, sizeof(dp));
if (strA[0] == strC[0])
dp[1][0] = 1;
if (strB[0] == strC[0])
dp[0][1] = 1;
for (int i = 0; i <= lenA; ++ i) {
for (int j = 0; j <= lenB; ++ j) {
if (0 == dp[i][j])
continue;
int ri = i + 1, rj = j + 1;
if (strA[i] == strC[dp[i][j]]) {
dp[ri][j] = dp[i][j] + 1;
if (ri + j == lenC)
bFlag = true;
}
if (strB[j] == strC[dp[i][j]]) {
dp[i][rj] = dp[i][j] + 1;
if (i + rj == lenC)
bFlag = true;
}
}
}
printf("Data set %d: %s\n", s + 1, bFlag? "yes": "no");
}
return 0;
}
最近看了点typescript的东西,加上以前看过的一点点Node.js,所以就想把他们系统地整理一下。
这玩意搞过Web开发的应该都知道吧,Javascript的语法我就不废话了,挺简单的。这里总结几个Javascript的核心机制部分吧。
慢慢一点一点看看Boost,这段时间就Asio库吧。 据说这货和libevent的效率差不多,但是Boost的平台兼容性,你懂得。还有它帮忙干掉了很多线程安全和线程分发的事情。
心情大好,给VPS升级了一下系统,然后自己配了LNMP安装脚本,用yum源安装的话更新比较方便点哈 这个过程挺麻烦啊,所以果断要记下来,以防以后要用到 如果是其他系统的话,几个配置路径和软件源地址还有yum指令替换掉,应该就可以了
最近研究了一下ARM的交叉编译环境搭建,太麻烦了必须作一下记录啊。 前两个方法比较简单一点,关键是淫家Google帮你弄好了大部分功能
使用Android NDK的第一步当然是下载Android NDK啦。 http://developer.android.com/tools/sdk/ndk/index.html 使用jni的话,还必须下载相应的Android SDK http://developer.android.com/sdk/index.html 下载完后可以使用 $ANDROID_SDK_ROOT/sdk/tools/android update sdk –no-ui 来更新SDK包 附注:ANDROID_SDK_ROOT指代Android SDK根目录,NDK_ROOT指代Android NDK根目录。下同。 为了方便可以把$ANDROID_SDK_ROOT/sdk/tools:$ANDROID_SDK_ROOT/sdk/platform-tools:$NDK_ROOT 加到环境变量PATH里去 另外,Android 如果要命令行编译,需要ant和ant扩展,需要安装 Android 依赖的32位库 大致上是 glibc.i686 libzip.i686 libzip-devel.i686 libstdc++.i686 ant ant-* jdk 其中 libc.i686 libzip.i686 libzip-devel.i686 libstdc++.i686 ant ant-* 可以用 yum install或apt-get install 安装 jdk最好是官网下一个rpm包安装 rpm -ivh *.rpm