动手吧,用C++写个json类——01

404 阅读2分钟

json基础

这里只讲编程,所以,略。

设计节点数据的类型

json节点数据支持的类型是有标准的,所以直接枚举吧:

    enum class config_value_type
    {
        number_integer,
        number_float,
        string,
        array,
        object,
        boolean,
        null,
    };

那么,它们在C++中又是如何实现的呢?这也不难:

    struct config_args
    {
        using boolean_type = bool;
​
        using integer_type = int64_t;
​
        using float_type = double;
​
        template <class _CharTy, class... _Args>
        using string_type = std::basic_string<_CharTy, _Args...>;
​
        template <class _Kty, class... _Args>
        using array_type = std::vector<_Kty, _Args...>;
​
        template <class _Kty, class _Ty, class... _Args>
        using object_type = std::map<_Kty, _Ty, _Args...>;
​
        template <class _Ty>
        using allocator_type = std::allocator<_Ty>;
    };

把这些类型统一放在一个class里,挺干净的,不是吗?但是,却不能说挺简单的,还是需要给些说明的:

  • 用using再做一次声明,多了这层抽象可以有效地屏蔽代码和真实类型之间的耦合
  • 注意这里的allocator_type了吗?这主要是为了给stl的容器类型用的,因为它可以自定义分配器,这里默认也是用的stl的分配器
  • 注意这里的string_type,为了最大限度的抽象,它用了一个可变参数的模板,实际上现在它仍然不是实际类型;array_type和object_type同理

注意这里没有指定_CharTy的实际类型,因为C++的字符类型现在实在是有些拥挤,不过常用的其实还是char和wchar_t,所以:

    struct json_args : config_args
    {
        using char_type = char;
    };
​
    struct wjson_args : config_args
    {
        using char_type = wchar_t;
    };
​
    struct json16_args : config_args
    {
        using char_type = char16_t;
    };
​
    struct json32_args : config_args
    {
        using char_type = char32_t;
    };

设计节点数据的存储结构

该怎么设计json节点数据的存储结构呢?其实也很简单,基本上,它就是一个type+一个值:

    namespace detail
    {
        template <typename _ConfTy>
        struct config_value
        {
            using string_type = typename _ConfTy::string_type;
            using integer_type = typename _ConfTy::integer_type;
            using float_type = typename _ConfTy::float_type;
            using boolean_type = typename _ConfTy::boolean_type;
            using array_type = typename _ConfTy::array_type;
            using object_type = typename _ConfTy::object_type;
​
            config_value_type type;
            union
            {
                boolean_type boolean;
                integer_type number_integer;
                float_type   number_float;
                string_type* string;
                object_type* object;
                array_type* vector;
            } data;
        };
    }

这里用using再一次的把类型声明了一遍,主要是为了看起来工整,其实你也可以不用using:

            config_value_type type;
            union
            {
                typename _ConfTy::boolean_type boolean;
                typename _ConfTy::integer_type number_integer;
                typename _ConfTy::float_type   number_float;
                typename _ConfTy::string_type* string;
                typename _ConfTy::object_type* object;
                typename _ConfTy::array_type* vector;
            } data;

这里用union,毫无疑问,是为了节省存储空间。

设计节点

我们先来个只有节点数据的最简单的节点吧,啥功能都没有:

    template <typename _Args>
    class basic_config
    {
    public:
        template <typename _Ty>
        using allocator_type = typename _Args::template allocator_type<_Ty>;
        using boolean_type = typename _Args::boolean_type;
        using integer_type = typename _Args::integer_type;
        using float_type = typename _Args::float_type;
        using char_type = typename _Args::char_type;
        using string_type =
            typename _Args::template string_type<char_type, std::char_traits<char_type>, allocator_type<char_type>>;
        using array_type = typename _Args::template array_type<basic_config, allocator_type<basic_config>>;
        using object_type =
            typename _Args::template object_type<string_type, basic_config, std::less<string_type>,
            allocator_type<std::pair<const string_type, basic_config>>>;
​
        using value_type = detail::config_value<basic_config>;
​
    public:
        value_type value_;
    };

这里用using将节点数据的类型映射为真实的数据类型。

好了,终于该我们的json登场了:

    using json = basic_config<json_args>;
    using wjson = basic_config<wjson_args>;

你看,因为我们设计的抽象,真正的json看起来就是这么的精明干练。

测试用例

就这点东西也要写测试用例啊,听我一句用无数血泪换来的经验之谈:

一名优秀的程序员,要把写测试用例这件事形成一种习惯。

        TEST_METHOD(TestBase)
        {
            json j;
            j.value_.type = config_value_type::number_integer;
            j.value_.data.number_integer = 1;
            Assert::AreEqual((int64_t)1, j.value_.data.number_integer);
        }

参考资料

github上的configor项目