最近在看strongswan源代码,看到strongswan的代码框架很有意思,用C语言实现类的思想。当我们编写完一个模块,我们需要提供的是H的文件给其他模块使用,我们希望H文件中就只能包含一些公有函数,和一些类型的申明,不希望其他模块篡改我们C文件私有的变量,访问我们的私有方法,strongswan的代码实现这种类思想,举个简单的例子,如图:
比如左侧C文件是一个主程序,可以调用模块1和模块2提供的公有函数,这些函数在相应模块H文件中,但是也仅仅只能调用这些公有函数,模块的C文件中的私有函数和私有变量无法访问。(当然如果模块2想调用模块1的函数也类似,模块2的C文件包含模块1的H文件,图没标识)
可以先看看strongswan中模块C文件中的一部分代码,主要为了显示这种框架,基本strongswan每个模块都是这种格式,包含如下:
1.包含自己模块的头文件,头文件中有模块公有的结构体定义。
2.模块C文件定义私有结构体,私有结构体包含一个公有结构体变量,以及包含自己的私有方法或者变量。
3.模块C文件定义了公有方法METHOD(当然也包括私有方法,不过先关注公有方法)。
4.模块C文件定义了如何创建公有结构体的函数并返回公有结构体的指针。
#include "crypto_factory.h"
struct private_crypto_factory_t {
crypto_factory_t public;
/*private method or value*/
}
METHOD(crypto_factory_t, create_verify_enumerator, enumerator_t*,
private_crypto_factory_t *this, transform_type_t type)
{
/*public method code*/
}
crypto_factory_t *crypto_factory_create() {
/*create public struct*/
}
H文件摘取一部分代码,主要显示这种格式,如图:
struct crypto_factory_t {
/*一堆公有函数指针*/
}
crypto_factory_t *crypto_factory_create();
而其中METHOD的方法的宏定义如下:
#define METHOD(iface, name, ret, this, ...) \
static ret name(union {iface *_public; this;} \
__attribute__((transparent_union)), ##__VA_ARGS__); \
static typeof(name) *_##name = (typeof(name)*)name; \
static ret name(this, ##__VA_ARGS__)
如果看到这个宏定义会有点晕,听我一一讲来,当然我自己编写了个简单例子可以直接理解这种框架思想,可以直接跳到例子看。
从这个宏定义来说:
1.先看第一句话,static ret name(union {iface *_public; this;} __attribute__((transparent_union)), ##__VA_ARGS__);
这个的意思是向编译器提供一个函数原型,而函数的第一个参数是一个透明联合类型,这个是用来干嘛呢,正常函数定义时候需要指定参数的类型,而设置成透明联合类型,可以让函数接受不同类型参数。
举个例子:已经定义了void func_a(int a)函数,功能是打印a的值,如果你想这个功能也接受类型float b,而你又不想重新定义另个函数,这时候可以使用透明联合类型,函数变成:void func_a(union { int a;float b;}__attribute__((transparent_union)))。
再看看第二个参数,是##__VA_ARGS__,这个是一个宏,可以接受可变数目的参数,代表就是METHOD最后面的省略号(可变参数),而加上##表示如果没有可变参数时候,去掉前面的逗号,如它允许这样调用,name(this, a, b, c),也允许只调用name(this)这样。
2.再看第二句话,先定义了一个函数指针,*_name,它的类型跟第一句话我们定义的一样,并且把我们第一句话函数定义的指针变量赋给它。可以简单理解为,第一句话我们定义了一个int *a,然后我们又定义了一个int *_a,并且把a的值赋给_a,_a=a。
3.最后一句话就很普通了,就是定义一个函数了。
第一看在想为什么要搞得这么复杂?后来慢慢理解了它的思想,这边可以先看看一个简单的例子,用这种框架思想写了一个简单的模块,我把METHOD相应的宏展开了,方便理解。
C文件,print_module.c
#include
#include
#include "print_module.h"
typedef struct private private;
struct private {
public p;
int private_v1;
};
typedef union u_t
{
public *p1;
private *p2;
} u_t __attribute__((__transparent_union__));
static void print_value(u_t u);
static typeof(print_value) *_print_value = (typeof(print_value)*)print_value;
static void print_value(private *p)
{
printf("%d\n",p->private_v1);
}
static void destory(u_t u);
static typeof(destory) *_destory = (typeof(destory)*)destory;
static void destory(private *p)
{
free(p);
}
public* create_public(void)
{
private *user;
user = malloc(sizeof(*(user)));
user->p.print = _print_value;
user->p.destory = _destory;
user->private_v1 = 5;
return &user->p;
}
H文件,print_module.h
typedef struct public public;
struct public {
void (*print) (public *this);
void (*destory) (public *this);
};
/**
* create a public instance.
* */
public* create_public(void);
main.c文件
#include
#include
#include "print_module.h"
int main(void)
{
public *user;
user = create_public();
user->print(user);
user->destory(user);
}
进行简单的编译并且执行
gcc -c print_module.c
gcc main.c print_module.o -o main
./main
5
从main函数开始讲起,这种框架的思想是想要调用模块,有如下几个步骤:
1.包含模块头文件;
2.调用create相关函数创建相应的模块的结构体;
3.利用这个结构体作为桥梁,访问公有的方法。
可能看到这里,这种思想已经理解了,每个C文件都会创建自己的私有结构体,C文件每个函数的输入参数都有一个私有结构体指针,这样函数就可以调用私有结构体的方法和变量,同时以一种巧妙形式把这个函数原型变形成输入参数既能用公有结构体也能用私有结构体,最终把函数指针赋值给公有结构体的成员,让公有结构体可以调用。
最后稍微讲讲public* create_public(void)这个函数作用,它通过先创建一个私有结构体,最终返回私有结构体的一部分----即公有结构体(因为公有结构体在私有结构体里,私有结构体比较大)。在这期间,给公有结构体的方法赋值,如上面看到METHOD这个宏那么复杂,其实是为了这个create函数,宏的第一句给出函数原型(也理解为定义了一个函数指针),第二句话给出了一个跟第一句话名字一样的但是加了一个下划线的函数指针,这个指针最终赋给公有结构体中相应的函数指针,为了让公有结构体能访问这个函数,还有就是给私有结构体赋自己变量和方法。
按照这种的框架思想,其他模块想要调用这个模块,就必须按照上述步骤,所得到的公有结构体自然访问不到私有变量和方法。