-
Notifications
You must be signed in to change notification settings - Fork 2.2k
feat: Allow decoding for types without default constructor (V1) #1479
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -200,6 +200,38 @@ Vec3 v = node["start"].as<Vec3>(); | |
| node["end"] = Vec3(2, -1, 0); | ||
| ``` | ||
|
|
||
| ## Non-default constructible types (requires c++17 and newer) | ||
| Yaml-cpp also supports types that are not default constructible. For this one need to specialize `YAML::convert<std::optional<>>`. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. default constructible -> default-constructible
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For this one need to specialize -> For this, one needs to specialize |
||
| Assuming you have: | ||
|
|
||
| ```cpp | ||
| class Vec3 { | ||
| double x, y, z; | ||
| public: | ||
| Vec3(double x, double y, double z} : x{x}, y{y}, z{z} {} | ||
| }; | ||
| ``` | ||
|
|
||
| you could write (for encoding the previous `convert<Vec3>` with the `encode` method is required) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for encoding the previous -> for encoding, the previous |
||
|
|
||
| ```cpp | ||
| namespace YAML { | ||
| template<> | ||
| struct convert<std::optional<Vec3>> { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd doubt that |
||
| static bool decode(const Node& node, std::optional<Vec3>& rhs) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this interface now has two |
||
| if(!node.IsSequence() || node.size() != 3) { | ||
| return false; | ||
| } | ||
| rhs.emplace( | ||
| node[0].as<double>(), | ||
| node[1].as<double>(), | ||
| node[2].as<double>() | ||
| ); | ||
| return true; | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| ## Partial specialization | ||
|
|
||
| If you need to specialize the `convert` struct for a set of types instead of just one you can use partial specialization with the help of `std::enable_if` (SFINAE). | ||
|
|
@@ -224,7 +256,7 @@ public: | |
| node["a"] = a; | ||
| return node; | ||
| } | ||
|
|
||
| int a; | ||
| }; | ||
|
|
||
|
|
@@ -252,7 +284,7 @@ public: | |
|
|
||
| // Implementation of convert::{encode,decode} for all classes derived from or being A | ||
| namespace YAML { | ||
| template<typename T> | ||
| template<typename T> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it possible to clean all whitespace in a separate effort, adding a static check for that? |
||
| struct convert<T, typename std::enable_if<std::is_base_of<A, T>::value>::type> { | ||
| static Node encode(const T &rhs) { | ||
| Node node = rhs.emit(); | ||
|
|
@@ -274,4 +306,4 @@ B b = node.as<B>(); | |
| b.a = 12; | ||
| b.b = 42; | ||
| node = b; | ||
| ``` | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,17 @@ | |
| #include <sstream> | ||
| #include <string> | ||
|
|
||
| // Check YAML_CPP_USE_OPTIONAL is set and language standard provides supports | ||
| // if available and supported include required header | ||
| // otherwise remove YAML_CPP_USE_OPTIONAL definition | ||
| #ifdef YAML_CPP_USE_OPTIONAL | ||
| #if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a weird check, maybe C++20 should be the base line so that more straightforward feature-test macros could be used instead. |
||
| #include <optional> | ||
| #else | ||
| #undef YAML_CPP_USE_OPTIONAL | ||
| #endif | ||
| #endif | ||
|
|
||
| namespace YAML { | ||
| inline Node::Node() | ||
| : m_isValid(true), m_invalidKey{}, m_pMemory(nullptr), m_pNode(nullptr) {} | ||
|
|
@@ -94,6 +105,54 @@ inline NodeType::value Node::Type() const { | |
| // access | ||
|
|
||
| // template helpers | ||
| #ifdef YAML_CPP_USE_OPTIONAL | ||
| template <typename T> | ||
| struct decode_box : std::optional<T> { | ||
| decode_box() = default; | ||
| template <typename S> | ||
| decode_box(S&&) {} | ||
| }; | ||
|
|
||
| template <typename T> | ||
| struct convert<decode_box<T>> { | ||
| static bool decode(const Node& node, decode_box<T>& rhs) { | ||
| return convert<std::optional<T>>::decode(node, rhs); | ||
| } | ||
| }; | ||
| template <typename T> | ||
| struct convert<std::optional<T>> { | ||
| static bool decode(const Node& node, std::optional<T>& rhs) { | ||
| if (node.IsNull()) { | ||
| return false; | ||
| } | ||
| rhs.emplace(); | ||
| if (!convert<T>::decode(node, *rhs)) { | ||
| rhs.reset(); | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
| }; | ||
|
|
||
|
|
||
| #else | ||
|
|
||
| template <typename T> | ||
| struct decode_box { | ||
| T t; | ||
| T& operator*() { | ||
| return t; | ||
| } | ||
| }; | ||
| template <typename T> | ||
| struct convert<decode_box<T>> { | ||
| static bool decode(const Node& node, decode_box<T>& rhs) { | ||
| return convert<T>::decode(node, *rhs); | ||
| } | ||
| }; | ||
|
|
||
| #endif | ||
|
|
||
| template <typename T, typename S> | ||
| struct as_if { | ||
| explicit as_if(const Node& node_) : node(node_) {} | ||
|
|
@@ -103,9 +162,9 @@ struct as_if { | |
| if (!node.m_pNode) | ||
| return fallback; | ||
|
|
||
| T t = fallback; | ||
| if (convert<T>::decode(node, t)) | ||
| return t; | ||
| decode_box<T> t{fallback}; | ||
| if (convert<decltype(t)>::decode(node, t)) | ||
| return *t; | ||
| return fallback; | ||
| } | ||
| }; | ||
|
|
@@ -133,9 +192,9 @@ struct as_if<T, void> { | |
| if (!node.m_pNode) // no fallback | ||
| throw InvalidNode(node.m_invalidKey); | ||
|
|
||
| T t; | ||
| if (convert<T>::decode(node, t)) | ||
| return t; | ||
| decode_box<T> t; | ||
| if (convert<decltype(t)>::decode(node, t)) | ||
| return *t; | ||
| throw TypedBadConversion<T>(node.Mark()); | ||
| } | ||
| }; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
c++17 and newer -> C++17 or newer