您的位置:首页 > 产品设计 > UI/UE

[doubango Programmer's Guide] 4.3.1 tinySAK 翻译

2011-12-06 23:24 483 查看
tinySAK API 概述 原文 http://www.doubango.org/apiref.html

------

tinySAK(轻量级瑞士军刀) 虽然是轻量级,但却包含了很多功能实用的API。其中的API也是为了能在内存和计算速度相对较差的嵌入式设备上运行做了很多的优化设计的。

这个库提供了一些基础对象类,方便用C进行面向对象编程。当然,其中也包含了一些其他的特性,例如:多线程,时间管理,编码,加密和内存管理。

6 面向对象ANSI-C 编程

也许你知道,C其实并不是一个面向对象的语言。

但在今天,OOP(面向对象编程)确实是一个能把程序完成的很好的思想。

在本章里 “所谓的对象”就是一个特殊的C结构体。在本章中出现的所有函数都是来自tinySAK工程中的。

为了能更好的说明“所谓的对象”的声明和使用,我将举一个“Person”的例子。

一个Person的对象是这样声明的:

typedef struct person_s
{
TSK_DECLARE_OBJECT; // Mandatory

char* name;
struct person_s* girlfriend;
}
person_t;

译者注:详细内容:
点击这里

6.1 对象声明

一个对象的声明实际上可以看成是一个类的声明。一次声明中包含了该对象的一些必须的函数,对象的大小和引用计数器。

必须的函数指的是构造函数,析构函数和比较函数。

一个C结构体依靠TSK_DECLARE_OBJECT 宏定义来声明为对象。

一个指向已定义的对象的指针应该是指向tsk_object_def_s结构体的。

typedef struct tsk_object_def_s
{
tsk_size_t size;
tsk_object_t*   (* constructor) (tsk_object_t *, va_list *);
tsk_object_t*   (* destructor) (tsk_object_t *);
int             (*comparator) (const tsk_object_t *, const tsk_object_t *);
}
tsk_object_def_t;

一个对象的生成可分为2个阶段。第一阶段是动态的在堆上分配内存;这就是为什么size必须强制存在于定义对象的结构体的原因。当一个新的对象在堆上分配了内存后,所有的他的成员(char*,void * ,int,long...) 将会被初始化为0.在第二个阶段中,新创建的对象会被所提供的构造函数初始化。为了能执行这两个步骤,你应当调用tsk_object_new()或者是tsk_object_new_2().

一个对象的销毁同样也是分为2个阶段.第一个阶段就是释放所有的成员(void* ,char* ...)。析构函数就是用来响应这个任务的。在第二阶段中,对象本身被销毁。由于对象不能销毁他自己,所以你应该用tsk_object_unref() 或者是tsk_object_delete()函数来执行这2个步骤。至于这两个函数的不同之处,我们将在接下来的内容中提到。一个定义好的对象一定不能用标准C语言中的free()函数去释放空间。

下面是一个关于如何声明一个对象定义的例子:

//(Object defnition)
static const tsk_object_def_t person_def_t =
{
sizeof(person_t),
person_ctor,
person_dtor,
person_cmp
};


6.2 构造函数

构造函数只是负责做一些初始化操作的并且是不会分配内存空间的。当调用到构造函数的时候,对象应该是已经分配过了内存空间的了。

下面是例子:

// (constructor)
static tsk_object_t* person_ctor(tsk_object_t * self, va_list * app)
{
person_t *person = self;
if(person){
person->name = tsk_strdup(va_arg(*app, const char *));
}
return self;
}


6.3 析构函数



析构函数将会释放对象中成员占用的内存空间,但不会销毁对象本身(第一阶段).析构函数必须返回一个指向他本身的指针,以便他的调用者可以执行第二个阶段,销毁他。

下面是例子:

// (constructor)
static tsk_object_t* person_ctor(tsk_object_t * self, va_list * app)
{
person_t *person = self;
if(person){
person->name = tsk_strdup(va_arg(*app, const char *));
}
return self;
}


6.4 比较函数

比较函数是用来比较两个定义好了的对象的。用于比较的对象应当拥有相同的定义(或者类型)。

下面是例子:

// (comparator)
static int person_cmp(const tsk_object_t *_p1, const tsk_object_t *_p2)
{
const person_t *p1 = _p1;
const person_t *p1 = _p2;
int ret;

// do they have the same name?
if((ret = tsk_stricmp(p1->name, p2->name))){
return ret;
}
// do they have the same girlfriend?
if((ret = tsk_object_cmp(p1->girlfriend, p2->girlfriend))){
return ret;
}

// they are the same
return 0;
}


6.5 引用计数器

引用计数器是用来做模拟一个内存回收器的。每个定义好了的对象都应包含一个引用计数器,以便记录有多少对象引用了这个真正的对象。

当一个对象被创建的时候(往下看)引用计数器的值将会被初始化为1;这是自动完成的,你不必去做任何事情。当你调用tsk_object_ref()的时候,这个计数器将会自动加1,当你调用tsk_object_unref()的时候,这个计数器就会减1。

当这个计数器的值为1的时候,这个对象就会被回收。

6.6 继承

正如你所知道的,继承在ANSI-C中并不被支持的。

基于C语言中的结构体能够创建一个指向他的第一个元素的指针,继承就能像这样被实现:

#include "tsk.h"
// (a student is a person)
typedef struct student_s
{
person_t* person; // Must be the first element
char* school;
}
student_t;

// (as a student is a person you can do)
student_t* s = tsk_null;
//....
((person_t*)s)->name = tsk_strdup("bob");


6.7 使用方法

一旦一个对象被声明了,并且它的所有必要函数都被实现了,那么它应当这样使用:

// creates a person: will call the constructor
person_t* bob = tsk_object_new(&person_def_t, "bob");
// creates bob's girlfriend
bob->girlfriend = tsk_object_new(&person_def_t, "alice");
// deletes bob: will delete both bob and bob's girlfriend field by calling their destructors
tsk_object_unref(bob);


由于很难得知构造函数到底需要什么样的参数,所以普遍的做法是用宏定义来帮忙。在我们的例子中宏定义将是这样的:

// create a person
#define PERSON_CREATE(name)     tsk_object_new(&person_def_t, (const char*)name)


由于析构函数通常是固定的参数,所以有一个一般的宏定义来销毁所有已定义的对象。

TSK_OBJECT_SAFE_FREE()就是用来销毁各种对象的。

对象只有当在当引用计数器递减到0的时候才会被真正销毁。其他所有情况(销毁或者没有)指针都会被设置成NULL.

上面的例子可以被写成这样:

#include "tsk.h"

// create a person: will call the constructor
person_t* bob = PERSON_CREATE("bob");
// create bob's girlfriend
bob->girlfriend = PERSON_CREATE("alice");
// delete bob: will delete both bob and bob's girlfriend field by calling their destructors
TSK_OBJECT_SAFE_FREE(bob);


7 线程支持

这个框架给我们提供了操作系统无关的线程函数,同时适用于WIN32和Unix-like系统。

7.1 线程

你无须用上千个函数去管理你的线程.在这个框架中,我们只需要创建,暂停和销毁线程即可。

使用tsk_thread_create()函数可以创建一个线程。使用tsk_thread_join()函数来把线程加入管理数组。

你可以使用tsk_thread_sleep()函数来使一个正在执行的线程进入暂停状态。

#include "tsk.h"

void* MyThreadFunction(void *arg)
{
printf("arg=%d", *((int*)arg));
return tsk_null;
}

void test_threads()
{
void* tid[1] = {tsk_null}; // thread id
int arg = 112; // arg to pass to the function

// creates the thread
tsk_thread_create(&tid[0], MyThreadFunction, &arg);

// joins the thread
tsk_thread_join(&(tid[0]));
}


7.2 互斥体

互斥体(相互排斥)是用于保护一部分代码,避免并行读写的情况。并行读写的情况发生在两个或两个以上线程在同一时间试图同时执行相同的代码的时候。

#include "tsk.h"

// create the mutext
tsk_mutex_handle_t *mutex = tsk_mutex_create();

tsk_mutex_lock(mutex);
// ...portion of code to protect
tsk_mutex_unlock(mutex);

// destroy the mutex
tsk_mutex_destroy(&mutex);


互斥体并不是一个定义好的对象;你应当调用tsk_mutex_destroy而非TSK_OBJECT_SAFE_FREE()来销毁他们。

7.3 线程安全体(代码锁)

一个C结构体可以通过声明一个TSK_DECLARE_SAFEOBJ宏来成为一个线程安全体。这里对于对象是否有定义好(加对象宏)并不是必须的。

一个线程安全体的初始化函数是tsk_safeobj_init(),反初始化函数是tsk_safeobj_deinit()。用于给代码加锁和解锁的函数分别是tsk_safeobj_lock()tsk_safeobj_unlock()

7.4 信号量

只有计数信号量才是被这个框架所支持的。计数信号量是用来对多线程程序对某段代码的访问进行控制的。只有信号量的计数不为零的情况下,一个线程才拥有权限访问被加了信号量的代码。在执行被加入信号量的代码之前,线程应当先检查它自己是否有权限。

#include "tsk.h"

// (create the semaphore)
tsk_semaphore_handle_t *sem = tsk_semaphore_create();
// (increment the counter)
tsk_semaphore_increment(sem);
// (decrement the counter)
tsk_semaphore_decrement(sem);
// (destoy the semaphore)
tsk_semaphore_destroy(&sem);


信号量并不是一个对象;你应该用tsk_semaphore_destroy来销毁他,而不是用TSK_OBJECT_SAFE_FREE()

互斥体其实就是只有0和1的信号量。

7.5 条件变量

条件变量是用来控制一部分有可能被多线程同时访问的代码的。线程会等待在那部分代码的开始初,直到某些条件成立,或者等待了指定的毫秒数。

tsk_condwait_create是用来创建一个条件变量的,tsk_condwait_wait()是用来等待某个条件成立的。

tsk_condwait_timedwait()是用来等待某条件成立,或者等待过去多长时间。

tsk_condwait_signal()是用来通知第一个进入等待的线程条件已经成立了,而tsk_condwait_broadcast()是用来通知所有的等待线程条件已经成立了的。

条件变量同样也不是一个定义好的对象,你必须用tsk_condwait_destroy()而不是TSK_OBJECT_SAFE_FREE()
来销毁它。

7.6 Runable(可运行的)

一个Runable对象是一个需要用TSK_DECLARE_RUNNABLE()宏来声明的定义好的对象。一个Runable对象需要调用tsk_runnable_start()来启动。并且在它被销毁的时候终止。你也可以调用tsk_runnable_stop()来终止这个对象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: