browser icon
You are using an insecure version of your web browser. Please update your browser!
Using an outdated browser makes your computer unsafe. For a safer, faster, more enjoyable user experience, please update your browser today or try a newer browser.

突破 static 的作用域限制

Posted by on 2013 年 12 月 22 日

你可以任意转载本文,但请在转载后的文章中注明作者和原始链接。
媒体约稿请联系 titilima_AT_163.com(把“_AT_”换成“@”)。

开源库往往会带给我们极为舒适的使用体验,这是因为它们有着封装良好的接口以及完善的功能实现。不过在某些情况下,我们会需要对库中代码进行更为深度的定制和调用,这样的话则不可避免地会触及到某些被 static 修饰符限制了作用域的函数。

例如,Lua 中字符串库的 match/gsub 功能非常赞,以至于我希望能使用 C/C++ 代码来调用它们。但是,这些实现完全被 static 以私有的形式限制在了 lstrlib.c 文件中,而对外开放的 luaL_gsub 则是一个接近残废的实现。于是,我不得不去寻找一个办法来以代价最小的方式突破 static 的限制。

当然,源码之前了无秘密,既然源码在手上,那么最简单的方法自然是:

  • 将想调用的函数原封不动地照抄在自己的代码里。
  • 修改开源库的代码,去掉 static 的限制。

虽然上述两种方法都是完全可行的,但是它们均带来了不小的代价:

  • 如果目标函数是一个简单的函数,那么这么玩当然没有问题。但是,在实际的情景中,我们希望调用的往往是较为复杂的函数,而且其中很可能调用了其它的 static 私有函数,比如上述的 match/gsub 实现;持续照抄代码一个最可能的结果就是把原有文件完整地抄写了一遍。
  • 在开源库升级后,我们需要同步代码,每一次升级都需要去掉 static 修饰。另外,作为一个有重度洁癖的程序员,我完全不能接受修改第三方库源代码的做法。

那么接下来是我的解决方案。考虑下面的例子,say_hello 函数是我希望调用的 static 函数,它所在的 static.c 则是某个开源库的一部分。

1
2
3
4
5
6
7
8
// static.c
 
#include <stdio.h>
 
static void say_hello(void)
{
    puts("Hello, World!");
}

然后是我的代码 my.c:

1
2
3
4
5
6
7
8
// my.c
 
#include "static.c"
 
void my_func(void)
{
    say_hello();
}

如你所见,我使用了 #include 这个预编译指令,它直接将 static.c 的内容纳入了 my.c,使 static.c 在 my.c 的作用域内成为了可见的,这样就可以直接调用 say_hello 了。

附带说一句,使用这个技巧的时候需要在工程或 makefile 中剔除已被 my.c 所引用的 static.c,否则链接器会报告重复符号的错误。很显然,这个修改较之照抄函数或修改原有实现的代价要小得太多了。

订阅本站

没有评论

(Required)
(Required, will not be published)