mirror of
https://github.com/ToruNiina/toml11.git
synced 2025-09-18 02:08:09 +08:00
Merge pull request #38 from ToruNiina/get-any-type
extended conversions
This commit is contained in:
142
README.md
142
README.md
@@ -457,6 +457,148 @@ int i = 0;
|
|||||||
toml::from_toml(i, data.at("something"));
|
toml::from_toml(i, data.at("something"));
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Conversion between toml value and your class
|
||||||
|
|
||||||
|
You can also use `toml::get` and other related functions with the types you defined
|
||||||
|
after you implement some stuff.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace ext
|
||||||
|
{
|
||||||
|
struct foo
|
||||||
|
{
|
||||||
|
int a;
|
||||||
|
double b;
|
||||||
|
std::string c;
|
||||||
|
};
|
||||||
|
} // ext
|
||||||
|
|
||||||
|
const auto data = toml::parse("example.toml");
|
||||||
|
|
||||||
|
const foo f = toml::get<ext::foo>(data.at("foo"));
|
||||||
|
```
|
||||||
|
|
||||||
|
There are 2 ways to use `toml::get` with the types that you defined.
|
||||||
|
|
||||||
|
The first one is to implement `from_toml(const toml::value&)` member function.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace ext
|
||||||
|
{
|
||||||
|
struct foo
|
||||||
|
{
|
||||||
|
int a;
|
||||||
|
double b;
|
||||||
|
std::string c;
|
||||||
|
|
||||||
|
void from_toml(const toml::value& v)
|
||||||
|
{
|
||||||
|
this->a = toml::find<int >(v, "a");
|
||||||
|
this->b = toml::find<double >(v, "b");
|
||||||
|
this->c = toml::find<std::string>(v, "c");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // ext
|
||||||
|
```
|
||||||
|
|
||||||
|
In this way, because `toml::get` first constructs `foo` without arguments,
|
||||||
|
the type should be default-constructible.
|
||||||
|
|
||||||
|
The second is to implement specialization of `toml::from` for your type.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace ext
|
||||||
|
{
|
||||||
|
struct foo
|
||||||
|
{
|
||||||
|
int a;
|
||||||
|
double b;
|
||||||
|
std::string c;
|
||||||
|
};
|
||||||
|
} // ext
|
||||||
|
|
||||||
|
namespace toml
|
||||||
|
{
|
||||||
|
template<>
|
||||||
|
struct from<ext::foo>
|
||||||
|
{
|
||||||
|
ext::foo from_toml(const toml::value& v)
|
||||||
|
{
|
||||||
|
ext::foo f;
|
||||||
|
f.a = toml::find<int >(v, "a");
|
||||||
|
f.b = toml::find<double >(v, "b");
|
||||||
|
f.c = toml::find<std::string>(v, "c");
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // toml
|
||||||
|
```
|
||||||
|
|
||||||
|
In this way, since the conversion function is introduced from out of the class,
|
||||||
|
you can add conversion between `toml::value` and classes defined in another library.
|
||||||
|
|
||||||
|
Note that you cannot implement both of the functions because the overload
|
||||||
|
resolution of `toml::get` become ambiguous.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
The opposite direction is also supported in a similar way. You can directly
|
||||||
|
pass your type to `toml::value`'s constructor by introducing `into_iter` or
|
||||||
|
`toml::into<T>`.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace ext
|
||||||
|
{
|
||||||
|
struct foo
|
||||||
|
{
|
||||||
|
int a;
|
||||||
|
double b;
|
||||||
|
std::string c;
|
||||||
|
|
||||||
|
toml::table into_toml() const // you need to mark it const.
|
||||||
|
{
|
||||||
|
return toml::table{{"a", this->a}, {"b", this->b}, {"c", this->c}};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // ext
|
||||||
|
|
||||||
|
ext::foo f{42, 3.14, "foobar"};
|
||||||
|
toml::value v(f);
|
||||||
|
```
|
||||||
|
|
||||||
|
The definition of `toml::into<ext::foo>` is similar to `from_toml()`.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace ext
|
||||||
|
{
|
||||||
|
struct foo
|
||||||
|
{
|
||||||
|
int a;
|
||||||
|
double b;
|
||||||
|
std::string c;
|
||||||
|
};
|
||||||
|
} // ext
|
||||||
|
|
||||||
|
namespace toml
|
||||||
|
{
|
||||||
|
template<>
|
||||||
|
struct into<ext::foo>
|
||||||
|
{
|
||||||
|
toml::table into_toml(const ext::foo& v)
|
||||||
|
{
|
||||||
|
return toml::table{{"a", this->a}, {"b", this->b}, {"c", this->c}};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // toml
|
||||||
|
|
||||||
|
ext::foo f{42, 3.14, "foobar"};
|
||||||
|
toml::value v(f);
|
||||||
|
```
|
||||||
|
|
||||||
|
Any type that can be converted to `toml::value`, e.g. `toml::table`, `toml::array`,
|
||||||
|
is okay to return from `into_toml`.
|
||||||
|
|
||||||
### visiting toml::value
|
### visiting toml::value
|
||||||
|
|
||||||
TOML v2.1.0+ provides `toml::visit` to apply a function to `toml::value` in the
|
TOML v2.1.0+ provides `toml::visit` to apply a function to `toml::value` in the
|
||||||
|
@@ -29,6 +29,7 @@ set(TEST_NAMES
|
|||||||
test_parse_unicode
|
test_parse_unicode
|
||||||
test_error_detection
|
test_error_detection
|
||||||
test_format_error
|
test_format_error
|
||||||
|
test_extended_conversions
|
||||||
)
|
)
|
||||||
|
|
||||||
CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL)
|
CHECK_CXX_COMPILER_FLAG("-Wall" COMPILER_SUPPORTS_WALL)
|
||||||
|
116
tests/test_extended_conversions.cpp
Normal file
116
tests/test_extended_conversions.cpp
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
#define BOOST_TEST_MODULE "test_extended_conversions"
|
||||||
|
#ifdef UNITTEST_FRAMEWORK_LIBRARY_EXIST
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
#else
|
||||||
|
#define BOOST_TEST_NO_LIB
|
||||||
|
#include <boost/test/included/unit_test.hpp>
|
||||||
|
#endif
|
||||||
|
#include <toml.hpp>
|
||||||
|
|
||||||
|
namespace extlib
|
||||||
|
{
|
||||||
|
struct foo
|
||||||
|
{
|
||||||
|
int a;
|
||||||
|
std::string b;
|
||||||
|
};
|
||||||
|
struct bar
|
||||||
|
{
|
||||||
|
int a;
|
||||||
|
std::string b;
|
||||||
|
|
||||||
|
void from_toml(const toml::value& v)
|
||||||
|
{
|
||||||
|
this->a = toml::find<int>(v, "a");
|
||||||
|
this->b = toml::find<std::string>(v, "b");
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
toml::table into_toml() const
|
||||||
|
{
|
||||||
|
return toml::table{{"a", this->a}, {"b", this->b}};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // extlib
|
||||||
|
|
||||||
|
namespace toml
|
||||||
|
{
|
||||||
|
template<>
|
||||||
|
struct from<extlib::foo>
|
||||||
|
{
|
||||||
|
static extlib::foo from_toml(const toml::value& v)
|
||||||
|
{
|
||||||
|
return extlib::foo{toml::find<int>(v, "a"), toml::find<std::string>(v, "b")};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct into<extlib::foo>
|
||||||
|
{
|
||||||
|
static toml::table into_toml(const extlib::foo& f)
|
||||||
|
{
|
||||||
|
return toml::table{{"a", f.a}, {"b", f.b}};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // toml
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_conversion_by_member_methods)
|
||||||
|
{
|
||||||
|
const toml::value v{{"a", 42}, {"b", "baz"}};
|
||||||
|
|
||||||
|
const auto foo = toml::get<extlib::foo>(v);
|
||||||
|
BOOST_CHECK_EQUAL(foo.a, 42);
|
||||||
|
BOOST_CHECK_EQUAL(foo.b, "baz");
|
||||||
|
|
||||||
|
const toml::value v2(foo);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(v, v2);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_conversion_by_specialization)
|
||||||
|
{
|
||||||
|
const toml::value v{{"a", 42}, {"b", "baz"}};
|
||||||
|
|
||||||
|
const auto bar = toml::get<extlib::bar>(v);
|
||||||
|
BOOST_CHECK_EQUAL(bar.a, 42);
|
||||||
|
BOOST_CHECK_EQUAL(bar.b, "baz");
|
||||||
|
|
||||||
|
const toml::value v2(bar);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(v, v2);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_recursive_conversion)
|
||||||
|
{
|
||||||
|
const toml::value v{
|
||||||
|
toml::table{{"a", 42}, {"b", "baz"}},
|
||||||
|
toml::table{{"a", 43}, {"b", "qux"}},
|
||||||
|
toml::table{{"a", 44}, {"b", "quux"}},
|
||||||
|
toml::table{{"a", 45}, {"b", "foobar"}},
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto foos = toml::get<std::vector<extlib::foo>>(v);
|
||||||
|
BOOST_CHECK_EQUAL(foos.size() , 4ul);
|
||||||
|
BOOST_CHECK_EQUAL(foos.at(0).a , 42);
|
||||||
|
BOOST_CHECK_EQUAL(foos.at(1).a , 43);
|
||||||
|
BOOST_CHECK_EQUAL(foos.at(2).a , 44);
|
||||||
|
BOOST_CHECK_EQUAL(foos.at(3).a , 45);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(foos.at(0).b , "baz");
|
||||||
|
BOOST_CHECK_EQUAL(foos.at(1).b , "qux");
|
||||||
|
BOOST_CHECK_EQUAL(foos.at(2).b , "quux");
|
||||||
|
BOOST_CHECK_EQUAL(foos.at(3).b , "foobar");
|
||||||
|
|
||||||
|
const auto bars = toml::get<std::vector<extlib::bar>>(v);
|
||||||
|
BOOST_CHECK_EQUAL(bars.size() , 4ul);
|
||||||
|
BOOST_CHECK_EQUAL(bars.at(0).a , 42);
|
||||||
|
BOOST_CHECK_EQUAL(bars.at(1).a , 43);
|
||||||
|
BOOST_CHECK_EQUAL(bars.at(2).a , 44);
|
||||||
|
BOOST_CHECK_EQUAL(bars.at(3).a , 45);
|
||||||
|
|
||||||
|
BOOST_CHECK_EQUAL(bars.at(0).b , "baz");
|
||||||
|
BOOST_CHECK_EQUAL(bars.at(1).b , "qux");
|
||||||
|
BOOST_CHECK_EQUAL(bars.at(2).b , "quux");
|
||||||
|
BOOST_CHECK_EQUAL(bars.at(3).b , "foobar");
|
||||||
|
}
|
20
toml/from.hpp
Normal file
20
toml/from.hpp
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright Toru Niina 2019.
|
||||||
|
// Distributed under the MIT License.
|
||||||
|
#ifndef TOML11_FROM_HPP
|
||||||
|
#define TOML11_FROM_HPP
|
||||||
|
#include "traits.hpp"
|
||||||
|
|
||||||
|
namespace toml
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct from;
|
||||||
|
// {
|
||||||
|
// static T from_toml(const toml::value& v)
|
||||||
|
// {
|
||||||
|
// // User-defined conversions ...
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
} // toml
|
||||||
|
#endif // TOML11_FROM_HPP
|
38
toml/get.hpp
38
toml/get.hpp
@@ -2,6 +2,7 @@
|
|||||||
// Distributed under the MIT License.
|
// Distributed under the MIT License.
|
||||||
#ifndef TOML11_GET_HPP
|
#ifndef TOML11_GET_HPP
|
||||||
#define TOML11_GET_HPP
|
#define TOML11_GET_HPP
|
||||||
|
#include "from.hpp"
|
||||||
#include "result.hpp"
|
#include "result.hpp"
|
||||||
#include "value.hpp"
|
#include "value.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -173,6 +174,20 @@ template<typename T, typename std::enable_if<detail::conjunction<
|
|||||||
>::value, std::nullptr_t>::type = nullptr>
|
>::value, std::nullptr_t>::type = nullptr>
|
||||||
T get(const toml::value& v);
|
T get(const toml::value& v);
|
||||||
|
|
||||||
|
template<typename T, typename std::enable_if<detail::conjunction<
|
||||||
|
detail::negation<detail::is_exact_toml_type<T>>, // not a toml::value
|
||||||
|
detail::has_from_toml_method<T>, // but has from_toml(toml::value) memfn
|
||||||
|
std::is_default_constructible<T> // and default constructible
|
||||||
|
>::value, std::nullptr_t>::type = nullptr>
|
||||||
|
T get(const toml::value& v);
|
||||||
|
|
||||||
|
template<typename T, typename std::enable_if<detail::conjunction<
|
||||||
|
detail::negation<detail::is_exact_toml_type<T>> // not a toml::value
|
||||||
|
>::value, std::nullptr_t>::type = nullptr,
|
||||||
|
std::size_t = sizeof(::toml::from<T>) // and has from<T> specialization
|
||||||
|
>
|
||||||
|
T get(const toml::value& v);
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// array-like types; most likely STL container, like std::vector, etc.
|
// array-like types; most likely STL container, like std::vector, etc.
|
||||||
|
|
||||||
@@ -297,6 +312,29 @@ T get(const toml::value& v)
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// user-defined, but compatible types.
|
||||||
|
|
||||||
|
template<typename T, typename std::enable_if<detail::conjunction<
|
||||||
|
detail::negation<detail::is_exact_toml_type<T>>, // not a toml::value
|
||||||
|
detail::has_from_toml_method<T>, // but has from_toml(toml::value) memfn
|
||||||
|
std::is_default_constructible<T> // and default constructible
|
||||||
|
>::value, std::nullptr_t>::type>
|
||||||
|
T get(const toml::value& v)
|
||||||
|
{
|
||||||
|
T ud;
|
||||||
|
ud.from_toml(v);
|
||||||
|
return ud;
|
||||||
|
}
|
||||||
|
template<typename T, typename std::enable_if<detail::conjunction<
|
||||||
|
detail::negation<detail::is_exact_toml_type<T>> // not a toml::value
|
||||||
|
>::value, std::nullptr_t>::type, std::size_t> // and has from<T>
|
||||||
|
T get(const toml::value& v)
|
||||||
|
{
|
||||||
|
return ::toml::from<T>::from_toml(v);
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// find and get
|
// find and get
|
||||||
|
|
||||||
|
20
toml/into.hpp
Normal file
20
toml/into.hpp
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright Toru Niina 2019.
|
||||||
|
// Distributed under the MIT License.
|
||||||
|
#ifndef TOML11_INTO_HPP
|
||||||
|
#define TOML11_INTO_HPP
|
||||||
|
#include "traits.hpp"
|
||||||
|
|
||||||
|
namespace toml
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct into;
|
||||||
|
// {
|
||||||
|
// static toml::value into_toml(const T& user_defined_type)
|
||||||
|
// {
|
||||||
|
// // User-defined conversions ...
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
} // toml
|
||||||
|
#endif // TOML11_INTO_HPP
|
@@ -9,6 +9,9 @@
|
|||||||
|
|
||||||
namespace toml
|
namespace toml
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class value; // forward decl
|
||||||
|
|
||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -45,6 +48,22 @@ struct has_resize_method_impl
|
|||||||
template<typename T> static std::false_type check(...);
|
template<typename T> static std::false_type check(...);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct has_from_toml_method_impl
|
||||||
|
{
|
||||||
|
template<typename T>
|
||||||
|
static std::true_type check(
|
||||||
|
decltype(std::declval<T>().from_toml(std::declval<::toml::value>()))*);
|
||||||
|
template<typename T>
|
||||||
|
static std::false_type check(...);
|
||||||
|
};
|
||||||
|
struct has_into_toml_method_impl
|
||||||
|
{
|
||||||
|
template<typename T>
|
||||||
|
static std::true_type check(decltype(std::declval<T>().into_toml())*);
|
||||||
|
template<typename T>
|
||||||
|
static std::false_type check(...);
|
||||||
|
};
|
||||||
|
|
||||||
/// Intel C++ compiler can not use decltype in parent class declaration, here
|
/// Intel C++ compiler can not use decltype in parent class declaration, here
|
||||||
/// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076
|
/// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076
|
||||||
#ifdef __INTEL_COMPILER
|
#ifdef __INTEL_COMPILER
|
||||||
@@ -62,6 +81,14 @@ struct has_mapped_type : decltype(has_mapped_type_impl::check<T>(nullptr)){};
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
struct has_resize_method : decltype(has_resize_method_impl::check<T>(nullptr)){};
|
struct has_resize_method : decltype(has_resize_method_impl::check<T>(nullptr)){};
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct has_from_toml_method
|
||||||
|
: decltype(has_from_toml_method_impl::check<T>(nullptr)){};
|
||||||
|
template<typename T>
|
||||||
|
struct has_into_toml_method
|
||||||
|
: decltype(has_into_toml_method_impl::check<T>(nullptr)){};
|
||||||
|
|
||||||
#ifdef __INTEL_COMPILER
|
#ifdef __INTEL_COMPILER
|
||||||
#undef decltype(...)
|
#undef decltype(...)
|
||||||
#endif
|
#endif
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
#ifndef TOML11_VALUE_HPP
|
#ifndef TOML11_VALUE_HPP
|
||||||
#define TOML11_VALUE_HPP
|
#define TOML11_VALUE_HPP
|
||||||
#include "traits.hpp"
|
#include "traits.hpp"
|
||||||
|
#include "into.hpp"
|
||||||
#include "utility.hpp"
|
#include "utility.hpp"
|
||||||
#include "exception.hpp"
|
#include "exception.hpp"
|
||||||
#include "storage.hpp"
|
#include "storage.hpp"
|
||||||
@@ -533,6 +534,46 @@ class value
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// user-defined =========================================================
|
||||||
|
|
||||||
|
// convert using into_toml() method -------------------------------------
|
||||||
|
|
||||||
|
template<typename T, typename std::enable_if<detail::conjunction<
|
||||||
|
detail::negation<detail::is_exact_toml_type<T>>, // not a toml::value
|
||||||
|
detail::has_into_toml_method<T> // but has `into_toml` method
|
||||||
|
>::value, std::nullptr_t>::type = nullptr>
|
||||||
|
value(const T& ud): value(ud.into_toml()) {}
|
||||||
|
|
||||||
|
template<typename T, typename std::enable_if<detail::conjunction<
|
||||||
|
detail::negation<detail::is_exact_toml_type<T>>, // not a toml::value
|
||||||
|
detail::has_into_toml_method<T> // but has `into_toml` method
|
||||||
|
>::value, std::nullptr_t>::type = nullptr>
|
||||||
|
value& operator=(const T& ud)
|
||||||
|
{
|
||||||
|
*this = ud.into_toml();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert using into<T> struct -----------------------------------------
|
||||||
|
|
||||||
|
template<typename T, typename std::enable_if<
|
||||||
|
detail::negation<detail::is_exact_toml_type<T>>::value,
|
||||||
|
std::nullptr_t>::type = nullptr,
|
||||||
|
std::size_t S = sizeof(::toml::into<T>)>
|
||||||
|
value(const T& ud): value(::toml::into<T>::into_toml(ud)) {}
|
||||||
|
|
||||||
|
template<typename T, typename std::enable_if<
|
||||||
|
detail::negation<detail::is_exact_toml_type<T>>::value,
|
||||||
|
std::nullptr_t>::type = nullptr,
|
||||||
|
std::size_t S = sizeof(::toml::into<T>)>
|
||||||
|
value& operator=(const T& ud)
|
||||||
|
{
|
||||||
|
*this = ::toml::into<T>::into_toml(ud);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// type checking and casting ============================================
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
bool is() const noexcept {return value_traits<T>::type_index == this->type_;}
|
bool is() const noexcept {return value_traits<T>::type_index == this->type_;}
|
||||||
bool is(value_t t) const noexcept {return t == this->type_;}
|
bool is(value_t t) const noexcept {return t == this->type_;}
|
||||||
|
Reference in New Issue
Block a user