Skip to content

Commit 3b49d36

Browse files
jini-zhEvgenii Zhemchugov
andauthored
Fix storing of a BStore in a BStore and implement JSON encoders (#24)
* BStore::BStore(const BStore&): copy slots properly * BStore::Serialise: serialise m_variables and m_type_info * BStore: implement JSON encoding Implement BStore::JsonEncode methods that can be used to serialise BStore contents in JSON format, if this representation is possible. This requires type checking enabled. BStore::operator>> is not changed to provide backwards compatibility and a way to serialise a BStore with type checking disabled (only useful if all values are strings). Functions in Json.h can also be used independently to serialise other C++ integral types, strings, vectors and maps. --------- Co-authored-by: Evgenii Zhemchugov <evgenii.zhemchugov@warwick.ac.uk>
1 parent d21188d commit 3b49d36

File tree

4 files changed

+215
-14
lines changed

4 files changed

+215
-14
lines changed

src/Store/BStore.cpp

Lines changed: 128 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include <BStore.h>
22

3+
#include <unordered_map>
4+
35
using namespace ToolFramework;
46

57
////////////////////////
@@ -48,22 +50,23 @@ BStore::BStore(const BStore &bs):m_version(1.0){
4850

4951
if(bs.Header){
5052
Header = new BStore(*bs.Header);
51-
}
53+
} else
54+
Header = nullptr;
5255

5356
m_lookup = bs.m_lookup;
5457
output = bs.output;
55-
m_file_end = m_file_end;
56-
m_file_name = m_file_name;
57-
m_open_file_end = m_open_file_end;
58-
m_previous_file_end = m_previous_file_end;
59-
m_type = m_type;
60-
m_type_checking = m_type_checking;
61-
m_has_header = m_has_header;
62-
m_header_start = m_header_start;
63-
m_flags_start = m_flags_start;
64-
m_lookup_start = m_lookup_start;
65-
m_update = m_update;
66-
m_version = m_version;
58+
m_file_end = bs.m_file_end;
59+
m_file_name = bs.m_file_name;
60+
m_open_file_end = bs.m_open_file_end;
61+
m_previous_file_end = bs.m_previous_file_end;
62+
m_type = bs.m_type;
63+
m_type_checking = bs.m_type_checking;
64+
m_has_header = bs.m_has_header;
65+
m_header_start = bs.m_header_start;
66+
m_flags_start = bs.m_flags_start;
67+
m_lookup_start = bs.m_lookup_start;
68+
m_update = bs.m_update;
69+
m_version = bs.m_version;
6770

6871
}
6972

@@ -800,7 +803,10 @@ bool BStore::Serialise(BinaryStream &bs){ // do return properly
800803

801804
bs & output;
802805
bs & m_lookup;
803-
GetEntry(0);
806+
bs & m_variables;
807+
bs & m_type_checking;
808+
if (m_type_checking) bs & m_type_info;
809+
804810
/*
805811
save;
806812
@@ -949,3 +955,111 @@ BStore::~BStore(){
949955

950956

951957
}
958+
959+
namespace ToolFramework {
960+
bool json_encode(std::ostream& output, const BStore& store) {
961+
return store.JsonEncode(output);
962+
}
963+
};
964+
965+
template <typename T>
966+
static bool json_encode_bs(std::ostream& output, const BinaryStream& input) {
967+
T datum;
968+
// input must be const to conform to the JSON encoders interface layed out in
969+
// Json.h (const std::vector<BStore>& must be JSON-serializable) and to the
970+
// general idea that reading an object shouldn't change it. However, reading
971+
// from BinaryStream changes its position. We have to either cast away the
972+
// constness here or declare BinaryStream::m_pos mutable. I don't know which
973+
// way is better. --- Evgenii
974+
auto& in = const_cast<BinaryStream&>(input);
975+
in.m_pos = 0;
976+
return (in >> datum) && json_encode(output, datum);
977+
}
978+
979+
// Maps typeid().name to a function that extracts data from BinaryStream and
980+
// prints it as JSON.
981+
//
982+
// Extend as needed
983+
std::unordered_map<std::string, bool (*)(std::ostream&, const BinaryStream&)> json_encoders {
984+
#define encoder(type) { typeid(type).name(), json_encode_bs<type> }
985+
encoder(bool),
986+
encoder(int),
987+
encoder(unsigned int),
988+
encoder(double),
989+
encoder(std::string),
990+
encoder(BStore),
991+
encoder(std::vector<int>),
992+
encoder(std::vector<double>),
993+
encoder(std::vector<std::string>),
994+
encoder(std::vector<BStore>)
995+
#undef encoder
996+
};
997+
998+
bool (*BStore::GetJsonEncoder(const std::string& key) const)(std::ostream&, const BinaryStream&){
999+
auto type = m_type_info.find(key);
1000+
if (type == m_type_info.end()) return nullptr;
1001+
auto encoder = json_encoders.find(type->second);
1002+
if (encoder == json_encoders.end()) {
1003+
std::clog
1004+
<< "BStore::JsonEncode: encoder for type "
1005+
<< type->second
1006+
<< " (field "
1007+
<< key
1008+
<< ") is not implemented"
1009+
<< std::endl;
1010+
return nullptr;
1011+
};
1012+
return encoder->second;
1013+
}
1014+
1015+
bool BStore::JsonEncode(std::ostream& stream) const {
1016+
if (!m_type_checking) {
1017+
std::clog
1018+
<< "BStore::JsonEncode: type checking is required for JSON encoding"
1019+
<< std::endl;
1020+
return false;
1021+
};
1022+
1023+
bool comma = false;
1024+
stream << '{';
1025+
for (auto& kv : m_variables) {
1026+
if (comma)
1027+
stream << ',';
1028+
else
1029+
comma = true;
1030+
if (!json_encode(stream, kv.first)) return false;
1031+
stream << ':';
1032+
auto encoder = GetJsonEncoder(kv.first);
1033+
if (!encoder || !encoder(stream, kv.second)) return false;
1034+
};
1035+
stream << '}';
1036+
return true;
1037+
}
1038+
1039+
bool BStore::JsonEncode(std::string& json) const {
1040+
std::stringstream ss;
1041+
if (!JsonEncode(ss)) return false;
1042+
json = ss.str();
1043+
return true;
1044+
}
1045+
1046+
bool BStore::JsonEncode(const std::string& key, std::ostream& stream) const {
1047+
if (!m_type_checking) {
1048+
std::clog
1049+
<< "BStore::JsonEncode: type checking is required for JSON encoding"
1050+
<< std::endl;
1051+
return false;
1052+
};
1053+
1054+
auto kv = m_variables.find(key);
1055+
if (kv == m_variables.end()) return false;
1056+
auto encoder = GetJsonEncoder(kv->first);
1057+
return encoder && encoder(stream, kv->second);
1058+
}
1059+
1060+
bool BStore::JsonEncode(const std::string& key, std::string& json) const {
1061+
std::stringstream ss;
1062+
if (!JsonEncode(key, ss)) return false;
1063+
json = ss.str();
1064+
return true;
1065+
}

src/Store/BStore.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <PointerWrapper.h>
1515

1616
#include <BinaryStream.h>
17+
#include <Json.h>
1718
#include <sys/stat.h>
1819

1920
namespace ToolFramework{
@@ -71,13 +72,18 @@ namespace ToolFramework{
7172
/////////importing
7273

7374
void JsonParser(std::string input); ///< Converts a flat JSON formatted string to Store entries in the form of key value pairs. @param input The input flat JSON string.
75+
bool JsonEncode(std::ostream& output) const; ///< Prints contents as a JSON object if possible
76+
bool JsonEncode(std::string& output) const; ///< Prints contents as a JSON objec if possible
77+
bool JsonEncode(const std::string& key, std::ostream& output) const; ///< Prints contents of the given key as a JSON if possible.
78+
bool JsonEncode(const std::string& key, std::string& output) const; ///< Prints contents of the given key as JSON if possible.
7479
bool Print();
7580
void Print(bool values); ///< Prints the contents of the BoostStore. @param values If true values and keys are printed. If false just keys are printed
7681
void Delete(); ///< Deletes all entries in the BoostStore.
7782
void Remove(std::string key); ///< Removes a single entry from the BoostStore. @param key The key of the entry to remove.
7883
std::string Type(std::string key); ///< Queries the type of an entry if type checking is turned on. @param key The key of the entry to check. @return A string encoding the type info.
7984
bool Has(std::string key); ///< Queries if entry exists in a BoostStore. @param key is the key of the varaible to look up. @return true if varaible is present in the store, false if not.
8085
// BoostStore *Header; ///< Pointer to header BoostStore (only available in multi event BoostStore). This can be used to access and assign header varaibles jsut like a standard non multi event store.
86+
bool TypeChecking() const { return m_type_checking; }; ///< Whether type checking is enabled (required for JSON serialisation)
8187

8288
/**
8389
Templated getter function for BoostStore content. Assignment is templated and via reference.
@@ -248,9 +254,11 @@ namespace ToolFramework{
248254
//int m_file_type; //0=gzopen, 1=fopen, 2=stringstream
249255
float m_version;
250256

257+
bool (*GetJsonEncoder(const std::string& key) const)(std::ostream&, const BinaryStream&);
251258

252259
};
253260

261+
bool json_encode(std::ostream&, const BStore&);
254262
}
255263

256264
#endif

src/Store/Json.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include <Json.h>
2+
3+
namespace ToolFramework {
4+
5+
bool json_encode(std::ostream& output, const std::string& datum) {
6+
output << '"';
7+
for (char c : datum) {
8+
if (c == '"' || c == '\\') output << '\\';
9+
output << c;
10+
};
11+
output << '"';
12+
return true;
13+
}
14+
15+
}

src/Store/Json.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#ifndef TOOLFRAMEWORK_JSON_H
2+
#define TOOLFRAMEWORK_JSON_H
3+
4+
#include <map>
5+
#include <ostream>
6+
#include <sstream>
7+
#include <string>
8+
#include <type_traits>
9+
#include <vector>
10+
11+
namespace ToolFramework {
12+
13+
template <typename T>
14+
typename std::enable_if<std::is_arithmetic<T>::value, bool>::type
15+
json_encode(std::ostream& output, T datum) {
16+
output << datum;
17+
return true;
18+
}
19+
20+
bool json_encode(std::ostream& output, const std::string& datum);
21+
22+
template <typename T>
23+
bool json_encode(std::ostream& output, const std::vector<T>& data) {
24+
output << '[';
25+
bool comma = false;
26+
for (const T& datum : data) {
27+
if (comma)
28+
output << ',';
29+
else
30+
comma = true;
31+
json_encode(output, datum);
32+
};
33+
output << ']';
34+
return true;
35+
}
36+
37+
template <typename T>
38+
bool json_encode(std::ostream& output, const std::map<std::string, T>& data) {
39+
output << '{';
40+
bool comma = false;
41+
for (auto& datum : data) {
42+
if (comma)
43+
output << ',';
44+
else
45+
comma = true;
46+
json_encode(output, datum.first);
47+
output << ':';
48+
json_encode(output, datum.second);
49+
};
50+
output << '}';
51+
return true;
52+
}
53+
54+
template <typename T>
55+
bool json_encode(std::string& output, T data) {
56+
std::stringstream ss;
57+
if (!json_encode(ss, data)) return false;
58+
output = ss.str();
59+
return true;
60+
}
61+
62+
}
63+
64+
#endif

0 commit comments

Comments
 (0)