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);
    }
};