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


在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

[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接口也不难构造。