【问题标题】:Efficient Value type有效值类型
【发布时间】:2017-08-20 09:50:49
【问题描述】:

我想创建高效且易于使用的值类型。 Value 的基础是 boost::variant (以及未来的 std::variant),但我是新手。 我有一些问题:

  • 在下面的代码中,是否需要使用递归变体?
  • 是否可以不继承boost::variant?也许存在更有效的方法?
  • 您对下面的代码有什么 cmet 或建议吗(不是完全完成的代码,只是草稿)?

    class Value;

    typedef std::string                 String;
    typedef std::vector<char>           BinData;
    typedef String                      URL;
    typedef unsigned long long          UID;
    TSW_STRONG_TYPEDEF(std::time_t, Time)

    typedef std::vector<Value>          ValueArray;
    typedef std::vector<String>         StringArray;
    //typedef std::pair<String, Value>    NameValue;

    typedef std::list<Value>            ValueList;
    typedef std::list<String>           StringList;
    typedef std::map<String, String>    StringStringMap;
    typedef std::map<String, Value>     NameValueMap;

    struct monostate
    {
        monostate() = default;
    };

    constexpr bool operator<(monostate, monostate) noexcept { return false; }
    constexpr bool operator>(monostate, monostate) noexcept { return false; }
    constexpr bool operator<=(monostate, monostate) noexcept { return true; }
    constexpr bool operator>=(monostate, monostate) noexcept { return true; }
    constexpr bool operator==(monostate, monostate) noexcept { return true; }
    constexpr bool operator!=(monostate, monostate) noexcept { return false; }


    typedef monostate Null;


    class Object
    {
    public:
        Object() = delete;
        Object(const Object &other) = default;
        Object(Object &&other);

        Object(const String &name);
        Object(String &&name);
        Object(const String &name, const NameValueMap &fields);
        Object(String &&name, const NameValueMap &fields);
        Object(const String &name, NameValueMap &&fields);
        Object(String &&name, NameValueMap &&fields);

        Object &operator=(const Object &other) = default;
        Object &operator=(Object &&other);

    public:
        const String &get_name() const;
        const NameValueMap &get_fields() const;

    public:
        bool operator<(const Object &other) const noexcept;
        bool operator>(const Object &other) const noexcept;
        bool operator<=(const Object &other) const noexcept;
        bool operator>=(const Object &other) const noexcept;
        bool operator==(const Object &other) const noexcept;
        bool operator!=(const Object &other) const noexcept;

    private:
        String name_;
        NameValueMap fields_;
    };


    enum class ValueType
    {
        Undefined, Null, Array, BinData, Boolean, DoubleNumber, Int64Number, String, Time, Object
    };

    // Types ordnung need to be same with ValueType ordnung.
    /// Base for the Value class
    typedef boost::variant<monostate, Null, ValueArray, BinData, bool, double, int64_t, String, Time, Object> ValueBase;


    /**
    * @brief The Value class, implements common framework value.
    *
    * This class is a container, which can store multiple values, including Values containers.
    *
    * @note
    * Class based on a variant class. It may be either boost::variant or std::variant in C++17 and higher.
    */
    class Value : public ValueBase
    {
    public:
        using ValueBase::ValueBase;
        Value() = default;
        Value(const String::value_type *v) : ValueBase(String(v)) {}

    public:
        bool is_array() const { return static_cast<ValueType>(which()) == ValueType::Array; }
        bool is_bool() const { return static_cast<ValueType>(which()) == ValueType::Boolean; }
        bool is_bindata() const { return static_cast<ValueType>(which()) == ValueType::BinData; }
        bool is_double() const { return static_cast<ValueType>(which()) == ValueType::DoubleNumber; }
        bool is_int64() const { return static_cast<ValueType>(which()) == ValueType::Int64Number; }
        bool is_null() const { return static_cast<ValueType>(which()) == ValueType::Null; }
        bool is_object() const { return static_cast<ValueType>(which()) == ValueType::Object; }
        bool is_string() const { return static_cast<ValueType>(which()) == ValueType::String; }
        bool is_time() const { return static_cast<ValueType>(which()) == ValueType::Time; }
        bool is_undefined() const { return static_cast<ValueType>(which()) == ValueType::Undefined; }

    public:
        bool          as_bool() const  { return as<bool>(); }
        BinData       &as_bindata()  { return as<BinData>(); }
        double        as_double() const  { return as<double>(); }
        int64_t       as_int64() const  { return as<int64_t>(); }
        Object        &as_object()  { return as<Object>(); }
        String        &as_string()  { return as<String>(); }
        Time          &as_time()  { return as<Time>(); }
        ValueArray    &as_array()  { return as<ValueArray>(); }

    public:
        ValueType     value_type() const  { return static_cast<ValueType>(which()); }

    public:
        template <typename T>
        const T& as() const { return boost::get<T>(*this); }

        template <typename T>
        T& as() { return boost::get<T>(*this); }

        template <typename T>
        const T& as(const T& default_value) const { return type() == typeid(T) ? boost::get<T>(*this) : default_value; }

        template <typename T>
        T& as(const T& default_value) { return type() == typeid(T) ? boost::get<T>(*this) : default_value; }

        template <typename T> boost::optional<T> as_optional() { return boost::make_optional(type() == typeid(T), as<T>()); }

    public:
        bool operator==(const ValueBase &other) const { return ValueBase::operator==(other); }
        bool operator<(const ValueBase &other) const { return ValueBase::operator<(other); }
        bool operator>(const ValueBase &other) const { return !((*this) < other || (*this) == other); }
        bool operator<=(const ValueBase &other) const { return ((*this) < other || (*this) == other); }
        bool operator>=(const ValueBase &other) const { return !((*this) < other); }
        bool operator!=(const ValueBase &other) const { return !((*this) == other); }

    private:
        // Force compile error, prevent Variant(bool) to be called
        Value(void *) = delete;
    };

【问题讨论】:

  • 你想用这个解决什么问题?
  • - 我需要双工总线来发送/接收消息。 - 消息是一个容器,包含字段和元数据链接。 - 在/从 BSON 中序列化/反序列化的消息。 - 我需要一个common value。 - 我需要很少的延迟来进行序列化/反序列化和从消息字段获取数据。
  • 这个问题可能更适合Code Review
  • 在反序列化的情况下,您可以模仿 protobuf 的设计并从底层二进制缓冲区进行惰性转换。 (但不要模仿 API 接口!)
  • @A.N.没有人喜欢 protobuf :) 我只是把它作为一个具体优化的例子

标签: c++ c++11 boost variant


【解决方案1】:

我觉得不错。

您可以不使用递归变体 IFF 您的标准库实现允许为不完整类型实例化容器类。

我会注意到,由于所有内容都公开地与基类相关联,因此在不更改(二进制)接口的情况下,没有什么可以更改的实现。因此,我肯定会内联实现所有成员,以便编译器即使没有 LTO 也会对其进行优化。

我不清楚to_X 成员做什么(可能只是a&lt;X&gt;,但也可能取决于can_convert()?)。如果它只是 as_&lt;&gt; 的一个环绕,我会将它们重命名为 as_X() 等。

您可能还想添加optional&lt;&gt;-like 成员,例如

template <typename T> T const& get_value_or(T const& default_value) const;

还有可能

template <typename T> optional<T> get() const;
// with boost optional you can prevent a copy²:
template <typename T> optional<T const&> get() const;

这会启用如下代码:

if (auto& s = value.get<String>()) {
     std::cout << "The string value is '" << *s << "'\n";
} else {
     std::cout << "Value has no string value\n";
}

¹ 这不是 - 还 - 指定的标准。你总是可以使用 Boost Container 来代替它,它保证了这一点,以及非分配构造

² 只需确保您不允许对右值进行操作,以消除可预测的错误类别,例如

template <typename T> optional<T const&> get()&& = delete;

【讨论】:

  • 我已经附加了实现。
  • @A.N.我对你的实现感到困惑。为什么评论引用value_?你会去粉刺吗?这会伤害优化器。就像我说的,我会定义成员 inline
  • 好的。现在我不知道can_convert 的用途是什么。以及为什么函数被(错误)命名混淆。但底线:该变体有效并且可以被认为是轻量级的。有了我在答案文本中所做的几句话。
  • 我认为,can_convert 现在没用了。更好的名字:is_conversible_to。我将其删除或重写为模板。
  • recursive_wrapper&lt;&gt; 是一个间接层,它允许与不允许 允许不完整类型的任何元素类型一起使用。如果您的容器不需要它,请高兴,但这不会让recursive_wrapper&lt;&gt; 过时。这不是兼容性问题,而是可扩展性功能。
猜你喜欢
  • 2021-10-12
  • 1970-01-01
  • 2022-11-20
  • 2021-11-26
  • 2019-11-18
  • 2021-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多