Yac*_*oby 10 c++ templates code-generation c-preprocessor
在我的史诗般的探索中,让C++做的事情不应该,我试图把编译时生成的类放在一起.
基于预处理器定义,例如(粗略概念)
CLASS_BEGIN(Name)
RECORD(xyz)
RECORD(abc)
RECORD_GROUP(GroupName)
RECORD_GROUP_RECORD(foo)
RECORD_GROUP_RECORD(bar)
END_RECORDGROUP
END_CLASS
Run Code Online (Sandbox Code Playgroud)
虽然我很确定我生成了一个使用这种结构从文件系统读取数据的类(甚至可能使用模板元编程实现),但我看不出如何生成访问数据的函数和功能来读取数据.
我想最终得到类似这样的课程
class Name{
public:
xyz_type getxyz();
void setxyz(xyz_type v);
//etc
list<group_type> getGroupName();
//etc
void readData(filesystem){
//read xyz
//read abc
//etc
}
};
Run Code Online (Sandbox Code Playgroud)
有没有人知道这是否可能?
- 编辑 -
澄清此用途的用途.我有我想要阅读的标准格式的文件.格式已经定义,因此无法更改.每个文件可以包含任何数字记录,每个记录可以包含任意数量的子记录.
众多记录类型各自包含一组不同的子记录,但它们可以被定义.因此,例如Heightmap记录必须包含Heightmap,但可选包含法线.
所以我想像这样定义一个Record:
CLASS_BEGIN(Heightmap)
RECORD(VHDT, Heightmap, std::string) //Subrecord Name, Readable Name, Type
RECORD_OPTIONAL(VNML, Normals, std::string)
END_CLASS
Run Code Online (Sandbox Code Playgroud)
我想要输出具有类的功能的东西:
class Heightmap{
public:
std::string getHeightmap(){
return mHeightmap->get<std::string>();
}
void setHeightmap(std::string v){
mHeight->set<std::string>(v);
}
bool hasNormal(){
return mNormal != 0;
}
//getter and setter functions for normals go here
private:
void read(Record* r){
mHeightmap = r->getFirst(VHDT);
mNormal = r->getFirst(VNML);
}
SubRecord* mHeightmap, mNormal;
}
Run Code Online (Sandbox Code Playgroud)
我遇到的问题是我需要两次预处理器定义.一次用于定义类中的函数定义,一次用于创建读取函数.由于预处理器纯粹是功能性的,我无法将数据推送到队列并在END_CLASS marco定义上生成类.
我无法看到解决这个问题的方法,但想知道是否有人对C++有更深入的了解.
如果您正在寻找使用C++代码生成序列化/反序列化数据的方法,我会查看Google protobufs(http://code.google.com/p/protobuf/)或Facebook的Thrift(http://incubator.apache) .org/thrift /).
对于protobufs,您可以编写如下数据定义:
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
}
Run Code Online (Sandbox Code Playgroud)
然后生成Person C++类,允许您加载,保存和访问此数据.你也可以生成python,java等.
您可以使用boost 元组解决此问题.它将导致设计与您现在想到的不同,但它应该允许您以通用方式解决问题.
以下示例定义"std :: string,bool"形式的记录,然后从流中读取该数据.
#include "boost/tuple/tuple.hpp"
#include <iostream>
#include <sstream>
using namespace ::boost::tuples;
Run Code Online (Sandbox Code Playgroud)
这些函数用于从istream中读取数据.在我们到达最后一个记录类型后,第一个重载会停止迭代通过元组:
//
// This is needed to stop when we have no more fields
void read_tuple (std::istream & is, boost::tuples::null_type )
{
}
template <typename TupleType>
void read_tuple (std::istream & is, TupleType & tuple)
{
is >> tuple.template get_head ();
read_tuple (is, tuple.template get_tail ());
}
Run Code Online (Sandbox Code Playgroud)
以下类为Record实现getter成员.使用RecordKind作为我们的关键,我们得到了我们感兴趣的特定成员.
template <typename TupleType>
class Record
{
private:
TupleType m_tuple;
public:
//
// For a given member - get the value
template <unsigned int MBR>
typename element <MBR, TupleType>::type & getMember ()
{
return m_tuple.template get<MBR> ();
}
friend std::istream & operator>> (std::istream & is
, Record<TupleType> & record)
{
read_tuple (is, record.m_tuple);
}
};
Run Code Online (Sandbox Code Playgroud)
下一个类型是我们记录的元描述.枚举为我们提供了一个可以用来访问成员的符号名称,即.字段名称.然后元组定义这些字段的类型:
struct HeightMap
{
enum RecordKind
{
VHDT
, VNML
};
typedef boost::tuple < std::string
, bool
> TupleType;
};
Run Code Online (Sandbox Code Playgroud)
最后,我们构建一个记录并从流中读取一些数据:
int main ()
{
Record<HeightMap::TupleType> heightMap;
std::istringstream iss ( "Hello 1" );
iss >> heightMap;
std::string s = heightMap.getMember < HeightMap::VHDT > ();
std::cout << "Value of s: " << s << std::endl;
bool b = heightMap.getMember < HeightMap::VNML > ();
std::cout << "Value of b: " << b << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
由于这是所有模板代码,您应该能够将记录嵌套在记录中.