【问题标题】:serialize std::map using sstream使用 sstream 序列化 std::map
【发布时间】:2018-01-03 20:15:11
【问题描述】:

我正在使用 C++ 中的 sstream 对 std::map 进行序列化。 序列化函数:

template <class key, class value>
std::string serializeMap(const std::map<key, value>& v){
    std::stringstream ss;
    std::for_each(v.begin(), v.end(), [&ss](const std::pair<key, value>& s){
            ss.write ((char*)&s, sizeof(std::pair<key,value>));
        });
    return ss.str();
}

并使用此反序列化:

template <class key, class value>
void deSerializeMap(const std::string& s, std::map<key, value>& v){

    std::stringstream ss1;

    ss1<<s;

    int pos = 0;
    printf("\nlen %d\n", s.size());
    while(ss1){
        char* ar = new char[sizeof(std::pair<key, value>)];
        ss1.read(ar, sizeof(std::pair<key, value>));
        v.insert(*(reinterpret_cast<std::pair<key, value>*>(ar)));
        pos+=sizeof(std::pair<key, value>);
        ss1.seekg(pos);
        delete[] ar;
    }

}

使用std::string 可以按预期工作。我必须使用一个将const char* 作为参数的C 函数,我尝试使用c_str() 并添加一个 char 数组的 NULL 字符,但 strlen 给出零。我有什么遗漏吗?

【问题讨论】:

  • 尝试分别写/读键和值。作为@user9169574,您无法使用 std::string 和 sizeof 获得对的大小
  • 我会试试的。谢谢。
  • 这里有很多问题,但基本上,如果您曾经为通用数据结构编写序列化代码,并且您没有在包含的类型上递归调用 serialize,那么就有问题了。您假设您的键和值是 POD 类型;在不必要的 pod 类型上调用 sizeof 是代码异味。另外,尽量不要在这样的代码中使用 new 和 delete;这也是代码气味(在非必要的 pod 类型上 reinterpret_cast 也是如此;你可以看到很多气味;-))。
  • 您还应该使用类型的默认重载以使其可序列化:template &lt;typename Key, typename T&gt; std::ostream&amp; operator&lt;&lt;(std::ostream&amp; os, const std::map&lt;Key, T&gt;&amp; map); 根据 C++ 重载实现序列化功能。然后,任何类型支持operator&lt;&lt; 的地图也将被支持。它也适用于任何输出流,而不仅仅是字符串流。

标签: c++ c++11 sstream


【解决方案1】:

正如@Nir Friedman 所提到的,如果您正在实现一个序列化程序而不是在容器元素上递归调用它,那么您可能做错了什么。

进行这种递归序列化意味着将 POD 类型作为“原子”(递归结束条件),递归步骤是在非 POD 类型的每个成员上调用序列化程序。

实现你想要的一个简单的方法是使用一个函数模板,比如:

template <typename T>
uint64_t write(const T& t);

write 函数通常返回一个无符号整数,表示已序列化的字节数。此模板化函数应专门用于所有 POD 和您希望序列化的任何非 POD 类型。因此,在您的情况下,除了int、float、double、char 等之外。您还需要write(const std::map&lt;K,T&gt;&amp; m),它会遍历地图并为每个键值对调用write。同样通常对于序列化,您在实际数据之前写一个标头,以便您可以反序列化数据(例如,您如何知道地图包含多少元素?)。

幸运的是(也许),标准库有@Alexander Huszagh 提到的这种模式(参见“装饰器”模式),那就是可以在任何 POD 上调用的流运算符 (operator&lt;&lt;)类型,因此序列化 std::map 的一种(天真且)简单的方法是:

template<typename K, typename V>
std::ostream& operator<<(std::ostream& os, const std::map<K,V>& m)
{
    os << "map" << '\n';             //write the type
    os << m.size() << '\n'; //write the size of the data
    for(auto&& kvp : m)
    {
        os << "pair" << '\n';
        os << kvp.first << '\n';  //Recursive call here...
        os << kvp.second << '\n'; //Recursive call here...
    }
    return os;
}

上面的反序列化会很复杂,因为递归调用可能只是一个数字、字符或字符串(带有新行),它们没有我添加的“标题”,但您的代码似乎假设它知道Key 和 Value 模板参数,所以对你来说可能没问题。

无论如何,您不应该尝试自己编写序列化程序,要使其正确和通用是一项艰苦的工作,而且完全浪费时间,因为网络上有很多公开可用的。 由于您对序列化地图感兴趣,因此我建议您使用一些 JSON 库。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-28
    • 1970-01-01
    • 2013-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-06
    相关资源
    最近更新 更多