使用了 loki 库的 typelist 技术. 当然,boost 中也有类似的技术实现。
编译器 : gcc4.0 loki-0.1.6
loki 要求,编译器至少要 gcc3.4 以上版本,vc++7.0 以上版本
一个描述学生信息的c/c++结构体看起来像下面这样:
struct studendDetail
{
int id;
char name[10];
char gread[20];
};
在后台程序,我们不关注结构体中每个字段的名字。只关注每个字段的类型,占用的内存长度。
OCI 中,应用程序与数据库的数据交互,是通过内存绑定实现的。长长的 bindbyparam()描述字串,像老婆娘的裹脚一样,又长又臭。
我们希望有一种办法,能对数据成员进行操作,不必关注它的数据存储方式,字段名。例如,我们按结构体成员的下标(或曰索引数)来实现对数据成员的操作。
loki 库的 typelist 和 ? 使这种操作成为可能。
1、定义结构体
在 loki 中,定义一个结构体的方法是
typedef Loki::Tuple< LOKI_TYPELIST_3( int, char[2], double ) > CGPstruct;
typedef 是泛型编程中的 类型声明关键字。 Tuple 是一个模板类,其有一个成员,名叫 value。
CGPstruct 是我们类的名称。这里意思是它是泛型编程(GP)技术实现的一个结构体。
LOKI_TYPELIST_3( int, char[2], double ) 。
以上,3 表示结构体有三个成员。如果要表示有4个成员的结构体,则是
LOKI_TYPELIST_4( int, char[2], char[4], double )
loki 库只支持最大18个结构体成员。
/ * 这里还没有落实。
需要注意的是: loki 技术生成的结构体,与我们 c++中定义的结构体,默认的对齐方式不一样的。loki 结构体中每个成员总是按最大成员的字节对齐。
例如:LOKI_TYPELIST_3( int, char[2], double ) 其大小是 24 ,因为 三个元素都按 double 的长度对齐 。
而
struct
{
int m_a;
char m_b[2];
double m_c;
}
其大小为 16 。
*/
2、定义结构体对象
定义一个结构体对象的语法和 c++ 完全一样:
CGPstruct myGP;
就这么简单,我们可以使用 Field 模板对其进行访问了。
Loki::Field< 0 >( myGP ) = 0;
Code
#include <loki/HierarchyGenerators.h>
#include <iostream>
#include <string>
using namespace std;
// Tuple 的对齐特性:总是按最长的内置类型
struct
{
char m1[13];
char m2[2];
double m3;
}mm;
typedef Loki::Tuple< LOKI_TYPELIST_3( int, long, double ) > Point3D;
int main()
{
Point3D pt;
cout << "sizeof( pt ):" << sizeof( pt ) << endl;
Loki::Field< 0 >(pt) = 0;
Loki::Field<1>(pt) = 100;
Loki::Field<2>(pt) = 300;
return 0;
}
3、取元素的地址。
char[4] 和 int 的取地址的方法不一样。 为了统一,我们声明 TYPELIST 时,给每种元素都加上一个下标。
Code
typedef Loki::Tuple< LOKI_TYPELIST_3( int[1], char[2], double[1] ) > CGPstruct;
这样,我们就可以方便地取元素的地址。
cout << Loki::Field< 0 >(pt) << endl;
cout << Loki::Field< 1 >(pt) << endl;
cout << Loki::Field< 2 >(pt) << endl;
这样做的原理是:由于没有定义精确匹配的转换,编译器将数组退化为指针处理。称做“退化”(decay)。
参见: <c++ templates the complate guide> 5.6 Using String Literals as Arguments for Function Templates
要想获得更详细的资料,可以参考 11.1 The Deduction Process
4、给元素赋值
传统的 c++ 中,给元素赋值是按元素名称来实现的。在 GP 中,给元素赋值只能通过元素的索引或元素的地址。正如我们上面介绍的一样。
通过元素的索引赋值,需要知道元素的数据类型。因为不同的数据类型赋值方法是不一样的。(例如, char[] 不能通过 "=" 来赋值)。
说明:
1、在 loki 库的 0.1.6 之前的版本中, 如果TypeList中有相同的数据类型,则编译时会报错,详细的介绍可参见 loki 的 HierarchyGenerators.h 中第 60行的说明。
Code
// ContainerOfAnyType.hpp
// by 范晨鹏
// 这个模板实现了构造了一个结构体并且实现了对结构体成员按索引访问。
// 欢迎光临我的博客 http://diylab.cnblogs.com
// 模板参数 T必须为 LOKI_TYPELIST_X 的数据类型。也即,必须是一个类型集合。
template < typename T >
class ContainerOfAnyType
{
public:
typedef Loki::Tuple< T > ArrayOfColumnsList;
public:
ContainerOfAnyType()
{
pRecordObject = new ArrayOfColumnsList();
printf( "objert is already created. size: %d\n", sizeof( ArrayOfColumnsList ) );
}
~ContainerOfAnyType()
{
delete pRecordObject;
printf( "objert is destoryed. \n" );
}
void DoSomething()
{
Bind( Int2Type< Loki::TL::Length< T >::value >() );
}
void* GetTypeContainer()
{
return this->pRecordObject;
}
string GetTypeDescStr()
{
string s;
}
string strdesc;
public:
private:
// 记录对象,或曰表结构对象
ArrayOfColumnsList* pRecordObject;
vector< void* > offsetOfElement;
private:
inline void Bind( Int2Type< 0 > )
{
// 空的结构体,抛出异常
// throw NO_TYPE_EXCEPTION
}
inline void Bind( Int2Type< 1 > )
{
Bind( Int2Type< 0 >() );
offsetOfElement.push_back( Loki::Field< 0 >( *pRecordObject ) );
printf( "addr: %p ,size: %d\n" , Loki::Field< 0 >( *pRecordObject ) , sizeof( Loki::Field< 0 >( *pRecordObject ) ) );
}
inline void Bind( Int2Type< 2 > )
{
Bind( Int2Type< 1 >() );
offsetOfElement.push_back( Loki::Field< 1 >( *pRecordObject ) );
printf( "addr: %p ,size: %d\n" , Loki::Field< 1 >( *pRecordObject ) , sizeof( Loki::Field< 1 >( *pRecordObject ) ) );
}
inline void Bind( Int2Type< 3 > )
{
Bind( Int2Type< 2 >() );
offsetOfElement.push_back( Loki::Field< 2 >( *pRecordObject ) );
printf( "addr: %p ,size: %d\n" , Loki::Field< 2 >( *pRecordObject ) , sizeof( Loki::Field< 2 >( *pRecordObject ) ) );
}
inline void Bind( Int2Type< 4 > )
{
Bind( Int2Type< 3 >() );
offsetOfElement.push_back( Loki::Field< 3 >( *pRecordObject ) );
}
inline void Bind( Int2Type< 5 > )
{
Bind( Int2Type< 4 >() );
offsetOfElement.push_back( Loki::Field< 4 >( *pRecordObject ) );
}
inline void Bind( Int2Type< 6 > )
{
Bind( Int2Type< 5 >() );
offsetOfElement.push_back( Loki::Field< 5 >( *pRecordObject ) );
}
inline void Bind( Int2Type< 7 > )
{
Bind( Int2Type< 6 >() );
offsetOfElement.push_back( Loki::Field< 6 >( *pRecordObject ) );
}
inline void Bind( Int2Type< 8 > )
{
Bind( Int2Type< 7 >() );
offsetOfElement.push_back( Loki::Field< 7 >( *pRecordObject ) );
}
inline void Bind( Int2Type< 9 > )
{
Bind( Int2Type< 8 >() );
offsetOfElement.push_back( Loki::Field< 8 >( *pRecordObject ) );
}
};
Code
// main.cpp
#include "ContainerOfAnyType.hpp"
int main()
{
// 测试任意类型的容器对象
// 有如下的结构体,描述了一个表对象
//struct studendDetail
//{
// int id;
// char name[10];
// char gread[20];
//};
typedef LOKI_TYPELIST_3( int[1], char[10], char[20] ) StudendDetail;
ContainerOfAnyType< StudendDetail > m;
m.DoSomething();
return 0;
}
相关资料:
2009 <程序员>有 侯捷的"boost系列",对 boost 库和 loki 库的 typetrain 技术做了详细的介绍。