我正在嵌入式设备(微控制器)上工作,我想将对象保存到永久存储器(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;
};
[save
和load
的代码很可能是通用的,并且将在单独的'manager'类中发挥最佳作用,而每个数据类仅负责将自身呈现为可重载的职责:
// 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数据序列化以进行网络传输的方式类似,因此可能是您可以利用库来做到这一点-即使您不这样做也可以。无法访问操作系统。
要将字符串保存为二进制,通常我们先保存其长度,然后保存其内容。为了保存其他原始数据,我们可以简单地存储它们的二进制形式。因此,对于您而言,您需要存储的全部是:
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;
要从二进制缓冲区中读取字符串,请先读取其长度,然后复制其数据。
工作示例https://github.com/gustavosinbandera1/serializableClass
在此URL中,您可以找到建议的解决方案的实现,非常不错