【问题标题】:Serializing object to byte-array in C++在 C++ 中将对象序列化为字节数组
【发布时间】:2014-12-07 19:21:59
【问题描述】:

我正在开发嵌入式设备(微控制器),我想将对象保存到永久存储(EEPROM)中。我能找到的大多数序列化解决方案都以某种方式使用文件系统,但我的目标没有文件系统。

因此我的问题是,如何将对象序列化为字节数组,以便之后将该字节数组保存到 EEPROM?

这是我正在尝试做的一个例子:

class Person{
     //Constructor, getters and setters are omitted

    void save(){
         char buffer[sizeof(Person)]; 
         serialize(buffer);
         EEPROM::Save(buffer, sizeof(Person)); 
    }

    void load(){
         char buffer[sizeof(Person)]; 
         EEPROM::Load(buffer, sizeof(Person));
         deserialize(buffer);
    }

    void serialize(char* result){
        //?????
    }

    Person deserialize(char* buffer){
        //??????
    }

private:
    char* name;  
    int   age; 
    float weight; 
};

【问题讨论】:

  • 序列化数据最简单的方法是添加分隔符,但这取决于您要序列化的数据。通常,您应该能够按字节读取/写入缓冲区并以相同的方式将其存储到您的 EEPROM

标签: c++ arrays serialization bytearray microcontroller


【解决方案1】:

saveload 的代码很可能是相当通用的,并且在单独的“管理器”类中工作得最好,让每个数据类只负责将自身呈现为可重新加载:

// Interface class
class Serializable
{
public:
    virtual size_t serialize_size() const = 0;
    virtual void serialize(char* dataOut) const = 0;
    virtual void deserialize(const char* dataIn) = 0;
};

// Load / save manager
class EEPromManager
{
public:
    void save( const Serializable& s )
    {
        char * data;
        size_t data_len;
        reserve_memory( data, data_len, s );
        s.serialize( data );
        EEPROM::Save( data , data_len );
        delete [] data;
    }

    void load( Serializable& s )
    {
        char * data;
        size_t data_len;
        reserve_memory( data, data_len, s );
        EEPROM::Load( data, data_len );
        s.deserialize( data );
        delete [] data;
    }

private:
    char* reserve_memory( char*& data, size_t& data_len, const Serializable& s )
    {
        return new char[ s.serialize_size() ];
    }
};

您打算序列化/反序列化的每个类都应该从一个接口继承,该接口要求这些功能的虚拟接口。请注意,您需要在此处进行自己的内存管理。我给出了一个简单的例子,但你可能想要更健壮的东西。

然后每个函数应该顺序序列化类的所有属性(链接基类并在需要时在聚合对象上调用serialize。)

class Person : public Serializable
{
public:
    virtual size_t serialize_size() const
    {
        return SerializablePOD<char*>::serialize_size(name) +
               SerializablePOD<int>::serialize_size(age) +
               SerializablePOD<float>::serialize_size(weight);
    }

    virtual void serialize(char* dataOut) const
    {
        dataOut = SerializablePOD<char*>::serialize(dataOut, name);
        dataOut = SerializablePOD<int>::serialize(dataOut, age);
        dataOut = SerializablePOD<float>::serialize(dataOut, weight);
    }
    virtual void deserialize(const char* dataIn)
    {
        dataIn = SerializablePOD<char*>::deserialize(dataIn, name);
        dataIn = SerializablePOD<int>::deserialize(dataIn, age);
        dataIn = SerializablePOD<float>::deserialize(dataIn, weight);
    }

private:
    char* name;
    int   age;
    float weight;
};

您将受益于通用代码来序列化/反序列化每个单独的类型,这样您就不必一直编写代码来编写字符串的长度等。即每个 POD 类型的序列化/反序列化:

template <typename POD>
class SerializablePOD
{
public:
    static size_t serialize_size(POD str)
    {
        return sizeof(POD);
    }
    static char* serialize( char* target, POD value )
    {
        return memcpy( target, &value, serialize_size(value) );
    }
    static const char* deserialize( const char* source, POD& target )
    {
        memcpy( &target, source, serialize_size(target) );
        return source + serialize_size(target);
    }
};

template<>
size_t SerializablePOD<char*>::serialize_size(char* str)
{
    return sizeof(size_t) + strlen(str);
}

template<>
const char* SerializablePOD<char*>::deserialize( const char* source, char*& target )
{
    size_t length;
    memcpy( &length, source, sizeof(size_t) );
    memcpy( &target, source + sizeof(size_t), length );
    return source + sizeof(size_t) + length;
}

顺便说一句,您可能还需要考虑如果在软件升级中更改对象的架构会发生什么。您保存的对象可能会在重新加载时损坏,除非您使用 - 例如 - 类版本标识符对此进行编码。

最后的想法:在微观层面上,您所做的在很多方面类似于 POD 数据序列化以进行网络传输的方式,因此您可能可以利用库来做到这一点 - 即使您无权访问操作系统。

【讨论】:

  • 其实这正是我已经开始实施它的方式。我只是不确定是否有更优雅的选择。但是这个确认对于答案同样有用。谢谢。
  • NP。我添加了一些代码作为一般示例,但我强调,未经测试 - 所以请谨慎对待。希望对您有所帮助。
【解决方案2】:

要将字符串保存为二进制,通常我们先保存它的长度,然后再保存它的内容。为了保存其他原始数据,我们可以简单地存储它们的二进制形式。所以在你的情况下,你只需要存储:

Length to name
char array of name
age
weight

所以串行的代码是:

size_t buffer_size = sizeof(int) + strlen(name) + sizeof(age) + sizeof(weight);
char *buffer = new char[buffer_size];
*(int*)p = strlen(name);  p += sizeof(int);
memcpy(p, name, strlen(name));  p += strlen(name);
*(int*)p = age;  p += sizeof(int);
*(float*)p = weight;
EEPROM::Save(buffer, buffer_size);
delete[] buffer;

要从二进制缓冲区中读取字符串,首先读取其长度,然后复制其数据。

【讨论】:

    猜你喜欢
    • 2013-04-29
    • 2014-09-02
    • 1970-01-01
    • 2016-04-18
    • 2017-03-03
    • 2018-02-04
    • 2014-03-24
    • 2021-05-25
    • 2016-05-08
    相关资源
    最近更新 更多