mirror of
https://github.com/ToruNiina/toml11.git
synced 2025-09-17 00:38:08 +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"));
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
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_error_detection
|
||||
test_format_error
|
||||
test_extended_conversions
|
||||
)
|
||||
|
||||
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.
|
||||
#ifndef TOML11_GET_HPP
|
||||
#define TOML11_GET_HPP
|
||||
#include "from.hpp"
|
||||
#include "result.hpp"
|
||||
#include "value.hpp"
|
||||
#include <algorithm>
|
||||
@@ -173,6 +174,20 @@ template<typename T, typename std::enable_if<detail::conjunction<
|
||||
>::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
|
||||
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.
|
||||
|
||||
@@ -297,6 +312,29 @@ T get(const toml::value& v)
|
||||
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
|
||||
|
||||
|
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
|
||||
{
|
||||
|
||||
class value; // forward decl
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
@@ -45,6 +48,22 @@ struct has_resize_method_impl
|
||||
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
|
||||
/// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076
|
||||
#ifdef __INTEL_COMPILER
|
||||
@@ -62,6 +81,14 @@ struct has_mapped_type : decltype(has_mapped_type_impl::check<T>(nullptr)){};
|
||||
template<typename T>
|
||||
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
|
||||
#undef decltype(...)
|
||||
#endif
|
||||
|
@@ -3,6 +3,7 @@
|
||||
#ifndef TOML11_VALUE_HPP
|
||||
#define TOML11_VALUE_HPP
|
||||
#include "traits.hpp"
|
||||
#include "into.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "exception.hpp"
|
||||
#include "storage.hpp"
|
||||
@@ -533,6 +534,46 @@ class value
|
||||
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>
|
||||
bool is() const noexcept {return value_traits<T>::type_index == this->type_;}
|
||||
bool is(value_t t) const noexcept {return t == this->type_;}
|
||||
|
Reference in New Issue
Block a user