picojson の value, array, object の生成を簡単にするヘルパーライブラリーの実装例

# 概要

[picojson](https://github.com/kazuho/picojson/) の value, array (`[ value1, value2, .. ]`), object (`{ "key1": value1, .. }`) の生成が頻出する場合、生成元の型によっては value, array, object を生成する前に一手間必要となり面倒な事がままある。

そこで、今回は picojson の value, array, object の生成について用意しておくと一般に便利なヘルパーの実装例を紹介したい。

# 実装例

## 前置き

- 今回紹介する実装例は [github - usagi/usagi - include/usagi/json/picojson](https://github.com/usagi/usagi/tree/master/include/usagi/json/picojson) に実装例全体が使用可能な状態で公開されています。

基本的な型について以下のエイリアスが "type.hxx" により定義されています:

```c++
#pragma once

#include <picojson.h>
#include <string>

namespace usagi::json::picojson
{
  using object_type = ::picojson::object;
  using array_type  = ::picojson::array;
  using value_type  = ::picojson::value;
  using null_type   = ::picojson::null;
  using number_type = double;
  using string_type = std::string;
}
```

## make_value

`make_value( x )` によりおおよそJSONの値として一般に放り込む number, string, array, object, null へ可換な型を想定した `picojson::value` を生成するヘルパーの実装例です。

- https://github.com/usagi/usagi/blob/master/include/usagi/json/picojson/make_value.hxx

```c++
/// @file
/// @brief 任意の何かから value_type を"生成"する make_value さん群

#pragma once

#include "type.hxx"

namespace usagi::json::picojson
{
  static inline auto make_value(                       ) -> value_type { return value_type(); }
  static inline auto make_value( const null_type       ) -> value_type { return make_value(); }
  static inline auto make_value( const value_type&  in ) -> value_type { return value_type( in ); }
  static inline auto make_value( value_type&&       in ) -> value_type { return std::move( in ); }
  static inline auto make_value( const array_type&  in ) -> value_type { return value_type( in ); }
  static inline auto make_value( array_type&        in ) -> value_type { return value_type( std::move( in ) ); }
  static inline auto make_value( const object_type& in ) -> value_type { return value_type( in ); }
  static inline auto make_value( object_type&&      in ) -> value_type { return value_type( std::move( in ) ); }
  static inline auto make_value( const char*        in ) -> value_type { return value_type( in ); }
  static inline auto make_value( const std::string& in ) -> value_type { return value_type( in ); }
  static inline auto make_value( std::string&&      in ) -> value_type { return value_type( std::move( in ) ); }
  static inline auto make_value( const bool         in ) -> value_type { return value_type( in ); }
  
  template < typename T >
  static inline auto make_value
  ( const T v
  ) -> value_type
  { return value_type( static_cast< double >( v ) ); }
}
```

Note: null, string, value, array, object 以外の型を放り投げると number （≃ double）として扱います。あらゆる型について厳密に定義を書く必要の無い一般的な用途向けに便利良く扱いが簡単です。浮動小数点数の丸め誤差や分解能が問題になる数値を放り込みたい場合は `long double x; make_value( std::to_string( x ) )` あるいは [Boost.Multiprecision を使い文字列にシリアライズ](https://boostjp.github.io/tips/multiprec-float.html) すれば扱いが簡単です。

## make_array, make_array_value

次のような使用法:

```c++
auto a = make_array( 1.23f, 1.23, 123, "hoge", "fuga"s, some_value, some_array, some_object, ... );
auto v = make_array_value( ... );
```

により、 array や array 入りの value を手軽に生成するヘルパーの実装例です。

- https://github.com/usagi/usagi/blob/master/include/usagi/json/picojson/make_array.hxx

```c++
#pragma once

#include "make_value.hxx"

namespace usagi::json::picojson
{
  namespace detail
  {
    template < typename T >
    static inline decltype( auto ) emplace_back
    ( array_type& a, const T& v )
    {
      a.emplace_back( make_value( v ) );
      return a;
    }
    
    static inline decltype( auto ) make_array_internal( array_type& a )
    {
      return a;
    }
    
    template < typename HEAD, typename ... TAIL >
    static inline decltype( auto ) make_array_internal
    ( array_type& a, const HEAD& v, const TAIL& ... vs )
    {
      return make_array_internal( emplace_back( a, v ), vs ... );
    }
  }
  
  template < typename ... TS >
  static inline auto make_array
  ( const TS& ... vs )
  {
    array_type a;
    return detail::make_array_internal( a, vs ... );
  }
  
  template < typename ... TS >
  static inline auto make_array_value
  ( const TS& ... vs )
  {
    return value_type( make_array( vs ... ) );
  }
}
```

## make_object, make_object_value

次のような使用法:

```c++
auto o =
  make_object
  ( "f", 1.23f
  , "d", 1.23
  , "i", 123
  , "c", "hoge"
  , "s", "fuga"s
  , "v", some_value
  , "a", some_array
  , "o", some_object
  , "some_key", something
  , ...
  );
auto v = make_object_value( ... );
```

により、 array や array 入りの value を手軽に生成するヘルパーの実装例です。

- https://github.com/usagi/usagi/blob/master/include/usagi/json/picojson/make_object.hxx

```c++
#pragma once

#include "make_value.hxx"

namespace usagi::json::picojson
{
  namespace detail
  {
    template < typename T >
    static inline decltype( auto ) emplace
    ( object_type& o, const std::string& k, const T& v )
    {
      o.emplace( k, make_value( v ) );
      return o;
    }
    
    static inline decltype( auto ) make_object_internal( object_type& o )
    {
      return o;
    }
    
    template < typename HEAD, typename ... TAIL >
    static inline decltype( auto ) make_object_internal
    ( object_type& o, const std::string& k, const HEAD& v, const TAIL& ... vs )
    {
      return make_object_internal( emplace( o, k, v ), vs ... );
    }
  }
  
  template < typename ... TS >
  static inline auto make_object( const TS& ... vs )
  {
    object_type o;
    return detail::make_object_internal( o, vs ... );
  }
  
  template < typename ... TS >
  static inline auto make_object_value
  ( const TS& ... vs )
  {
    return value_type( make_object( vs ... ) );
  }
}
```

# 使用例

```c++
#include <usagi/json/picojson/make_array.hxx>
#include <usagi/json/picojson/make_object.hxx>

#include <iostream>

auto main() -> int
{
  using namespace std;
  using namespace usagi::json::picojson;
  
  std::cout
    << "f: " << make_value( 1.23f ) << '\n'
    << "d: " << make_value( 1.23 ) << '\n'
    << "i: " << make_value( 123 ) << '\n'
    << "n: " << make_value( ) << '\n'
    << "b: " << make_value( true ) << '\n'
    << "s: " << make_value( string( "hoge" ) ) << '\n'
    << "c: " << make_value( "hoge" ) << '\n'
    << "v: " << make_value( make_value( "hoge" ) ) << '\n'
    << "a: " << make_array_value( "hoge", string( "fuga" ), 1.23f, 1.23, 123, null_type(), make_array( 1,2,3 ) ) << '\n'
    << "o: " << make_object_value( "f", 1.23f, "d", 1.23, "i", 123, "n", null_type(), "a", make_array( 1,2,3 ), "o", make_object( "a", 1 ) ) << '\n'
    ;
}
```

## 実行結果

```json
f: 1.2300000190734863
d: 1.23
i: 123
n: null
b: true
s: "hoge"
c: "hoge"
v: "hoge"
a: ["hoge","fuga",1.2300000190734863,1.23,123,null,[1,2,3]]
o: {"a":[1,2,3],"d":1.23,"f":1.2300000190734863,"i":123,"n":null,"o":{"a":1}}
```