OOC-GCC特性介绍与使用说明

Posted on Sat, 11 Jun 2011 22:47:50 -1100

OOC-GCC 特性介绍

•核心部分仅仅是"宏",所以编译的时候更加灵活.

•两层宏定义,底层的"宏"完全采用英文全称,表意清晰.同时严格控制每个宏的定义,尽力做到不烂用.

•对于OO的模拟有构造也有析构,宏的定义具有对称性,同时符合语言规则,如表述表示 类的"名词" + 表示方法的"动词+名词".

•类的定义多数采用全开放的形式,这也使得现在的版本和VC兼容.

•方便和C++的配合.

•相对完整的类空间与实例(对象)空间的模拟,类空间的构造和析构通过类似引用计数的方法对开发者"透明".

•相对完整的单根继承的模拟,无需的在子类的构造/析构函数中显示地调用父类的构造/析构函数.

•和主流C编译工具兼容,无论GCC或是M$的编译工具都可无warning编译通过.

    ----实际测试为GCC 32bit 4.5.2 MinGW环境以及Ubuntu环境,64bit的找别人帮忙测试过,可能会有几个关于size_t的warning

    -----微软的是编译器  32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86

    -----微软的是编译器  32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86

•提供较为便利的调试层(仅针对GCC),

    ----提供更加方便的断言宏

    ----提供针对纯C的异常捕获机制的模拟

    ----提供和堆内存分配的日志监测

    ----提供仪表函数(就是监控每一个函数的调用)日志监测

    ----提供手动调用日志监测

    ----日志输出格式较为简洁,配合addr2line及graphviz等工具可以较为方便的生成图表

•提供一些常规的函数(这一部分是独立出来的)

•文件结构较为简洁,且功能上比较独立,同时有OOCfg.h中定义了一些宏开关方便一些特性的开启与停用.

•提供独立的单文件宏OOC.h,方便Release版的使用.

•提供了一些测试用例,以及在win下配合MinGW及VC编译用的批处理,*nix的GCC使用时改一下即可

-------------------------------------------------------------------------------------------------------------------------------------

关于OOC-GCC中宏的使用

类的设计(模块化的编程应在.h文件中使用)

 
假定有一个名为"A""类"

CLASS(A){
    ......  这里为实例成员
    STATIC(A);
    ......  这里是类成员(实际结构体为struct _StA,并被重定义为StA)
};

假定有一个名为"B"的继承了上面"B""类"

CLASS_EX(A,B){
    .......
    STATIC_EX(A,B); 
    .......
};

注意继承时,前面是父类,后面是要定义的子类.

类的构造与析构(模块化的编程应在.c文件中使用)

static int A_reload(A *THIS,void *p){
        THIS->.... 这里使用第一个参数来初始化实例成员
        return 0;
}
static int A_reloadSt(StA *THIS,void *p){
        THIS->.... 这里使用第一个参数来初始化类成员
        return 0;
}
static int A_unload(A *THIS,void *p){
        THIS->.... 这里使用第一个参数来析构实例成员
        return 0;
}
ASM(A,A_reload,A_unload,A_reloadSt,NULL)
注意上面最后一个参数为类成员的析构
使用NULL是为了说明无需某个函数时,可以不设置

类的使用

普通类"A"的使用
A *a=NEW0(A);  //声明第一个A的实例时调用StA和A的构造,
               //以后再声明A的实例只调用A的构造
StA *fA=ST(a);
fA->someFunc(a,someParam);
DELETE0(a);    //销毁A的实例时调用A的析构
               //当销毁显存的最后一个A的实例时,
               //调用A和StA的构造

带有继承的类"B"的使用
B *b=NEW0(B);   //注意父类的构造会自动调用
StB *fB=ST(b);
fB->someFunc(b,someParam);
StA *fA=ST(b);
fA->someFunc(b,someParam); 使用父类的方法
DELETE0(b);  //无需知道b的具体类型时A或B,都会调用正确的析构
             //如果是A的指针,则调用A的析构,如果是B的指针则调用B和A的析构

--------------------------------------------------------------------------------------------------------------------------------------------------------

关于OOC-GCC中单根继承的说明

假设我们定义四个类,A,B,C,D,并且后面的类继承前面的类,则有


CLASS(A){
        STATIC(A);
};
static int A_reload(A *THIS,char *name){
        printf("%s\n",__FUNCTION__);
        return 0;
}
static int A_unload(A *THIS,void *PARAM){
        printf("%s\n",__FUNCTION__);
        return 0;
}
static int A_reloadSt(StA *THIS,char *name){
        printf("%s\n",__FUNCTION__);
        return 0;
}
static int A_unloadSt(StA *THIS,void *PARAM){
        printf("%s\n",__FUNCTION__);
        return 0;
}
ASM(A,A_reload,A_unload,A_reloadSt,A_unloadSt)

CLASS_EX(A,B){
        STATIC_EX(A,B);
};
static int B_reload(B *THIS,char *name){
        printf("%s\n",__FUNCTION__);
        return 0;
}
static int B_unload(B *THIS,void *PBRBM){
        printf("%s\n",__FUNCTION__);
        return 0;
}
int B_reloadSt(StB *THIS,char *name){
        printf("%s\n",__FUNCTION__);
        return 0;
}
static int B_unloadSt(StB *THIS,void *PBRBM){
        printf("%s\n",__FUNCTION__);
        return 0;
}
ASM_EX(A,B,B_reload,B_unload,B_reloadSt,B_unloadSt)

CLASS_EX(B,C){
        STATIC_EX(B,C);
};
static int C_reload(C *THIS,char *name){
        printf("%s\n",__FUNCTION__);
        return 0;
}
static int C_unload(C *THIS,void *PCRCM){
        printf("%s\n",__FUNCTION__);
        return 0;
}
static int C_reloadSt(StC *THIS,char *name){
        printf("%s\n",__FUNCTION__);
        return 0;
}
static int C_unloadSt(StC *THIS,void *PCRCM){
        printf("%s\n",__FUNCTION__);
        return 0;
}
ASM_EX(B,C,C_reload,C_unload,C_reloadSt,C_unloadSt)

CLASS_EX(C,D){
        STATIC_EX(C,D);
};
static int D_reload(D *THIS,char *name){
        printf("%s\n",__FUNCTION__);
        return 0;
}
static int D_unload(D *THIS,void *PDRDM){
        printf("%s\n",__FUNCTION__);
        return 0;
}
static int D_reloadSt(StD *THIS,char *name){
        printf("%s\n",__FUNCTION__);
        return 0;
}
static int D_unloadSt(StD *THIS,void *PDRDM){
        printf("%s\n",__FUNCTION__);
        return 0;
}
ASM_EX(C,D,D_reload,D_unload,D_reloadSt,D_unloadSt)

上面的代码再调用相关的函数是会打印出对应的__FUNCTION__,

        printf("\n*****************NEW A\n");
        A *a=NEW0(A);
        printf("\n*****************NEW B\n");
        B *b=NEW0(B);
        printf("\n*****************NEW C\n");
        C *c=NEW0(C);
        printf("\n*****************DEL C\n");
        DELETE0(c);
        printf("\n*****************DEL B\n");
        DELETE0(b);
        printf("\n*****************DEL A\n");
        DELETE0(a);
        printf("\n*****************NEW D\n");
        D *d=NEW0(D);
        printf("\n*****************DEL D\n");
        DELETE0(d);

如果像上面这样调用,则会有如下输出

*****************NEW A
A_reloadSt
A_reload

*****************NEW B
A_reload
A_reloadSt
B_reloadSt
B_reload

*****************NEW C
A_reload
B_reload
A_reloadSt
B_reloadSt
C_reloadSt
C_reload

*****************DEL C
C_unload
B_unload
A_unload
A_unloadSt
B_unloadSt
C_unloadSt

*****************DEL B
B_unload
A_unload
A_unloadSt
B_unloadSt

*****************DEL A
A_unload
A_unloadSt

*****************NEW D
A_reloadSt
A_reload
A_reloadSt
B_reloadSt
B_reload
A_reloadSt
B_reloadSt
C_reloadSt
C_reload
A_reloadSt
B_reloadSt
C_reloadSt
D_loadSt
D_load

*****************DEL D
D_unload
C_unload
B_unload
A_unload
A_unloadSt
A_unloadSt
B_unloadSt
A_unloadSt
B_unloadSt
C_unloadSt
A_unloadSt
B_unloadSt
C_unloadSt
D_unloadSt

构造的时候遵循先父再子,析构的时候遵循先子再父.

另外关于静态部分的使用,在某个类第一次使用时自动的调用静态构造

当销毁某个类的最后一个实例时自动的调用静态析构

Hello,OOC World![Chap2Sec1-3][GFDL]

Posted on Sat, 28 May 2011 20:24:56 -1100

项目地址(点击超链) OOC_GCC 

注意:OOC_GCC 源码以LGPL发布,文档为GFDL,文档相关测试用例为GPL3

Web版文档发布暂定为项目托管地SVN仓库,WikiPage,"这里",以及我的博客.

转载请注明出处


Hello,OOC World!


                ---- 原始作者: 大孟  pingf0@gmail.com


                                          崇 尚 开 源 , 热 爱 分 享 !  


这份Tutorial性质的文档其实很扯的,非要啰嗦些用C语言进行面向对象开发的基本内容

 

版权说明

License Type: GFDL

Copyright (C) 2011-2011 Jesse Meng  pingf0@gmail.com
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation;with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.A copy of the license is included in the section entitled “GNU Free Documentation License”.

OOC-GCC为开源软件,遵循LGPL3协议发布,本手册为相关配套文档,采用GFDL协议发布,本手册中提供的相关示例代码以及简化版的OOC-LITE 需遵循GPL3协议.任何使用请遵循相关协议, 如有和协议相冲突的特殊要求, 请联系我[ pingf0@gmail.com ]

第二章 { 伸展运动 }

 

 


 

1. < CLASS,CTOR,DTOR >

宏是一个十分强大的特性,某些人的观点是通过宏实际上我们可以创立一个新的语言!当然个人认为只是形式上罢了.不过通过宏,代码看起来真的可以很简洁,很强大.比如下面所介绍的,将用一些简单的宏让C代码看上去有点C++的味道.


#include "OOC.h"
CLASS(A){
        int a;
        void (*showA)(A *);
};
static void A_showA(A *THIS){
        printf("the value of obj's a is %d\n",THIS->a);
}
CTOR(A){
        THIS->a=100;
        THIS->showA=A_showA;
}
DTOR(A){
        THIS->a=0;
        THIS->showA=NULL;
}
int main(){
        A * obj=newA();
        obj->showA(obj);
        delA(&obj);
        return 0;
}

这个例子中用到了三个宏,分别是CLASS,CTOR(对应CONSTRUCTOR),DTOR(对应DESTRUCTOR). 如果把他们展开,其实和前面"对称之美"一节中的代码是一样的.但又不一样,因为使用了宏,可读性大大提高, 而代码量大大减少(上面的代码和上一章中的对比减少了约1/3)!这只是一个简单的类的模拟,如果类比较多且继生关系比较复杂, 那么节省的代码量还是相当可观的.

 

不过这里还是要再次强调一下,宏是好东西,但如果滥用反而会使可读性大大降低,难懂不说,还不利于以后的维护.

关于本手册中的宏的设计,我尽量做到简单明了,易于记忆.

 

闲话少扯,现在具体来看一下CLASS,CTOR和DTOR宏具体是怎么实现的.

#include <stdio.h>
#include <stdlib.h>
#define CLASS(Type) \
typedef struct _##Type Type; \
void ini##Type(struct _##Type *); \
void fin##Type(struct _##Type *); \
Type * new##Type(); \
void del##Type(struct _##Type **); \
struct _##Type

#define CTOR(Type) \
Type * new##Type() { \
    Type *THIS; \
    THIS=(Type*)calloc(1,sizeof(Type)); \
    if( NULL==THIS ) { \
        return NULL; \
    } \
    ini##Type(THIS); \
    return THIS; \
} \
void ini##Type(Type * THIS)

#define DTOR(Type) \
void del##Type(Type **THIS) { \
    fin##Type(*(THIS)); \
    free(*(THIS)); \
    (*(THIS))=NULL; \
} \
void fin##Type(Type *THIS)

没错,就是这么简单!(当然如果你觉得这都算复杂了话,还是不要继续下面的文字了,这是我目前能做到的最简化的宏了)

下一节将会闲扯一些为什么这样设计,以及仅仅是这样还有那些不足.

 

2. < 从CLASS说起 >

 

C语言中直接使用结构体其实是件很麻烦的事,因为每次声明都要使用额外的"struct"来声明.一些初学C的人,特别从VC开始入门那些,总会分不清什么是C,什么是C++.特别是在struct声明这一块也特别容易按照懒省事儿的方法来写.但是我们应该知道一些事情----真正的纯C编译器如果不事先typedef一下是无法声明一个自定的结构体的实例的.

下面看一小段代码,演示如何将typedef与struct来结合.

typedef struct {int a;} A;
int main(){
        A obj;
        ojb.a=1;
        return 0;
}

关于typedef的语法,如果不牵扯结构体,是比较好理解的,比如"typedef int bool;"就是将原有的int型重定义为一个全新的bool型,尽管本质上它们是一样的.而一旦与结构体结合,就略微有些绕了.为了简化说明它的语法,上面的代码我做了一些处理,一是结构体本身匿名话,二是简化结构体内容并将其写到一行之内.这样我们看上去会十分明了还是"typedef struct ... A;"的形式,而"..."中我们其实定义了一个结构体的内容,只不过这个结构体本身是"匿名的".

更加常规的写法是,像下面这样.

typedef struct _A A;
struct _A{
        int a;
};
int main(){
        A obj;
        ojb.a=1;
        return 0;
}

CLASS宏中,也使用了和上面类似的做法来简化struct声明实例时的步骤.

#define CLASS(Type) \
typedef struct _##Type Type; \
void ini##Type(struct _##Type *); \
void fin##Type(struct _##Type *); \
Type * new##Type(); \
void del##Type(struct _##Type **); \
struct _##Type

CLASS宏不但简化了struct声明实例的工作,还声明了"四大函数"(将函数声明放在最前面是有好处的,它会让编译器在一开始就知道有这么些类型的函数).这四个函数其实是"两队",一对是ini和fin打头,另一对是new和del打头.其分别对应栈内存和堆内存上实例的构造与析构.

而CLASS宏的最后则是一个全开放的形式(这里的全开放是指某个宏不需要语气配合相对应的"截止"宏来保护一段代码),开头和结尾都是用C语言自身的"{"和"}"符合来指明.

在下一节,将会介绍用于构造和析构的CTOR和DTOR宏,而它们则封装了"四大函数"的具体实现.

 

3. < CTOR & DTOR >

这一节来说一下用于模拟构造函数的宏CTOR以及用于模拟析构函数的宏DTOR.先回顾下CTOR的定义.

#define CTOR(Type) \
Type * new##Type() { \
    Type *THIS; \
    THIS=(Type*)calloc(1,sizeof(Type)); \
    if( NULL==THIS ) { \
        return NULL; \
    } \
    ini##Type(THIS); \
    return THIS; \
} \
void ini##Type(Type * THIS)

假设我们还是要定义一个名为"A"的类,那么上面的代码中我们要把"Type"换成"A",并删除"##"(注意##在宏的使用中表示"连结").这样的话,我们就看到了一个完整的"newA"函数和一个没有写完的"iniA"函数(请参考上一节最后的"四大函数"一说).

"iniA"函数实际上是真正的构造函数,其负责一个"类"成员的初始化.但其没有写完,因为具体如何初始化是由我们自己说了算的,我们可以在其后面紧跟着写上"{ THIS->a=1; }"(假设A类中有int型成员变量a),这样就完成了一个简单的构造函数的实现.

我们要注意的是iniA函数要接受一个类实例指针,然后再对其进行初始化.也就是说这个函数本身是不涉及内存的分配的,我们可以传入一个指向栈内存的指针,也可以传入一个指向堆内存的指针,它都会对其进行"初始化"!但是如果我们使用堆内存,还要先定义一个指针,并指向一块malloc返回的内存再传入ini函数,这多少有些麻烦.为了方便的使用堆内存,上面的CTOR宏还给出了一个new函数,这里仍以一个名为"A"的类为例.newA就将malloc函数和iniA函数和二为一!我们使用的时候只需像"A * obj=newA();"这样即可,这大大方便了堆实例的使用!

不难发现,newA就是对iniA的一个简单封装,其做的工作就是"1.分配堆内存.2.调用ini函数.3.返回一个指向已分配内存的指针."这三块工作,这是有章可循的.也正因此,我们将其完全封装在CTOR宏之中.使用的时候我们几乎感觉不到它的存在,直接new就好.

下面再来看一下用于析构的DTOR宏,

#define DTOR(Type) \
void del##Type(Type **THIS) { \
    fin##Type(*(THIS)); \
    free(*(THIS)); \
    (*(THIS))=NULL; \
} \
void fin##Type(Type *THIS)

这里面定义了"四大函数"中用于析构的del函数和fin函数.其分别对应着前面的new函数和ini函数.我们可以对比着前面的讲述来理解.ini函数是真正的构造函数,fin函数是真正的析构函数.new是ini函数的一个封装,其做的具体工作就是先malloc在ini,del则是fin函数的一个封装,其做的具体工作是先fin再free.

当然因为有了new函数和del函数这样针对堆内存的封装,ini函数和fin函数更多的时候是与栈内存来配合使用的.

总的来看CTOR宏和DTOR宏,将涉及内存分配的部分独立出来可谓好处多多,不但可以更加自由且明确的使用栈内存和堆内存,还使得用于构造和析构的宏也能保持和前面CLASS宏一样的"全开放性",同时CTOR宏和DTOR宏几乎是完全对称的,这意味着我们只需理解一半,另一半就迎刃而解了.

 

Hello,OOC World![Chap1sec3-X][DRAFT 0.1.0518]

Posted on Tue, 17 May 2011 18:49:05 -1100

 

项目地址 OOC-GCC : http://code.google.com/p/ooc-gcc/ 源码以LGPL发布,文档为GFDL,文档相关测试用例为GPL3
 转载请注明出处
1.3 为什么不用CPP?
    CPP过于冗杂,标准不够统一.编译器干了太多的活,想弄明白需要相当的时间去折
腾,而想弄精则更难,学习曲线既长又陡. 而且很多人因为入门时拜师不慎(比如“邪恶
的M”),把C和C++混为一谈,而忽略了C++的复杂性,另外这导致了相当一部人拿着C++来
写C的代码! 这本没错,只是你不觉得这很另类很诡异么?!!!
    个人罗列几个问题,有些并不难,有的甚至都不算严格的问题,只是一些概念.但我
相信只学了两三个月的C++新手,总有没听说过或是不会的.
 
1. 引用和指针有哪些区别
2. 通过哪种方式可以屏蔽C++中默认构造函数的使用
3. 大陆编程书籍中常出现的接口这个概念,C++中有借口么或是有相关对应么,怎么用?
4. 友元这一概念存在的意义以及如何使用
5. C++结构体和类相比有哪些限制,可以在C++结构体中定义函数,静态函数,虚函数么?
6. C++重载是否适用于使用同名同参数但不同返回值的情形.
7. 解释虚表虚函数的概念
8. C++中auto_ptr(只能指针)是怎么回事儿,有何作用,怎么用?!
9. C++中多重继承如何避免名称冲突
10.解释下隐式类型转换
11.C++可以用哪些类型转换措施,有静态动态之分么,向上向下都允许么,如果有限制硬是
使用会产生哪类问题.
12.我想定义个函数指针指向某类中的一个方法,怎么做?如果是指向类中的一个虚方法或
是静态方法,有却别么?
13.virtual和rtti有何关系
14.如何使用c++中的rtti
15.类中的函数,虚函数,静态函数是按类来分配还是按实例来分配.
16.类中的函数,虚函数,静态函数的具体内存分布大致是什么样的,先后顺序如何.
17.不同类的函数,虚函数,静态函数是在统一的一大块区域中分配,还是离散的毫无关联
的.
18.如何获得一个类的虚表指针.
19.如何重载或是能否重载+,++,<<,=,==这些运算符,重载时有限制么?
20.如何或是能否声明一个函数指针,其参数或返回值有模板参数.
21.rtti的使用有何副作用
22...bla..bla..
 
    这里面大部分问题我还是知道或是有印象的,有一小部分我也比较好奇.不过上面的
问题还不涉及C语法中相对复杂的东西(比如复杂指针的使用). 总之想真正掌握C已经不
容易了,再要引入这么多东西真是有点吃饱撑的感觉了,总之我表示我的脑容量有限,估计
从一点不知道开始学, 要花很长时间来学,而且如果用的不多或是一段时间不用,可能又
忘了,可能细节就往干了,可能要从头再来.人生苦短,如果你真想学的又快又好又高级的
语言个人建议你还是用python吧.如果你要编译后的高效,不妨试试go语言.或者仅仅学
习C语言,再学习下这份简单的OOC教程.
 
    开始下一节前说一下,本手册“热身运动”一节介绍了一种简单且对称的OO风格,
“伸展运动”介绍了一些宏来简化这一部分的代码. 当然这两节中所涉及的都比较简
单,主要是为了便于大家的理解和使用.后面的章节则会循序渐进的加强这些代码! 从下
节开始真正就有代码了,如果你是从头看到这里,相信你已经受够我的啰嗦了.
 
1.4 一个结构体+一个函数?!
#include <stdio.h>
struct A {
    int a;
};
void iniA(struct A *THIS){
    THIS->a=100;
}
int main(){
    struct A obj;
    iniA(&obj);
    printf("the value of obj's a is %d\n",obj.a);
    return 0;
}
    个代码?干啥的?
    个例子很是简单,只涉及一个结构体的定义和一个相关初始化函数,但在这份手册
中却有着非同一般的意义,而一切用C去模拟OO的东西也源于此.
    谓面向对象的编程,其本质目的是把数据一层一层封装起来从而使代码看上去更
加易懂,更易维护.单从封装数据的角度来看,C语言中的结构体足矣.而封装之后如何
使用它,自然要涉及所谓的构造函数,对应到上面的代码,就是那个名为iniA的函数啦.
 
1.5 不得不说的函数指针
    面的代码似乎过于简单了,肯定会有人说这不是面向对象的编程,从个人观点来看,
面向对象只是一种思想,而这种思想也主要用在数据的封装上,而上面的代码其实已经
体现了一点点.而下面将要做的就是一点点对其加强.
#include <stdio.h>
struct A {
    int a;
    void (*showA)(struct A *);
};
    static void A_showA(struct A *THIS){
    printf("the value of obj’s a is %d\n",THIS->a);
}
void iniA(struct A *THIS){
    THIS->a=100;
    THIS->showA=A_showA;
}
int main(){
    struct A obj;
    iniA(&obj);
    obj.showA(&obj);
    return 0;
}
    C语言中没有C++所谓的“方法”,其实也没有这个必要,因为本质上那就是一个函
数.还有些语言只有所谓的“过程”,他们本质上都是一样的(后面的文字不再区分这三个
概念,如遇到都理解成C语言中的函数就好,同样后面关于类和结构的称呼只要是在C语言
中本质也是一样的),只是细节上有些许差异. 另外要说的是C语言的结构体是可以包含
函数指针的,这一特性也使得用C去模拟对象变得可行且有意义.比如上面的代码就演示如
何使用“封装”在结构体重的函数指针.
 
1.6 栈内存vs堆内存
    针对于初学C的人来说是个难点,而更要命的是堆内存和栈内存的问题(如果你仍不
清楚这个问题,那么先回去补一补C语言的基础,我在这里不在赘述,因为这一块是会者不
难,但是如果不会讲起来可啰嗦了). 个人认为国内学生初学C语言时容易犯这样的错误
很大的一个原因就是入门教材一直用的是国内最流行的那本...
    于模拟OO的编程,分清堆内存和栈内存是很有必要的,特别是堆内存的使用会使你
的程序具备一定的“动态”特性. 但使用堆内存有利也有弊,特别对于C乃至C++这样的
语言来说,使用不当就杯具了,内存泄露这样的问题也源于此.下面再看一段代码
#include <stdio.h>
#include <stdlib.h>
struct A {
    int a;
    void (*showA)(struct A *);
};
static void A_showA(struct A *THIS){
    printf("the value of obj’s a is %d\n",THIS->a);
}
void iniA(struct A *THIS){
    THIS->a=100;
    THIS->showA=A_showA;
}
struct A * newA(){
    struct A * mem=(struct A*)malloc(sizeof(struct A));
    iniA(mem);
    return mem;
}
int main(){
    struct A * obj=newA();
    obj->showA(obj);
    free(obj);
    return 0;
}
    里要说明一下,iniA和newA这两个函数是为了模拟构造函数,本质上iniA进行真正
的初始化,newA实际上在外边又封装了一次,在堆内存上分配这个结构体. 这两个函数让
我们可以按需在堆上(newA)或栈上(iniA)声明一个结构实例,其实也就是本手册要模拟
的所谓的“类”.
    得注意的是上面的代码有一点很不爽的地方就是最后还要free一下堆内存,而与之
对应的malloc则放在了newA这个函数中, 这样的代码让我很是不爽,所以下面继续完善,
引入“析构”函数的模拟.
 
1.7 对称之美
    C++中有构造也有析构,只是编译器替我们做了太多的工作,以致有些初学者对此毫
不知情.这是我反感C++的原因之一,它掩饰了太多的东西,虽然有时看上去简单了, 但如
果你不了解底层,很多东西会觉得很不明晰,也因此这是门初学更容易犯错的语言.
    面要说的就是析构函数的模拟了.有了构造,再有析构,代码才会有对称性,至少看
上去才更加的OO.
#include <stdio.h>
#include <stdlib.h>
struct A {
    int a;
    void (*showA)(struct A *);
};
static void A_showA(struct A *THIS){
    printf("the value of obj’s a is %d\n",THIS->a);
}
void iniA(struct A *THIS){
    THIS->a=100;
    THIS->showA=A_showA;
}
struct A * newA(){
    struct A * mem=(struct A*)malloc(sizeof(struct A));
    iniA(mem);
    return mem;
}
void finA(struct A *THIS){
    THIS->a=0;
    THIS->showA=NULL;
}
void delA(struct A **THIS){
    finA(*THIS);
    free(*THIS);
    (*THIS)=NULL;
}
int main(){
    struct A * obj=newA();
    obj->showA(obj);
    delA(&obj);
    printf("is obj NULL ?\n%s\n",obj==NULL?"True":"False");
    return 0;
}
    finA函数比较简单,其对应构造用函数iniA,是真正的析构部分. 而delA则对
应newA,仅仅是又把finA再次封装了一下,但要注意的是这个例子中用到了指向指针的
指针(其实也可以普通的指针), 这样做有一个好处,比如上面的代码,在delA执行之
后,obj已经指向NULL了,如果我们再次使用已经执行过析构的obj对象,则错误会比较明
显. 最后的那个打印函数也是为了验证obj释放后置零这一特性.
    在趁热打铁,总结一下本手册以后常用的几个重要函数(假定我们有一个类名
曰Class),下面为了加强记忆放在一起总结一下.
 
1. void iniClass(Class *THIS); →→ 用于栈上对象的构造,
    ini作为一个prefix(前缀),表示初始的意思,相关词汇initialization,initiate,initial
 
2. void finClass(Class *THIS); →→ 用于栈上对象的析构,
    fin作为一个prefix(前缀),表示终止的意思,相关词汇finish,final
 
3. Class * newClass(void); →→ 用于堆上对象的构造,是iniClass的封装,
    new作为一个prefix(前缀),表示新建的意思,相关词汇new,neo-系部分词汇,
    在某些语言中直接  对应new这个KeyWord
 
4. void delClass(Class **THIS); →→ 用于堆上对象的析构,是finClass的封装,
    del作为一个prefix(前缀),表示删除的意思,相关词汇delete,de-系部分词汇,
    在某些语言中直接对应delete这个KeyWord
 
1.8 继承与多态
    C++的编程中关于“数据封装”有两种关系很是重要,一是继承关系,另一是包含关
系.本质上其实一样的,都是“包含”, 只不过C++的编译器再度不辞劳苦的帮我们做了
点工作.让所谓的继承关系使用起来似乎容易了些.
    C语言本身没有继承方面的语法糖,但是包含关系应该是所有的计算机语言都能描述
的,因为汇编都可以,其它更高级的语言自然也能描述. 在C语言中,一种常见的模拟继承
的做法是把父类放在子类的首部. 具体见下面的代码.
#include <stdio.h>
#include <stdlib.h>
struct A {
    int a;
    void (*show)(void *);
};
static void A_showA(struct A *THIS){
    printf("the value of obj’s a is %d\n",THIS->a);
}
void iniA(struct A *THIS){
    THIS->a=100;
    THIS->show=(void *)A_showA;
}
struct B {
    struct A A;
    int b;
};
static void B_showB(struct B *THIS){
    printf("the value of obj’s a is %d\n"
    "the value of obj’s b is %d\n",
    THIS->A.a,THIS->b);
}
void iniB(struct B *THIS){
    iniA((struct A*)THIS);
    THIS->b=200;
    ((struct A*)THIS)->show=(void *)B_showB;
}
int main(){
    struct B obj;
    iniB(&obj);
    struct A *s=&(obj.A);
    s->show(&obj);
    return 0;
}
    为上面这段代码只使用了栈内存,而对象本身也没有指向堆内存的指针,所以为了
简便只保留了“四大函数”中的ini系列. 同时也有一些细节上的修改,比如A中的函
数showA改为show,这是为了演示如何在子类中重写父类的函数.
    们假定子类B继承了父类A,自然用C的包含关系去模拟继承关系时应将父类放在首
部(当然也有些特殊应用要求统一放在尾部),这样做的一个好处是方便指针型的强制转
换. 而在使用的时候这种子类指针型强制转型成父类函数指针型的做法一般被称为“向
上转型”,在使用的时候我们用的其实是一个父类指针,这样的“数据抉择” 体现了“多
态”的思想.在父类中定义方法(接口) ,子类中具体实现.使用时则通过父类的形式来
调用.
 
1.9 贴心的匿名结构体
    一段代码相比会让大家觉得有些丑陋,因为用到了不少强制转换.其实现代的
主流C编译器都支持匿名结构体(anonymous struct或unamed struct)这一特性.这
样用C去模拟OO的继承关系时就更加舒服了.下面的代码和上一段代码功能完全相同,但得
益于匿名结构体这一特性,看上去更加悦目了.
#include <stdio.h>
#include <stdlib.h>
struct A {
    int a;
    void (*show)(void *);
};
static void A_showA(struct A *THIS){
    printf("the value of obj’s a is %d\n",THIS->a);
}
void iniA(struct A *THIS){
    THIS->a=100;
    THIS->show=(void *)A_showA;
}
struct B {
    struct A;
    int b;
};
static void B_showB(struct B *THIS){
    printf("the value of obj’s a is %d\n"
    "the value of obj’s b is %d\n",
    THIS->a,THIS->b);
}
void iniB(struct B *THIS){
    iniA((struct A*)THIS);
    THIS->b=200;
    THIS->show=(void *)B_showB;
}
int main(){
    struct B obj;
    iniB(&obj);
    obj.show(&obj);
    return 0;
}
    名结构体的使用可以让我们省去不少强制转换的麻烦.但是,有一个很大的问题就
是当有重名成员时到底如何处理,是直接报错, 还是不额外多分配重名成员所占的内存,
还是额外分配重名成员所占的内存.关于这个问题GCC4.5和GCC 4.6的处理方式多少有些
不同, 具体可以参见GCC testsuite中关于anonymous struct的部分.
这里多啰嗦几句,GCC原生支持的如下这种匿名结构体,暂用Type A简记
stuct B{
    struct A{
        int a;
    };
    int b;
};
注意A是在B中声明的
而据GCC官方文档所说,M$的编译器则原生支持如下形式的,暂用Type B简记
struct A{
    int a;
};
stuct B{
    struct A;
    int b;
};
     果想让GCC支持Type B这种类型的匿名结构体,4.5版本在编译时须加
上-fms-extensions, 而4.6版本则加上-fplan9-extensions,比如上面的代码如果
要用GCC编译,就应加上这些选项.
 
1.10 休息一下
    这一节,仅仅是回顾与总结
    个人认为前面已经大致说清了OO思想中关于“数据封装”部分最为重要一些东西.
而要用C来模拟OO,首先要解决则是如下的一些问题
 
1. 结构定义        2. 构造方法
3. 析构方法        4. 包含关系
 
 
    果你逐行的看完前面的代码,一定会觉得很累,虽然他们实现的功能很简单,仅仅是
打印一两个数值而已.由此看来上面的工作似乎时间扯淡无比的事情啊!!!
    错,上面的代码的确无比扯淡,很适合我们吃饱了撑着的时候去研究,也省的吃吗丁
啉了.但是,这种一层层的封装本身还是很有必要的, 只是我们不应重复的去敲这么多的
代码,也不应去记忆那么多繁琐的细节.
    得庆幸的是C语言是支持宏定义的,想想<<电子世界争霸战>>中那些华丽的nested
macros!但是程序毕竟不是时装秀,宏的滥用也绝不是件好事. 也因此很多更为高级的语
言抛弃了这一可能带来灾难错误的特性.但是不可否认,一旦某些宏称为一种约定,则是既
简洁有好用的.
    下面的章节中, 我会使用三个宏CLASS,CTOR( 对应CONSTRUCTOR) 和DTOR( 对
应DESTRUCTOR),通过它们来完成类本身的设计, 以及构造函数和析构函数的设计,进而
简化上面的OO风格所带来的冗余代码. 如果把这三个宏展开来看,和本章的代码并无差
异.

OOC与CPP生成代码大小对比

Posted on Sun, 15 May 2011 22:45:46 -1100

今天做了一个简单的对比测试,看一下gcc模拟oo与g++生成代码(strip无用符号后)大小的比较

下面附上测试demo,先来OOC的

 

/*
 * test_Animals.c
 *
 *  Created on: 2011-5-16
 *      Author: Jesse Meng [pingf0@gmail.com]
 */
#include "OOStd.h"

CLASS(Animal){
	char *name;
	STATIC(Animal)
	vFn talk;
};
int Animal_reload(Animal *THIS,char *name){
	THIS->name=ALLOC(strlen(name)+1);
	memcpy(THIS->name,name,strlen(name));
	return 0;
}
int Animal_unload(Animal *THIS,void *PARAM){
	FREE(THIS->name);
	return 0;
}
ASM(Animal,Animal_reload,Animal_unload,NULL,NULL)

CLASS_EX(Animal,Cat){
	STATIC_EX(Animal,Cat)
};
static void Meow(Animal *THIS){
	printf("Meow!My name is %s!\n",THIS->name);
}
int Cat_reloadSt(StAnimal *THIS,void *PARAM){
	THIS->talk=(void *)Meow;
	return 0;
}
ASM_EX(Animal,Cat,NULL,NULL,Cat_reloadSt,NULL)


CLASS_EX(Animal,Dog){
	STATIC_EX(Animal,Dog)
};
static void Woof(Animal *THIS){
	printf("Woof!My name is %s!\n",THIS->name);
}
int Dog_reloadSt(StAnimal *THIS,void *PARAM){
	THIS->talk=(void *)Woof;
	return 0;
}
ASM_EX(Animal,Dog,NULL,NULL,Dog_reloadSt,NULL)

int main(){

	Animal *a[]={
			///////////////////////////
			(void*)newDog("Jack"),
			(void *)newCat("Lily"),
			(void*)newDog("Mike"),
			(void *)newCat("Lucy"),

//       100个,这里懒得写了,因为测试时是cccv的 .....

	};

	int i=0;
	//StAnimal *f=ST((Dog*)(a[0]));
	StAnimal *f=ST((Dog*)(a[0]));
	for(i=0;i<100;++i){
		f->talk(a[i]);
	}

	for(i=0;i<100;++i){
		//delDog((Dog**)&(a[i]),NULL);
		delDog((Dog**)&(a[i]),NULL);
	}
	return 0;
}

下面来看下CPP的

 

#include <iostream>
#include <string>

using namespace std;

class Animal
{
        public:
        Animal(const string& name) : name(name) {}
        virtual void talk() = 0;
        const string name;
};

class Cat : public Animal
{
        public:
        Cat(const string& name) : Animal(name) {}
        virtual void talk() { cout<<"Meow!Myname is "+name<<endl; }
};

class Dog : public Animal
{
        public:
        Dog(const string& name) : Animal(name) {}
        virtual void talk() { cout<<"Woof!Myname is "+name<<endl; }
};
 
int main()
{
        Animal* animals[] =
        {
        		///////////////
        	new Dog("Jack"),
        	new Dog("Mike"),
                new Cat("Lily"),
                new Cat("Lucy"),
//     这里同样100个 .....

        };

        for(int i = 0; i < 100; i++){
            animals[i]->talk();
        }
        for(int i = 0; i < 100; i++){
        	delete animals[i];
        }
        return 0;
}

尽管我已经将OOC-GCC写的足够简单了,但还是比CPP写出来稍长一点(感觉还好一般20行左右)

不过编译出来的我还是比较满意的,

上面两段代码输出一致,风格一致,都是最基本的形式.

类设计为一个基类,两个近似的继承类,100个任意继承类的对象.

先生成100堆对象,执行100次各对象的基本方法,然后逐一销毁.

实际测试后cpp编译出来的体积为32246,通过OOC-GCC宏出来仅为21518(而这里面还包括内存分配日志,时间测试等函数的调用)

这个多少有点出乎我的意料,因为OOC-GCC通过宏引入了相当多的辅助函数,比如上面的实际用到的关于对象辅助生成的函数应该至少有24个,而且每个体积也不小的.

 

Hello,OOC World![Chap1Sec1-2][DRAFT]

Posted on Sun, 15 May 2011 18:17:32 -1100
项目地址 OOC-GCC : http://code.google.com/p/ooc-gcc/ 源码以LGPL发布,文档为GPL3
 转载请注明出处
1.1 什么是OOC
 
    OOC即Object Oritened C,当然也有些人称之为OOPC,多出来的P指Programming.
而OO指面向对象,当然有OB(Object Based)一说,就是基于对象,在这两个名词之间不多
啰嗦,一句话概括的化,OO比OB更“纯”一些,尽管这样概括并不够准确.
本手册所说的面向对象的C主要涉及两个方面,风格与简化!
 
OOC涉及的两个主要方面
 
1. 采用何种面向对象的编码风格
2. 如何简化因为使用某种编码风格而产生的额外代码
 
    一方面不同的人会有不同的见解,虽然千差万别,但却大同小异. 第二方面主要的
实现机制还有两种,一种是通过独立的预处理程序来生成额外的代码,另一种是用C语言本
身支持的宏特性来简化. 前者可以让代码获得更多的特性,后者可能无法很好的简化部
分因为OO风格而产生的冗余代码,但是使用起来却更加方便.
    然准确一些的话OOC还应该有一个较为完整的类型系统,比如GObject库这方面做
的比较全,但是个人认为其前两方面做得不怎么样,所以用纯C开发起来并不方便.
    手册主要是围绕一个名为OOC-GCC的小项目展开的,自然介绍的是其中采用的OO风
格,而这个项目本身针对更多是纯C的开发, 并且在使用上尽力做到简易,所以采用了宏来
完成“偷懒”的工作.
 
1.2 面向对象中常涉及的概念
 
1. 类,成员,接口与方法
2. 包,模块,命名空间,访问权限
3. 重载,重写,继承,多态,泛型,反射与自省
 
    面写的东西可能不全面,但即使这些展开说也多了去了,而且具体到某些细节我
的理解也不一定全都正确. 所以一来为了简洁,二来为了不去一不小心误人子弟.这些东
西我争取用一两句话来介绍.而因为本手册是针对C来使用的,也会给出一些在C语言里的
对应.
    及类成员是啥我就不多说了,就像C中的结构与结构成员.接口与方法到C里面都指
函数.tmd也不知哪个龟孙开始提出的OO,整出这么多概念来. C语言常用结构体来模拟
类,自然普通结构成员就对应类成员,而类的方法采用函数指针来模拟,个人的理解是这种
指针的模拟都对应接口或是虚方法. 当我们把某个函数指针指向具体的函数,就相当于
真正的实现了某个接口或是将虚方法化虚为实.
    C语言里没有什么包,命名空间等这类东西,不过模块化的C一般就是采用独立文件
的编写方式. 常把一个模块放在一个.c和一个.h文件中,不同的模块经过编译产生了
众多的.o文件,再通过链接将各个.o统一起来. 关于访问权限,一般的OO语言常涉
及public,private,protected这些,主要是限制是类中访问与类外访问以及继承时的权
限. C语言可没这么多条条框框,大体上除了局部括号内受限以及static关键字限定某些
文件内访问.其它的都很自由,你自己掌控一切.
    于继承,我见过两种描述,当然个人觉得这两种说法并不冲突,只是侧重点不太
相同. 一种感觉重在描述“数据抉择”这一块,就是如何去使用数据. 其说法是子类
使用父类的方法这是继承,而子类实现父类的接口在“向上转型”成父类再来使用以获得
不同的实现这是多态(的一种体现). 另一种关于继承的解释重在“数据封装”,就是继承
就是子类包含父类的东西,从本之上说是一种包含关系.
    人比较喜欢从“数据封装”方面来阐述继承,因为这更和字面意思,和现实生活也
更加对应.比如儿子继承母亲的某些性格特征, 继承遗产等等,侧重的也是“数据封装”.
当然现实中的继承远比程序中的复杂,而且很多现实继承是那种父类大,子类小,子类局部
继承的情况. 而程序中的继承基本都是由小到大的模式,这就要求我们进一步分析并
采用一些模式来完成数据的抽象.
    “数据封装”方面来看代码中的继承,大体也有两种.一种是一般成员继承, 这
种就是前面从“数据抉择”上的通过子类使用父类,另一种是接口等的继承,这种具体
实现是在子类中完成的, 但使用时以父类的形式使用,如前所述“数据抉择”上通过父类
使用子类的情形,一个父类定义接口,有很多子类的不同实现,这就体现了多态这一概念.
用C语言来完成“数据封装”上的继承,一种常规方式是子类结构体包含父类结构体
的形式.并且把父类结构体放在子类结构体的首部, 这样做的一个好处是当我们在堆上分
配一个对象,只需强制转换其指针到父类指针形态,就可以按照父类来使用了,当然这种
使用本身看上去是通过父类使用子类,也就是多态(的一种).
   接下来说一下重载重写等相关的定义,这一块我直接用英文了(因为我有些整体相当不
错的书的中译这一块翻译上总有差异), 就是overload,override,overwrite(还有人
有时还会用到一个词overlap).关于这一块我想多说几句,就是关于这几个词不同语言上
也可能有一些细微的差异. 而且很多所谓的OO语言对这一块虽然支持,但并不完美,尤其
体现在overload运算符以及返回值的处理的支持上. 简单说overload是允许某类中名
称相同但参数等不同的方法的存在,这需要编译器或解释器能够通过传入参数等进行判断
进而确定到底是用哪个方法. override和overwrite都是子类中有和父类重名的方法,
不同的是overwrite会抹杀掉父类的方法,而override只是隐藏了父类的方法. 从C语言
来说,因为方法都是用函数指针来模拟,所以这些都可以模拟,但是都不是自动的,需要相
当的额外代码来辅助,甚至是需要构件一个抽象类作为中间层来模拟.
    overXXXX系的对比着可以说一下的就是泛型,针对的是几个不同参数的方法,让编
译器根据参数等来判断.泛型通常是只写一个方法,但是参数采用抽象的形式, 编译器通
过类型推衍等来判断具体是什么数据(当然这么概括泛型是为了简便,可能不太全面).
    后说一下反射(reflection)和自省(introspection),这是更加动态的机制,可能
需要类型系统等的支持.这两个概念有些人分不清,也有些人干脆把他们揉在一起来理解,
不过这也不影响,只要知道其动态特性怎么用就好. 简单的说反射式通过一个动态的字
符串,获得相应的类或方法等.自省则是通过一个已有的但未知类型的实例直接获得其相
关信息或是直接使用.C99好像支持了__TYPEOF__(gnu套件一般用小写的typeof),有点
自省的意思了. 但是去模拟反射却不容易,需要额外的数据结构以及约定好的类型系统
库才行(个人觉得).还有要说的是反射和自省看似美丽但是用起来代价也是不小的,比如
额外引入的数据处理对性能影响比较大, 很多时候使用这些机制会将代码变得冗长难懂,
类型不安全等.
    广义上讲多态就是只从多种数据中选择需要的适合的来用,从这个角度来说它与前面
说的“两大部分”中“数据抉择”部分直接对应了.这样理解的话,它就包含了前面说
的overload,泛型,反射,自省等等. 而广义上的继承则也占据了“数据封装”中的相当
一部分.因此某种意义上说广义的继承广义的多态就是OO的核心(不要动不动就下定
义,所以我加了个限定词“某种意义上”)
    C去模拟OO,其实无论“数据封装”或是“数据抉择”哪一部分都能模拟,只是模拟
的时候额外的代码量也是相当可观的. 而单从用宏来简化这些额外代码上,主要能简化
的就是“数据封装”的部分,“数据抉择”部分如果想要简化最好还是通过间接代码生成
的方式. 也因此OOC-GCC中的宏主要是在解决“数据封装”方面的问题,对于“数据抉
择”方面的OO模拟还是要写不少额外的代码(最近测试了一些和完全对应的C++代码
编译出来大小相差不大,相对还小些,特别使用到堆内存的时候).
 
注意
 
上面的概念可能有不少地方不够准确或是存在错误, 如果有所发现, 还请联系
我pingf0@gmail.com. 另外后面的章节尽量不再解释和OO相关的抽象概念,如有需要,
请参阅此节.
 

OOC-GCC基本结构图[0.6][终于有时间整点相关的文档了]

Posted on Thu, 07 Apr 2011 18:44:24 -1100

这个图片主要是说明OOC_GCC中关于类的宏的基本结构[v0.6]

[注:此图不包括单根继承部分]

http://code.google.com/p/ooc-gcc/downloads/list

注意编译时要开启-fms-extensions[GCC 4.5]或-fplan9-extensions[GCC 4.6]

OOC_GCC Basic Structure

OOC-GCC新的测试用例[0.6]

Posted on Fri, 25 Mar 2011 04:57:50 -1100

这个例子主要用来掩饰如何实现继承关系[v0.6]

http://code.google.com/p/ooc-gcc/downloads/list

 

//
#include "inc/core/OOStd.h"
 
CLASS(Human){
    int age;
    int gender;
    STATIC(Human)
    iFn sayHi;
};
ASM(Human,NULL,NULL,NULL,NULL) 
 
CLASS_EX(Human,Mom){    
    STATIC_EX(Human,Mom)  
};
int Mom_sayHi(Mom * THIS){
    printf("Hi,Mom age:%d gender:%s\n",THIS->age,THIS->gender?"male":"female");
    return 0;
} 

int Mom_reload(Mom * THIS,void *PARAM){
    printf("%s %p\n",__func__,THIS);
    THIS->age=36;
    THIS->gender=0;
    return 0;
}
int Mom_unload(Mom * THIS,void *PARAM){
    printf("%s %p\n",__func__,THIS);
    return 0;
}
 
int Mom_reloadStatic(StaticMom * THIS,void *PARAM){
    printf("%s %p\n",__func__,THIS);
    THIS->sayHi=(void *)Mom_sayHi;
    return 0;
}
int Mom_unloadStatic(StaticMom * THIS,void *PARAM){
    printf("%s %p\n",__func__,THIS);
    THIS->sayHi=NULL;
    return 0;
}
ASM_EX(Human,Mom,Mom_reload,Mom_unload,Mom_reloadStatic,Mom_unloadStatic)

CLASS_EX(Mom,Kid){
    int id;
    STATIC_EX(Mom,Kid) 
};

int Kid_sayHi(Kid * THIS){
    printf("Hi,Kid%d age:%d gender:%s\n",THIS->id,THIS->age,THIS->gender?"male":"female");
    return 0;
}

int Kid_reload(Kid * THIS,void *PARAM){
    static int id=0;
    printf("%s %p\n",__func__,THIS);
    THIS->age=3;
    THIS->gender=1;
    THIS->id=++id;
    return 0;
}
int Kid_unload(Kid * THIS,void *PARAM){
    printf("%s %p\n",__func__,THIS);
    return 0;
}
 
int Kid_reloadStatic(StaticKid * THIS,void *PARAM){
    printf("%s %p\n",__func__,THIS);
    THIS->sayHi=(void *)Kid_sayHi;
    return 0;
}
int Kid_unloadStatic(StaticKid * THIS,void *PARAM){
    printf("%s %p\n",__func__,THIS);
    THIS->sayHi=NULL;
    return 0;
}
ASM_EX(Mom,Kid,Kid_reload,Kid_unload,Kid_reloadStatic,Kid_unloadStatic)
 
int main(){
     
    printf("\n-----------\n");
    Mom * m=newMom(NULL);
    printf("\n-----------\n");
    StaticMom *stm=ST(m);
    Kid *k1=newKid(NULL);
    printf("\n-----------\n");
    Kid *k2=newKid(NULL);
    printf("\n-----------\n");
    Kid *k3=newKid(NULL);
    printf("\n-----------\n");
    stm->sayHi(m);
    printf("\n-----------\n");
    StaticKid *stk=ST(k1);
    stk->sayHi(k1);
    printf("\n-----------\n");
    stk->sayHi(k2);
    printf("\n-----------\n");
    stk->sayHi(k3);
    printf("\n-----------\n");
    delKid(&k1,NULL);
    printf("\n-----------\n");
    delKid(&k2,NULL);
    printf("\n-----------\n");
    delKid(&k3,NULL);
    printf("\n-----------\n");
    delMom(&m,NULL);
    printf("\n-----------\n");
    
    return 0;
}
正确的输出为,
-----------
Mom_reloadStatic 003E2BF0
Mom_reload 003E2BD0

-----------
Mom_reload 003E2C18
Mom_reloadStatic 003E2C40
Kid_reloadStatic 003E2C40
Kid_reload 003E2C18

-----------
Mom_reload 003E2C80
Kid_reload 003E2C80

-----------
Mom_reload 003E2CA8
Kid_reload 003E2CA8

-----------
Hi,Mom age:36 gender:female

-----------
Hi,Kid1 age:3 gender:male

-----------
Hi,Kid2 age:3 gender:male

-----------
Hi,Kid3 age:3 gender:male

-----------
Kid_unload 003E2C18
Mom_unload 003E2C18

-----------
Kid_unload 003E2C80
Mom_unload 003E2C80

-----------
Kid_unload 003E2CA8
Kid_unloadStatic 003E2C40
Mom_unloadStatic 003E2C40
Mom_unload 003E2CA8

-----------
Mom_unload 003E2BD0
Mom_unloadStatic 003E2BF0

-----------

Time Elapsed
Seconds : 0
Microseconds : 0
另外内存分配检测Log输出为
[,][;]
+,0x003E2BD0,20;
+,0x003E4CF0,8;
+,0x003E2BF0,32;
+,0x003E2C18,28;
+,0x003E2C40,52;
+,0x003E2C80,28;
+,0x003E2CA8,28;
-,0x003E2C18;
-,0x003E2C80;
-,0x003E2C40;
-,0x003E2CA8;
-,0x003E2BF0;
-,0x003E4CF0;
-,0x003E2BD0;
!,0;
最后的0表示分配的都释放了,而前面的数据可以配合graphviz来实现一些图表
具体文件见项目地址,我一直比较懒,懒得用svn,git这些工具,因为这个目前依旧是自己自娱自乐
P.S.
注意编译时要开启-fms-extensions[GCC 4.5]或-fplan9-extensions[GCC 4.6]

Proteus中8259的仿真[无dos,纯手工]

Posted on Wed, 16 Mar 2011 14:05:32 -1100

因为研究生复试的需要所以最近折腾了下8086的汇编, 
不过proteus总是没有我预期的完美[可能也跟和谐的版本有关] 
下面是折腾8259的一些总结,不过有一些问题,希望知道的能予以指正 
 
电路搭建,基于自带的例子 (原文件名:8259_1.png) 

 
程序源码 (原文件名:8259_2.png) 

 
断点设置 (原文件名:8259_3.png) 

 
仿真1 (原文件名:8259_4.png) 

 
仿真2 (原文件名:8259_5.png) 



另外,一些问题罗列如下 
-.我用的老王的77sp2和谐,不过逻辑仿真器貌似用不了啊 
-.8259模型需要改一下[加几个字符]才能用于仿真 
0.一开始会莫名其妙的产生一个终端,一般位于前16字节,所以我的程序在那里也设置了捕获的proc,要不然会跑飞,不知为何 
1.下一次触发时才会相应正确的中断,而当前相应的为上一次的?不过Simulation Log里的信息倒是正确的 
2.级联模式,次级的irr不会自动清除,至少我觉的应该自动清除的,导致响应时会被进制,当然可能是我程序的问题 
3.软件触发的中断没问题,直接int xx出来的中断都可以正确的响应,个人感觉要么是我程序的问题,要么是8259模型的问题 
4.Proteus中仿真用exe和com格式的都可以,我的程序是使用masm的,顺带试了下mingw套件里的gas汇编,com格式输出时也是可以的 
  不过没法调试,即使-gstabs加上了也不行. 

*.上面的问题可能是本人程序上的错误,希望熟悉8086汇编的人能指正一下 
工程文件ourdev_623172B3W6H8.rar(文件大小:32K) (原文件名:8086 Demo Board.rar)  [放在ourdev上了]

源程序

.MODEL  SMALL
 
io8259_main_addr equ 0600H        
io8259_sub_addr  equ 0800H        

.8086
.code
        io8259_std1_init macro base,icw1_val,icw2_val,icw4_val
                mov dx,base
                mov al,icw1_val  
                out dx,al
                
                mov dx,base+2
                mov al,icw2_val
                out dx,al
                
                mov al,icw4_val
                out dx,al
        endm
        io8259_std_init macro base,icw1_val,icw2_val,icw3_val,icw4_val
                mov dx,base
                mov al,icw1_val  
                out dx,al
                
                mov dx,base+2
                mov al,icw2_val
                out dx,al
                
                mov al,icw3_val
                out dx,al
                
                mov al,icw4_val
                out dx,al
        endm
        
        setup_int macro int_irq,int_cs,int_ip
                push di
                push ds
                mov di,0
                mov ds,di
                mov di,(int_irq)*4
                mov word ptr ds:[di],int_ip
                mov word ptr ds:[di+2],int_cs
                pop ds
                pop di
        endm
        setup_ints macro int_irq,int_cs,int_ip
                setup_int int_irq,int_cs,int_ip
                setup_int (int_irq+1),int_cs,int_ip
                setup_int (int_irq+2),int_cs,int_ip
                setup_int (int_irq+3),int_cs,int_ip
                setup_int (int_irq+4),int_cs,int_ip
                setup_int (int_irq+5),int_cs,int_ip
                setup_int (int_irq+6),int_cs,int_ip
                setup_int (int_irq+7),int_cs,int_ip
        endm

        intxx proc far
                iret
        intxx endp
        intl1 proc far
                iret
        intl1 endp
        intl2 proc far
                iret
        intl2 endp
        intl1_s proc far
                iret
        intl1_s endp
        intl2_s proc far
                iret
        intl2_s endp
        


.startup
        cli
       
        setup_ints 00h,seg intxx,offset intxx
        setup_ints 08h,seg intxx,offset intxx
        setup_ints 10h,seg intxx,offset intxx
        setup_ints 18h,seg intxx,offset intxx
        
        setup_int 30h,seg intl1,offset intl1
        setup_int 31h,seg intl2,offset intl2
        setup_int 40h,seg intl1_s,offset intl1_s
        setup_int 41h,seg intl2_s,offset intl2_s
   main:
        io8259_std_init io8259_main_addr,\ 
                        11h,30h,04h,13h
        ;io8259_std1_init io8259_main_addr,\ 
                        ;13h,30h,03h
   slave:
        io8259_std_init io8259_sub_addr,\ 
                        11h,40h,02h,03h

        sti
        jmp $

.data
.stack
END
编译链接选项[masm] 
ml /c /Zd /Zi sample.asm  
link16 /CODEVIEW sample.obj,sample.exe,,,nul.def 

 

Win32下AT&T汇编Hello world教程

Posted on Tue, 08 Mar 2011 18:50:26 -1100

因为复试的需要,学习了下和汇编相关的东西,

虽然当年学过微机原理,不过没下功夫[其实是老师水,教材水啊....]

现在只能恶补了

这两天玩了下masm,nasm,gas,当然只是搭建了基本的环境,跑了下基本的实例

因为gas相关文档比较稀缺,所以本篇日志记录一些和其相关的

主要目的是备份一下,省的以后忘了

顺便说一下,我用的是和谐的WinXP系统

1.环境搭建,从sf下MinGW套件,这一步不在赘述

2.基本的程序

先来一个最简单的hello world,不过用到了c运行时,

当然也可以写那种纯粹的保护模式的,不过比较麻烦,现在也没深入研究,故暂不讨论

.section .data
out_text:
    .asciz "hello world"
.section .text
.globl _main
_main:
    pushl $out_text
    call _printf
    pushl $0
    call _exit
这里用到了c运行时的main,exit,printf函数,因为win32的原因多加了个下划线
3.编译
as test_win.s -o test_win.o -gstabs 
ld test_win.o -o test_win.exe -Le:/mingw/lib -lcrtdll
-gstabs保留了调试相关的符号表
-Le:/mingw/lib -lcrtdll链接了c运行时,注意mingw路径是我机器上的
4.运行

上面那个是带有调试信息的,可以看到strip后只有2K了
5.调试
注意不要strip

这里面要注意的是设置断点时的*号,以及main和_main的区别
另外查看寄存器可用"i r"指令
6.com版本的hello world
这个不管怎样还是大了点,想想当年学习微机原理的伪dos程序貌似是按字节论的,
下面就来个com版的hello world
(1)代码test.s
.code16
.text
    movw %cs, %ax
    movw %ax, %ds
    movw %ax, %es
    movw %ax, %ss
     
    movw $11, %cx
    movw $0x1301,%ax
    movb $0x07,%bl
    movw $0,%dx
    movw $outstring,%bp
    int $0x10
    jmp .
outstring:
    .asciz "hello world"
注意.code16是很有必要的
(2)编译
as test.s -o test.o 
ld test.o -Ttext 0x0100 -o test.exe
objcopy.exe -R .pdr -R .comment -R .note -S -O binary test.exe test.com
最后的objcopy将编译后的按binary输出成.com格式的
[另外如果跳过第二部exe生成,直接从.o生成.com也是可以运行的,不过对比发现有1bit的异常引起了输出字符的乱码
具体原因我就不去纠结的探索了,如果修改了那一比特,字符显示就正常了]
另外链接时的-Ttext 0x0100也可以用如下lds文件来控制
SECTIONS
{
. = 0x0100;
.text : {*(.text)}
}
同时链接时的选项应该为-Ttest.lds
(3)输出

(4)修改
此时如果ls -l一下会发现有将近4k之巨,这是因为填充了很多的0,用HxD这样的软件把结尾的0
给干掉再看其大小

看看最后的那个文件52字节,终于瘦身成功了!


SlickEdit完美解析Qt4

Posted on Mon, 31 Jan 2011 08:05:49 -1100

这两天折腾Qt,为了让SE更好的解析它,还是花了些时间的,下面的图片展示了SE强大的自动补全功能

Qt类方法的补全

编写类方法时的补全

this指针补全

this指针补全

ui类补全[设计namespace解析]

头文件补全

浏览QString源代码

使用GDB调试Qt程序

解析ui布局文件[xml]