mirror of
https://github.com/ToruNiina/toml11.git
synced 2025-09-17 17:21:43 +08:00
375 lines
11 KiB
C++
375 lines
11 KiB
C++
#ifndef TOML11_TYPES_HPP
|
|
#define TOML11_TYPES_HPP
|
|
|
|
#include "comments.hpp"
|
|
#include "compat.hpp"
|
|
#include "error_info.hpp"
|
|
#include "format.hpp"
|
|
#include "ordered_map.hpp"
|
|
#include "value.hpp"
|
|
|
|
#include <ostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
|
|
namespace toml
|
|
{
|
|
|
|
// forward decl
|
|
template<typename TypeConfig>
|
|
class basic_value;
|
|
|
|
// when you use a special integer type as toml::value::integer_type, parse must
|
|
// be able to read it. So, type_config has static member functions that read the
|
|
// integer_type as {dec, hex, oct, bin}-integer. But, in most cases, operator<<
|
|
// is enough. To make config easy, we provide the default read functions.
|
|
//
|
|
// Before this functions is called, syntax is checked and prefix(`0x` etc) and
|
|
// spacer(`_`) are removed.
|
|
|
|
template<typename T>
|
|
result<T, error_info>
|
|
read_dec_int(const std::string& str, const source_location src)
|
|
{
|
|
constexpr auto max_digits = std::numeric_limits<T>::digits;
|
|
assert( ! str.empty());
|
|
|
|
T val{0};
|
|
std::istringstream iss(str);
|
|
iss >> val;
|
|
if(iss.fail())
|
|
{
|
|
return err(make_error_info("toml::parse_dec_integer: "
|
|
"too large integer: current max digits = 2^" + std::to_string(max_digits),
|
|
std::move(src), "must be < 2^" + std::to_string(max_digits)));
|
|
}
|
|
return ok(val);
|
|
}
|
|
|
|
template<typename T>
|
|
result<T, error_info>
|
|
read_hex_int(const std::string& str, const source_location src)
|
|
{
|
|
constexpr auto max_digits = std::numeric_limits<T>::digits;
|
|
assert( ! str.empty());
|
|
|
|
T val{0};
|
|
std::istringstream iss(str);
|
|
iss >> std::hex >> val;
|
|
if(iss.fail())
|
|
{
|
|
return err(make_error_info("toml::parse_hex_integer: "
|
|
"too large integer: current max value = 2^" + std::to_string(max_digits),
|
|
std::move(src), "must be < 2^" + std::to_string(max_digits)));
|
|
}
|
|
return ok(val);
|
|
}
|
|
|
|
template<typename T>
|
|
result<T, error_info>
|
|
read_oct_int(const std::string& str, const source_location src)
|
|
{
|
|
constexpr auto max_digits = std::numeric_limits<T>::digits;
|
|
assert( ! str.empty());
|
|
|
|
T val{0};
|
|
std::istringstream iss(str);
|
|
iss >> std::oct >> val;
|
|
if(iss.fail())
|
|
{
|
|
return err(make_error_info("toml::parse_oct_integer: "
|
|
"too large integer: current max value = 2^" + std::to_string(max_digits),
|
|
std::move(src), "must be < 2^" + std::to_string(max_digits)));
|
|
}
|
|
return ok(val);
|
|
}
|
|
|
|
template<typename T>
|
|
result<T, error_info>
|
|
read_bin_int(const std::string& str, const source_location src)
|
|
{
|
|
constexpr auto is_bounded = std::numeric_limits<T>::is_bounded;
|
|
constexpr auto max_digits = std::numeric_limits<T>::digits;
|
|
const auto max_value = (std::numeric_limits<T>::max)();
|
|
|
|
T val{0};
|
|
T base{1};
|
|
for(auto i = str.rbegin(); i != str.rend(); ++i)
|
|
{
|
|
const auto c = *i;
|
|
if(c == '1')
|
|
{
|
|
val += base;
|
|
// prevent `base` from overflow
|
|
if(is_bounded && max_value / 2 < base && std::next(i) != str.rend())
|
|
{
|
|
base = 0;
|
|
}
|
|
else
|
|
{
|
|
base *= 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert(c == '0');
|
|
|
|
if(is_bounded && max_value / 2 < base && std::next(i) != str.rend())
|
|
{
|
|
base = 0;
|
|
}
|
|
else
|
|
{
|
|
base *= 2;
|
|
}
|
|
}
|
|
}
|
|
if(base == 0)
|
|
{
|
|
return err(make_error_info("toml::parse_bin_integer: "
|
|
"too large integer: current max value = 2^" + std::to_string(max_digits),
|
|
std::move(src), "must be < 2^" + std::to_string(max_digits)));
|
|
}
|
|
return ok(val);
|
|
}
|
|
|
|
template<typename T>
|
|
result<T, error_info>
|
|
read_int(const std::string& str, const source_location src, const std::uint8_t base)
|
|
{
|
|
assert(base == 10 || base == 16 || base == 8 || base == 2);
|
|
switch(base)
|
|
{
|
|
case 2: { return read_bin_int<T>(str, src); }
|
|
case 8: { return read_oct_int<T>(str, src); }
|
|
case 16: { return read_hex_int<T>(str, src); }
|
|
default:
|
|
{
|
|
assert(base == 10);
|
|
return read_dec_int<T>(str, src);
|
|
}
|
|
}
|
|
}
|
|
|
|
inline result<float, error_info>
|
|
read_hex_float(const std::string& str, const source_location src, float val)
|
|
{
|
|
#if defined(_MSC_VER) && ! defined(__clang__)
|
|
const auto res = ::sscanf_s(str.c_str(), "%a", std::addressof(val));
|
|
#else
|
|
const auto res = std::sscanf(str.c_str(), "%a", std::addressof(val));
|
|
#endif
|
|
if(res != 1)
|
|
{
|
|
return err(make_error_info("toml::parse_floating: "
|
|
"failed to read hexadecimal floating point value ",
|
|
std::move(src), "here"));
|
|
}
|
|
return ok(val);
|
|
}
|
|
inline result<double, error_info>
|
|
read_hex_float(const std::string& str, const source_location src, double val)
|
|
{
|
|
#if defined(_MSC_VER) && ! defined(__clang__)
|
|
const auto res = ::sscanf_s(str.c_str(), "%la", std::addressof(val));
|
|
#else
|
|
const auto res = std::sscanf(str.c_str(), "%la", std::addressof(val));
|
|
#endif
|
|
if(res != 1)
|
|
{
|
|
return err(make_error_info("toml::parse_floating: "
|
|
"failed to read hexadecimal floating point value ",
|
|
std::move(src), "here"));
|
|
}
|
|
return ok(val);
|
|
}
|
|
template<typename T>
|
|
cxx::enable_if_t<cxx::conjunction<
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, double>>,
|
|
cxx::negation<std::is_same<cxx::remove_cvref_t<T>, float>>
|
|
>::value, result<T, error_info>>
|
|
read_hex_float(const std::string&, const source_location src, T)
|
|
{
|
|
return err(make_error_info("toml::parse_floating: failed to read "
|
|
"floating point value because of unknown type in type_config",
|
|
std::move(src), "here"));
|
|
}
|
|
|
|
template<typename T>
|
|
result<T, error_info>
|
|
read_dec_float(const std::string& str, const source_location src)
|
|
{
|
|
T val;
|
|
std::istringstream iss(str);
|
|
iss >> val;
|
|
if(iss.fail())
|
|
{
|
|
return err(make_error_info("toml::parse_floating: "
|
|
"failed to read floating point value from stream",
|
|
std::move(src), "here"));
|
|
}
|
|
return ok(val);
|
|
}
|
|
|
|
template<typename T>
|
|
result<T, error_info>
|
|
read_float(const std::string& str, const source_location src, const bool is_hex)
|
|
{
|
|
if(is_hex)
|
|
{
|
|
return read_hex_float(str, src, T{});
|
|
}
|
|
else
|
|
{
|
|
return read_dec_float<T>(str, src);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
return 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 read_float<floating_type>(str, src, is_hex);
|
|
}
|
|
};
|
|
|
|
using value = basic_value<type_config>;
|
|
using table = typename value::table_type;
|
|
using array = typename value::array_type;
|
|
|
|
struct ordered_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 = ordered_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 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 read_float<floating_type>(str, src, is_hex);
|
|
}
|
|
};
|
|
|
|
using ordered_value = basic_value<ordered_type_config>;
|
|
using ordered_table = typename ordered_value::table_type;
|
|
using ordered_array = typename ordered_value::array_type;
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// meta functions for internal use
|
|
|
|
namespace detail
|
|
{
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// check if type T has all the needed member types
|
|
|
|
template<typename T, typename U = void>
|
|
struct has_comment_type: std::false_type{};
|
|
template<typename T>
|
|
struct has_comment_type<T, cxx::void_t<typename T::comment_type>>: std::true_type{};
|
|
|
|
template<typename T, typename U = void>
|
|
struct has_integer_type: std::false_type{};
|
|
template<typename T>
|
|
struct has_integer_type<T, cxx::void_t<typename T::integer_type>>: std::true_type{};
|
|
|
|
template<typename T, typename U = void>
|
|
struct has_floating_type: std::false_type{};
|
|
template<typename T>
|
|
struct has_floating_type<T, cxx::void_t<typename T::floating_type>>: std::true_type{};
|
|
|
|
template<typename T, typename U = void>
|
|
struct has_string_type: std::false_type{};
|
|
template<typename T>
|
|
struct has_string_type<T, cxx::void_t<typename T::string_type>>: std::true_type{};
|
|
|
|
template<typename T, typename U = void>
|
|
struct has_array_type: std::false_type{};
|
|
template<typename T>
|
|
struct has_array_type<T, cxx::void_t<typename T::template array_type<int>>>: std::true_type{};
|
|
|
|
template<typename T, typename U = void>
|
|
struct has_table_type: std::false_type{};
|
|
template<typename T>
|
|
struct has_table_type<T, cxx::void_t<typename T::template table_type<int, int>>>: std::true_type{};
|
|
|
|
template<typename T, typename U = void>
|
|
struct has_parse_int: std::false_type{};
|
|
template<typename T>
|
|
struct has_parse_int<T, cxx::void_t<decltype(std::declval<T>().parse_int(
|
|
std::declval<std::string const&>(),
|
|
std::declval<::toml::source_location const&>(),
|
|
std::declval<std::uint8_t>()
|
|
))>>: std::true_type{};
|
|
|
|
template<typename T, typename U = void>
|
|
struct has_parse_float: std::false_type{};
|
|
template<typename T>
|
|
struct has_parse_float<T, cxx::void_t<decltype(std::declval<T>().parse_float(
|
|
std::declval<std::string const&>(),
|
|
std::declval<::toml::source_location const&>(),
|
|
std::declval<bool>()
|
|
))>>: std::true_type{};
|
|
|
|
template<typename T>
|
|
using is_type_config = cxx::conjunction<
|
|
has_comment_type<T>,
|
|
has_integer_type<T>,
|
|
has_floating_type<T>,
|
|
has_string_type<T>,
|
|
has_array_type<T>,
|
|
has_table_type<T>,
|
|
has_parse_int<T>,
|
|
has_parse_float<T>
|
|
>;
|
|
|
|
} // namespace detail
|
|
} // namespace toml
|
|
|
|
#if defined(TOML11_COMPILE_SOURCES)
|
|
namespace toml
|
|
{
|
|
extern template class basic_value<type_config>;
|
|
extern template class basic_value<ordered_type_config>;
|
|
} // toml
|
|
#endif // TOML11_COMPILE_SOURCES
|
|
|
|
#endif // TOML11_TYPES_HPP
|