BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

代码之丑(十)——条件编译那些事儿​

| 作者 郑晔 关注 2 他的粉丝 发布于 2010年12月31日. 估计阅读时间: 9 分钟 | CNUTCon 了解国内外一线大厂50+智能运维最新实践案例。

C语言出现之初,跨平台是个极大的卖点。于是,我们有机会看到这样的代码:

int sys_old_mmap(struct tcb *tcp) {
    long u_arg[6];
    #if defined(IA64)
    int i, v;
    for (i = 0; i < 6; i++)
        if (umove(tcp, tcp->u_arg[0] + (i * sizeof(int)), &v) == -1)
            return 0;
        else
            u_arg[i] = v;
    #elif defined(SH) || defined(SH64)
    int i;
    for (i=0; i<6; i++)
        u_arg[i] = tcp->u_arg[i];
    #else
    if (umoven(tcp, tcp->u_arg[0], sizeof(u_arg), (char *) u_arg) == -1)
        return 0;
    #endif  // defined(IA64)
    return print_mmap(tcp, u_arg);
}

你已经知道了我要说什么了,是的,条件编译。

条件编译在解决跨平台的问题上,确实是个利器,但这么用条件编译,就把它变成了一柄双刃剑。Robert Martin在《Clean Code》里告诉我们,函数应该只做一件事。从逻辑上来说,这段代码是做了一件事。但是,它还有另外一个维度,也就是条件编译的条件。这个维度的存在让一件事变成了多件事。

无他,提取函数吧!

int sys_old_mmap(struct tcb *tcp) {
    #if defined(IA64)
    return sys_old_mmap_for_ia64(tcp);
    #elif defined(SH) || defined(SH64)
    return sys_old_mmap_for_sh_or_sh64(tcp);
    #else
    return default_sys_old_mmap(tcp);
    #endif  // defined(IA64)
}

int sys_old_mmap_for_ia64(struct tcb *tcp) {
    long u_arg[6];
    int i, v;
    for (i = 0; i < 6; i++)
       if (umove(tcp, tcp->u_arg[0] + (i * sizeof(int)), &v) == -1)
            return 0; 
       else 
           u_arg[i] = v; 
    return print_mmap(tcp, u_arg);
}
int sys_old_mmap_for_sh_or_sh64(struct tcb *tcp) {
    long u_arg[6];
    int i;
    for (i=0; i<6; i++)
        u_arg[i] = tcp->u_arg[i];
    return print_mmap(tcp, u_arg);
}
int default_sys_old_mmap(struct tcb *tcp) {
    long u_arg[6];
    if (umoven(tcp, tcp->u_arg[0], sizeof(u_arg), (char *) u_arg) == -1)
        return 0;

    return print_mmap(tcp, u_arg);
}

好,经过一番分解,函数比原来的规模小了许多,更重要的是,我们把针对不同的条件做法已经拆解开来了。

相对于原来的版本,这是一段可以接受的代码。制约原有代码的出现,我们也可以用一个简单的规则:

  • 条件编译里面不允许包含多条执行语句。

不过,这还不是终点。针对上面的情况,这种改法没有问题,因为提取出来的小函数在各个平台上都可以编译,但如果涉及到特定平台的操作,简单的提取就不起作用了。比如,用到了Windows API的代码在Linux上恐怕连编译这关都过不了。

一种可能的解决方案是,把不同条件的内容放进不同的文件。

int sys_old_mmap(struct tcb *tcp) {
    long u_arg[6];
    int i, v;
    for (i = 0; i < 6; i++)
      if (umove(tcp, tcp->u_arg[0] + (i * sizeof(int)), &v) == -1)
        return 0;
      else
        u_arg[i] = v;
    return print_mmap(tcp, u_arg);
}
(ia64.c)
int sys_old_mmap(struct tcb *tcp) {
    long u_arg[6];
    int i;
    for (i=0; i<6; i++)
      u_arg[i] = tcp->u_arg[i];
    return print_mmap(tcp, u_arg);
}
(sh.c)
int sys_old_mmap(struct tcb *tcp) {
    long u_arg[6];
    if (umoven(tcp, tcp->u_arg[0], sizeof(u_arg), (char *) u_arg) == -1)
      return 0;
    return print_mmap(tcp, u_arg);
}
(default.c)

同之前的一个差别在于,不再有一个统一的sys_old_mmap,而是大家有了各自的sys_old_mmap,分别放在了对应的文件里。构建的时候,我们可以根据不同的条件编译链接不同的文件,这样就可以回避前面提到的问题。

之前谈及的那些丑陋代码大多是在一个文件内部做着各种各样的变换,而这次的变动显然大了很多,甚至需要配合构建过程的修改。为了对付丑陋的代码,我们总是有办法的。

即便劳神费力的修改构建过程,我依然认为是值得的。其实,我们想要的,无非是明天的日子好过一些。

作者简介:

郑晔,ThoughtWorks公司咨询师,拥有多年企业级软件开发经验,热衷于探索各种程序设计语言在真实软件开发中所能发挥的威力,致力于探寻合理的软件开发方式,加入ThoughtWorks公司后,投入到敏捷开发方法的实践之中,为其他公司提供敏捷开发方法方面的咨询服务。他的blog是梦想风暴

查看原文:代码之丑(十)

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

努力重构,明天会更好 by 关 墨辰

其实,我们想要的,无非是明天的日子好过一些。

代码的复杂性能被消除吗? by Wong Peter

“同之前的一个差别在于,不再有一个统一的sys_old_mmap,而是大家有了各自的sys_old_mmap,分别放在了对应的文件里。构建的时候,我们可以根据不同的条件编译链接不同的文件,这样就可以回避前面提到的问题。”

只是把条件编译放到了构建的环节,并没有真正消除条件编译。

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

2 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT