GCC Internal Programming Tips

It is inevitable to understand the APIs provided by GCC while modifying or adding new features to it. GCC Internal Manual is a very good reference, but it misses some very important APIs. So I write this GCC Internal Programming Tips according to my understanding of GCC source code and experience, and hope it can serve as a complement to the manual. The article is written in latex and under Creative Commons Attribution 3.0 Unported License, so you can do anything to it as long as fulfilling the license. I have put the pdf file and the source file on github.com, the webpage is http://github.com/hopecream/GCC-Internal-Programming-Tips. Thus you can either check out a readonly version or folk a writable version as you like. Note that the repository supports svn access as well.

卡套

五一和小马出去腐败完后,顺便逛了逛街,无聊买了个卡套十字绣回来绣啊绣啊绣。终于在今天晚上完工了,进度那叫一个的慢啊~~

比较郁闷的是,买回来绣了一会,就发现这个卡套的线很有问题,有些颜色应该有很多线的竟然不够,而某些颜色的线竟然很多,无奈之下就自己搭配了下。绣出来的卡套真有点丑啊~

曹说我不像在绣东西,像缝衣服的大妈=。=

发下图片

Git杂货铺之(一)

这几天用git(一种版本管理软件)遇到了几个关于迁移的问题,和大家共同学习。

我在一台电脑(称它为A)上有一个git的本地仓库(众所周知,用git init就可以轻松创建了),但是我并没有将这个仓库在某个服务器上托管,因为里面有一些敏感信息我不想公开。某天我想在另一台电脑(称它为B)上继续使用这个仓库,于是就要将仓库的数据拉过来。通常情况下,我需要在A上开启一个git的服务器端服务,然后再在B上获取A中仓库里的数据,网上讲了很多,貌似可以用什么git daemon之类的。我试了一下,没有成功,我也懒得折腾,就没有继续了。后来,我突然想到git是支持SSH协议的啊,能不能直接用SSH协议就可以同步数据了呢,答案是可以。下面就讲讲怎么做。

首先要在A上开启SSH Server服务。
假设A的仓库放在/home/AA/repo,我要在B的/home/BB/repo目录下同步A的仓库的数据。于是在B的/home/BB/repo目录下先用git init初始化git仓库,然后这个目录下执行git clone ssh://‘A中的用户名‘@‘A的IP地址/home/AA/repo’,这样就可以在B的/home/BB/repo目录下同步A的仓库里的数据了。

但是这些还没有完,假如我们在B中修改了仓库里的数据,然后提交到A的话,可能会被拒绝,原因是A并不是一个git的服务器端,在B中的数据被修改的同时,A中的数据也可能被修改,所以为了避免不必要的麻烦,一般直接提交是会被拒绝的。但是如果还是想强行提交怎么办呢?先在A的/home/AA/repo目录里执行git config receive.denyCurrentBranch warn,这里最后一个值默认可能是reject,然后我们要填warn或者ignore就可以了。当B提交完数据后,还要在A的仓库的目录上执行git reset –hard,然后才能在A上看到数据的改动。

总的来说,自己用的话,用这个方法还好,但是如果有两个人了的话,都最好还是专门架设一个git server的服务器端,比较稳妥。

再说另外一个问题。这个问题是我现在要把我在某网站里用git托管的数据,迁移到另一个只有svn的服务器里,而且我不能去操作那两台服务器。网上一般都是讲如何从svn迁移数据到git,但是很少讲怎样从git迁移到svn。当然我这里的迁移指的是在进来保留原有的日志等信息前提下的迁移。最后我在一个英文网站上找到了答案,网址是http://markpasc.livejournal.com/186297.html,我简化一下给大家介绍介绍。

先假设一下,用git托管的数据在本地的/home/AA/repo目录下,然后svn服务器下的svn仓库的地址是http://some.svn.server.com/BB/repo。

1. 首先在本地建一个临时目录,假设为/tmp/import,然后用git初始化这个目录,即cd /tmp/import; git init
2. 在/tmp/import目录下执行git svn clone -T ‘ ‘ http://some.svn.server.com/BB/repo。
3. 执行git remote add dev file:///home/AA/repo,指定当前仓库的数据来源
4. 如果是要把这些数据放到svn仓库的某个子目录中,就先进入那个目录。否则直接执行git pull dev master。这里要注意的是,最好把要放到svn托管的数据放到master分支。
5. 执行git pull rebase
6. 执行git svn dcommit

如果没有遇到任何冲突的话,这样就可以实现从git到svn的数据迁移了。如果很不幸遇到了冲突,其实也不麻烦,怎么做去看看那个网站吧,我这里就不讲了。

总结一下,git真的相当好用,相当方便,各位还在使用svn的同志们啊,别再固守svn了,试试git吧,试了以后你们也会喜欢上它的。

人在网上走,哪有不被抓

我明明在block spider里设置了Google的spider和Baiduspider,可是为什么最近还是被Google访问了一次,Baidu访问了3次呢。而且现在在两个搜索引擎里都可以搜到这里了。是不是哪里设置的不对,例如spider的名字,大家指点指点啊。

初识GCC Plugin(二)

如果RSS导入的格式有问题,请查看原文。

这回介绍一下怎么写一个GCC Plugin。上次提到了用gcc -print-file-name=plugin检测gcc是否支持plugin,如果支持的话,则相应的开发需要的头文件就在返回目录的/include下。其中最重要的一个就是gcc-plugin.h,plugin所用到的一些结构声明和注册函数等包含在这个头文件里。

在plugin文件中,我们必须声明一个int型的名字为plugin_is_GPL_compatible的全局变量,用来表示这个插件是在GPL或在于GPL兼容的协议下开发的。否则在编译插件的时候,就会报错。

GCC在使用插件的时候会调用plugin_init()这个函数,用来注册plugin,因此plugin_init()函数也就是所有plugin起始的函数,不懂的话,可以理解结为main()函数。这个函数的原型如下:

int
plugin_init (struct plugin_name_args *plugin_info,
             struct plugin_gcc_version *version);

第一个参数比较有用,这个结构体里包含的信息有plugin的名字以及在编译时,用户传给这个插件的参数。第二个参数就是一些描述gcc版本等信息的结构,我个人认为用处不大。

gcc调用plugin_init()时,还未开始正式的编译工作,所以这里主要进行一下初始化的工作就可以了,当然最重要的就是注册callback函数。注册callback函数需要使用register_callback()函数,原型如下:

extern void
register_callback (const char *plugin_name,
                   int event,
                   plugin_callback_func callback,
                   void *user_data);

这个函数的意义就是当发生event事件的时候,执行plugin_name这个插件中的callback函数,并且用户可以在注册时传入自定义数据user_data,并且在时间发生时使用这些数据,不过plugin_name貌似传什么都没关系。event可选择的值是在如下的枚举结构中定义的。

enum plugin_event
{
  PLUGIN_PASS_MANAGER_SETUP,
  PLUGIN_FINISH_TYPE,
  PLUGIN_FINISH_UNIT,
  PLUGIN_PRE_GENERICIZE,
  ... ...
  PLUGIN_NEW_PASS,
  PLUGIN_EVENT_FIRST_DYNAMIC
};

除了最后一个是一个Dummy Event以外,其他都是可以使用的事件类型,具体可以在GCC Internal Manual中或者头文件中自己查。我之后会选PLUGIN_PASS_MANAGER_SETUP,和PLUGIN_PRE_GENERICIZE介绍。

callback函数,可以看到它的类型是plugin_callback_func,这个类型的声明如下:

typedef void (*plugin_callback_func)(void *gcc_data, void *user_data);

其中gcc_data是指在GCC可能利用这个指针,给callback函数传一些数据,具体传什么是由具体的事件决定的,而user_data一般就是我们在注册的时候传入的那个数据。没用用过函数指针的话,看到上面的声明,也不用害怕,在使用的时候,声明一个如下的函数就可以做为callback函数了,我结合一个例子一起说。

static void
my_callback_func (void *gcc_data, void *user_data)
{
   ... ...
}

int
plugin_init (struct plugin_name_args *plugin_info,
             struct plugin_gcc_version *version)
{
  ... ...
  register_callback(plugin_info->fullname, PLUGIN_PRE_GENERICIZE,
                    my_plugin_callback, NULL);
  ... ...
  return 0;
}

这样,当GCC触发PLUGIN_PRE_GENERICIZE事件的时候,就会调用我们的my_plugin_callback()函数了。

下面说说PLUGIN_PRE_GENERICIZE和PLUGIN_PASS_MANAGER_SETUP事件。PLUGIN_PRE_GENERICIZE事件是每当GCC分析完一个函数获得抽象语法树(AST)后,就会被触发,同时将这个函数的AST的根节点作为gcc_data返回给callback函数。

PLUGIN_PASS_MANAGER_SETUP事件是用来在GCC的GIMPLE PASSES中或者在RTL PASSES中插入或替换一个PASS用的,至于什么是GIMPLE, 什么是PASS,以后有时间再讲,这个内容已经超过PLUGIN本身了,不过了解这个确实很有用,我这里就讲讲怎么注册PLUGIN_PASS_MANAGER_SETUP事件。

注册PLUGIN_PASS_MANAGER_SETUP事件呢需要用到register_pass_info, opt_pass这两个结构体,以及pass_positioning_ops这个枚举类型,它们的声明如下:

struct opt_pass
{
  /* Optimization pass type.  */
  enum opt_pass_type type;

  /* Terse name of the pass used as a fragment of the dump file
     name.  If the name starts with a star, no dump happens. */
  const char *name;

  /* If non-null, this pass and all sub-passes are executed only if
     the function returns true.  */
  bool (*gate) (void);

  /* This is the code to run.  If null, then there should be sub-passes
     otherwise this pass does nothing.  The return value contains
     TODOs to execute in addition to those in TODO_flags_finish.   */
  unsigned int (*execute) (void);

  /* A list of sub-passes to run, dependent on gate predicate.  */
  struct opt_pass *sub;

  /* Next in the list of passes to run, independent of gate predicate.  */
  struct opt_pass *next;

  /* Static pass number, used as a fragment of the dump file name.  */
  int static_pass_number;

  /* The timevar id associated with this pass.  */
  /* ??? Ideally would be dynamically assigned.  */
  timevar_id_t tv_id;

  /* Sets of properties input and output from this pass.  */
  unsigned int properties_required;
  unsigned int properties_provided;
  unsigned int properties_destroyed;

  /* Flags indicating common sets things to do before and after.  */
  unsigned int todo_flags_start;
  unsigned int todo_flags_finish;
};

enum pass_positioning_ops
{
  PASS_POS_INSERT_AFTER,  /* Insert after the reference pass.  */
  PASS_POS_INSERT_BEFORE, /* Insert before the reference pass.  */
  PASS_POS_REPLACE        /* Replace the reference pass.  */
};

struct register_pass_info
{
  struct opt_pass *pass;            /* New pass to register.  */
  const char *reference_pass_name;  /* Name of the reference pass for hooking
                                       up the new pass.  */
  int ref_pass_instance_number;     /* Insert the pass at the specified
                                       instance number of the reference pass.
                                       Do it for every instance if it is 0.  */
  enum pass_positioning_ops pos_op; /* how to insert the new pass.  */
};

opt_pass这个结构体就是用来描述要插入或者替换其他pass的pass的一些基本信息: type用来描述pass的类型,一共有4种,包括GIMPLE_PASS, RTL_PASS, SIMPLE_IPA_PASS, 和IPA_PASS。我个人目前使用过GIMPLE_PASS这种类型。name就是这种pass的名字,用来在GCC使用-fdump-tree-*开关[1]编译程序时,这个pass的dump文件的后缀,如果名字最前名是一个“*”号,在dump时就不会dump这个pass执行后的信息。gate和execute这两个函数指针一个用来判断是否需要执行这个pass,一个用来执行pass。properties_required参数可以填上PROP_any以使这个pass可以看到所有的gimple信息,而在cfg这个pass之后可以使用PROP_cfg来获得control flow graph的信息。当然,PROP_any和PROP_cfg可以使用“|”连接赋值给properties_required。 todo_flags_finish顾名思义就是在执行完这个pass以后需要执行什么额外的操作,可以用的值有很多,我用到的就是TODO_verify_stmts和TODO_dump_func,源码和手册里都没解释这两个参数,我猜,顾名思义吧,第一个就是执行完之后检查所有的语句是否合法,以及在使用-fdump-tree-*时dump这个pass处理完后,程序的信息。这些值都可以用或”|”连接后赋给todo_flags_finish。其他的参数,我基本都没有用到,一般不是填NULL就是填0,就可以了,大家如有需要,就看看源码里的注释,也许会能找到解释。

register_pass_info这个结构体有四个参数:pass就是上面说的pass结构体实例的地址;reference_pass_name和pos_op用来说明这个新的pass是放在叫reference_pass_name这个pass的前面一个处理呢,还是后面一个处理呢,还是替换它,而决定操作方法的就是pass_positioning_ops这个枚举类型,也就是pos_op变量了;ref_pass_instance_number我一般填0,具体的解释是大家可以看源码注释,我虽然能看懂字面意思,不过没试过其他值,不是很理解。

大家可能说,我怎么知道有哪些pass,他们的名字又是什么呢?这个只有在源码里找了,在passes.c文件中,大家搜NEXT字段,注意是大写。然后就可以搜到NEXT_PASS这个宏,这个就是gcc内部注册各种pass的地方,而它的参数就是上面说的opt_pass结构体及其变型了,当然也就可以找到各个pass的名字。

最后,在注册的时候,在callback函数处填0或NULL,而在user_data处把register_pass_info的实例的地址填上就可以了。可能会有人问,没有callback函数我们怎么处理事件呢?还记得在opt_pass结构里的gate和execute函数指针吗?

给一个例子如下:

static unsigned int
my_handler(void);

struct gimple_opt_pass my_gimple_pass =
{
    {
        GIMPLE_PASS,
        "my_gimple_pass_name",
        NULL,
        my_handler,
        NULL,
        NULL,
        0,
        0,
        PROP_gimple_any | PROP_cfg,
        0,
        0,
        0,
        TODO_verify_stmts | TODO_dump_func
    }
};

int
plugin_init(struct plugin_name_args *plugin_info,
            struct plugin_gcc_version *version)
{
    const char *plugin_name = plugin_info->full_name;
    struct register_pass_info rp_info;

    rp_info.pass = &my_gimple_pass.pass;
    rp_info.pos_op = PASS_POS_INSERT_AFTER;
    rp_info.ref_pass_instance_number = 0;
    rp_info.reference_pass_name = "cfg";

    register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, 0, &rp_info);

    return 0;
}

其中:

struct gimple_opt_pass
{
  struct opt_pass pass;
};

这样就可以在cfg这个pass后插入我的my_gimple_pass,当执行到这个pass的时候,会调用my_handler()

最后,我想说GCC Internal Manual上并没有描述每个事件会返回什么样的gcc_data,我是在源代码里自己找到的。怎么找呢?gcc里,除了GIMPLE_PASS_MANAGE_SETUP外,都是使用invoke_plugin_callbacks()函数调用的callback,所以我们到gcc的源码的目录下,使用如下命令就可以找到所有的事件触发的地方,包括会返回什么参数等。

find ./ -maxdepth 1 -regex ".*\.c" -exec grep -H -n "invoke_plugin_callbacks" {} \;

今天先说到这,以后的话,再说就说GCC里的东西,GCC PLUGIN差不多也就这些内容了。

注:
[1]-fdump-tree-*开关中的”*”只是一个占位符,大家可以按照需要,填入要dump的pass的名称,或者填入all,代表dump所以可以dump的信息,例如:
gcc -fdump-tree-all -o fibonacci fibonacci.c fibonacci_main.c

新加坡飞禽公园小记

上周末,也就是11号,去新加坡飞禽公园转了一圈,继续我的“先遣探路”工作。以前真的没有听说过单独为鸟类建立一个公园的,新加坡算是一个先行者。刚在wikipedia上查了一下,原来这个鸟园早在1971年就已经建成,现在里面有600种,超过8000只得鸟类,其中还包括了29种濒危鸟类。(照片见最后)

观鸟,据说要起得很早,因为那些鸟一般7点就起床开会去了。前几次,我都是不得不起得很早,说实话,可能比我上班起得还早。不过幸好,鸟园8点半才开门迎客,我大概7点起床,赶到那边时间刚好。换了两次地铁,然后又换上公交,目的地总算就快到了。我在车上四处张望,看能不能看到一张盖在天上的大网,真的很好奇鸟园到底是什么样的。直到司机提醒到了,我才知道到了鸟园了,不过却还始终没看到想象中的天网。

下了车,找了半天才找到鸟园的大门,不过很庆幸的是,看到门外有一个小池塘,水上就有黑天鹅和小鹅们在四处游水,没有任何围栏,那感觉是相当的说不出来。在岸边的水泥地上,还有一只黑天鹅在睡觉,我估计离它都没有一米,我拿着相机一顿狂拍,它鸟都没鸟我,我想这应该是个好兆头,说明这里的鸟不怕人。

整个公园最外圈是一个环路,基本所有的鸟舍都能到达,我本能地选择了从右边逆时针开始转。刚没走几步就看到了一些没见过的大鸟,貌似是非洲的吧,然后右手还有企鹅馆。我觉得企鹅馆里的设计真的很好,透过大玻璃,即可以看到水上也可看到水下的部分,看到企鹅跳下水,然后从身边游过的感觉真好玩。出了企鹅馆,一路走出去,天上都还没有看到网,只看到各种鸟类四处飞翔,心里不禁赞叹。一路看到了各种鹦鹉,火烈鸟,各种鹤等等。当看到一群鹤站在树枝的最高处的时候,我心里还吃了一惊,那么大的鹤,难道在树上筑巢?我真的不确定。然后就是水鸟,我原本以为是个小湖什么的,然后人站在围栏外面远远的看。我找了半天没找到,后来看到一个木制的通道,走过去才发现,这里就是观察水鸟的地方了。这个通道很特别的地方是,它的一面是一个玻璃墙,墙外是一个小湖,而这个湖是完全开放的,鸟能飞来,也能飞走。而透过玻璃,还是既能看到水上和水下的部分,在这里就可以观察水鸟了。我真的没有那么近的观察过野生的水鸟,各种鸭子,超近的观察距离,最近的鸭子还是离我不到一米,只可惜没有看到公园地图上画的白头硬尾鸭。后面还看到很多鸟,白天鹅,各种鹈鹕,一些不知道名字的鸟,等等。那时的心情到达了最高峰,直到走到了鸟园的最远点。那边有一个鹦鹉舍,从这里开始,心情开始持平,并一路低落。

到了鹦鹉舍,鸟园露出了动物园的本来面目,各种鹦鹉被分类关在笼子里,当一些鹦鹉看到我,就飞到笼子边上,用爪和嘴把自己固定在网上,看着我,看可怜啊。不过这一趟下来,我发现鹦鹉真的相当的吵,而且貌似也很臭,不爽。后来就是什么非洲鸟舍,这个就是用一大块天网盖在天上。这个算是比之前那种小笼子稍微好一点吧。据说每天中午12点,这里还会制造一场人造雷阵雨,好在我去的早,没有挨淋。里面的鸟也不少,不过我后来发现不用到处去找,守着投食点就可以把所有的鸟基本看全了。

继续往前走就是更多的笼子,天网。印象中也就鸵鸟类的那块是个围栏,总之心里有点压抑。在鸵鸟区还看到了传说中的世界上最危险的动物之一的食火鸟。不过当时那只鸟看上去一点都没有战斗力,就坐在那里,低着头,眼神露出憋屈难过的目光。之后还看到了一些奇特的鸟,什么天堂鸟啊,犀鸟啊,巨嘴鸟啊。还有一种叫十二线天堂鸟的,因为正在孵化小鸟,所以不能进笼子里看,在笼子外面也没有看到,很是可惜。之后还看到了鸬鹚啊,翠鸟啊之类的,最后还看到了最最期待的雪鸮,不过我那时心情已经比较低落了,而且又累又饿。最后花了大概三个小时吧,总算是逛完了,好久每锻炼,体力跟不上了啊。

总得来说吧,来鸟园认认鸟还是不错的,也确实值得来。不过平常都是看野外的鸟,现在看着那些关在鸟,我的心情还是很难受的。

最后来贴贴照片,精选了几张,我可没有用长焦哦,可以想象出我和鸟有多近吧。如果RSS不能输出照片,就来原文看,应该就可以看到。

我和HP笔记本

大一那年的寒假(08年),一月底,我去买本,脑残的我去买了台惠普的V3706。格掉烦人的vista,换上xp,结果因为bios版本的问题,换了n个驱动都不能阻止风扇嗡嗡响。到了将近五月份的时候,发现出了个新版本的bios,说是可以解决掉这些七七八八烦人的问题,于是我到惠普的中文官方网站上下了这个驱动,一刷,这下好玩了,刷出问题了,急得不行,找bolour求救,最后felix和sandy都来围观我的本本,sandy这人还顺带鄙视了下我和我的本本,这个没有同情心的人呐。最后找到惠普的金牌服务,惠普工程的面无表情地跟我说:中文官方网站(www.hp.com.cn)的最新版本的bios刷新程序是有问题的,以后驱动什么的都不要去那下。当时那叫一个的狂汗啊,重新下了个bios刷新程序刷新了bios就好了。接下来就没出过什么问题了,这个本本陪伴我完成各种各样的作业,陪伴我完成各种各样的实验,陪伴我玩过各种各样的游戏,满足我各种各样的娱乐需求。去年五月份我还特地去加了个2G内存条,某个时间还跑去金牌服务那清了下灰。原本以为不会再出现什么问题了,谁知接下来又开始悲剧了,11月份的时候,本本时不时“黄屏”,没办法就只好又去找惠普金牌服务,一听说“黄屏”,那惠普的工程师楞了一下,随后看了下本本的症状,那人很无语的说:这是白屏,可能是变压器坏了。我心里想,靠,都变成黄色了还白屏。由于那些天忙着做实验,就把本拿回来,等配件到了再拿过去换。结果那些天被接口大实验折腾的,没时间过去,最后tuphie帮送本本过去换配件外加取本本回来。拿回来后我发现屏幕下方貌似有一条亮线,当时心里咯噔一下,该不会给我换了个有问题的屏幕吧,但是还是没有很在意,过了两个礼拜,屏幕下方开始晃,紧接着屏幕上方也跟着晃,没办法,我又去找这个惠普金牌服务了,最后又折腾了四五天,把屏幕给换了,最后取机的时候我超级自习认真地验收,还自带了一个检测工具,总算搞定了。今年寒假回家,本本被老哥拿去玩,可怜的我的双飞燕无线鼠标被弄坏了,虽然是别人送我的,但是我心里还是肉疼得很啊!今年开学后我又去买了个鼠标,总算恢复了以往用本本的状态。但是这种状态也就持续了一个多月,周一傍晚,正要刷网页的时候,本本挂了,我看着就感觉是显卡挂了= =||送到惠普售后那去,工程师说是本本的主板挂了。我靠啊!我咋感觉是显卡挂了,难不成是惠普的这破低端机上的独显是焊死在主板上的吗?关键是还已经过保了!无良的惠普啊!个人认为惠普做得比较好的也就只能是它的金牌服务了,那些个大叔大婶们的态度让我火气消了不少。3个星期啊,我有3个星期没得本用了,最近就只能用用曹的电脑,蹭蹭大姐的本本,不过按照以往的经验,估计2个礼拜就能拿回来了吧。wayne最好了,以前一天或者隔一天给我打一个电话,现在一天给我打四五个电话,嘿嘿~他晚上见不到我,没心思对着电脑,早早回去睡觉也好啊~~那个比较可恶的snoopy同学,最近买了个X200,还在那向我炫耀,丝毫没有怜悯同情之心,也不晓得要慰问下我这被惠普整得千疮百孔的心灵,真是只无良的老狗啊~老妈说给我打钱买个新本,我想了一下,感觉比较奢侈,算了。老爸听说我的本本坏了,就打个电话慰问了一下我。本坏了?恩。送去修了?恩。要不要买新电脑?不买了。哦,奖学金发了?恩。哦,那我就不给你打钱了。= =

好啦,我和HP笔记本之间比较纠结的故事就先写到这了,希望以后不会再有比较纠结的事情了。以后HP的本本,倒贴我都不要。以后我有钱了,买个小白,买个小黑,然后找snoopy这个老家伙炫耀,哼~~~

小记嵌套评论

昨天突然发现blog所用模板竟然没有嵌套回复的功能,于是就想增加这个功能。到网上查了一下,有两种方法可以解决这个问题:第一、使用插件,网上有好多来着;第二、直接修改主题代码。 

使用插件的方法就不用赘述了。这里稍微记录下如何修改代码实现嵌套评论的功能: 

1、在当前主题的head.php文件中找到<?php wp_head() ?>,在此语句之前添加以下语句: 

<?php if ( is_singular() ) wp_enqueue_script( 'comment-reply' ); ?>

2、在comments.php中找到<input name=”submit”……/>,有些主题中可能会是<input id=”submit”……/>,在此语句后加入以下代码: 

<?php comment_id_fields(); ?> 

3、在comments.php中,找到<div id=”respond”>,在其后加上以下代码 

<div>
<small><?php cancel_comment_reply_link(); ?></small>
</div>

4、在comments.php中找到以下代码: 

<?php foreach ($comments as $comment) : ?>
.......
<?php endforeach; ?>

将此段代码替换为以下代码:

<?php if ( function_exists('wp_list_comments') ) : ?>
<?php wp_list_comments(); ?>
<?php else : ?>
<p><?php _e('No comments yet.'); ?></p>
<?php endif; ?>

更新文件后发现没有任何改变,删除了下cache,页面上的确就实现了评论的嵌套,但是,好丑啊!!!!!

重新找了个模板,换之,多方便,嘿嘿。

=====================华丽丽的分隔线======================================

最近上课上得越来越颓废。简单评论下我上的几门专选课。

人工智能是最有意思的一门课了,本人也超级喜欢,朱老师人虽然有点老,但是讲起课来倒蛮有意思的,时不时冒出几句雷人语句,比如说,“大家都有自己的偶像,有些人喜欢刘德华,有些人喜欢陈冠希”。

网络管理课是超级无趣的一门课,不过冲着张老师,我们都选了。这老师依旧那么可爱,并在我们面前展露了他的八卦以及他的超级损。某次课间聊天讲到保研,他指着我说:“她不保研,人家要去新加坡。”又是某次课间聊天,只有大姐和小葛去上课了,他问人都去哪了?大姐就解释我去拿奖学金证书,小马回家。他说:“奖学金?钱拿了不就行了?马XX?就是和她(指着小葛,小葛超级瘦)长得截然相反的那个吗?”= =

多媒体技术上得挺平淡的,就记得老师拼命问“大家听懂没”

网络安全上得也挺平淡的。老师的孩子是一对可爱的龙凤胎,次次课都拿出来赤裸裸地炫耀,真是的。我以后也想有对龙凤胎,哈哈

Clash of the Titans观影归来

今天去看了Clash of Titans, 学校提供的半价票,7元,算是第一次体验3D电影吧。之前很是期待,不过看了以后发现3D电影对于电脑制作出的动画、特效什么会有很不一样的感觉,但是对于直接拿摄像机,真人演员,比较plain的那种电影,就没什么区别了。带不带3D眼睛感觉效果都一样,唯一不同的地方估计就是字幕。3D电影的字幕感觉是浮在电影上面的,而不是贴上去了。说到字幕,比较有意思的是,这部电影居然配了简体字幕,看的那个相当的情切啊。还有就是电影院用的是那种红蓝3D眼镜,听说还有一种效果更好,以后有机会再体验体验。

说到剧情,大概就是借着希腊神话的背景写的故事,不是很曲折,有同去的同事说比阿凡达的还简单点,我就不剧透了。感觉西方的这种电影都是以效果为主吧。

先说这么多,想起来再补充。

初识GCC Plugin(一)

有关GCC的介绍,我就不再这里废话了,直奔主题。GCC Plugin,顾名思义,就是用来增强GCC,并且可以给GCC添加额外功能的插件。引入GCC Plugin,我的理解就是使GCC的开发能够变得模块化,减小二次开发的难度。以前的开发都是直接改动GCC的代码,这些都还好,但是编译GCC的过程是非常痛苦的,比编译Kernel有过之而无不及。我花了一个下午才编译好,有人在网上说他用了4个多小时才编译好。所以我觉得,引入GCC Plugin,是把程序员们从无尽的等待中解脱出来。GCC Plugin可以做到的事情,包括在GCC编译过程的指定的步骤前或后(GCC中称为Pass)加入用户定义的步骤,或者用户定义的步骤替换指定的步骤,还包括在Attibute registration或pragma registration时(我不知道这两个该怎么翻译)调用用户定义的callback函数,等等功能。总之还是比较强大的。

而GCC Plugin其实是一个挺新的特性,因为这个是在GCC 4.5中才提供的,而GCC 4.5目前还是experiment版本,并没有release的版本。所以如果想要使用的话,至少要在系统上装上GCC 4.5。如果想测试一下自己的GCC是不是支持GCC Plugin的话, 可以用一下命令:

gcc -print-file-name=plugin

如果返回的值不是一个完整的路径,而只是’plugin’这个单词的话,那就是不支持。如果返回的是完整的路径,则表明支持,而那个路径就是GCC提供的GCC Plugin的头文件所在的路径,例如/usr/local/lib/gcc/plugin。

GCC Internal Manual上有一部分关于GCC Plugin的章节,我大概介绍一下。如果有了GCC Plugin的话,想要在使用GCC时加载这个Plugin,需要在GCC后加上以下的开关:

'-fplugin=/path/to/NAME.so' '-fplugin-arg-NAME-<key1>[=value1]'

例如:

gcc -fplugin=plugin.so somefile.c -o somefile.o

其中第二个开关其实不是一定要有的,这个只是在要加载的plugin需要一些外部参数时才需要的。另外在使用中我发现,plugin的名字中,好像不能有‘-’,这个我不确定,大家可以自己试试。
如果写好了一个GCC Plugin的话,编译的方法如下:

gcc -I`gcc -print-file-name=plugin`/include -fPIC -shared -O2 plugin.c -o plugin.so

需要注意的是,gcc -print-file-name…外的那个不是单引号,是反撇号,我想熟悉linux的话,应该知道单引号和反撇号的区别。

Page 1 of 212»