1

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

例如:

#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 中创建的 Stream Reader 它从文件中读取数据并根据读取的数据量来推进它的位置。

如果你不明白我在问什么,我很乐意重新措辞。

4

3 回答 3

2

如果不将它们全部列出,就无法遍历结构的成员。

您可以在编译时使用::std::tupleC++11 迭代结构之类的东西。

你也不能真正以这种方式打开类型。你可以这样做,但你这样做的方式是让几个函数具有相同的名称,每个函数都采用不同的参数类型。就像是:

 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);
}

请注意,没有明确writeToStreamasset类型。编译器将在运行时通过解包::std::tuple它的派生并写出每个单独的字段来编写它。

此外,如果您有裸指针,那么您正在编写糟糕的 C++。如果您要编写 C++,请编写惯用的、好的 C++。你想用运行时反射做的这整件事并不是做事的方式。

这就是我将你char *name的和字段表示的::std::string大小分隔BYTE数组转换为 a 的原因。使用这些类型是编写 C++ 的惯用正确方法。不像以前那样使用裸指针。此外,拥有两个具有高度相关值的字段(和)字段没有行为或任何其他指示它们相关联的字段,即使对于在运行时进行自省的编译器来说,也很难找出正确的做法。它无法知道所指向的数组有多大,也无法知道您决定将其编码为.sizedata::std::vectordatasizeBYTEdatasize

于 2013-03-09T01:48:59.417 回答
2

你要的是一种叫做反射的东西——它是:

计算机程序在运行时检查和修改对象的结构和行为(特别是值、元数据、属性和函数)的能力。

C++ 没有“本机”。

我的意思是 - 已经有一些尝试引入它的某些方面 - 取得了不同程度的成功 - 这产生了反射的某些方面,但不是“完整的”反射本身,因为你会在像 Ruby 这样的语言中获得。

但是,如果您喜欢冒险,可以尝试一个名为Reflectabit的反射库:

要查看它是否值得(它可能正在考虑您的代码),请在此处引用它 - 其中有很多关于如何使用 API 的示例:

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

祝你好运!

于 2013-03-09T02:01:22.140 回答
0

C++ 中的常见模式不是试图找出类型的成员是什么,而是提供一个由该类型的实现者实现的运算符,该运算符能够序列化/反序列化到磁盘。

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

于 2013-03-09T02:17:15.023 回答