Skip to content

Commit e386c75

Browse files
committed
add more json functionalities
1 parent 27f4f28 commit e386c75

File tree

8 files changed

+337
-177
lines changed

8 files changed

+337
-177
lines changed

include/behaviortree_cpp/basic_types.h

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,23 @@ using KeyValueVector = std::vector<std::pair<std::string, std::string>>;
6565
struct AnyTypeAllowed
6666
{};
6767

68+
/**
69+
* @brief convertFromJSON will parse a json string and use JsonExporter
70+
* to convert its content to a given type. it will work only if
71+
* the type was previously registered. May throw if it fails.
72+
*
73+
* @param json_text a valid JSON string
74+
* @param type you must specify the typeid()
75+
* @return the object, wrapped in Any.
76+
*/
77+
[[nodiscard]] Any convertFromJSON(StringView json_text, std::type_index type);
78+
79+
/// Same as the non template version, but with automatic casting
80+
template <typename T> [[nodiscard]]
81+
inline T convertFromJSON(StringView str)
82+
{
83+
return convertFromJSON(str, typeid(T)).cast<T>();
84+
}
6885

6986
/**
7087
* convertFromString is used to convert a string into a custom type.
@@ -75,8 +92,15 @@ struct AnyTypeAllowed
7592
* If you have a custom type, you need to implement the corresponding template specialization.
7693
*/
7794
template <typename T> [[nodiscard]]
78-
inline T convertFromString(StringView /*str*/)
95+
inline T convertFromString(StringView str)
7996
{
97+
// if string starts with "json:{", try to parse it as json
98+
if(str.size() > 6 && std::strncmp("json:{", str.data(), 6) == 0)
99+
{
100+
str.remove_prefix(5);
101+
return convertFromJSON<T>(str);
102+
}
103+
80104
auto type_name = BT::demangle(typeid(T));
81105

82106
std::cerr << "You (maybe indirectly) called BT::convertFromString() for type ["

include/behaviortree_cpp/blackboard.h

Lines changed: 165 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
#pragma once
22

3-
#include <iostream>
43
#include <string>
54
#include <memory>
6-
#include <stdint.h>
75
#include <unordered_map>
86
#include <mutex>
9-
#include <sstream>
107

118
#include "behaviortree_cpp/basic_types.h"
9+
#include "behaviortree_cpp/contrib/json.hpp"
1210
#include "behaviortree_cpp/utils/safe_any.hpp"
1311
#include "behaviortree_cpp/exceptions.h"
1412
#include "behaviortree_cpp/utils/locked_reference.hpp"
@@ -78,150 +76,19 @@ class Blackboard
7876
* Note that this method may throw an exception if the cast to T failed.
7977
*/
8078
template <typename T> [[nodiscard]]
81-
bool get(const std::string& key, T& value) const
82-
{
83-
if (auto any_ref = getAnyLocked(key))
84-
{
85-
value = any_ref.get()->cast<T>();
86-
return true;
87-
}
88-
return false;
89-
}
79+
bool get(const std::string& key, T& value) const;
9080

9181
/**
9282
* Version of get() that throws if it fails.
9383
*/
9484
template <typename T> [[nodiscard]]
95-
T get(const std::string& key) const
96-
{
97-
if (auto any_ref = getAnyLocked(key))
98-
{
99-
const auto& any = any_ref.get();
100-
if(any->empty())
101-
{
102-
throw RuntimeError("Blackboard::get() error. Entry [", key, "] hasn't been initialized, yet");
103-
}
104-
return any_ref.get()->cast<T>();
105-
}
106-
else
107-
{
108-
throw RuntimeError("Blackboard::get() error. Missing key [", key, "]");
109-
}
110-
}
85+
T get(const std::string& key) const;
11186

11287
/// Update the entry with the given key
11388
template <typename T>
114-
void set(const std::string& key, const T& value)
115-
{
116-
std::unique_lock lock(mutex_);
89+
void set(const std::string& key, const T& value);
11790

118-
// check local storage
119-
auto it = storage_.find(key);
120-
if (it == storage_.end())
121-
{
122-
// create a new entry
123-
Any new_value(value);
124-
lock.unlock();
125-
std::shared_ptr<Blackboard::Entry> entry;
126-
// if a new generic port is created with a string, it's type should be AnyTypeAllowed
127-
if constexpr (std::is_same_v<std::string, T>)
128-
{
129-
entry = createEntryImpl(key, PortInfo(PortDirection::INOUT));
130-
}
131-
else {
132-
PortInfo new_port(PortDirection::INOUT, new_value.type(),
133-
GetAnyFromStringFunctor<T>());
134-
entry = createEntryImpl(key, new_port);
135-
}
136-
lock.lock();
137-
138-
storage_.insert( {key, entry} );
139-
entry->value = new_value;
140-
}
141-
else
142-
{
143-
// this is not the first time we set this entry, we need to check
144-
// if the type is the same or not.
145-
Entry& entry = *it->second;
146-
std::scoped_lock scoped_lock(entry.entry_mutex);
147-
148-
Any& previous_any = entry.value;
149-
150-
Any new_value(value);
151-
152-
// special case: entry exists but it is not strongly typed... yet
153-
if (!entry.info.isStronglyTyped())
154-
{
155-
// Use the new type to create a new entry that is strongly typed.
156-
entry.info = TypeInfo::Create<T>();
157-
previous_any = std::move(new_value);
158-
return;
159-
}
160-
161-
std::type_index previous_type = entry.info.type();
162-
163-
// check type mismatch
164-
if (previous_type != std::type_index(typeid(T)) &&
165-
previous_type != new_value.type())
166-
{
167-
bool mismatching = true;
168-
if (std::is_constructible<StringView, T>::value)
169-
{
170-
Any any_from_string = entry.info.parseString(value);
171-
if (any_from_string.empty() == false)
172-
{
173-
mismatching = false;
174-
new_value = std::move(any_from_string);
175-
}
176-
}
177-
// check if we are doing a safe cast between numbers
178-
// for instance, it is safe to use int(100) to set
179-
// a uint8_t port, but not int(-42) or int(300)
180-
if constexpr(std::is_arithmetic_v<T>)
181-
{
182-
if(mismatching && isCastingSafe(previous_type, value))
183-
{
184-
mismatching = false;
185-
}
186-
}
187-
188-
if (mismatching)
189-
{
190-
debugMessage();
191-
192-
auto msg = StrCat("Blackboard::set(", key, "): once declared, "
193-
"the type of a port shall not change. "
194-
"Previously declared type [", BT::demangle(previous_type),
195-
"], current type [", BT::demangle(typeid(T)), "]");
196-
throw LogicError(msg);
197-
}
198-
}
199-
// if doing set<BT::Any>, skip type check
200-
if constexpr(std::is_same_v<Any, T>)
201-
{
202-
previous_any = new_value;
203-
}
204-
else {
205-
// copy only if the type is compatible
206-
new_value.copyInto(previous_any);
207-
}
208-
}
209-
}
210-
211-
void unset(const std::string& key)
212-
{
213-
std::unique_lock lock(mutex_);
214-
215-
// check local storage
216-
auto it = storage_.find(key);
217-
if (it == storage_.end())
218-
{
219-
// No entry, nothing to do.
220-
return;
221-
}
222-
223-
storage_.erase(it);
224-
}
91+
void unset(const std::string& key);
22592

22693
[[nodiscard]] const TypeInfo* entryInfo(const std::string& key);
22794

@@ -250,5 +117,165 @@ class Blackboard
250117
bool autoremapping_ = false;
251118
};
252119

120+
/**
121+
* @brief ExportBlackboardToJSON will create a JSON
122+
* that contains the current values of the blackboard.
123+
* Complex types must be registered with JsonExporter::get()
124+
*/
125+
nlohmann::json ExportBlackboardToJSON(const Blackboard &blackboard);
126+
127+
/**
128+
* @brief ImportBlackboardToJSON will append elements to the blackboard,
129+
* using the values parsed from the JSON file created using ExportBlackboardToJSON.
130+
* Complex types must be registered with JsonExporter::get()
131+
*/
132+
void ImportBlackboardToJSON(const nlohmann::json& json, Blackboard& blackboard);
133+
134+
135+
//------------------------------------------------------
136+
137+
template<typename T> inline
138+
T Blackboard::get(const std::string &key) const
139+
{
140+
if (auto any_ref = getAnyLocked(key))
141+
{
142+
const auto& any = any_ref.get();
143+
if(any->empty())
144+
{
145+
throw RuntimeError("Blackboard::get() error. Entry [", key, "] hasn't been initialized, yet");
146+
}
147+
return any_ref.get()->cast<T>();
148+
}
149+
else
150+
{
151+
throw RuntimeError("Blackboard::get() error. Missing key [", key, "]");
152+
}
153+
}
154+
155+
156+
inline void Blackboard::unset(const std::string &key)
157+
{
158+
std::unique_lock lock(mutex_);
159+
160+
// check local storage
161+
auto it = storage_.find(key);
162+
if (it == storage_.end())
163+
{
164+
// No entry, nothing to do.
165+
return;
166+
}
167+
168+
storage_.erase(it);
169+
}
170+
171+
template<typename T> inline
172+
void Blackboard::set(const std::string &key, const T &value)
173+
{
174+
std::unique_lock lock(mutex_);
175+
176+
// check local storage
177+
auto it = storage_.find(key);
178+
if (it == storage_.end())
179+
{
180+
// create a new entry
181+
Any new_value(value);
182+
lock.unlock();
183+
std::shared_ptr<Blackboard::Entry> entry;
184+
// if a new generic port is created with a string, it's type should be AnyTypeAllowed
185+
if constexpr (std::is_same_v<std::string, T>)
186+
{
187+
entry = createEntryImpl(key, PortInfo(PortDirection::INOUT));
188+
}
189+
else {
190+
PortInfo new_port(PortDirection::INOUT, new_value.type(),
191+
GetAnyFromStringFunctor<T>());
192+
entry = createEntryImpl(key, new_port);
193+
}
194+
lock.lock();
195+
196+
storage_.insert( {key, entry} );
197+
entry->value = new_value;
198+
}
199+
else
200+
{
201+
// this is not the first time we set this entry, we need to check
202+
// if the type is the same or not.
203+
Entry& entry = *it->second;
204+
std::scoped_lock scoped_lock(entry.entry_mutex);
205+
206+
Any& previous_any = entry.value;
207+
208+
Any new_value(value);
209+
210+
// special case: entry exists but it is not strongly typed... yet
211+
if (!entry.info.isStronglyTyped())
212+
{
213+
// Use the new type to create a new entry that is strongly typed.
214+
entry.info = TypeInfo::Create<T>();
215+
previous_any = std::move(new_value);
216+
return;
217+
}
218+
219+
std::type_index previous_type = entry.info.type();
220+
221+
// check type mismatch
222+
if (previous_type != std::type_index(typeid(T)) &&
223+
previous_type != new_value.type())
224+
{
225+
bool mismatching = true;
226+
if (std::is_constructible<StringView, T>::value)
227+
{
228+
Any any_from_string = entry.info.parseString(value);
229+
if (any_from_string.empty() == false)
230+
{
231+
mismatching = false;
232+
new_value = std::move(any_from_string);
233+
}
234+
}
235+
// check if we are doing a safe cast between numbers
236+
// for instance, it is safe to use int(100) to set
237+
// a uint8_t port, but not int(-42) or int(300)
238+
if constexpr(std::is_arithmetic_v<T>)
239+
{
240+
if(mismatching && isCastingSafe(previous_type, value))
241+
{
242+
mismatching = false;
243+
}
244+
}
245+
246+
if (mismatching)
247+
{
248+
debugMessage();
249+
250+
auto msg = StrCat("Blackboard::set(", key, "): once declared, "
251+
"the type of a port shall not change. "
252+
"Previously declared type [", BT::demangle(previous_type),
253+
"], current type [", BT::demangle(typeid(T)), "]");
254+
throw LogicError(msg);
255+
}
256+
}
257+
// if doing set<BT::Any>, skip type check
258+
if constexpr(std::is_same_v<Any, T>)
259+
{
260+
previous_any = new_value;
261+
}
262+
else {
263+
// copy only if the type is compatible
264+
new_value.copyInto(previous_any);
265+
}
266+
}
267+
}
268+
269+
template<typename T> inline
270+
bool Blackboard::get(const std::string &key, T &value) const
271+
{
272+
if (auto any_ref = getAnyLocked(key))
273+
{
274+
value = any_ref.get()->cast<T>();
275+
return true;
276+
}
277+
return false;
278+
}
279+
253280
} // namespace BT
254281

0 commit comments

Comments
 (0)