【问题标题】:Using boost::hash with boost::property_tree?将 boost::hash 与 boost::property_tree 一起使用?
【发布时间】:2017-12-27 21:03:54
【问题描述】:

我正在尝试为包含 boost::property_tree (boost::property_tree::basic_ptree<std::basic_string<char>, std::basic_string<char> >) 的对象生成哈希值。在属性树的 boost 头文件中搜索我找不到任何已定义的 hash_value 函数。我正在努力实现的基本示例:

class MyClass{
public:
    friend std::size_t hash_value(const MyClass & obj);
private:
    boost::property_tree m_data;
}

inline std::size_t hash_value(const MyClass & obj){
    std::size_t seed = 0;
    boost::hash_combine(seed,obj.m_data);
    return seed;
}

此代码将无法编译: "没有匹配函数调用 'hash_value(const boost::property_tree &)'"

我的问题:在我没有找到的某些头文件中是否为 boost::property_tree 定义了 hash_value。如果不是,那么通过遍历它来散列 property_tree 的 boost 惯用方法是什么?

我应该使用 ptree 序列化转换为 std::string 并对其进行散列,还是手动遍历树并创建递归散列?

【问题讨论】:

  • 不要使用序列化,因为它容易改变表示/松散的信息。

标签: boost hash boost-propertytree


【解决方案1】:

只需将hash<> 专门用于 ptree:

#include <boost/property_tree/ptree.hpp>
#include <boost/functional/hash.hpp>

namespace boost {
    template<typename Key, typename Data, typename KeyCompare>
    struct hash<boost::property_tree::basic_ptree<Key, Data, KeyCompare> > {
        size_t operator()(boost::property_tree::basic_ptree<Key, Data, KeyCompare> const& pt) const {
            std::size_t seed = 0;
            boost::hash_combine(seed, pt.template get_value<std::string>());
            boost::hash_range(seed, pt.begin(), pt.end());
            return seed;
        }
    };
}

够了!这是一个小的 MyClass,它从类似 json 的文字中读取:

#include <boost/property_tree/json_parser.hpp>

class MyClass{
  public:
      MyClass(std::string const& json) {
          std::istringstream iss(json);
          read_json(iss, m_data);
      }
  private:
    boost::property_tree::ptree m_data;

    friend inline std::size_t hash_value(const MyClass& obj){
        std::size_t seed = 0;
        boost::hash_combine(seed, obj.m_data);
        return seed;
    }
};

现在你可以测试它了

Live On Coliru

#include <iostream>

int main() {
    for (std::string const data : {
            R"({"a":[1,2,3],"b":{"nest":"hello","more":"world"}})",
            R"({"b":{"nest":"hello","more":"world"},"a":[1,2,3]})",
            R"({ })",
            R"({})",
        })
    {
        MyClass o(data);
        std::cout << "object hash: " << hash_value(o) << " " << data << "\n";
    }
}

打印:

object hash: 3573231694259656572 {"a":[1,2,3],"b":{"nest":"hello","more":"world"}}
object hash: 11176663460548092204 {"b":{"nest":"hello","more":"world"},"a":[1,2,3]}
object hash: 3864292196 { }
object hash: 3864292196 {}

注意:哈希和等式

对于许多容器,hash 假设有一个对应的 相等比较器。如果它们不匹配,你会得到Undefined Behaviour。

您可能很想根据ptree 的有序(关联)接口来定义hash_range:

boost::hash_range(seed, pt.ordered_begin(), pt.not_found()); // CAUTION

这具有{"a":1,"b":2} 将匹配{"b":2,"a":1} 的优势。

不要这样做,除非你知道自己在做什么。具体来说,您需要将兼容的相等比较器传递给使用此哈希的每个容器/算法。

如果你这样写,并使用类似的驱动程序进行测试:

int main() {
    MyClass a{ R"({"a":[1,2,3],"b":{"nest":"hello","more":"world"}})" },
            b{R"({"b":{"nest":"hello","more":"world"},"a":[1,2,3]})" },
            c{R"({ })" },
            d{R"({})" };

    for (auto& lhs : {a,b,c,d})
    for (auto& rhs : {a,b,c,d})
    {
        std::cout << "hash: " << hash_value(lhs) << " " << hash_value(rhs) << " - equality: " << std::boolalpha << (lhs==rhs) << "\n";
        if ((hash_value(lhs) == hash_value(rhs)) != (lhs==rhs))
            std::cout << " -- MISMATCH\n";
    }
}

它将打印:

hash: 10737438301360613971 10737438301360613971 - equality: true
hash: 10737438301360613971 10737438301360613971 - equality: false
 -- MISMATCH
hash: 10737438301360613971 3864292196 - equality: false
hash: 10737438301360613971 3864292196 - equality: false
hash: 10737438301360613971 10737438301360613971 - equality: false
 -- MISMATCH
hash: 10737438301360613971 10737438301360613971 - equality: true
hash: 10737438301360613971 3864292196 - equality: false
hash: 10737438301360613971 3864292196 - equality: false
hash: 3864292196 10737438301360613971 - equality: false
hash: 3864292196 10737438301360613971 - equality: false
hash: 3864292196 3864292196 - equality: true
hash: 3864292196 3864292196 - equality: true
hash: 3864292196 10737438301360613971 - equality: false
hash: 3864292196 10737438301360613971 - equality: false
hash: 3864292196 3864292196 - equality: true
hash: 3864292196 3864292196 - equality: true

MISMATCH 警告表明相等性和散列不一致。

如果您使用原始哈希(首屏)运行测试驱动程序,它将打印:

hash: 3573231694259656572 3573231694259656572 - equality: true
hash: 3573231694259656572 11176663460548092204 - equality: false
hash: 3573231694259656572 3864292196 - equality: false
hash: 3573231694259656572 3864292196 - equality: false
hash: 11176663460548092204 3573231694259656572 - equality: false
hash: 11176663460548092204 11176663460548092204 - equality: true
hash: 11176663460548092204 3864292196 - equality: false
hash: 11176663460548092204 3864292196 - equality: false
hash: 3864292196 3573231694259656572 - equality: false
hash: 3864292196 11176663460548092204 - equality: false
hash: 3864292196 3864292196 - equality: true
hash: 3864292196 3864292196 - equality: true
hash: 3864292196 3573231694259656572 - equality: false
hash: 3864292196 11176663460548092204 - equality: false
hash: 3864292196 3864292196 - equality: true
hash: 3864292196 3864292196 - equality: true

【讨论】:

  • 刚刚意识到您只能使用 ptree 专业化(std::pair is taken care of by boost,而不是 the standard library)。
  • 我根据您的回答使用了一个实现,然后在其中发现了一个错误。返回您的答案,我能够在您提供的示例中重现该错误:try it live。当前实现不检查 ptree 中项目的值,只检查键。
  • @Carter12s 你说得对。我 - 令人尴尬 - 忘记了节点可以具有除子节点之外的“内容”值。固定的。 coliru.stacked-crooked.com/a/ba934612afdb7bc1
  • 你是C++之神!我只是自己想出了解决方案,但你已经打败了我。谢谢您的帮助。有小费罐吗?
猜你喜欢
  • 2016-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-07
  • 1970-01-01
  • 2018-07-21
  • 2019-02-10
相关资源
最近更新 更多