载前的说明
forward declaration可以达到向用户隐蔽信息的目的,linux内核源码,关于forward declaration的使用无处不在,这也体现了linux内核高内聚低耦合的设计思想。jserv老师在这方面做了很详细的解说,非常值得深入学习和研究。
由于头条无法添加外部链接,因此我会在标有下划线文字部分添加相应的链接地址以及说明。
forward declaration 搭配指针的技巧
案例: oltk是Openmoko为了测试而开发出的精简绘图系统,支持触摸屏操作,源码不到1000行C语言程序。执行画面: (oltk的开发者是olv )
- oltk.h
struct oltk; // 声明 (incomplete type, void)
struct oltk_button;
typedef void oltk_button_cb_click(struct oltk_button *button, void *data);
typedef void oltk_button_cb_draw(struct oltk_button *button,
struct oltk_rectangle *rect, void *data);
struct oltk_button *oltk_button_add(struct oltk *oltk,
int x, int y,
int width, int height);
struct oltk和struct oltk_button没有具体的定义(definition)或实现(implementation),仅有声明(declaration)。
- oltk.c
struct oltk {
struct gr *gr;
struct oltk_button **zbuttons;
...
struct oltk_rectangle invalid_rect;
};
软件接口(interface)位于oltk.h,不管struct oltk内容怎么修改,已公开的函数如oltk_button_add都是透过pointer存取给定的地址,而不用顾虑具体struct oltk的实现,如此一来,不仅可以隐藏实现细节,还能有非常良好的兼容性(binary compatibility)。
同理,struct oltk_button 不管怎么变更,在struct oltk 里面也是用给定的pointer 去存取,保留未来实现的弹性。
想更深入了解oltk的实现,具体参考
oltk的gihub地址:https://github.com/openmoko/system-test-suite/tree/master/gta02-dm2/src/oltk
简单的例子参考
说明
- a.h,开放给客户的接口头文件
- internal.h, 定义了一些结构体,且会被多个.c调用
- a.c,提供了a.h中的一部分接口,且会创建主要的handle
- b.c,提供了a.h中的另一部分接口
相关文件
a.h
这里只声明了test_lib_t 就是struct test_lib_st 类型 但没有给test_lib_st的定义,它的定义可以放到其它的内部c/h文件中, 从而达到向用户隐藏的目的。
即,用户可以使用test_lib_t,但看不到它的内部成员定义。
#ifndef INCLUDE_A_H_
#define INCLUDE_A_H_
typedef struct test_lib_st test_lib_t;
// 创建并返回test_lib的handle
test_lib_t* test_lib_create(void);
void test_lib_destroy(test_lib_t* test_lib);
...
int test_print(test_lib_t* test_lib);
...
#endif // INCLUDE_A_H_
internal.h
#ifndef INCLUDE_INTERNAL_H_
#define INCLUDE_INTERNAL_H_
// 这里声明内部需要用到的所有成员变量
struct test_lib_st
{
int i;
char* p;
...
};
#endif // INCLUDE_INTERNAL_H_
a.c
#include "a.h"
#include "internal.h"
test_lib_t* test_lib_create(void)
{
test_lib_t* m = malloc(sizeof(test_lib_t));
memset(m, 0, sizeof(test_lib_t));
// 初始化各成员变量
...
return m;
Error:
return NULL;
}
void test_lib_destroy(test_lib_t* test_lib)
{
free(test_lib);
return;
}
b.c
#include "a.h"
#include "internal.h"
/*因为这里也include了internal.h,
所以此C文件中也可以使用 test_lib_t */
int test_print(test_lib_t* test_lib)
{
...
}