C++ Code Generation with Elisp

尽管使用Emacs有些年头了,但是在读PHP Code Generation with Elisp之前,我还没意识到可以在Emacs里做代码生成这样的事情。

用了这么久Emacs,感觉唯一不如其它IDE的地方就是没有生成class的wizard。于是只好每次不厌其烦的在头文件里写完类定义,再到C文件里写成员函数的定义,每个字母都是手工敲进去的,最多也是复制粘贴。直到有一天,我对自己说:“够了,为什么不懒一点呢?”于是,下面就有了下面这段代码。

(defun make-cpp-function-definition (buffer class-name start end)
"generate c++ function definition and insert it into `buffer'"
(interactive "BAppend to buffer: nMClass name: nr")
(setq function-declaration (buffer-substring-no-properties start end))
(setq function-definition nil)
(defun iter (pos)
(if (string-match
"\(?:\(?:virtual\|inline\|static\)[ tn]*\)?\(?:\(\(?:const[ tn]*\)?[^ tn;* tn]*\([^;]+\)\);"
function-declaration
pos)
(progn
(setq return-type
(match-string 1 function-declaration))
(setq one-function-definition
(match-string 2 function-declaration))
(if (equal class-name "")
(setq one-function-declaration
(concat return-type "n" one-function-definition))
(setq one-function-declaration
(concat return-type "n"
class-name "::" one-function-definition)))
(setq function-definition
(concat function-definition
one-function-declaration "n{n}nn"))
(iter (match-end 0)))
'()))
(save-excursion
(iter 0)
(set-buffer (get-buffer-create buffer))
(setq pos (point))
(insert function-definition)
(indent-region pos (point)))
(if (one-window-p)
(split-window-vertically))
(switch-to-buffer-other-window buffer))

用法如下:

  1. 把这段代码复制到你的.emacs文件里,这样在每次Emacs启动时都会自动安装该段代码。
  2. 如果不想重新启动Emacs的话,可以手动安装该函数,方法是把光标放在最后一个右括号后面,然后C-x C-e安装,更方便的方法是使用C-M-x,因为这个命令只要求光标在要安装的函数体内就可以了。
  3. 随便打开一个buffer,写一个class,如:
    class A {
    public:
    void print () const;
    virtual int get_constant () const;
    };
  4. It’s show time now. 🙂
  5. 选中两个成员函数(mark the region in which two member functions are),然后运行M-x make-cpp-function-definition,该命令会问两个问题,第一个是自动生成的空成员函数体要放到哪个buffer里,你要给出一个buffer,如果这个buffer不存在,Emacs会创建一个。第二个问题是class的名字是什么,对于这个例子来说,你只要输入A。当然,如果我足够牛的话,可以让这个函数自动发现类名,可惜我现在还是个菜鸟。:-(
  6. 回答两个问题后,生成的代码会插入指定buffer的光标所在处。这就是说,如果这是一个已经存在的buffer,你应该在运行该函数前选好光标的位置。生成的代码是这个样子:
    void
    A::print () const
    {
    }

    int
    A::get_constant () const
    {
    }
  7. 最后,当前frame会垂直分成两个窗口,分别包含类和成员函数体。

这个函数有个bug,就是它无法正确处理构造函数和析构函数。当然,你仍然可以用它,然后稍稍修改一下生成的函数体,这样也比一个字母一个字母敲要好。另一个bug是关于缩进的,因为缩进是跟buffer的mode相关的,所以如果要插入函数体的buffer不是c++ mode,那需要手工修改一下缩进。当然,在Emacs里,这很容易。

注:由于屡次受到垃圾评论的骚扰,本贴评论被关闭,有事发邮件。

Install Perl Module from CPAN

我们的测试过程大部分已经自动化了,但还是需要在每次代码更新后手工提交。虽然我只负责其中一种测试,但对时刻关注是否有代码更新感到十分厌烦,有时候甚至会忘记为某次代码更新做测试。于是想着这个过程是否也能自动化。

我负责的测试是以web方式提供的,每次都是打开浏览器,填几项内容,然后提交,测试跑完后会用email的方式发出测试报告。因此,我的自动化要做这么几件事:

  1. 定期检查是否有代码更新。因为测试十分耗时,所以两次代码更新之间的间隔会比较长,所以我认为每一小时检查一次就足够了。
  2. 如果发现有代码更新(我们每次代码更新都会修改一个特定的文件,因此只要检查那一个文件就可以了),就更新所有代码,然后编译。
  3. 提交测试。

为了实现第三步的自动化,我决定使用LWP这个Perl Module。但是Cygwin本身并没有这个module,必须要自己装。下面是我安装这个module的方法。

  1. 首先配置CPAN Module,方法是在bash下面运行

    $ perl -MCPAN -eshell

    配置过程会询问一些问题,其中CPAN的镜像可以选择http://cpan.linuxforum.net/

  2. 接下来要更新CPAN Module,而不是直接装LWP。否则会遇到undefined subroutine &Digest::base::new之类的错误。方法是运行

    cpan> install Bundle::CPAN

    这一步是关键,切记切记。我开始不知道,还在网上搜索这个错误信息,发现有人说是Perl的问题,我就把整个Cygwin更新到1.5.19.4,结果啥用也没有,还把其它东西弄得一塌糊涂。

  3. 装好新的CPAN Module,先退出,然后再运行1里面的命令进入CPAN的shell,这时就可以装LWP了

    cpan> install Bundle::LWP

    安装Bundle::LWP意味着不仅安装LWP,而且安装它需要的其它module。