C ++ rapidjson:GenericValue :: IsNull在任何情况下都返回false

时间:2014-10-06 18:02:17

标签: c++ performance design-patterns rapidjson

在我们的项目中发现了一个神秘的问题后,我仍感到震惊。

我们意识到调用HasMember(“string”)正在进行额外搜索。因此,出于性能原因,我们会对其进行更改。

主要思想是:

而不是调用HasMember,然后预先缓存引用,如:

rapidjson::Document d;
d.Parse<0>(json);

if(d.HasMember("foo"))
{
    const rapidjson::Value& fooValue = d["foo"];

    // do something with fooValue
}

更改为:

rapidjson::Document d;
d.Parse<0>(json);

const rapidjson::Value& fooValue = d["foo"];
if( !fooValue.IsNull() )
{
    // do something with fooValue
}

这非常好,我们保存以执行两次搜索,而不是只执行一次。但是,这就是问题所在。

如果您开始查看rapidjson如何实现nullvalue(在搜索失败时默认返回),您将看到以下代码:

//! Get the value associated with the object's name.
GenericValue & operator[](const Ch* name) {
    // Check
    if (Member * member = FindMember(name)) {
        return member->value;
    } else {
        // Nothing
        static GenericValue NullValue;
        return NullValue;
    }
}

// Finder
const GenericValue & operator[] (const Ch* name) const { 
    // Return
    return const_cast<GenericValue &> (* this)[name]; 
}

因此,如果没有找到该成员,我们返回一个本地静态变量。乍一看这可能听起来不错,但由于这是通过引用返回可能很容易导致隐藏的错误。

想象一下有人改变了静态NullValue的引用。这将导致对IsNull的所有进一步调用(在查找之后)将失败,因为NullValue已更改为其他类型甚至是随机存储器。

那么,你是做什么的?你认为这是一个很好的空模式的例子吗?

我很困惑,我喜欢返回默认空值的想法,但由于不是作为const返回,这是危险的。而且,即使我们确实在所有情况下都将它作为const返回,开发人员仍然可以使用const_cast(但我不希望如果他们这样做,将由他们负责)。

我想听听其他案例和例子。如果有人可以在rapidjson代码下提供真正的解决方案,那将基本上非常棒和令人惊叹。

1 个答案:

答案 0 :(得分:7)

很久以前社区已经提出了这种设计的缺陷。由于operator[]也需要非const版本,因此无法保持静态变量的完整性。

因此,在较新版本的RapidJSON中更改了此API。 operator[]只是断言不存在的密钥。如果不确定密钥是否存在,则最好使用

MemberIterator FindMember(const Ch* name);
ConstMemberIterator FindMember(const Ch* name) const;

将值与MemberEnd()进行比较,以检查密钥是否存在。这也记录在案here

此外,请注意RapidJSON已移至GitHub。许多问题已得到解决。如果可能,请使用最新版本。谢谢。

P.S。我是RapidJSON的作者。