Skip to content

Commit 3be2e1b

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 2f89975 commit 3be2e1b

File tree

4 files changed

+170
-2
lines changed

4 files changed

+170
-2
lines changed

docs/Tutorial.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,4 +198,80 @@ 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+
A() = default;
215+
A(int a) : a{a} {}
216+
217+
// virtual load/emit methods
218+
virtual void load(const YAML::Node &node) {
219+
a = node["a"].as<int>();
220+
}
221+
222+
virtual YAML::Node emit() const {
223+
YAML::Node node;
224+
node["a"] = a;
225+
return node;
226+
}
227+
228+
int a;
229+
};
230+
231+
// Derived class
232+
class B : public A
233+
{
234+
public:
235+
B() = default;
236+
B(int a, int b) : A{a}, b{b} {}
237+
238+
// override virtual load/emit methods
239+
virtual void load(const YAML::Node &node) override {
240+
A::load(node);
241+
b = node["b"].as<int>();
242+
}
243+
244+
virtual YAML::Node emit() const override {
245+
YAML::Node node = A::emit();
246+
node["b"] = b;
247+
return node;
248+
}
249+
250+
int b;
251+
};
252+
253+
// Implementation of convert::{encode,decode} for all classes derived from or being A
254+
namespace YAML {
255+
template<typename T>
256+
struct convert<T, typename std::enable_if<std::is_base_of<A, T>::value>::type> {
257+
static Node encode(const T &rhs) {
258+
Node node = rhs.emit();
259+
return node;
260+
}
261+
262+
static bool decode(const Node &node, T &rhs) {
263+
rhs.load(node);
264+
return true;
265+
}
266+
};
267+
}
268+
```
269+
270+
Which can then be use like this:
271+
```cpp
272+
YAML::Node node = YAML::Load("{a: 1, b: 2}");
273+
B b = node.as<B>();
274+
b.a = 12;
275+
b.b = 42;
276+
node = b;
201277
```

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: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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(1);
78+
Node n;
79+
n = a;
80+
EXPECT_EQ(a.a, n["a"].as<int>());
81+
}
82+
83+
TEST(ConvertPartialSpecializationTest, DecodeDerivedClass) {
84+
B b(1, 2);
85+
Node n;
86+
n = b;
87+
EXPECT_EQ(b.a, n["a"].as<int>());
88+
EXPECT_EQ(b.b, n["b"].as<int>());
89+
}
90+
91+
} // namespace
92+
} // namespace YAML

0 commit comments

Comments
 (0)