2013-03-09 140 views
1

我试图找到不同结构中任何给定变量的“类型”并能够读取它们。 (请记住,这是伪代码)如何读取C/C++中的结构类型

例如:

#include "stream.h" //a custom stream reader class I made 

typedef unsigned char BYTE; 

/***** SERIES OF DIFFERENT STRUCTS ******/ 

struct asset 
{ 
    char *name; 
    int size; 
    BYTE *data; 
}; 

struct asset2 
{ 
    char *lang; 
    char *entry; 
}; 

/*****************************************/ 


    void readAsset(Enumerable<struct> &istruct) 
    { 
     foreach(object o in istruct) 
     { 
      switch(o) 
      { 
       case int: 
        &o = _stream->ReadInt32(); 
        break; 
       case char: 
        &o = _stream->ReadChar(); 
        break; 
       case *: 
        &o = _stream->ReadInt32(); 
        break; 
       default: break; 
      } 
     } 
    } 

我希望它能够做到以下几点:

asset a1; 
asset2 a2; 

readAsset(a1); 
readAsset(a2); 

,并从文件传递的所有信息,以a1和a2。

我想知道是否有一种方式在C/C++中从结构中的任何对象获取数据的类型,然后根据它读取?复杂的枚举有可能吗?对不起,代码错误,但我希望它更容易理解我想要做什么。

附加信息:

_stream是一个指向Stream类我做了类似的流读取器在.net它从文件中读取数据,并推进它的基础上有多大的数据被读取的位置。

如果你不明白我在问什么,我会很乐意再重复一遍。

+3

C++中没有反射。 – 2013-03-09 01:47:27

+1

@YochaiTimmer:这不完全正确。有一种编译时反射。在一个模板中,我可以问“这个类型是否支持'push_back'操作?”。但严格来说在编译时,而不是运行时。 'typeof'运算符支持基本运行时'反射'。但它不是很强大。 – Omnifarious 2013-03-09 01:53:46

+1

C或C++语言不支持反射的概念,即在运行时反演对象以确定类型及其结构。 C++支持使用RTTI的一种简单的内省方式,但与您正在寻找的内容相比,它非常有限。如果您对此感兴趣,请阅读'typeid'更多信息 – Tuxdude 2013-03-09 01:54:09

回答

2

没有办法遍历结构的成员而没有全部列出。

您可以在编译时使用C++ 11中的::std::tuple来遍历类似结构的东西。

您也无法真正地以这种方式打开类型。你可以这样做,但是你的做法是有几个具有相同名称的函数,每个函数采用不同的参数类型。喜欢的东西:

void doRead(StreamType &stream, int &data) 
{ 
    data = stream.readInt32(); 
} 
void doRead(StreamType &stream, char &data) 
{ 
    data = stream.readChar(); 
} 
// etc... 

然后你只需要调用doRead与结构成员和编译神奇挑选基于该类型是正确的。

在C++中,解决您在这里解决的问题的方法是序列化库。如果您可以控制写入的格式和读取的格式,则可以使用类似protobufboost::serialization的东西来相对简单地完成此操作,而无需编写大量自己的代码。

此外,您的代码有几个问题。不要在标识符中使用前导_字符。带有前导_的标识符被保留供编译器或标准库实现使用。许多编译器都有特殊的关键字,这些关键字是以_字符开头的编译器特定语言扩展。使用带有前导字符_的标识符可能会导致代码神秘地无法在某些环境中编译各种奇怪的难以理解的错误。


你可以得到类似于在编译时可枚举的结构的东西。但它的丑陋:

#include <tuple> 
#include <string> 
#include <vector> 
#include <type_traits> 

class asset : public ::std::tuple< ::std::string, ::std::vector<BYTE> > 
{ 
public: 
    ::std::string &name()     { return ::std::get<0>(*this); } 
    const ::std::string &name() const  { return ::std::get<0>(*this); } 
    ::std::vector<BYTE> &data()    { return ::std::get<1>(*this); } 
    const ::std::vector<BYTE> &data() const { return ::std::get<1>(*this); } 
}; 

void writeToStream(Stream *out, const ::std::string &field) 
{ 
    out->writeString(field); 
} 
void writeToStream(Stream *out, const ::std::vector<BYTE> &field) 
{ 
    out->writeInt(field.size()); 
    out->writeRaw(field.data(), field.size()); 
} 

template <unsigned int fnum, typename... T> 
typename ::std::enable_if< (fnum < sizeof...(T)), void >::type 
writeToStream_n(Stream *out, const::std::tuple<T...> &field) 
{ 
    writeToStream(out, ::std::get<fnum>(field)); 
    writeToStream_n<fnum+1, T...>(out, field); 
} 

template <unsigned int fnum, typename... T> 
typename ::std::enable_if< (fnum >= sizeof...(T)) >::type 
writeToStream_n(Stream *, const::std::tuple<T...> &) 
{ 
} 

template <typename... Tp> 
void writeToStream(Stream *out, const ::std::tuple<Tp...> &composite) 
{ 
    writeToStream_n<0, Tp...>(out, composite); 
} 

void foo(Stream *out, const asset &a) 
{ 
    writeToStream(out, a); 
} 

注意,没有为asset类型没有明确writeToStream。编译器会在运行时通过解压它从每个字段派生出来并写出它来编写它。另外,如果你有裸指针,那么你正在编写不好的C++。如果你要编写C++,请写出惯用的,好的C++。你想用运行时反射来做这件事情不是做事情的方式。

这就是我之所以转变你的char *name::std::string和你的尺寸界定你sizedata领域代表为::std::vector​​阵列。使用这些类型是编写C++的惯用正确方法。使用裸指针的方式不是。此外,如果有两个具有强相关值的字段(datasize)字段没有任何行为或任何其他关联指示,那么即使对于在运行时进行自省的编译器来说,也很难找出正确的事情做。它不知道data指向的​​阵列有多大,并且它不知道您在size中对此编码的决定。

+0

是的,我明白了,但我会如何列出他们全部? :/我有种寻找方式,如: 结构资产 { \t char * name; \t int size; \t BYTE * data; }; 资产a; ((char *)a,sizeof(资产),1,file); – MysteryDev 2013-03-09 01:55:03

+0

@ user1425433:你必须列出所有的个人成员。或者做很多模板魔法,并为您的数据结构使用'tuple'模板类。 – Omnifarious 2013-03-09 02:02:05

2

什么你要求的是一种叫Reflection - 这就是:

的计算机程序来检查 和修改结构和行为(具体的数值, 元数据,属性的能力和函数)在运行时的一个对象。

C++不具有 “本地”。

我的意思是 - 已经有一些尝试来介绍它的某些方面 - 不同程度的成功 - 它产生了反射的某些方面,但不是“完整的”反射本身,因为你会用一种语言像Ruby等等。

不过,如果你喜欢冒险,你可以尝试反思库调用Reflectabit

看它可能是值得的(它可能会考虑你的代码),这里引用的 - 其中有相当一点关于如何使用API​​的例子:

http://www.altdevblogaday.com/2011/09/25/reflection-in-c-part-1-introduction/

祝你好运!

+0

谢谢我会检查出来。 – MysteryDev 2013-03-09 02:02:27

0

C++中的通常模式不是试图弄清楚类型的成员是什么,而是提供一个由该类型的实现者实现的操作符,该操作符能够序列化/反序列化到磁盘。

你可以看一下,例如,boost :: serialize库。这个用法并不太复杂,你需要提供一个按照某种顺序列出你的成员的函数,然后这个函数库会从那里获取它并实现序列化到不同的格式。