2013-03-09 177 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庫。這個用法並不太複雜,你需要提供一個按照某種順序列出你的成員的函數,然後這個函數庫會從那裏獲取它並實現序列化到不同的格式。