TimothyQiu's Blog

keep it simple stupid

std::string comparison

分类:技术

众所周知,char 类型是独立于 unsigned charsigned char 之外的一种实现相关类型,这着实带来了不少麻烦。

std::string a = "Hello";
std::string b = "世界";  // UTF-8 或者 GBK 皆可

那么请问,a[0] < b[0] 是什么结果?OK,那么 a < b 呢?

当然,貌似在开篇头一句就已经被我剧透的情况下作出错误的回答还是蛮困难的。没错,无论是 GBK 还是 UTF-8 存储,答案都应该是:

前者很好理解,因为实质是两个 char 在比较,所以谁大谁小还得看 char 到底是无符号的还是有符号的。而后者为什么肯定是 true 呢?

尽管 std::stringstd::basic_string 在字符类型为 char 时的特化,但 C++ 标准中明确规定:

The two-argument members eq and lt shall be defined identically to the built-in operators == and < for type unsigned char.

也算是 C++ 造福人类的某种方式吧。

以上。

Cocos2d-x 笔记:人类一大步

分类:技术

上一次看 cocos2d-x 是去年的事情了,下了个 SDK 而后按照官方的那个 pew-pew-pew 教程接二连三捣鼓了几下,之后几乎就没怎么去碰了。最近再看感觉连当时自己写的代码都快不认识了,果然还是需要记下笔记 = =

获取 SDK

前往 cocos2d-x 官网下载稳定版打包,或者去官方 GitHub 仓库 git clone 一份(注意看清分支哟)。

个人这回是下载了最新稳定版本,目前是 2.1.4。

阅读剩余部分...

Bad Apple in Windows Task Manager

分类:技术

这几天 Bad Apple 又逆袭了,比如这个这个。虽然周末时候自己也做了一个,但介于身心憔悴和一个遗留问题,暂时先不和上次一样出视频了……

首先,看到那两个演示视频就可以联想到《编程之美》里面的一道面试题,但显然完全不是一回事,因为图像的 x → yy 并不唯一。既然如此,那么就可以推定这是某种程度的「造假」,因为图像已经不是 CPU 占用率曲线了。

既然造假,我们就要造得有良心!那种类似直接在任务管理器上新建/覆盖一个视频窗口的做法略无节操了一些。作为一个死程,我们还是慢慢用程序解决吧……

视频预处理

这个就不用写程序了,看过我之前视频的童鞋一定知道我要用 ffmpeg 来把视频转成位图序列,没错,这次还是它!

而 ffmpeg 同时还提供了非常好用的滤镜支持,要把视频变成 CPU 占用率曲线的样子,我们需要边缘检测(edgedetect)和颜色通道混合器(colorchannelmixer)。

ffmpeg -i <文件名> -r <FPS> -vcodec bmp -vf edgedetect,colorchannelmixer=0:0:0:0:1:1:1:0:0:0:0:0 <输出文件名>

这样就能直接输出黑底绿线的位图序列,以备使用了。

屏蔽掉 CPU 占用率曲线的绘制

这里只考虑 Win 7 及之前的任务管理器,因为这货很好改。首先,这曲线一看就是 LineTo() 函数画出来的,那么我们用 OllyDbg 直接查看任务管理器中所有对于 LineTo() 的调用就可以找到对应代码。

我是 Win 7 的系统,用 OllyDbg 直接打开后,对 LineTo() 的调用只有:

这几处,熟悉汇编的都可以很快定位整个语句的范围。至此,我们得到了一个地址及一个长度,把这个范围内的指令全部变成 Nop 指令即可。

当然 OllyDbg 中反汇编的地址是不能直接用的,还需要减去当前模块的基地址,得到偏移量以备使用……

那么来到 C++,FindWindowGetWindowThreadProcessIdOpenProcess 即可获得任务管理器的进程句柄。

得到进程句柄后,我们首先要获得 taskmgr.exe 模块当前的基地址:EnumProcessModulesGetModuleInformation。而后,就可以用上之前得到的偏移量和长度,使用 WriteProcessMemory 把绘制 CPU 曲线的代码覆写为一串 Nop 指令。

世界清静了。

绘制动画

这里就没有什么技术含量了。无非就是自己 SetTimer 开一个定时器,按照一定的帧率往窗口上 TransparentBlt 以前处理好的图片。

需要注意的是,背景网格,这货最麻烦了。我目前没有用 Hook,所以暂时的做法是:自己维护一份干净的背景网格。这样做的缺点是,网格更新时有一定几率察觉到曲线的缺失(用 Hook 后应该会好:Hook 后替换掉 Window Proc,然后正确的 WM_DRAWITEM/WM_PAINT 之后立即把当前的图片绘制上去)。

以上。

将父类成员函数提升到子类

分类:技术

今天在看 C++11 的 Inherited Constructors 特性时发现了一个以前不知道的传统 C++ 奇技淫巧。

class Base {
public:
    void foo(float a);
};

class Derived: public Base {
public:
    void foo(int a);
};

这样的代码,显然 Derived::foo() 会把 Base::foo() 覆盖。

今天得知,using 居然还可以把父类的成员「提升」到子类中:

class Derived: public Base {
public:
    using Base::foo;    // 看这里看这里看这里
    void foo(int a);
};

如此,就相当于在 Derived 中添加了一个和 Base::foo() 一模一样的成员。(当然,从字面上也是很顾名思义的嘛。)

Derived d;
d.foo(3.14f);  // 这样调用的就是 Derived::foo(float a) 了

之前我曾想,两个完全符合「is-a」关系的类,做成聚合显然是不甘心的。但如果父类接口有 N 个,子类只是想添加 1 个接口,然后将父类中的 M 个(M < N)接口暴露出来该怎么办?想想 private 继承,然后自己在子类中写 M 个 wrapper 似乎是个可行的方法,但如果 M 很大似乎依旧不甘心。现在知道了这个技巧似乎好解决了很多呢。(不过,整个类的声明也会随之变得脑残起来。)

顺带一提,传统 C++ 中的这个方法是无法提升构造函数的,而 C++11 中则加入了允许继承(提升)构造函数的特性(当然,目前貌似还没有编译器支持 = =)。

以上。

Vim 中使用 clang complete 为 C/C++ 自动补全

分类:技术

前文再续,书接上回。上回咱们讲到,如何在 Vim 中使用 OmniCppComplete 为 C/C++ 自动补全;今天偶然间发现了个新玩意儿:clang complete。

前情提要

OmniComplete 是 Vim 中的智能补全功能,而 OmniComplete 本身并不知道如何补全,具体的「通过光标前的内容猜测光标后可能出现的内容」的工作是由不同的外部插件实现的。

上回说到的 OmniCppComplete 就是这样一个插件。实际需要预先调用 ctags 对源代码进行词法分析(吧?)生成 tags 文件(token 列表),然后在这个 tags 文件中去进行匹配。所以局限也很快暴露出来:无论是库的头文件还是自己的源代码,都要用户自己事先对它运行一遍 ctags。

clang complete 则是借助 clang 来分析源代码,毕竟没有比编译器更了解代码的东西了。

阅读剩余部分...