C++ 如何在C/C中读取结构的类型++

C++ 如何在C/C中读取结构的类型++,c++,c,types,struct,C++,C,Types,Struct,我试图在不同的结构中找到任何给定变量的类型,并能够读取它们。请记住,这是psuedo代码 例如: #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; }; stru

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

例如:

#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;
            }
        }
    }
并将文件中的所有信息传递给a1和a2

我想知道在C/C++中是否有一种方法可以从结构中的任何对象获取数据类型,然后根据该类型进行读取?是否可以使用复杂的枚举?很抱歉代码不好,但我想让它更容易理解我正在尝试做什么

其他信息:

_stream是一个指向我创建的流类的指针,类似于.Net中的stream Reader。它从文件中读取数据,并根据读取的数据量提升其位置


如果您不明白我的问题,我很乐意重新措辞。

在不列出所有成员的情况下,无法遍历结构的成员

您可以在编译时使用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++中,解决这个问题的方法是一个序列化库。如果您可以控制写入的格式和读取的格式,那么您可以使用类似或的方法来相对轻松地完成这项工作,而无需编写大量自己的代码

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

您可以得到类似于在编译时可枚举的结构的东西。但这很难看:

#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);
}
请注意,资产类型没有显式的writeToStream。编译器将在运行时通过解包它派生的::std::tuple并写出每个字段来编写它

此外,如果你有空指针,你写的是很差的C++。如果你打算写C++,请写好的,好的C++。您希望通过运行时反射来完成的这整件事情并不是解决问题的方法


这就是为什么我将char*名称转换为::std::string,并将由大小和数据字段表示的大小分隔字节数组转换为::std::vector。使用这些类型是编写C++的习惯性正确方法。使用裸指针的方式与以前不同。此外,如果有两个值密切相关的字段,即数据字段和大小字段,它们没有行为或任何其他相关指示,那么即使是在运行时进行内省的编译器也很难找到正确的操作。它不知道数据所指向的字节数组有多大,也不知道您是否决定对其进行大小编码。

如果不列出结构的所有成员,就无法迭代结构的成员

您可以在编译时使用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++中,解决这个问题的方法是一个序列化库。如果您可以控制写入的格式和读取的格式,那么您可以使用类似或的方法来相对轻松地完成这项工作,而无需编写大量自己的代码

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

您可以得到类似于在编译时可枚举的结构的东西。但这很难看:

#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);
}
请注意,资产类型没有显式的writeToStream。编译器将在运行时通过解包它派生的::std::tuple并写出每个字段来编写它

此外,如果你有空指针,你写的是很差的C++。如果你打算写C++,请写好的,好的C++。 您希望通过运行时反射来完成的这整件事情并不是解决问题的方法


这就是为什么我将char*名称转换为::std::string,并将由大小和数据字段表示的大小分隔字节数组转换为::std::vector。使用这些类型是编写C++的习惯性正确方法。使用裸指针的方式与以前不同。此外,如果有两个值密切相关的字段,即数据字段和大小字段,它们没有行为或任何其他相关指示,那么即使是在运行时进行内省的编译器也很难找到正确的操作。它不知道数据所指向的字节数组有多大,也不知道您是否决定对其进行大小编码。

您所要求的是一种称为-即:

计算机程序检查数据的能力 并修改结构和行为,特别是值, 运行时对象的元数据、属性和功能

C++本机没有这种功能

我的意思是——有人试图介绍它的某些方面——取得了不同程度的成功——它们产生了反射的某些方面,但并不像Ruby之类的语言那样产生了完全反射

但是,如果您喜欢冒险,可以尝试一个名为:

为了了解它是否值得考虑您的代码,这里引用了它-这里有很多关于如何使用API的示例:


祝你好运

你所要求的是一种叫做-的东西,即:

计算机程序检查数据的能力 并修改结构和行为,特别是值, 运行时对象的元数据、属性和功能

C++本机没有这种功能

我的意思是——有人试图介绍它的某些方面——取得了不同程度的成功——它们产生了反射的某些方面,但并不像Ruby之类的语言那样产生了完全反射

但是,如果您喜欢冒险,可以尝试一个名为:

为了了解它是否值得考虑您的代码,这里引用了它-这里有很多关于如何使用API的示例:


祝你好运

> P>通常C++中的模式不是尝试找出类型的成员,而是提供由类型的实现器实现的操作符,它能够序列化/反序列化到磁盘。
例如,您可以查看boost::serialize库。用法不是太复杂,需要提供一个函数,它列出了成员的顺序,然后库将从那里取出并实现序列化到不同的格式。

< P> C++中的通常模式不是尝试找出类型的成员,而是提供一个操作符,由能够序列化/反序列化到磁盘的类型的实现者实现


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

C++中没有反射。@ YoCaiTimMe:这不是真的。有一种编译时反射。在模板中,我可以问这样的问题:这种类型是否支持推回操作?。但它严格地说是在编译时,而不是运行时。typeof运算符支持基本运行时“反射”。但是C语言或C++语言不支持反射的概念,即在运行时内省对象来确定类型和结构。C++支持使用RTTI进行简单的内省,但与你所寻找的相比,它非常有限。如果你对此感兴趣,请阅读更多typeid@Omnifarious我可以用这个。它不需要那么强大,只要能够识别int、char或我可以一直使用char-pointer[4]或int@user1425433您正在采取错误的方法来解决C++中的问题。请不要试图以这种方式扭曲事物。在这种情况下,运行时类型标识实际上对您没有帮助,因为如果变量可能是没有基类的基元类型,那么在不知道其完整类型的情况下,没有可靠的方法来获取函数中的变量值。考虑使用重载函数。这就是他们的目的。C++中没有反射。@约克蒂默:这不是真的。有一种编译时反射。在模板中,我可以问这样的问题:这种类型是否支持推回操作?。但它严格地说是在编译时,而不是运行时。typeof运算符支持基本运行时“反射”。但是它不是很强大。C语言或C++语言不支持
反射的概念,即在运行时内省对象以确定is类型及其结构。C++支持使用RTTI进行简单的内省,但与你所寻找的相比,它非常有限。如果你对此感兴趣,请阅读更多typeid@Omnifarious我可以用这个。它不需要那么强大,只要能够识别int、char或我可以一直使用char-pointer[4]或int@user1425433您正在采取错误的方法来解决C++中的问题。请不要试图以这种方式扭曲事物。在这种情况下,运行时类型标识实际上对您没有帮助,因为如果变量可能是没有基类的基元类型,那么在不知道其完整类型的情况下,没有可靠的方法来获取函数中的变量值。考虑使用重载函数。这就是他们的目的。是的,我明白了,但我怎样才能把他们全部列出来呢我在寻找一种方法,比如:结构资产{char*name;int size;BYTE*data;};资产a;fread char*a,sizeofasset,1,文件@user1425433:您必须列出所有单个成员。或者做大量的模板魔术,在数据结构中使用tuple template类。是的,我明白了,但是我如何将它们全部列出呢?:/我在寻找一种方法,比如:结构资产{char*name;int size;BYTE*data;};资产a;fread char*a,sizeofasset,1,文件@user1425433:您必须列出所有单个成员。或者做大量的模板魔术,为数据结构使用tuple模板类。