Tips 2013-02-17

  • 如果有需要把工程从Linux下面移植一份到Windows下,却又不熟悉Visual Studio的各种配置,那么用CMake来管理工程是一个不错的选择。从Makefile改写CMakeLists.txt并不麻烦,而且CMake可以自动生成VS的工程文件,很好用。
  • 用 floor 和 ceil 这类函数最好先对参数做显示的类型转换,否则VS会报错。
  • isinf 和 isnan 能不能则不用,VS没有现成好用的对应版本。
  • 关于把一份Git工程同步到SVN的版本库里去的方法,网上讨论的很多,比如这个: http://stackoverflow.com/questions/661018/pushing-an-existing-git-repository-to-svn 但是如果你的Git的工程已经有了悠久历史,那么在rebase那一步你可能会有非常多的conflicts要处理。一个小办法是每次出现了冲突都用这一行代码:
    $git checkout . --theirs && git add . && git rebase --continue

    当然前提是当前目录下面没有不在git管理下的其他文件。这个虽然不解决根本问题,但是会方便很多。

总是有想要重构自己实验工程的想法,看起来工程的框架还是不太好。我总觉得好的框架应该是很容易修改的,现在每次想要往工程里新加一组实验就觉得有些代码碍手碍脚。如果不想有重复代码函数粒度就太小,而且接口复杂不可读,时间一长就看不出它们都是干什么的了。否则就有大段的重复代码,十分难看。如果把实验操作部分放到Bash脚本里面,移植又是一个问题。很少看到讲这种不大的工程怎么去搭框架的问题,苦于自己技术水平不够,想想真是头疼…

[OpenCV]detectMultiScale

I met a problem when using the interface ‘detectMultiScale’ of OpenCV. The rectangles it gives out may not be fully inside the frame of the original image. As a result, if these rectangles are applied directly on the original image to crop out the detected objects, your programs crash.

These are the interfaces

virtual void detectMultiScale( const Mat& image,
                               CV_OUT vector& objects,
                               double scaleFactor=1.1,
                               int minNeighbors=3, int flags=0,
                               Size minSize=Size(),
                               Size maxSize=Size() );

and

virtual void detectMultiScale( const Mat& image,
                               CV_OUT vector& objects,
                               vector& rejectLevels,
                               vector& levelWeights,
                               double scaleFactor=1.1,
                               int minNeighbors=3, int flags=0,
                               Size minSize=Size(),
                               Size maxSize=Size(),
                               bool outputRejectLevels=false );

I am copying what I wrote for a pull request on Github, this ‘may-be issue’ can be fixed easily by modifying one line in the source file

modules/objdetect/src/cascadedetect.cpp

Replace this one

Size processingRectSize( scaledImageSize.width - originalWindowSize.width + 1, scaledImageSize.height - originalWindowSize.height + 1 );

with this line

Size processingRectSize( scaledImageSize.width - originalWindowSize.width , scaledImageSize.height - originalWindowSize.height);

My explanation goes here, ignore the line numbers if they look wrong to you.
“Actually, in the code, the workflow is more complicated. In the file cascadedetect.cpp

This is the line building the final detected rectangle

995 rectangles->push_back(Rect(cvRound(x*scalingFactor), cvRound(y*scalingFactor), winSize.width, winSize.height));

the winSize is assigned here

969 Size winSize(cvRound(classifier->data.origWinSize.width * scalingFactor), cvRound(classifier->data.origWinSize.height * scalingFactor));

while the maximum value of x and y can be find here, they are related to the processingRectSize.

971         int y1 = range.start * stripSize;
972         int y2 = min(range.end * stripSize, processingRectSize.height);
973         for( int y = y1; y < y2; y += yStep )
974         {
975             for( int x = 0; x < processingRectSize.width; x += yStep )

Say the original image size is O, the original window size is W, scaling factor is F. O and W is integer and F is a decimal usually larger than 1. The width and height are assumed to be the same for example.

If we calculate the right-most point of the detected rectangle, it should be:

the maximum x is: (cvRound(O/F) - W), current winSize is W*F, following the line 995 we get:

cvRound( (cvRound(O/F) - W) * F ) + W*F

This can be larger than O, say O is 600, F is 4.177250, W is 24, the number we can get above is 601.254 which is larger than 600."

Hope these help.

比较变量地址并不可靠

最近云风大牛又在黑我C++,可是在我学会之前,我还是要坚定不移地待在这贼船上。
嘿嘿 :]

“用比较地址的方法来判断两个变量(的引用)是不是同一个变量是不可靠的”,这个问题很简单,却也容易忽视。

现实的情况是这样的。在写Computer Vision实验的时候,因为程序要面临的计算量往往很大,对程序的性能的优化是十分重要的。
所以我就写出了这样的代码:

void foo_func(Foo& foo)
{
    //< 如果foo和上次传入的是同一个,就省掉一部分重复计算
    static Foo *p_foo = NULL;
    if (p_foo != &foo)
    {
        p_foo = &foo;
        //< 大量计算
    }

    //< 后续计算
}

在实验里,Foo这个类本身往往比较复杂,不可能做内容的逐一比较,同时这个类的实例在初始化之后内容就保持不变。写这个函数的时候没有觉得有什么问题,结果也是正确的。

但是,这么做显然是不可靠的。
这段代码就说明问题了:

#include 

int main(int argc, char **argv)
{
    using namespace std;
    for (int i = 0; i < 100; i++)
    {   
        int j;
        cout << "i: " << i << " &j: " << &j << endl;
    }   
    return 0;
}

在Mac下输出是这样的:

i: 0 &j: 0x7fff52decad0
i: 1 &j: 0x7fff52decad0
i: 2 &j: 0x7fff52decad0
i: 3 &j: 0x7fff52decad0
i: 4 &j: 0x7fff52decad0
i: 5 &j: 0x7fff52decad0
i: 6 &j: 0x7fff52decad0
i: 7 &j: 0x7fff52decad0
i: 8 &j: 0x7fff52decad0
i: 9 &j: 0x7fff52decad0
....

编译器在分配内存的时候,会去复用之前刚刚释放掉的空间,这个是很显然的道理。在上面这种循环里面,局部变量所在的那块空间正好一直被同一个变量复用,如果我每次给j初始化不同的值,它们的地址一致但却不是同一个变量,所以像这样比较变量地址并不可靠。

现在看来,一个可能的解决方法就是给Foo的每一个实例增加一个唯一的ID了。
有没有不用修改Foo就能达到快速比较两个Foo实例的方法呢?

Mac远程桌面到Linux服务器

假期结束,回到学校开始干活 :]

为了远程使用Linux服务器,折腾了一个下午。最终看来还是用vnc最简单了。

实验室有两台强劲的Linux服务器用来做研究。之前我一直都是用ssh登到服务器上去码代码,反应速度很快,感觉很不错。但是因为在做机器视觉,难免需要看远程的图片。命令行虽然快,总不能每次都把图片拷贝到本地再看,有时候需要可视化中间结果,ssh也行不通。

当本地机器是Linux系统的时候比较好办。可以用ssh加X forwarding的方法。在本地开一个X,然后把远程服务器的X指令通过ssh转发到本地的X,码代码没有什么延迟,感觉还是很不错的。

sudo X :11 vt11 2>&1 >/dev/null &

这样可以在本地新开一个X,Ubuntu下用Ctrl+Alt+F11可以切到第11个虚拟终端

回到之前的终端,开ssh和xterm

xterm -display :11 -e ssh -X server-host &

然后可以切换到第11个虚拟终端来使用远程Linux服务器上的X了。

这样虽然好,但是要求本地机器上有安装X。在Windows和MacOS下虽然有解决方法,但是比较麻烦。

用VNC的话就没有这个问题,毕竟VNC的客户端是很容易找的。

当然需要先ssh登录到Linux服务器上安装vncserver

sudo apt-get install vnc4server

然后启动vncserver

vncserver

这样就搞定了。

在本地的Mac下可以用自带的Screen Sharing App或者著名的Chicken of the VNC连接到server-host:5901来查看和控制远程Linux桌面。

在服务器上启动了vncserver之后,可以通过修改

~/.vnc/xstartup

这个文件,来指定远程的X启动之后要执行什么命令。我喜欢用openbox,所以我的xstartup文件就是这样子

#!/bin/sh

# Uncomment the following two lines for normal desktop:
# unset SESSION_MANAGER
# exec /etc/X11/xinit/xinitrc

[ -x /etc/vnc/xstartup ] && exec /etc/vnc/xstartup
[ -r $HOME/.Xresources ] && xrdb $HOME/.Xresources
xsetroot -solid grey
vncconfig -iconic &
openbox-session&
#x-terminal-emulator -geometry 80x24+10+10 -ls -title "$VNCDESKTOP Desktop" &
#x-window-manager &

另外,在启动vncserver的时候可以使用参数修改远程桌面的分辨率

vncserver  -geometry 1280x1024

这么一来就可以在命令行下写代码,同时再开一个vnc窗口看图,感觉不错!

这篇文章有列举一些远程到Linux服务器上的其它解决方法,如果有服务器的root用户权限的话,freeNX貌似也是一个不错的选项。

dup, pipe 和 fork

(好久没更新了,呼…一大波死线刚刚结束…)

我几乎一直在用Bash,可是却少有接触到Unix系的系统编程,对系统调用还是知之甚少。这两天实验室里讨论了一个比较基础的问题: 在自己写的程序中,怎么样得到另一个可执行文件的输出?

比如我们有/bin/pwd这个可执行文件,我们可以在自己的程序中用

system("/bin/pwd");

或者是

execl("/bin/pwd",".",NULL);

调用它。

如果想要得到它的输出,应该怎么做比较好?

这个其实是作业题的范畴,涉及到了几个Unix的系统调用:

     #include 

     pid_t
     fork(void);
     #include 

     int
     pipe(int fildes[2]);
     #include 

     int
     dup(int fildes);

     int
     dup2(int fildes, int fildes2);

手册里有各个函数的详细解释。
在这个例子里,pipe用来生成一对文件描述子 (file descriptor,我觉得描述子这个翻译不是很好…),往第二个描述子里写内容,从第一个里面读内容。fork用来得到一个子进程,这里,我们会让子进程来执行pwd,并且把输出写到pipe的一端,然后父进程可以从另一端读入。

但是pwd默认输出到屏幕,也就是标准输出,我们需要使用dup2,把标准输出指向pipe的一端,这样就可以完成任务了。

所以,最终的代码是这样的

#include 
#include 
#include 
#include 

int main(int argc, char **argv)
{
    int fd[2];
    int pid;
    pipe(fd);
    int rpipe = fd[0];
    int wpipe = fd[1];
    pid = fork();
    if (pid == 0)
    {   
        /* 子进程关掉读的那端,只用写的一端 */
        close(rpipe); 

        /* 把标准输入指向pipe的写的一端 */
        dup2(wpipe, STDOUT_FILENO);

        /* 执行pwd */
        execl("/bin/pwd",".",NULL);

        /* 嗯 */
        exit(0);
    }   
    else
    {   
        /* 父进程关掉写的那端,只用读的一端 */
        close(wpipe);

        printf("begin parent process\n");
        char readbuffer[1024];

        /* 从读的这端读出pwd的输出 */
        int nbytes = read(rpipe, readbuffer, sizeof(readbuffer));

        printf("Received string: %s | %d\n", readbuffer, nbytes);
        printf("end parent process\n");

        wait(&pid);
    }   
    return 0;
}

所以最后的输出就是像这样的

begin parent process
Received string: /Users/XXX/XXXX
 | 16
end parent process

话说fork除了考试之外,似乎就没有用过了,代码还是写得太少了…

(Image from Github)

rlwrap – 命令行下readline的封装

发现了rlwrap这个好东西。

在Mac OS和Linux下一直都用bash,bash下命令的输入都是通过readline这个库来处理的。也就是说,上下箭头查看历史命令,Ctrl+r反向查找匹配历史输入,以及Ctrl+w, Ctrl+a等等操作都是由readline提供的。rlwrap提供了readline的封装!

rlwrap runs the specified command, 
intercepting user input in order to provide readline's line editing, 
persistent history and completion.

也就是说rlwrap提供一个输入环境,在这个输入环境下可以使用readline的各种功能。如果一个程序在命令行下接受输入,那么用rlwrap直接就可以得到像在bash下输入那样的效果。

比如一个简单的反转输入行的程序

#include 
#include 
#include 

int main(int argc, char **argv)
{
    using namespace std;
    string str;
    cout << "[I] ";
    while (getline(cin,str))
    {   
        reverse(str.begin(), str.end());
        cout << "[O] " << str << endl;
        cout << "[I] ";
    }   
    return 1;
}

编译之后执行

$./a.out 
[I] a + b + c + d
[O] d + c + b + a
[I]

输入完一行,按上箭头是没有回滚到上一条命令这种效果的,更不用说行头行尾跳转了。

如果通过rlwrap输入

$rlwrap ./a.out 
[I] a + b + c + d
[O] d + c + b + a

再试试上下箭头,就和其他使用了readline的程序一致了!

对于一些没有提供readline操作的程序,rlwrap就是必备工具!
以后再写需要交互的bash脚本或者命令行程序就会非常轻松了 :]

Linux Swap文件

想象一下,两个实验进程跑了两天,还有一天就跑完了,这个时候你发现如果再跑一会儿内存就要爆了…怎么办? (好惊险的感觉 XD)

好吧,其实用到的只是很基本的操作系统知识,不过还真难得用到一回。

程序面对的都是虚拟内存。64位的操作系统下,虚拟内存非常大,但是实际物理内存相对而言小得多。所以,操作系统对内存分页 (就是分成一块一块的,每一块儿叫做一页) 物理内存一旦满了,把暂时不需要的页写到硬盘里。过了一会儿程序又要访问被写到硬盘里的那部分内容,操作系统就在物理内存中选一个页 (怎么选很讲究的),把硬盘里的那个给换回来。程序不停的运行,操作系统就换来换去…

所以,上面我们遇到的情形就可以解决了。把其中一个进程挂起 (suspend),Linux下可以用

Ctrl+Z

,然后这部分内存就是暂时不用的了。这个时候用

$top

查看内存使用情况,可以看到一个CPU占用率为0的进程占用的内存越来越少,另一个越来越多。这样就行了,等一个进程跑完,再用

$fg

命令把挂起的进程调到前台就可以了。

但是等等。虚拟内存具体是在哪里呢?数据终究是写在内存/硬盘上的,Linux下被换到硬盘上的内存在Swap分区 (交换分区) 里。安装系统的时候需要格式化一个分区为Swap格式,就是这个分区。

$swapon -s

可以查看交换分区的大小。

糟糕!刚才那个被挂起的进程占用了24G的内存,但是现在看到我的交换分区只用12G,怎么办?一旦交换分区和内存都满了,会发生神马事情,我也没有体验过,估计应该是系统卡死或者卡而不死吧。

所以,应该赶紧增加交换分区的大小才是。可是如果你和我一样,很悲催的没有Root权限 (Root权限貌似是必须的…),而且也根本没有多余的分区可以挂载了,怎么办?

可以用Swap文件 (点题) ! 就是把一个文件用做swap分区,Linux下什么都是文件,分区应该也是吧。要增加系统可用的虚拟内存,当然这要求你硬盘剩余空间够大…

$dd if=/dev/zero of=~/swapfile bs=1024 count=41943040

会在HOME下创建一个40G的文件”~/swapfile”,命令要执行一会儿,需要写一段时间硬盘,执行完了会显示写硬盘的速度,可以用来做测速的。
然后告诉系统用这个文件做交换文件

mkswap ~/swapfile

就没问题了。
(实际上不行,还需要这个命令, –!)

sudo swapon ~/swapfile

其实最一开始那个情形下,如果两个进程继续跑下去,操作系统仍然会把一部分内容换出来的。只是大量换页操作会让程序执行的时间更长,而且如果交换空间不够大,系统最终仍然可能会被卡死。

感叹一下,一个实验要跑三天的同学伤不起啊..

libstdc++ 4.6 type_traits 的一个bug

如果你的编译环境和我一样,然后又在用C++11的时候,不小心直接或者间接用到了<chrono>这个头文件,应该就会遇到这个bug。

完整的错误信息如下:

|| In file included from /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../../include/c++/4.6/thread:37:
/usr/include/c++/4.6/chrono|240 col 10| error: cannot cast from lvalue of type 'const long' to rvalue reference type 'rep' (aka 'long &&'); types are not compatible
||           : __r(static_cast(__rep)) { }
||                 ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|128 col 13| note: in instantiation of function template specialization 'std::chrono::duration >::duration' requested here
||             return _ToDur(static_cast(__d.count()));
||                    ^
/usr/include/c++/4.6/chrono|182 col 9| note: in instantiation of function template specialization 'std::chrono::__duration_cast_impl<std::chrono::duration >, std::ratio, long &&, true, true>::__cast >' requested here
||         return __dc::__cast(__d);
||                ^
/usr/include/c++/4.6/chrono|247 col 10| note: in instantiation of function template specialization 'std::chrono::duration_cast<std::chrono::duration >, long, std::ratio >' requested here
||           : __r(duration_cast(__d).count()) { }
||                 ^
/usr/include/c++/4.6/chrono|466 col 9| note: in instantiation of function template specialization 'std::chrono::duration >::duration, void>' requested here
||         return __ct(__lhs).count() < __ct(__rhs).count();
||                ^
/usr/include/c++/4.6/chrono|667 col 7| note: in instantiation of function template specialization 'std::chrono::operator<, long, std::ratio >' requested here
||                     < system_clock::duration::zero(),
||                     ^
/usr/include/c++/4.6/chrono|128 col 13| error: call to implicitly-deleted copy constructor of 'std::chrono::duration >'
||             return _ToDur(static_cast(__d.count()));
||                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|182 col 9| note: in instantiation of function template specialization 'std::chrono::__duration_cast_impl<std::chrono::duration >, std::ratio, long &&, true, true>::__cast >' requested here
||         return __dc::__cast(__d);
||                ^
/usr/include/c++/4.6/chrono|247 col 10| note: in instantiation of function template specialization 'std::chrono::duration_cast<std::chrono::duration >, long, std::ratio >' requested here
||           : __r(duration_cast(__d).count()) { }
||                 ^
/usr/include/c++/4.6/chrono|466 col 9| note: in instantiation of function template specialization 'std::chrono::duration >::duration, void>' requested here
||         return __ct(__lhs).count() < __ct(__rhs).count();
||                ^
/usr/include/c++/4.6/chrono|667 col 7| note: in instantiation of function template specialization 'std::chrono::operator<, long, std::ratio >' requested here
||                     < system_clock::duration::zero(),
||                     ^
/usr/include/c++/4.6/chrono|233 col 12| note: explicitly defaulted function was implicitly deleted here
||         constexpr duration(const duration&) = default;
||                   ^
/usr/include/c++/4.6/chrono|349 col 6| note: copy constructor of 'duration >' is implicitly deleted because field '__r' is of rvalue reference type 'rep' (aka 'long &&')
||         rep __r;
||             ^
/usr/include/c++/4.6/chrono|182 col 9| error: call to implicitly-deleted copy constructor of 'typename enable_if<__is_duration<duration > >::value, duration > >::type' (aka 'std::chrono::duration >')
||         return __dc::__cast(__d);
||                ^~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|247 col 10| note: in instantiation of function template specialization 'std::chrono::duration_cast<std::chrono::duration >, long, std::ratio >' requested here
||           : __r(duration_cast(__d).count()) { }
||                 ^
/usr/include/c++/4.6/chrono|466 col 9| note: in instantiation of function template specialization 'std::chrono::duration >::duration, void>' requested here
||         return __ct(__lhs).count() < __ct(__rhs).count();
||                ^
/usr/include/c++/4.6/chrono|667 col 7| note: in instantiation of function template specialization 'std::chrono::operator<, long, std::ratio >' requested here
||                     < system_clock::duration::zero(),
||                     ^
/usr/include/c++/4.6/chrono|233 col 12| note: explicitly defaulted function was implicitly deleted here
||         constexpr duration(const duration&) = default;
||                   ^
/usr/include/c++/4.6/chrono|349 col 6| note: copy constructor of 'duration >' is implicitly deleted because field '__r' is of rvalue reference type 'rep' (aka 'long &&')
||         rep __r;
||             ^
/usr/include/c++/4.6/chrono|255 col 11| error: rvalue reference to type 'long' cannot bind to lvalue of type 'long'
||         { return __r; }
||                  ^~~
/usr/include/c++/4.6/chrono|247 col 39| note: in instantiation of member function 'std::chrono::duration >::count' requested here
||           : __r(duration_cast(__d).count()) { }
||                                              ^
/usr/include/c++/4.6/chrono|466 col 9| note: in instantiation of function template specialization 'std::chrono::duration >::duration, void>' requested here
||         return __ct(__lhs).count() < __ct(__rhs).count();
||                ^
/usr/include/c++/4.6/chrono|667 col 7| note: in instantiation of function template specialization 'std::chrono::operator<, long, std::ratio >' requested here
||                     < system_clock::duration::zero(),
||                     ^
/usr/include/c++/4.6/chrono|666 col 21| error: static_assert expression is not an integral constant expression
||       static_assert(system_clock::duration::min()
||                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|666 col 21| note: undefined function 'operator<, long, std::ratio >' cannot be used in a constant expression
/usr/include/c++/4.6/chrono|460 col 7| note: declared here
||       operator& __lhs,
||       ^
/usr/include/c++/4.6/chrono|141 col 40| error: cannot cast from lvalue of type 'const intmax_t' (aka 'const long') to rvalue reference type 'long &&'; types are not compatible
||               static_cast(__d.count()) / static_cast(_CF::den)));
||                                               ^~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|182 col 9| note: in instantiation of function template specialization 'std::chrono::__duration_cast_impl<std::chrono::duration >, std::ratio, long &&, true, false>::__cast >' requested here
||         return __dc::__cast(__d);
||                ^
/usr/include/c++/4.6/chrono|679 col 21| note: in instantiation of function template specialization 'std::chrono::duration_cast<std::chrono::duration >, long, std::ratio >' requested here
||         return std::time_t(duration_cast
||                            ^
/usr/include/c++/4.6/chrono|154 col 40| error: cannot cast from lvalue of type 'const intmax_t' (aka 'const long') to rvalue reference type 'long &&'; types are not compatible
||               static_cast(__d.count()) * static_cast(_CF::num)));
||                                               ^~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|182 col 9| note: in instantiation of function template specialization 'std::chrono::__duration_cast_impl<std::chrono::duration >, std::ratio, long &&, false, true>::__cast >' requested here
||         return __dc::__cast(__d);
||                ^
/usr/include/c++/4.6/chrono|577 col 22| note: in instantiation of function template specialization 'std::chrono::duration_cast<std::chrono::duration >, long, std::ratio >' requested here
||         return __time_point(duration_cast(__t.time_since_epoch()));
||                             ^
/usr/include/c++/4.6/chrono|687 col 9| note: in instantiation of function template specialization 'std::chrono::time_point_cast<std::chrono::duration >, std::chrono::system_clock, std::chrono::duration > >' requested here
||         return time_point_cast
||                ^
|| 7 errors generated.

Google搜到了这个帖子 http://urfoex.blogspot.com/2012/06/c11-fixing-bug-to-use-chrono-with-clang.html
和我的环境略有不同,但是出错的地方是一样的。

我自己的环境是

Ubuntu 12.04, clang 3.1, libstdc++ 4.6

解决方法是改

/usr/include/c++/4.6/type_traits

这个文件的第1113行

{ typedef decltype(true ? declval<_Tp>() : declval<_Up>()) type; };

改成

{ typedef typename decay() : declval<_Up>())>::type type; }

出错的原因,大概是因为没有正确的做类型转换。decay这个函数我从没用过,看文档是C++11引入的函数,用做类型转换。对此不理解,不能在这里误导人,文档原文如下

Applies lvalue-to-rvalue, array-to-pointer, and function-to-pointer implicit conversions to the type T, removes cv-qualifiers, and defines the resulting type as the member typedef type. This is the type conversion applied to all function arguments when passed by value.

总之,把那行改掉再重新编译自己的工程就没有问题了。

如果你是和我一样没有root权限去改/usr/include下的文件,也可以这么做:
1. 把/usr/include/c++/4.6/type_traits拷贝到自己的目录下面,比如

$cp /usr/include/c++/4.6/type_traits $HOME/local/include/c++/4.6

2. 改自己目录下的版本,然后编译的时候把这个目录放到编译选项里,clang会优先使用这个目录下的版本

$clang++ blahblah.cpp -I$HOME/local/include/c++/4.6/

好吧,虽然没有完全搞清楚这个bug是怎么回事,但是解决掉一大堆报错的感觉挺好 :]

git submodules

刚刚知道的一个用法,记录一下。

git可以把另一个git工程作为子项目(submodule),如果项目很大的话可以把一些子项目独立出来,单独作为一些个git项目,最后再作为库提供给主工程使用。遵照这个做法正好也能培养个好习惯。

几个命令:

$git submodule add git://github.com/chneukirchen/rack.git rack

submodules由.gitmodules文件管理。内容如此:

[submodule "rack"]
      path = rack
      url = git://github.com/chneukirchen/rack.git

如果你clone了一个有submodules的git工程,需要额外的操作来clone其中的submodules.

$ git submodule init
$ git submodule update

知道这么几个命令就可以试试了。

参考自:6.6 Git Tools – Submodules

Linux Tips (1)

这里记录一些小技巧,比较杂。

1. bash下,x{a,b}会被展开为xa xb,很适合文件备份。
如果你要复制一份a.txt作为备份,到a.txt的路径又太长了,这么写比较方便。

cp /a/long/long/long/path/to/file/a.txt{,bak}

2. vim下,要把某些内容替换成为行号,可以用\=line(“.”)来处理。“.”用来连接行号和其它内容。

:%s/xxxx/\=line(".") . " "/g

3. bash脚本下,如果要写多行到文件里,可以用

cat > file << EOF
file content line 1
file content line 2
file content line 3
file content line 4
EOF

这是一种固定写法,从第二行起,遇到EOF就停止,把之前的内容写到file里。EOF也可以换成别的。

4. Trap SIGINT的时候记得要处理SIGINT信号。

#! /bin/bash

trap "echo 'hello'; exit 1" SIGINT SIGTERM

while [ 1=1 ];
do
    sleep 1
done

exit 1这部分是必须的,否则虽然按下Ctrl + C的时候会显示hello,但是脚本不会被终止。