configuring types

# Customizing Types #

The `toml::value` class uses `std::int64_t` for `integer_type` and `std::unordered_map<key_type, value_type>` for `table_type`.

However, in some cases, you may want to use `boost::multiprecision::int128_t` or `std::map`.

To accommodate this, `toml::value` is implemented with template parameters that allow you to change the stored types.

Just as `std::string` is actually an alias for `std::basic_string<char, std::char_traits<char>, std::allocator<char>>`, `toml::value` is an alias for `toml::basic_value<toml::type_config>`.

Here, we will explain the types contained in `toml::type_config` and how to define a different `config` type.

## `type_config` #

The `type_config` class contains the following member types and `static` member functions:

    namespace toml
    {
    struct type_config
    {
        using comment_type  = preserve_comments;

        using boolean_type  = bool;
        using integer_type  = std::int64_t;
        using floating_type = double;
        using string_type   = std::string;

        template<typename T>
        using array_type = std::vector<T>;
        template<typename K, typename T>
        using table_type = std::unordered_map<K, T>;

        static result<integer_type, error_info>
        parse_int(const std::string& str, const source_location src, const std::uint8_t base);

        static result<floating_type, error_info>
        parse_float(const std::string& str, const source_location src, const bool is_hex);
    };
    }

`toml::basic_value<TypeConfig>` defines the stored `boolean_type` as `TypeConfig::boolean_type`, the stored `integer_type` as `TypeConfig::integer_type`, and so on.

Additionally, `array_type` is defined as `TypeConfig::array_type<toml::basic_value<TypeConfig>>` and `table_type` is defined as `TypeConfig::table_type<key_type, toml::basic_value<TypeConfig>>`.

By passing a class that defines these member types and functions to `toml::basic_value`, you can customize the types used by that `toml::basic_value`.

The `parse_int` and `parse_float` functions provide parsing methods when custom numeric types are used. These functions receive strings with prefixes like `0x` and digit separators like `_` removed, such as `123456` or `DEADBEEF`. The `base` parameter will be one of `10`, `16`, `8`, or `2`. Implement these functions to parse your custom `integer_type` and `floating_type`.

As a default implementation, `toml::read_int` and `toml::read_float` are provided.

    static result<integer_type, error_info>
    parse_int(const std::string& str, const source_location src, const std::uint8_t base)
    {
        return toml::read_int<integer_type>(str, src, base);
    }

    static result<floating_type, error_info>
    parse_float(const std::string& str, const source_location src, const bool is_hex)
    {
        return toml::read_float<floating_type>(str, src, is_hex);
    }

The `read_int` function uses `istream` and employs `std::hex` and `std::oct` for hexadecimal and octal parsing, respectively. For binary parsing, it is implemented using multiplication and addition. If your type supports these operations, you can use `read_int` as-is.

The `read_float` function also uses `istream`. Hexadecimal floating-point numbers are only supported for `double` and `float` types. If `read_float` is called with any other type and `hexfloat` is used, it will always return a parse error. Therefore, if you need to use a floating-point type other than `double` or `float` with `hexfloat`, you will need to implement support for that. If `hexfloat` is not used, no additional implementation is necessary.

## Preserving Order of Values in Tables #

In addition to the default `toml::type_config`, there is also `toml::ordered_type_config`. This changes the `table_type` to an ordered_map.

Using this, `toml::ordered_value` is defined, along with aliases for its array and table types as `toml::ordered_array` and `toml::ordered_table`, respectively.

You can use `toml::ordered_value` by calling `toml::parse(...)` as `toml::parse<toml::ordered_type_config>(...)`.

    #include <toml.hpp>

    int main()
    {
        toml::ordered_value input = toml::parse<toml::ordered_type_config>("example.toml");
        std::cout << toml::format(input) << std::endl;
        return 0;
    }

## Not Preserving Comments #

The `type_config` defines a container for storing comments via `comment_type`.

If comments do not contain significant information and can be discarded during parsing, specify `toml::discard_comments` for `comment_type`.

    struct wo_comment_config
    {
        using comment_type  = toml::discard_comments; // XXX

        using boolean_type  = bool;
        using integer_type  = std::int64_t;
        using floating_type = double;
        using string_type   = std::string;

        template<typename T>
        using array_type = std::vector<T>;
        template<typename K, typename T>
        using table_type = std::unordered_map<K, T>;

        static result<integer_type, error_info>
        parse_int(const std::string& str, const source_location src, const std::uint8_t base)
        {
            return toml::read_int<integer_type>(str, src, base);
        }

        static result<floating_type, error_info>
        parse_float(const std::string& str, const source_location src, const bool is_hex)
        {
            return toml::read_float<floating_type>(str, src, is_hex);
        }
    };

## Using Containers Other Than `std::vector` for Arrays #

To use a container other than `vector` (e.g., `std::deque`) for implementing TOML arrays, modify `array_type` as follows.

Similarly, to use a container other than `unordered_map` (e.g., `std::map`) for table types, modify `table_type` as shown below.

    struct deque_map_config
    {
        using comment_type  = toml::preserve_comments;

        using boolean_type  = bool;
        using integer_type  = std::int64_t;
        using floating_type = double;
        using string_type   = std::string;

        template<typename T>
        using array_type = std::deque<T>; // XXX
        template<typename K, typename T>
        using table_type = std::map<K, T>; // XXX

        static result<integer_type, error_info>
        parse_int(const std::string& str, const source_location src, const std::uint8_t base)
        {
            return toml::read_int<integer_type>(str, src, base);
        }

        static result<floating_type, error_info>
        parse_float(const std::string& str, const source_location src, const bool is_hex)
        {
            return toml::read_float<floating_type>(str, src, is_hex);
        }
    };

## Using `boost::multiprecision` for Numeric Types #

By using `boost::multiprecision::cpp_int` and `boost::multiprecision::cpp_bin_float_oct`, you can utilize a wider integer type and a more precise floating-point type.

These types implement stream operators, so you can use the default implementations of `read_int` and `read_float` without modification.

    struct large_num_config
    {
        using comment_type  = toml::preserve_comments;

        using boolean_type  = bool;
        using integer_type  = boost::multiprecision::cpp_int;
        using floating_type = boost::multiprecision::cpp_bin_float_oct;
        using string_type   = std::string;

        template<typename T>
        using array_type = std::vector<T>;
        template<typename K, typename T>
        using table_type = std::unordered_map<K, T>;

        static toml::result<integer_type, toml::error_info>
        parse_int(const std::string& str, const toml::source_location src, const std::uint8_t base)
        {
            return toml::read_int<integer_type>(str, src, base);
        }
        static toml::result<floating_type, toml::error_info>
        parse_float(const std::string& str, const toml::source_location src, const bool is_hex)
        {
            return toml::read_float<floating_type>(str, src, is_hex);
        }
    };