C/C++反射技术的替代方案

来源:互联网 发布:java程序员的自我修养 编辑:程序博客网 时间:2024/04/29 17:25

C/C++反射技术的替代方案:解决数据库-实体对应问题

上一篇 / 下一篇  2008-10-28 13:53:30

查看( 423 ) / 评论( 3 ) / 评分( 11 / 0 )

在JAVA中,反射技术令人赞叹,据此,产生了大量的ORM和JAVA BEANS。主要解决关系数据库与对象实体的对应关系,使得数据库的访问简洁方便,与业务逻辑能够脱离开来。
        这些特性使传统的C/C++程序员羡慕不已。许多有识之士开始探讨C/C++环境下的反射技术,以便能够像JAVA那样优雅的写出数据库访问框架。他们的工作取得了某些成效,但总的来说,使用的工具、技术比较繁杂,未能实现实用的工具框架。
        如果能用其他方法解决类似ORM(Object Relational Mapping)的问题,也是一种思路。实际上,我们在数年前就采用了一种方法,当时的目的是在三层客户-服务器模式下,服务器代理执行SQL语句问题,其中要解决结果集向客户端传送问题,这实际上就涉及了数据库与实体对象对应的问题。
在C语言里,数据实体就是struct,因此,对应机制我们就称之为SRM(Struct Relational Mapping) 。我们知道,关系数据库有一个关于第一范式的规定,就是每一个元组(ROW),必须由简单属性(columns)构成。因此,我们的结构实体就由简单变量构成,不包含结构、联合、指针等复杂类型。这种结构我们模仿POJO(Plain Ordinary Java Object),就叫做POCS(Plain Ordinary C Struct)。这个条件非常有利于描述SRM的映射关系。通过一个实例我们来看看这个系统在应用中的表现形态,然后进一步剖析它的内部机制。

这是一个实际程序的一部分,是ORACLE数据库。
数据库表定义
[code]
create table seat (                /* 席位表 */
        start_date        date,        /* 始发日期 YYYY-MM-DD*/
        beg_station number(4),        /* 上车站 */
        Train_no varchar2(12),        /* 始发车次 */
                run_rain varchar2(6),        /* 运行·车次 */
        on_date date,                        /* 上车时间 YYYY-MM-DD HH24:mi*/
        Carno number(2), /* 车厢号,不分车厢的票车厢号=0 */
        seat_type number(4),        /*席别,0=无号*/
        seat_no   number(3),        /* 席位号 */
        end_station number(3),        /* 售前是最远站,售后改为下车站 */
        shortest_station number(3),        /* 限售以远 */
        purpose        number(9),                /* 用途 */
               gride varchar2(50),           /* 列车等级,新空调直达特快等 */
        flag        number(1),        /* 标志:0正常,1占用,2已售,3暂不可用,-1禁售 */
        used_dev varchar2(16), /* 最后操作终端 */
        used_uid varchar2(16), /* 最后操作人 */
        used_time date ,                /* 最后操作时间 YYYY-MM-DD HH24:MI:SS */
        primary key(start_date,beg_station,Train_no,Carno,
                        seat_no,seat_type)
);


SRM的映射模板,相当于iBates的映像文件:
T_PkgType seat_type[] = {                /* 席位表 */
        {CH_DATE,YEAR_TO_DAY_LEN,"start_date",YEAR_TO_DAY,-1}, /* 始发日期 YYYY-MM-DD*/
        {CH_CHAR,5,"beg_station"},        /* 上车站 */
        {CH_CHAR,13,"Train_no"},        /* 始发车次 */
        {CH_CHAR,7,"run_train"},        /* 运行车次 */
        {CH_DATE,YEAR_TO_MIN_LEN,"on_date",YEAR_TO_MIN,-1}, /* 上车时间 YYYY-MM-DD HH24:mi*/
        {CH_TINY,1,"Carno"},        /* 车厢号,不分车厢的票车厢号=0 */
        {CH_SHORT,sizeof(short),"seat_type"},        /*席别,0=无号*/
        {CH_SHORT,sizeof(short),"seat_no"},        /* 席位号 */
        {CH_SHORT,sizeof(short),"end_station"},        /* 售前是最远站,售后改为下车站 */
        {CH_SHORT,sizeof(short),"shortest_station"},        /* 限售以远 */
        {CH_INT,sizeof(int),"purpose"},        /* 用途 */
        {CH_CHAR,51,"gride"},           /* 列车等级,新空调直达特快等 */
        {CH_TINY,1,"flag"},                /* 标志:0正常,1占用,2已售,-1禁售 */
        {CH_CHAR,17,"used_dev"},        /* 最后操作终端 */
        {CH_CHAR,17,"used_uid"},        /* 最后操作人 */
        {CH_TIME,sizeof(INT64),"used_time",YEAR_TO_SEC}, /* 最后操作时间 YYYY-MM-DD HH24:MI:SS */
        {CH_CHAR,20,"ROWID"},
        {-1,0,0}
};

这里补充一点,T_PkgType的定义如下:
typedef struct  {
        int type;                                                /*数据类型*/
        int len;                                                        /*数据长度*/
        char *name;              /*字段名称*/
        char *format;             /*格式*/
        char offset;               /*数据在记录中的位置*/
} T_PkgType;
基本数据类型,在“scpkg.h”中定义:
           CH_CHAR                                 /*字符类型,相当于C 语言char[]  */
        CH_TINY            /* 1字节整数*/
        CH_SHORT                           /*短整型,相当于C 语言short*/
               CH_INT                                         /*整型,相当于C 语言int */
               CH_LONG                                 /*长整型,相当于C 语言long*/
               CH_DOUBLE                         /*浮点型,相当于C 语言double*/
        CH_INT4            /* =CH_INT,在任何系统上都是不变的4字节长度 */
        CH_INT64                /*8字节整数*/
        CH_CLOB               /* CLOB,内存中为指针型(char *) */
                //CLOB对应ORACLE的long,目前只能读,不能写。结构模板是char*类型,
拆包后,数据还在原来的buffer里,在未处理完之前,原buffer不要释放。
        派生的数据类型,在“sdbc.h”中定义:
        CH_DATE           /*Oracle 日期型,CH_CHAR派生*/
        CH_JUL            /*Oracle 日期型,CH_INT派生*/
        CH_CJUL           /*Oracle字符日期型,CH_INT派生*/
        CH_MINUTS        /* 分钟型,CH_INT派生*/
        CH_TIME           /* 秒型,CH_INT64 派生 */
        CH_CMINUTS       /* 分钟型,CH_INT派生,数据库中是字符型*/
        CH_CTIME          /* 秒型,CH_INT64 派生 ,数据库中是字符型*/
        CH_CNUM          /* 字符型派生,数据库中是NUM型*/
       
NULL值,在“scpkg.h”中定义:
        INTNULL
        SHORTNULL
        LONGNULL
        DOUBLENULL

数据结构POCS:
typedef struct {
        char start_date[YEAR_TO_DAY_LEN];        /* 始发日期 YYYY-MM-DD*/
        char beg_station[5];        /* 上车站 */
        char Train_no[13];        /* 始发车次 */
        char run_train[7];        /* 运行车次 */
        char on_date[YEAR_TO_MIN_LEN];        /* 开车时间 */
        char Carno;                /* 车厢号,不分车厢的票车厢号=0 */
        short seat_type;        /*席别,0=无号*/
        short seat_no;                /* 席位号 */
        short end_station;        /* 售前是最远站,售后改为下车站 */
        short shortest_station;        /* 限售以远 */
        int purpose;                /* 用途 */
        char gride[51];                     //  列车等级,新空调直达特快等
        char flag;                /* 标志:0正常,1占用,2已售,-1禁售 */
        char used_dev[17];        /* 最后操作终端 */
        char used_uid[17];        /* 最后操作人 */
        INT64 used_time;        /* 最后操作时间 YYYY-MM-DD HH24:MI:SS */
        char ROWID[20];
} seat_s;

下面的实例程序说明了怎样利用模板把数据库和POCS对应起来。
程序的前面需要include相应的.h(工具和POCS.h)和.c(映像表,T_PkgType)
以上模板和数据结构要严格互相对应。当数据库结构改变时,要相应的改变。
下面是数据访问程序的片段,程序的前部已经根据:上车日期、上车站、运行车次、下车站、席位、用途,取得了上车站(fz),车次(train),下车站(dz)参数,现在根据这些参数查找席位库:


sprintf(stmt,"select /*+role*/ %s from %s.seat where "
                    "start_date=to_date('%s','%s') and "
                    "beg_station = '%s' and "
                    "Train_no = '%s' and "
                    "seat_type = %d and "
                    "end_station >= %d and purpose = %d and "
                    "flag=0 and rownum<%d order by end_station,Carno,seat_no ",
                    "for update WAIT 10 SKIP LOCKED",
                        mkfield(tmp,seat_type,0),        //SRM,从模板生成select的数据
                        SQL_Connect->DBOWN,
                        strdate,YEAR_TO_DAY, //日期,根据上车日期计算出来的。
                        t_param->fz.station_code, //上车站
                                t_param->train.Train_no,  //全车次,根据运行车次算出来的
                        app->xb,                                 //席别
                                t_param->dz.sequence, //到站顺号,算出来的
                                app->purpose,                //用途
                        app->quantity );   //数量
/* 生成了这样的语句:
select to_char(start_date,'YYYY-MM-DD') start_date,beg_station,Train_no, run_train,to_char(on_date,'YYYY-MM-DD HH24:MI') on_date,Carno,seat_type, seat_no,end_station,shortest_station,purpose,gride,pro,flag,used_dev,used_uid,to_char(used_time,'YYYY-MM-DD HH24:MI:SS') used_time,ROWID from ticket.seat where start_date=to_date('2008-08-19','YYYY-MM-DD') and beg_station = 'TSHP' and Train_no = 'H6A' and seat_type = 12 and end_station >= 5 and purpose = 0 and flag=0 and rownum<3 order by end_station,Carno,seat_no for update WAIT 10 SKIP LOCKED
*/
int seat_curno=___SQL_Prepare__(SQL_Connect,stmt);      //数据访问接口, SQL_Connect是数据库连接句柄,早已打开的。
        if(seat_curno<0) { //出错处理
                sprintf(msg,"%s,err=%d,%s",stmt,
                        SQL_Connect->Errno,
                        SQL_Connect->ErrMsg);
                return(seat_curno);
        }

seat_s seat;// 定义席位结构
        for(i=0;i<app->quantity;i++) { /* 取每条记录  */
        JSON_OBJECT *json;
               ret=___SQL_Fetch__(SQL_Connect,seat_curno,tmp);//数据访问接口
                if(ret==SQLNOTFOUND || ret==100) break;
                if(ret) { //出错处理
                        if(SQL_Connect->Errno==LOCKED) {
                                ShowLog(5,"getxw LOCKED continue next");
                                i--;
                                continue;
                        }
                        break;
                }
               net_dispack(&seat,tmp,seat_type);// 结果集解析到结构,由于select语句是seat_type生成的,注定其结果集可以解析到seat
                seat.flag=1; //进行一点简单处理,占用该席位
                seat.used_time=now; //占用时间
                strcpy(seat.used_uid,ctx->contex.userid);//占用人
                strcpy(seat.used_dev,ctx->contex.devid); //占用窗口
               net_pack(tmp,&seat,seat_type);// SRM
                sprintf(stmt,"update %s.seat %s where ROWID='%s'",
                        SQL_Connect->DBOWN,
                        mkupdate(tmp1,tmp,seat_type),seat.ROWID); //SRM
/* 生成如下语句:
update ticket.seat SET (start_date,beg_station,Train_no,run_train,on_date,Carno,seat_type,seat_no,end_station,shortest_station,purpose,gride,pro,flag,used_dev,used_uid,used_time)=(SELECT to_date('2008-08-19','YYYY-MM-DD'),'TSHP','H6A','H6',to_date('2008-08-19 00:26','YYYY-MM-DD HH24:MI'),2,12,1,5,0,0,'G04','',1,'SP0102003','ylh', to_date('2008-08-15 10:24:15','YYYY-MM-DD HH24:MI:SS') FROM DUAL) where ROWID='AAAM3iAAFAAABWQAAA'
注意,ROWID不在其中,mkupdate会自动排除它 */
               ret=___SQL_Exec(SQL_Connect,stmt);//发出占用指令
                if(ret != 1) { // 修改成功的行数不是1,出错处理
                        ShowLog(1,"占用席位失败,stmt=%s,err=%d,%s",
                                stmt,
                                SQL_Connect->Errno,
                                SQL_Connect->ErrMsg);
                        i--;
                        continue;
                }
// 以下用JSON对结果打包
                seat.end_station = t_param->dz.sequence;
                if(i==0) {
                         p=strdup("/"seat_common/":");
                        json = json_object_new_object();
                        if(!json) {
                                if(p) free(p);
                                ___SQL_Close__(SQL_Connect,seat_curno);
                                ShowLog(1,"getxw json_object_new: MEMERR!");
                                SQL_Connect->Errno=MEMERR;
                                return MEMERR;
                        }
                       struct_to_json(json,&seat,seat_type,"0-3,8-11");//席位的公共数据
                        p=strappend(p,json_object_to_json_string(json));
                        strcat(p,",/"seat_data/":[");
                        json_object_put(json);
                }
                json = json_object_new_object();
                if(!json) { // 出错处理
                        if(p) free(p);
                       ___SQL_Close__(SQL_Connect,seat_curno);   

                       ShowLog(1,"getxw json_object_new: MEMERR!");
                        SQL_Connect->Errno=MEMERR;
                        return MEMERR;
                }
               struct_to_json(json,&seat,seat_type,"Carno,seat_type,seat_no,ROWID");//席位的特殊部分。如果我们不怕数据冗余,全部数据打包,就不怕数据变更了:
// struct_to_json(json,&seat,seat_type,0);
                p=strappend(p,json_object_to_json_string(json));加入到结果中
                strcat(p,",");
                json_object_put(json);
        }  // 循环尾
        if(p) strcpy(&p[strlen(p)-1],"]");//完成JSON结构
       ___SQL_Close__(SQL_Connect,seat_curno);//数据访问接口

[/code]

现在,访问完成了,结果(JSON格式)已经在p中了,将提交给客户端进行进一步处理。
可以看出,如果席位库发生变化,通常是增加一些字段,只需要修改模板seat_type和POCS:seat_s,这个程序是不需要变化的。当然,利用这些工具,在C++环境下是很容易写出真正的DAO来的。
本文重点是谈SRM,关于数据库接口部分先放一放,只提及一下,那些函数(___SQL_*)是包装了OCI的,如果包装了CT_LIB,就是SYBASE了,应用软件换个数据库也是很便捷的。

我们知道,JAVA是用反射来进行ORM的,我们C语言用什么来进行SRM呢?看了上边的程序,你应该可以猜出来,我们用结构的偏移量算法来“盲人摸象”式的反射结构的成员。

现在来看“盲人摸象”程序:

[code]
int set_offset(T_PkgType *pkg_type)
{
int i,k;
int ali,dali;
struct {
        char a;
        long b;
} align;
struct {
        char a;
        double b;
} dalign;
        ali=(int)&align.b - (int)&align-1;
        dali=(int)&dalign.b - (int)&dalign-1;
        k=0;
        for(i=0;pkg_type.type>-1;i++){
                pkg_type.offset=k;
                if((pkg_type.type&127)!=CH_CLOB)k+=pkg_type.len;
                else k+=sizeof(char *);
                switch(pkg_type[i+1].type&127) {
                        case 127:
                        case CH_CHAR:
                        case CH_BYTE:
                        case CH_TINY:
                                break;
                        case CH_INT64:
                        {
                        struct {
                                char a;
                                INT64 b;
                        } lfali;
                        int lali;
                                lali=(int)&lfali.b - (int)&lfali-1;
                                k=(k+lali)&~lali;
                        }
                                break;
                        case CH_LDOUBLE:
                        {
                        struct {
                                char a;
                                long double b;
                        } lfali;
                        int lali;
                                lali=(long)&lfali.b - (long)&lfali-1;
                                k=(k+lali)&~lali;
                        }
                                break;
                         case CH_LDOUBLE:
                        {
                        struct {
                                char a;
                                long double b;
                        } lfali;
                        int lali;
                                lali=(long)&lfali.b - (long)&lfali-1;
                                k=(k+lali)&~lali;
                        }
                                Break;                        case CH_SHORT:
                                k=(k+1)&~1;
                                break;
                        case CH_CLOB:
                                k=(k+(sizeof(char *)-1)) & ~(sizeof(char *)-1);
                                break;
                        case CH_FLOAT:
                        case CH_INT:
                                k=(k+3)&~3;
                                break;
                        case CH_LONG:
                        default:
                                k=(k+ali)&~ali;
                                break;
                }
             }
        pkg_type.offset=k;
        return i;
}

[/code]
一个模板,经过这个处理,每个成员的offset值都被设定成结构的布局。返回值是成员的个数。Pkg_type.Offset就是整个数据区的尺寸。

下面的程序从结构(data)中取出一个成员的值,转换成字符串,放在buf,i是模板中第几个成员,CURDLM是分隔符。这个函数本身不使用分隔符,数据中如果出现分隔符将被转义,CURDLM=0不转义。程序中出现一些函数用于判断汉字状态和处理转义,与原理无关,略。有些数据类型,日期时间类,是具体系统要用到的,你可以删去。模板pkg_type,使用前要经过set_offset()处理。 

[code]  
        int get_one(char *buf,void *data,T_PkgType *pkg_type,int i,char CURDLM)
        {
        int cnt,J,len;
        char datebuf[31],*cp1,*cp2;
        int type;
        char *sp;
        short iTiny;
        T_PkgType Char_Type[2];
                cp1=buf;
                *cp1=0;
                cnt=0;
                cp2=(char *)data;
                cp2 += pkg_type.offset;
                sp=cp2;
                type=pkg_type.type;
                if(isnull(cp2,type)) return cnt;
                switch(type) {
                
                case CH_CLOB:
                        Char_Type[0].type=CH_CHAR;
                        Char_Type[0].len=-1;
                        Char_Type[0].offset=0;
                        Char_Type[1].type=-1;
                        Char_Type[1].len=0;
                        J=get_one(buf,*(char **)cp2,Char_Type,0,CURDLM);
                        cnt += J;
                  break;
                case CH_DATE:
                case CH_CNUM:
                case CH_CHAR:
                        len=(pkg_type.len>0)?pkg_type.len:strlen(cp2)+1;
                        for(J=0;J<len-1&&*cp2;J++,cnt++) {
                                if(!CURDLM) goto norm;
                                switch(*cp2) {
                                case ESC_CHAR:
                                        if(cp2>sp && firstcc(sp,cp2-1)) goto norm;
                                        *cp1++=*cp2;
                                        *cp1++=*cp2++;
                                        cnt++;
                                        break;
                                case '/n':
                                        if(cp2>sp && firstcc(sp,cp2-1)) cp1[-1]&=0x7f;
                                        *cp1++=ESC_CHAR;
                                        *cp1++='n';
                                        cp2++;
                                        cnt++;
                                        break;
                                default:
                                        if(*cp2==CURDLM) {
                                                if(cp2>sp && firstcc(sp,cp2-1))
                                                        goto norm;
                                                *cp1++=ESC_CHAR;
                                                *cp1++='G';
                                                cp2++;
                                                cnt++;
                                                break;
          }
        norm:
                                        *cp1++=*cp2++;
                                        break;
                                }
                        }
                        *cp1=0;
                        if(cp2>sp) {
                                if(firstcc(sp,cp2-1)) cp1[-1] &= 0x7f;
                        }
                        break;
                case CH_FLOAT:
                case CH_DOUBLE:
                        if(!pkg_type.format)
                         cnt=sprintf(cp1,"%g", *(double *)cp2);
                        else
                         cnt=sprintf(cp1,pkg_type.format,*(double *)cp2);
                        break;
                case CH_LDOUBLE:
                        if(!pkg_type.format)
                         cnt=sprintf(cp1,"%Lg", *(long double *)cp2);
                        else
                         cnt=sprintf(cp1,pkg_type.format,*(long double *)cp2);
                        break;
                case CH_TINY:
                        iTiny=*cp2;
                        cnt=sprintf(cp1,"%hd",iTiny);
                        break;
                case CH_SHORT:
                        cnt=sprintf(cp1,"%hd",*(short *)cp2);
                        break;
                                                                  
                   break;
                case CH_INT:
                        cnt=sprintf(cp1,"%d",*(int *)cp2);
                        break;
                case CH_LONG:
                        cnt=sprintf(cp1,"%ld",*(long *)cp2);
                        break;
                case CH_INT64:
                        cnt=sprintf(cp1,FMT64,*(INT64 *)cp2);
                        break;
                case CH_CJUL:
                case CH_JUL:
                        if(pkg_type.format) {
                                 rjultostrfmt(datebuf,*(int *)cp2,
                                            pkg_type.format);
                        } else {
                                 rjultostrfmt(datebuf,*(int *)cp2,
                                            "YYYYMMDD");
                        }
                        cnt=sprintf(cp1,"%s",datebuf);
                        break;
                case CH_MINUTS:
                case CH_CMINUTS:
                        if(pkg_type.format) {
                                rminstrfmt(datebuf,*(INT4 *)cp2,pkg_type.format);
                        } else rminstr(datebuf,*(INT4 *)cp2);
                        cnt=sprintf(cp1,"%s",datebuf);
                        break;
                case CH_TIME:
                case CH_CTIME:
                        if(pkg_type.format) {
                           rsecstrfmt(datebuf,*(INT64 *)cp2,pkg_type.format);
                        } else rsecstrfmt(datebuf,*(INT64 *)cp2,"YYYYMMDDHH24MISS");
                        cnt=sprintf(cp1,"%s",datebuf);
                        break;
                default:
                        break;
                }
                return cnt;
        }

下面的程序把字符串cp放到结构(buf)中,i是模板中第几个成员,CURDML是分隔符。
        int put_one(void *buf,char *cp,T_PkgType *pkg_type,int i,char CURDLM)
        {
        int k,ret;
        char *cp1;
                k=pkg_type.offset;
                cp1=(char *)buf;
                cp1 += k;
                ret=0;
                switch(pkg_type.type) {
                        case CH_CLOB:
                                *(char **)cp1=cp;
                                strcpy_esc(cp,cp,-1,CURDLM);
                                pkg_type.len=strlen(cp);
                                ret=1;
                                break;
                     case CH_DATE:
                        case CH_CNUM:
                        case CH_CHAR:
                                strcpy_esc(cp1,cp,pkg_type.len,CURDLM);
                                ret=1;
                           break;
                        case CH_FLOAT:
                                *(float *)cp1=0.;
                                ret=sscanf(cp,"%f",(float *)cp1);
                                break;
                        case CH_DOUBLE:
                                *(double *)cp1=0.;
                                ret=sscanf(cp,"%lf",(double *)cp1);
                                break;
                        case CH_LDOUBLE:
                                *(long double *)cp1=0.;
                                ret=sscanf(cp,"%Lf",(long double *)cp1);
                                break;
       
                        case CH_TINY:
                            {
        int tmp;
                                *cp1=TINYNULL;
                                ret=sscanf(cp,"%hd",&tmp);
                                if(ret==1) *cp1=(char)tmp;
                                break;
                            }
                        case CH_SHORT:
                                *(short *)cp1=SHORTNULL;
                                ret=sscanf(cp,"%hd",(short *)cp1);
                                break;
                        case CH_INT:
                                *(int *)cp1=INTNULL;
                                ret=sscanf(cp,"%d",(int *)cp1);
                                break;
                        case CH_JUL:
                  case CH_CJUL:
                                if(!*cp) *(INT4 *)cp1=TIMENULL;
                                else if(pkg_type.format){
                                    *(int *)cp1=rstrfmttojul(cp,
                                                pkg_type.format);
                                } else {
                                    *(int *)cp1= rstrjul(cp);
                                }
                                ret=1;
                                break;
                        case CH_MINUTS:
                        case CH_CMINUTS:
                                if(!*cp) *(INT4 *)cp1=TIMENULL;
                                else if(pkg_type.format){
                                    *(INT4 *)cp1=rstrminfmt(cp,pkg_type.format);
                                } else *(INT4 *)cp1=rstrmin(cp);
                                ret=1;
                                break;
                        case CH_TIME:
                        case CH_CTIME:
                                if(!*cp) *(INT64 *)cp1=INT64NULL;
                                else if(pkg_type.format){
                                    *(INT64 *)cp1=rstrsecfmt(cp,pkg_type.format);
                                } else *(INT64 *)cp1=rstrsecfmt(cp,"YYYYMMDDHH24MISS");
                                ret=1;
                                break;
                        case CH_LONG:
                                *(long *)cp1=LONGNULL;
                                ret=sscanf(cp,"%ld",(long *)cp1);
                                break;
                        case CH_INT64:
                          *(INT64 *)cp1=INT64NULL;
                                ret=sscanf(cp,FMT64,(INT64 *)cp1);
                                break;
                        default:
                                ret=0;
                                break;
                }
                return ret;
        }

[/code]
到此,SRM的核心问题-反射问题就解决了,其余是外围工具,利用上述工具组合成例子中的实用程序,如果有兴趣,可以另外开一个专题进行讨论。

下一个专题,SRM:如何把结构和数据库对应起来。但是这个方法必须使用我们的数据库包装接口,用于接受SRM形成的语句和返回适当格式的结果集,以便SRM进行处理。这个数据库包装接口还可以独立于数据库,目前已存在ORACLE和SYBASE接口,MYSQL和ODBC接口也不难构造。

原创粉丝点击