Skip to content

Commit 0085ebf

Browse files
Allowing partial specialization of the convert struct
This is done by adding a second, defaulted, template parameter that can be used in conjunction of std::enable_if An example of usage is added to the tutorial
1 parent 51ce663 commit 0085ebf

File tree

4 files changed

+167
-2
lines changed

4 files changed

+167
-2
lines changed

docs/Tutorial.md

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,4 +198,74 @@ Then you could use `Vec3` wherever you could use any other type:
198198
YAML::Node node = YAML::Load("start: [1, 3, 0]");
199199
Vec3 v = node["start"].as<Vec3>();
200200
node["end"] = Vec3(2, -1, 0);
201+
```
202+
203+
## Partial specialization
204+
205+
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).
206+
207+
Here is a small example showing how to partially specialize the `convert` struct for all types deriving from a base class:
208+
209+
```cpp
210+
// Base class
211+
class A
212+
{
213+
public:
214+
// virtual load/emit methods
215+
virtual void load(const YAML::Node &node) {
216+
a = node["a"].as<int>();
217+
}
218+
219+
virtual YAML::Node emit() const {
220+
YAML::Node node;
221+
node["a"] = a;
222+
return node;
223+
}
224+
225+
int a;
226+
};
227+
228+
// Derived class
229+
class B : public A
230+
{
231+
public:
232+
// override virtual load/emit methods
233+
virtual void load(const YAML::Node &node) override {
234+
A::load(node);
235+
b = node["b"].as<int>();
236+
}
237+
238+
virtual YAML::Node emit() const override {
239+
YAML::Node node = A::emit();
240+
node["b"] = b;
241+
return node;
242+
}
243+
244+
int b;
245+
};
246+
247+
// Implementation of convert::{encode,decode} for all classes derived from or being A
248+
namespace YAML {
249+
template<typename T>
250+
struct convert<T, typename std::enable_if<std::is_base_of<A, T>::value>::type> {
251+
static Node encode(const T &rhs) {
252+
Node node = rhs.emit();
253+
return node;
254+
}
255+
256+
static bool decode(const Node &node, T &rhs) {
257+
rhs.load(node);
258+
return true;
259+
}
260+
};
261+
}
262+
```
263+
264+
Which can then be use like this:
265+
```cpp
266+
YAML::Node node = YAML::Load("{a: 1, b: 2}");
267+
B b = node.as<B>();
268+
b.a = 12;
269+
b.b = 42;
270+
node = b;
201271
```

include/yaml-cpp/node/convert.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
namespace YAML {
2828
class Binary;
2929
struct _Null;
30-
template <typename T>
30+
template <typename T, typename Enable>
3131
struct convert;
3232
} // namespace YAML
3333

include/yaml-cpp/node/node.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ YAML_CPP_API bool operator==(const Node& lhs, const Node& rhs);
141141

142142
YAML_CPP_API Node Clone(const Node& node);
143143

144-
template <typename T>
144+
template <typename T, typename Enable = void>
145145
struct convert;
146146
}
147147

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#include "yaml-cpp/emitterstyle.h"
2+
#include "yaml-cpp/eventhandler.h"
3+
#include "yaml-cpp/yaml.h" // IWYU pragma: keep
4+
#include "gtest/gtest.h"
5+
6+
// Base class
7+
class A {
8+
public:
9+
A() = default;
10+
A(int a) : a{a} {}
11+
12+
// virtual load/emit methods
13+
virtual void load(const YAML::Node &node) { a = node["a"].as<int>(); }
14+
15+
virtual YAML::Node emit() const {
16+
YAML::Node node;
17+
node["a"] = a;
18+
return node;
19+
}
20+
21+
int a{};
22+
};
23+
24+
// Derived class
25+
class B : public A {
26+
public:
27+
B() = default;
28+
B(int a, int b) : A{a}, b{b} {}
29+
30+
// override virtual load/emit methods
31+
virtual void load(const YAML::Node &node) override {
32+
A::load(node);
33+
b = node["b"].as<int>();
34+
}
35+
36+
virtual YAML::Node emit() const override {
37+
YAML::Node node = A::emit();
38+
node["b"] = b;
39+
return node;
40+
}
41+
42+
int b{};
43+
};
44+
45+
// Implementation of convert::{encode,decode} for all classes derived from or
46+
// being A
47+
namespace YAML {
48+
template <typename T>
49+
struct convert<T, typename std::enable_if<std::is_base_of<A, T>::value>::type> {
50+
static Node encode(const T &rhs) {
51+
Node node = rhs.emit();
52+
return node;
53+
}
54+
55+
static bool decode(const Node &node, T &rhs) {
56+
rhs.load(node);
57+
return true;
58+
}
59+
};
60+
61+
namespace {
62+
63+
TEST(ConvertPartialSpecializationTest, EncodeBaseClass) {
64+
Node n(Load("{a: 1}"));
65+
A a = n.as<A>();
66+
EXPECT_EQ(a.a, 1);
67+
}
68+
69+
TEST(ConvertPartialSpecializationTest, EncodeDerivedClass) {
70+
Node n(Load("{a: 1, b: 2}"));
71+
B b = n.as<B>();
72+
EXPECT_EQ(b.a, 1);
73+
EXPECT_EQ(b.b, 2);
74+
}
75+
76+
TEST(ConvertPartialSpecializationTest, DecodeBaseClass) {
77+
A a;
78+
a.a = 1;
79+
Node n;
80+
n = a;
81+
EXPECT_EQ(a.a, n["a"].as<int>());
82+
}
83+
84+
TEST(ConvertPartialSpecializationTest, DecodeDerivedClass) {
85+
B b;
86+
b.a = 1;
87+
b.b = 2;
88+
Node n;
89+
n = b;
90+
EXPECT_EQ(b.a, n["a"].as<int>());
91+
EXPECT_EQ(b.b, n["b"].as<int>());
92+
}
93+
94+
} // namespace
95+
} // namespace YAML

0 commit comments

Comments
 (0)