Skip to content
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

Seamlessly support non-default-constructable custom class deserialization #1087

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 50 additions & 4 deletions docs/Tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,11 +178,12 @@ struct convert<Vec3> {
return node;
}

static bool decode(const Node& node, Vec3& rhs) {
static Vec3 decode(const Node& node) {
if(!node.IsSequence() || node.size() != 3) {
return false;
throw YAML::conversion::DecodeException("");
}

Vec3 rhs;
rhs.x = node[0].as<double>();
rhs.y = node[1].as<double>();
rhs.z = node[2].as<double>();
Expand All @@ -197,5 +198,50 @@ Then you could use `Vec3` wherever you could use any other type:
```cpp
YAML::Node node = YAML::Load("start: [1, 3, 0]");
Vec3 v = node["start"].as<Vec3>();
node["end"] = Vec3(2, -1, 0);
```
node["end"] = Vec3{2, -1, 0};
```

Observe that in the above example the custom type is, decalred as
a struct, explicit default constructable and all its members are
exposed. For non default constructable types like

```cpp
class NonDefCtorVec3 : public Vec3 {
using Vec3::x;
using Vec3::y;
using Vec3::z;
public:
NonDefCtorVec3(double x, double y, double z)
: Vec3() { this->x=x; this->y=y; this->z=z;
};
};
```
a new API is available, that freshens up the signature of the 'convert<T>::decode'
method and introduces the abortion of the deserialization process by throwing
an `DecodeException`.

```cpp
namespace YAML {
template <>
struct convert<NonDefCtorVec3> {
static Node encode(const NonDefCtorVec3& rhs) {
return convert<Vec3>::encode(rhs);
}

static NonDefCtorVec3 decode(const Node& node) {
if (!node.IsSequence() || node.size() != 3) {
throw YAML::conversion::DecodeException();
}
return {node[0].as<double>(), node[1].as<double>(), node[2].as<double>()};
}
};
}
```

The behavior is exactly the same

```cpp
YAML::Node node = YAML::Load("start: [1, 3, 0]");
NonDefCtorVec3 v = node["start"].as<NonDefCtorVec3>();
node["end"] = NonDefCtorVec3(2, -1, 0);
```
9 changes: 9 additions & 0 deletions include/yaml-cpp/exceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,15 @@ class YAML_CPP_API BadFile : public Exception {
BadFile(const BadFile&) = default;
~BadFile() YAML_CPP_NOEXCEPT override;
};


namespace conversion{
class DecodeException : public std::runtime_error {
public:
DecodeException(const std::string& s="") : std::runtime_error(s) {};
};
}

} // namespace YAML

#endif // EXCEPTIONS_H_62B23520_7C8E_11DE_8A39_0800200C9A66
53 changes: 53 additions & 0 deletions include/yaml-cpp/node/api_switch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// Created by marcel on 3/3/22.
//

#ifndef YAML_CPP_API_SWITCH_H
#define YAML_CPP_API_SWITCH_H

#if defined(_MSC_VER) || \
(defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || \
(__GNUC__ >= 4)) // GCC supports "pragma once" correctly since 3.4
#pragma once
#endif

#include "yaml-cpp/node/node.h"
#include "yaml-cpp/exceptions.h"
#include <type_traits>

namespace YAML{
namespace detail {

//detect the method of the new api
template <typename>
std::false_type has_decode_new_api(long);

template <typename T>
auto has_decode_new_api(int)
-> decltype( T::decode(std::declval<const Node&>()), std::true_type{});

template <bool AorB>
struct static_api_switch;

template<> //new api call-path
struct static_api_switch<true> {
template<class T>
static T decode(const Node& node) {
return convert<T>::decode(node);
}
};

template<> //old api call-path
struct static_api_switch<false> {
template<class T>
static T decode(const Node& node) {
T t;
if (convert<T>::decode(node, t))
return t;
throw conversion::DecodeException();
}
};
}
}

#endif // YAML_CPP_API_SWITCH_H
Loading