Skip to content

Commit 66eed7d

Browse files
committed
WIP
1 parent 3073f29 commit 66eed7d

File tree

5 files changed

+123
-104
lines changed

5 files changed

+123
-104
lines changed

examples/t13_custom_type.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ template <> inline
3636

3737
Vector4D output;
3838
output.w = convertFromString<double>(parts[0]);
39-
output.x = convertFromString<double>(parts[1]);
40-
output.y = convertFromString<double>(parts[2]);
39+
output.x = convertFromString<double>(parts[1]);
40+
output.y = convertFromString<double>(parts[2]);
4141
output.z = convertFromString<double>(parts[3]);
4242
return output;
4343
}

include/behaviortree_cpp/json_export.h

Lines changed: 86 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,46 @@
66
// Use the version nlohmann::json embedded in BT.CPP
77
#include "behaviortree_cpp/contrib/json.hpp"
88

9-
namespace BT
10-
{
9+
namespace BT {
1110

1211
/**
1312
* To add new type, you must follow these isntructions:
1413
* https://json.nlohmann.me/features/arbitrary_types/
1514
*
16-
* For instance the type Foo requires the implementation:
15+
* Considering for instance the type:
16+
*
17+
* struct Point2D {
18+
* double x;
19+
* double y;
20+
* };
21+
*
22+
* This would require the implementation of:
23+
*
24+
* void to_json(nlohmann::json& j, const Point2D& point);
25+
* void from_json(const nlohmann::json& j, Point2D& point);
1726
*
18-
* void to_json(json& j, const Foo& f);
27+
* To avoir repeating yourself, we provide the macro BT_JSON_CONVERTION
28+
* that implements both those function, at once.
29+
* Usage:
1930
*
20-
* Later, you MUST register this calling:
31+
* BT_JSON_CONVERTER(Point2D, point)
32+
* {
33+
* add_field("x", &point.x);
34+
* add_field("y", &point.y);
35+
* }
2136
*
22-
* RegisterJsonDefinition<Foo>();
37+
* Later, you MUST register the type in main using:
38+
*
39+
* RegisterJsonDefinition<Point2D>();
2340
*/
2441

25-
class JsonExporter{
42+
//-----------------------------------------------------------------------------------
43+
44+
/**
45+
* Use RegisterJsonDefinition<Foo>();
46+
*/
47+
48+
class JsonExporter {
2649

2750
public:
2851
static JsonExporter& get() {
@@ -43,81 +66,78 @@ class JsonExporter{
4366
dst = val;
4467
}
4568

46-
/// Register new JSON converters with addConverter<Foo>(),
47-
/// But works only if this function is implemented:
48-
///
49-
/// void nlohmann::to_json(nlohmann::json& destination, const Foo& foo)
50-
template <typename T> void addConverter()
51-
{
52-
auto converter = [](const BT::Any& entry, nlohmann::json& dst) {
53-
nlohmann::to_json(dst, entry.cast<T>());
54-
};
55-
type_converters_.insert( {typeid(T), std::move(converter)} );
56-
}
57-
58-
template <typename T> void addConverter(std::function<void(nlohmann::json&, const T&)> func)
59-
{
60-
auto converter = [func](const BT::Any& entry, nlohmann::json& dst) {
61-
func(dst, entry.cast<T>());
62-
};
63-
type_converters_.insert( {typeid(T), std::move(converter)} );
64-
}
65-
66-
/// Register directly your own converter.
67-
template <typename T>
68-
void addConverter(std::function<void(const T&, nlohmann::json&)> to_json)
69-
{
70-
auto converter = [=](const BT::Any& entry, nlohmann::json& dst) {
71-
to_json(entry.cast<T>(), dst);
72-
};
73-
type_converters_.insert( {typeid(T), std::move(converter)} );
74-
}
69+
/// Register new JSON converters with addConverter<Foo>().
70+
/// You should have used first the macro BT_JSON_CONVERTER
71+
template <typename T> void addConverter();
7572

76-
private:
73+
private:
7774

7875
using ToJonConverter = std::function<void(const BT::Any&, nlohmann::json&)>;
79-
std::unordered_map<std::type_index, ToJonConverter> type_converters_;
76+
using FromJonConverter = std::function<BT::Any(const nlohmann::json&)>;
77+
78+
std::unordered_map<std::type_index, ToJonConverter> to_json_converters_;
79+
std::unordered_map<std::string, FromJonConverter> from_json_converters_;
8080
};
8181

82-
/* Function to use to register a specific implementation of nlohmann::to_json
82+
//-------------------------------------------------------------------
8383

84-
Example:
84+
template<typename T> inline
85+
void JsonExporter::addConverter()
86+
{
87+
ToJonConverter to_converter = [](const BT::Any& entry, nlohmann::json& dst)
88+
{
89+
using namespace nlohmann;
90+
to_json(dst, *const_cast<BT::Any&>(entry).castPtr<T>());
91+
};
92+
to_json_converters_.insert( {typeid(T), to_converter} );
8593

86-
namespace nlohmann {
87-
void to_json(nlohmann::json& j, const Position2D& p)
88-
{
89-
j["x"] = p.x;
90-
j["y"] = p.y;
91-
}
92-
} // namespace nlohmann
94+
FromJonConverter from_converter = [](const nlohmann::json& dst) -> BT::Any
95+
{
96+
T value;
97+
using namespace nlohmann;
98+
from_json(dst, value);
99+
return BT::Any(value);
100+
};
101+
102+
// we need to get the name of the type
103+
nlohmann::json const js = T{};
104+
auto const type_name = js.contains("__type") ?
105+
std::string(js["__type"]) :
106+
BT::demangle(typeid(T));
107+
108+
from_json_converters_.insert( {type_name, from_converter} );
109+
}
93110

94-
// In you main function
95-
RegisterJsonDefinition<Position2D>()
96-
*/
97111
template <typename T> inline void RegisterJsonDefinition()
98112
{
99113
JsonExporter::get().addConverter<T>();
100114
}
101115

102-
/* Function to use to register a specific implementation of "to_json"
103-
104-
Example:
105-
106-
RegisterJsonDefinition([](nlohmann::json& j, const Position2D& p)
107-
{
108-
j["x"] = p.x;
109-
j["y"] = p.y;
110-
} );
111-
*/
112-
113-
template <typename T> inline
114-
void RegisterJsonDefinition(std::function<void(nlohmann::json&, const T&)> func)
115-
{
116-
JsonExporter::get().addConverter<T>(func);
117-
}
118116

119117
nlohmann::json ExportBlackboardToJSON(BT::Blackboard& blackboard);
120118

121119
} // namespace BT
122120

121+
//------------------------------------------------
122+
//------------------------------------------------
123+
//------------------------------------------------
124+
125+
// Macro to implement to_json() and from_json()
126+
127+
#define BT_JSON_CONVERTER(Type, value) \
128+
template <class AddField> void _JsonTypeDefinition(Type&, AddField&); \
129+
\
130+
inline void to_json(nlohmann::json& js, const Type& p, bool add_type_name = false) { \
131+
auto op = [&js](const char* name, auto* val) { to_json(js[name], *val); }; \
132+
_JsonTypeDefinition(const_cast<Type&>(p), op); \
133+
js["__type"] = #Type; \
134+
} \
135+
\
136+
inline void from_json(const nlohmann::json& js, Type& p) { \
137+
auto op = [&js](const char* name, auto* v) { js.at(name).get_to(*v); }; \
138+
_JsonTypeDefinition(p, op); \
139+
} \
140+
\
141+
template <class AddField> inline \
142+
void _JsonTypeDefinition(Type& value, AddField& add_field)\
123143

src/bt_factory.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
*/
1212

1313
#include <filesystem>
14-
#include <fstream>
1514
#include "behaviortree_cpp/bt_factory.h"
1615
#include "behaviortree_cpp/utils/shared_library.h"
1716
#include "behaviortree_cpp/contrib/json.hpp"

src/json_export.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ bool JsonExporter::toJson(const Any &any, nlohmann::json &dst) const
2626
}
2727
else
2828
{
29-
auto it = type_converters_.find(type);
30-
if(it != type_converters_.end())
29+
auto it = to_json_converters_.find(type);
30+
if(it != to_json_converters_.end())
3131
{
3232
it->second(any, dst);
3333
}

tests/gtest_json.cpp

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
//----------- Custom types ----------
55

6+
namespace TestTypes {
7+
68
struct Vector3D {
79
double x;
810
double y;
@@ -21,37 +23,39 @@ struct Pose3D {
2123
Quaternion3D rot;
2224
};
2325

24-
//----------- JSON specialization ----------
25-
26-
void to_json(nlohmann::json& j, const Vector3D& v)
26+
BT_JSON_CONVERTER(Vector3D, v)
2727
{
28-
// compact syntax
29-
j = {{"x", v.x}, {"y", v.y}, {"z", v.z}};
28+
add_field("x", &v.x);
29+
add_field("y", &v.y);
30+
add_field("z", &v.z);
3031
}
3132

32-
void to_json(nlohmann::json& j, const Quaternion3D& q)
33+
BT_JSON_CONVERTER(Quaternion3D, v)
3334
{
34-
// verbose syntax
35-
j["w"] = q.w;
36-
j["x"] = q.x;
37-
j["y"] = q.y;
38-
j["z"] = q.z;
35+
add_field("w", &v.w);
36+
add_field("x", &v.x);
37+
add_field("y", &v.y);
38+
add_field("z", &v.z);
3939
}
4040

41-
void to_json(nlohmann::json& j, const Pose3D& p)
41+
BT_JSON_CONVERTER(Pose3D, v)
4242
{
43-
j = {{"pos", p.pos}, {"rot", p.rot}};
43+
add_field("pos", &v.pos);
44+
add_field("rot", &v.rot);
4445
}
4546

47+
} // namespace TestTypes
48+
49+
50+
//----------- JSON specialization ----------
4651

47-
using namespace BT;
4852

4953
TEST(JsonTest, Exporter)
5054
{
51-
JsonExporter exporter;
55+
BT::JsonExporter exporter;
5256

53-
Pose3D pose = { {1,2,3},
54-
{4,5,6,7} };
57+
TestTypes::Pose3D pose = { {1,2,3},
58+
{4,5,6,7} };
5559

5660
nlohmann::json json;
5761
exporter.toJson(BT::Any(69), json["int"]);
@@ -61,27 +65,23 @@ TEST(JsonTest, Exporter)
6165
ASSERT_FALSE( exporter.toJson(BT::Any(pose), json["pose"]) );
6266

6367
// now it should work
64-
exporter.addConverter<Pose3D>();
68+
exporter.addConverter<TestTypes::Pose3D>();
6569
exporter.toJson(BT::Any(pose), json["pose"]);
6670

67-
nlohmann::json json_expected;
68-
json_expected["int"] = 69;
69-
json_expected["real"] = 3.14;
70-
71-
json_expected["pose"]["pos"]["x"] = 1;
72-
json_expected["pose"]["pos"]["y"] = 2;
73-
json_expected["pose"]["pos"]["z"] = 3;
71+
std::cout << json.dump(2) << std::endl;
7472

75-
json_expected["pose"]["rot"]["w"] = 4;
76-
json_expected["pose"]["rot"]["x"] = 5;
77-
json_expected["pose"]["rot"]["y"] = 6;
78-
json_expected["pose"]["rot"]["z"] = 7;
73+
ASSERT_EQ(json["int"],69);
74+
ASSERT_EQ(json["real"], 3.14);
7975

80-
ASSERT_EQ(json_expected, json);
76+
ASSERT_EQ(json["pose"]["__type"], "Pose3D");
77+
ASSERT_EQ(json["pose"]["pos"]["x"], 1);
78+
ASSERT_EQ(json["pose"]["pos"]["y"], 2);
79+
ASSERT_EQ(json["pose"]["pos"]["z"], 3);
8180

82-
std::cout << json.dump(2) << std::endl;
81+
ASSERT_EQ(json["pose"]["rot"]["w"], 4);
82+
ASSERT_EQ(json["pose"]["rot"]["x"], 5);
83+
ASSERT_EQ(json["pose"]["rot"]["y"], 6);
84+
ASSERT_EQ(json["pose"]["rot"]["z"], 7);
8385
}
8486

8587

86-
87-

0 commit comments

Comments
 (0)