我想写C程序都通常是把系统头文件放在最上面,然后是第三方库的,最后是我自己的,比如下面这样
-
#include <stdio.h>
-
#include <stdlib.h>
-
-
#include <mysql.h>
-
-
#include <my_foo.h>
-
...
前天动手把我们的 apache module 移植到 nginx 上,里面自然是要 include 一堆 nginx 自己的头文件的。但它需要调 openssl 的库,那把 openssl 的头文件放哪里呢?我想了一下,觉得 openssl-devel 是系统自己 yum 安装上的,应该比 nginx 有更高的优先级,于是就是这么一个顺序
-
#include <openssl/rsa.h>
-
#include <openssl/pem.h>
-
-
#include <ngx_config.h>
-
#include <ngx_core.h>
-
#include <ngx_http.h>
(你看,我还是相当注重长幼尊卑的,正统思想啊~~)
这样程序似乎也没有问题,可就一个麻烦事情——调用 nginx 的 log 功能时就会 crash!用 gdb 跟踪,发现是使用 request->connection->log 的指针为空...
但就在 nginx 的代码里,向我的 handler 传入 request 之前,r->c->log 还是一个合法的指针,只要一进我的函数,这个就成了 NULL,而且一旦 handler 结束,指针值又非空了..
写到这里,聪明的读者就应该知道这是由于头文件预处理,导致两边结构体定义不一致。connection 这个结构体是如此定义的:
-
struct ngx_connection_s {
-
...
-
#if (NGX_SSL)
-
ngx_ssl_connection_t *ssl;
-
#endif
-
...
-
};
就这么一个小小的 bug,足足花了2个小时才找到原因。
获得什么教训呢?
- 下回再用 gdb 跟踪到这种情况,应该能第一时间意识到结构体错误
- 如果要提供开发接口给第三方,那第三方可能直接使用的结构体里千万不要包括预处理代码
- 只要小心,是可以避免预处理的。但人总有大意的时候,那就养成一个好习惯,把预处理部分都放在结构体的最后,这就考验第三方开发者的人品了
最后是一篇新鲜热辣刚出炉的Nginx模块开发备忘:http://www.dup2.org/files/nginx_module_development.html
nginx 的中文 wiki 上有人写了一篇很好的翻译,我的备忘和其比起来,多了一些数据结构的分析。
Topic:
技术
评论
>下回再用 gdb
>下回再用 gdb 跟踪到这种情况,应该能第一时间意识到结构体错误
>如果要提供开发接口给第三方,那第三方可能直接使用的结构体里千万不要包括预处理代码
我们这样处理的,写个脚本,发布接口之前根据编译环境,把宏去掉,选择性的保留原来宏保护的内容。
结果是,情况更加糟糕;没有当初发布时的整个配置的话,根本不知道发布的接口要和哪个引擎适配。
>只要小心,是可以避免预处理的。但人总有大意的时候,那就养成一个好习惯,把预处理部分都>放在结构体的最后,这就考验第三方开发者的人品了
我觉得 opera
我觉得 opera 每个开发包应该只提供一个头文件,强制要求必须放在源代码的第一行..
智能些的就提供一个类似 apache 的 apxs 那样的编译脚本.. 做强制检查
ugg boots store
不学C已经很多年了
这个是最新型的上下文相关spam??
这个是最新型的上下文相关spam??
哦,记住了,结构体里最好不要包括预处理代码,谢谢楼主
哦,记住了,结构体里最好不要包括预处理代码,谢谢楼主