Skip to content

Commit 37af483

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 37af483

File tree

4 files changed

+161
-2
lines changed

4 files changed

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

0 commit comments

Comments
 (0)