Skip to content

Commit cf51673

Browse files
authored
refactor:Serialisible concept (#2560)
1 parent 5a133a9 commit cf51673

103 files changed

Lines changed: 987 additions & 873 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/base/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ add_library(
1515
outputHandler.cpp
1616
outputHandler.h
1717
parserLibrary.cpp
18+
serialiser.h
19+
serialiser.cpp
1820
sysFunc.cpp
1921
sysFunc.h
2022
timer.cpp

src/base/geometry.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,39 @@ int Geometry::indices(int i) const { return indices_[i]; }
3434

3535
bool Geometry::operator==(const Geometry &rhs) const { return value_ == rhs.value_ && indices_ == rhs.indices_; }
3636
bool Geometry::operator!=(const Geometry &rhs) const { return !(rhs == *this); }
37+
38+
namespace Serialisable
39+
{
40+
void serialiseOnto(const Geometry::GeometryType &e, std::string tag, SerialisedValue &node)
41+
{
42+
switch (e)
43+
{
44+
case Geometry::GeometryType::AngleType:
45+
node["tag"] = "angle";
46+
case Geometry::GeometryType::DistanceType:
47+
node["tag"] = "distance";
48+
case Geometry::GeometryType::TorsionType:
49+
node["tag"] = "torsion";
50+
default:
51+
throw std::runtime_error("Unhandled geometry type - can't convert to TOML value.\n");
52+
}
53+
}
54+
}; // namespace Serialisable
55+
56+
namespace Deserialisable
57+
{
58+
void deserialiseOnto(Geometry::GeometryType &e, const SerialisedValue &target)
59+
{
60+
auto typeString = target.as_string();
61+
if (typeString == "angle")
62+
e = Geometry::GeometryType::AngleType;
63+
else if (typeString == "distance")
64+
e = Geometry::GeometryType::DistanceType;
65+
else if (typeString == "torsion")
66+
e = Geometry::GeometryType::TorsionType;
67+
else
68+
throw toml::type_error(
69+
std::format("Unhandled geometry type '{}' - can't convert from TOML value.\n", std::string(typeString)),
70+
target.location());
71+
}
72+
} // namespace Deserialisable

src/base/geometry.h

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

44
#pragma once
55

6+
#include "base/serialiser.h"
67
#include <format>
78
#include <map>
8-
#include <toml11/toml.hpp>
99

1010
// Geometry Definition
1111
class Geometry
@@ -42,41 +42,12 @@ class Geometry
4242
};
4343

4444
// TOML Conversion
45-
namespace toml
45+
namespace Serialisable
4646
{
47-
template <> struct from<Geometry::GeometryType>
48-
{
49-
static Geometry::GeometryType from_toml(const toml::value &node)
50-
{
51-
auto typeString = node.as_string();
52-
if (typeString == "angle")
53-
return Geometry::GeometryType::AngleType;
54-
else if (typeString == "distance")
55-
return Geometry::GeometryType::DistanceType;
56-
else if (typeString == "torsion")
57-
return Geometry::GeometryType::TorsionType;
58-
else
59-
throw toml::type_error(
60-
std::format("Unhandled geometry type '{}' - can't convert from TOML value.\n", std::string(typeString)),
61-
node.location());
62-
}
47+
void serialiseOnto(const Geometry::GeometryType &e, std::string tag, SerialisedValue &node);
6348
};
6449

65-
template <> struct into<Geometry::GeometryType>
50+
namespace Deserialisable
6651
{
67-
static toml::basic_value<toml::preserve_comments> into_toml(const Geometry::GeometryType &e)
68-
{
69-
switch (e)
70-
{
71-
case Geometry::GeometryType::AngleType:
72-
return "angle";
73-
case Geometry::GeometryType::DistanceType:
74-
return "distance";
75-
case Geometry::GeometryType::TorsionType:
76-
return "torsion";
77-
default:
78-
throw std::runtime_error("Unhandled geometry type - can't convert to TOML value.\n");
79-
}
80-
}
81-
};
82-
} // namespace toml
52+
void deserialiseOnto(Geometry::GeometryType &e, const SerialisedValue &target);
53+
}

src/base/serialiser.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
// Copyright (c) 2026 Team Dissolve and contributors
3+
4+
#include "base/serialiser.h"
5+
6+
namespace Serialisable
7+
{
8+
void serialiseOnto(int a, std::string tag, SerialisedValue &target) { target[tag] = a; }
9+
void serialiseOnto(double a, std::string tag, SerialisedValue &target) { target[tag] = a; }
10+
void serialiseOnto(std::string a, std::string tag, SerialisedValue &target) { target[tag] = a; }
11+
} // namespace Serialisable
12+
13+
namespace Deserialisable
14+
{
15+
void deserialiseOnto(bool &a, const SerialisedValue &target) { a = target.as_boolean(); }
16+
void deserialiseOnto(int &a, const SerialisedValue &target) { a = target.as_integer(); }
17+
void deserialiseOnto(long &a, const SerialisedValue &target) { a = target.as_integer(); }
18+
void deserialiseOnto(float &a, const SerialisedValue &target) { a = target.as_floating(); }
19+
void deserialiseOnto(double &a, const SerialisedValue &target) { a = target.as_floating(); }
20+
void deserialiseOnto(std::string &a, const SerialisedValue &target) { a = target.as_string(); }
21+
} // namespace Deserialisable

src/base/serialiser.h

Lines changed: 14 additions & 238 deletions
Original file line numberDiff line numberDiff line change
@@ -3,252 +3,28 @@
33

44
#pragma once
55

6-
#include "templates/keyedVector.h"
76
#include "templates/orderedMap.h"
8-
#include "templates/resolvableKeyedVector.h"
9-
#include <map>
107
#include <toml11/toml.hpp>
118
#include <vector>
129

1310
// The type we use for the nodes of our serialisation tree
1411
using SerialisedValue = toml::basic_value<toml::discard_comments, dissolve::OrderedMap, std::vector>;
1512

16-
// We need a way at compile time to detect all the types of smart
17-
// pointers for things that can be serialised
18-
template <typename T>
19-
concept serialisablePointer = requires(T a, std::string tag, SerialisedValue target) { a->serialise(tag, target); };
20-
21-
// An interface for classes that can be serialised into an input file
22-
class Serialisable
13+
namespace Serialisable
2314
{
24-
public:
25-
Serialisable() = default;
26-
virtual ~Serialisable() = default;
27-
// Express as a serialisable value
28-
virtual void serialise(std::string tag, SerialisedValue &target) const = 0;
29-
// Read values from a serialisable value
30-
virtual void deserialise(const SerialisedValue &node) {}
31-
32-
/* Functions that hook into the toml11 library */
33-
// Wrapper for deserialise that toml11 will check for
34-
void from_toml(const toml::value &node) { deserialise(node); }
35-
// Wrapper for serialise that toml11 will check for
36-
SerialisedValue into_toml() const
37-
{
38-
SerialisedValue result;
39-
serialise("inner", result);
40-
return result["inner"];
41-
}
42-
43-
// Perform an action on a child node in a table if the node exists.
44-
// This cuts out quite a bit of boilerplate.
45-
template <typename Lambda> static bool optionalOn(const SerialisedValue &node, std::string name, Lambda action)
46-
{
47-
if (node.contains(name))
48-
{
49-
auto child = toml::find(node, name);
50-
if (!node.is_uninitialized())
51-
action(child);
52-
return true;
53-
}
54-
55-
return false;
56-
}
57-
// Place the named value into the supplied object, but only if it exists
58-
template <typename T, typename U> bool getIfPresent(const SerialisedValue &node, std::string name, U &destination)
59-
{
60-
if (!node.contains(name))
61-
return false;
62-
destination = toml::find<T>(node, name);
63-
return true;
64-
}
65-
// A helper function to add elements of a vector to a node under the named heading
66-
template <serialisablePointer T>
67-
static void fromVectorToTable(const std::vector<T> &vector, std::string name, SerialisedValue &node)
68-
{
69-
fromVectorToTable(vector, name, node, [](const auto &item) { return item->name().data(); });
70-
}
71-
// A helper function to add elements of a vector to a node
72-
template <typename T, typename Lambda>
73-
static SerialisedValue fromVectorToTable(const std::vector<T> &vector, Lambda getName)
74-
{
75-
SerialisedValue group;
76-
for (const auto &value : vector)
77-
value->serialise(getName(value), group);
78-
return group;
79-
};
80-
// A helper function to add elements of a KeyedVector to a node
81-
template <typename KeyClass, typename ValueClass, typename Lambda>
82-
static SerialisedValue fromVectorToTable(const KeyedVector<KeyClass, ValueClass> &keyedVector, Lambda getName)
83-
{
84-
SerialisedValue group;
85-
for (const auto &[key, value] : keyedVector)
86-
group[std::string(getName(key))] = value;
87-
return group;
88-
};
89-
// A helper function to add elements of a ResolvableKeyedVector to a node
90-
template <typename KeyClass, typename ValueClass>
91-
static SerialisedValue fromVectorToTable(const ResolvableKeyedVector<KeyClass, ValueClass> &keyedVector)
92-
{
93-
SerialisedValue group;
94-
for (const auto &[resolvable, value] : keyedVector)
95-
group[std::string(resolvable.name())] = value;
96-
return group;
97-
}
98-
template <typename KeyClass, typename ValueClass, typename Lambda>
99-
static SerialisedValue fromVectorToTable(const ResolvableKeyedVector<KeyClass, ValueClass> &keyedVector, Lambda getInner)
100-
{
101-
SerialisedValue group;
102-
for (const auto &[resolvable, value] : keyedVector)
103-
group[std::string(resolvable.name())] = getInner(value);
104-
return group;
105-
}
106-
// A helper function to add elements of a vector to a node under the named heading
107-
template <typename T, typename Lambda>
108-
static void fromVectorToTable(const std::vector<T> &vector, std::string name, SerialisedValue &node, Lambda getName)
109-
{
110-
if (vector.empty())
111-
return;
112-
node[name] = fromVectorToTable(vector, getName);
113-
};
114-
// A helper function to add elements of a vector to a node. This
115-
// is more generic than fromVectorToTable and the later could be
116-
// be implemented in terms of this function, but the two template
117-
// types conflict with the resolution of other overloads. While
118-
// this could be solved with C++20 Concepts, it's probably better
119-
// to just remove the other overloads. That should be another
120-
// issue before TOML is merged.
121-
template <typename T, typename Lambda, typename Lambda2>
122-
static SerialisedValue fromVectorToMap(const std::vector<T> &vector, Lambda getName, Lambda2 getValue)
123-
{
124-
SerialisedValue group;
125-
for (auto &value : vector)
126-
group[getName(value)] = getValue(value);
127-
return group;
128-
};
129-
// A helper function to add the elements of a vector to a node under a name
130-
template <typename T>
131-
static void fromVector(const std::vector<std::unique_ptr<T>> &vector, std::string name, SerialisedValue &node)
132-
{
133-
fromVector(vector, name, node,
134-
[](const auto &item)
135-
{
136-
SerialisedValue outer;
137-
item->serialise("inner", outer);
138-
return outer["inner"];
139-
});
140-
}
141-
// A helper function to add the elements of a vector to a node under a name
142-
template <typename T>
143-
static void fromVector(const std::vector<std::shared_ptr<T>> &vector, std::string name, SerialisedValue &node)
144-
{
145-
fromVector(vector, name, node, [](const auto &item) { return item->serialise(); });
146-
}
147-
// A helper function to add the elements of a vector to a node under a name
148-
template <typename T> static void fromVector(const std::vector<T> &vector, std::string name, SerialisedValue &node)
149-
{
150-
fromVector(vector, name, node,
151-
[](const auto &item)
152-
{
153-
SerialisedValue outer;
154-
item.serialise("inner", outer);
155-
return outer["inner"];
156-
});
157-
}
158-
// A helper function to add the elements of a vector to a node under a name
159-
template <typename T, typename Lambda>
160-
static void fromVector(const std::vector<T> &vector, std::string name, SerialisedValue &node, Lambda toSerial)
161-
{
162-
if (vector.empty())
163-
return;
164-
node[name] = fromVector(vector, toSerial);
165-
}
166-
// A helper function to add the elements of a vector to a node under a name
167-
template <typename T, typename Lambda> static SerialisedValue fromVector(const std::vector<T> &vector, Lambda toSerial)
168-
{
169-
SerialisedValue result = SerialisedValue::array_type{};
170-
std::transform(vector.begin(), vector.end(), std::back_inserter(result), toSerial);
171-
return result;
172-
}
173-
// A helper function to add the elements of a ranged object to a node under a name
174-
template <std::ranges::input_range Range, typename Lambda>
175-
static SerialisedValue fromRange(const Range &range, Lambda toSerial)
176-
{
177-
SerialisedValue result = SerialisedValue::array_type{};
178-
std::ranges::transform(range, std::back_inserter(result), toSerial);
179-
return result;
180-
}
181-
// A helper function to add the elements of a map to a node under a name
182-
template <typename K, typename V> static void fromMap(const std::map<K, V> &map, std::string name, SerialisedValue &node)
183-
{
184-
SerialisedValue result;
185-
for (auto &[key, value] : map)
186-
if constexpr (serialisablePointer<V>)
187-
value->serialise(std::format("{}", key), result);
188-
else if constexpr (std::is_base_of_v<Serialisable, V>)
189-
value.serialise(std::format("{}", key), result);
190-
else
191-
// We use the direct value (with casting) instead of
192-
// value.serialise() to handle the case where the value
193-
// is a raw type (e.g. int)
194-
result[std::format("{}", key)] = value;
195-
if (!map.empty())
196-
node[name] = result;
197-
}
198-
// A helper function to add the elements of a map to a node under a name
199-
// Only add values that pass the test lambda
200-
template <typename K, typename V, typename Lambda>
201-
static void fromMap(const std::map<K, V> &map, std::string name, SerialisedValue &node, Lambda filter)
202-
{
203-
SerialisedValue result;
204-
bool changed = false;
205-
for (auto &[key, value] : map)
206-
{
207-
if (!filter(key, value))
208-
continue;
209-
changed = true;
210-
if constexpr (serialisablePointer<V>)
211-
value->serialise(std::string(key), result);
212-
else
213-
// We use the direct value (with casting) instead of
214-
// value.serialise() to handle the case where the value
215-
// is a raw type (e.g. int)
216-
result[std::string(key)] = value;
217-
}
218-
if (changed)
219-
node[name] = result;
220-
}
22115

222-
// Act over each value in a node table, if the key exists
223-
template <typename Lambda> static void toMap(const SerialisedValue &node, Lambda action)
224-
{
225-
for (auto &[key, value] : node.as_table())
226-
action(key, value);
227-
}
16+
void serialiseOnto(const int a, std::string tag, SerialisedValue &target);
17+
void serialiseOnto(const double a, std::string tag, SerialisedValue &target);
18+
void serialiseOnto(const std::string a, std::string tag, SerialisedValue &target);
19+
} // namespace Serialisable
22820

229-
// Act over each value in a node table, if the key exists
230-
template <typename Lambda> static void toMap(const SerialisedValue &node, std::string key, Lambda action)
231-
{
232-
if (!node.contains(key))
233-
return;
234-
235-
for (auto &[subKey, value] : toml::find<SerialisedValue::table_type>(node, key))
236-
action(subKey, value);
237-
}
238-
239-
// Act over each value in a node array
240-
template <typename Lambda> static void toVector(const SerialisedValue &node, Lambda action)
241-
{
242-
for (auto &item : node.as_array())
243-
action(item);
244-
}
245-
246-
// Act over each value in a node table, if the key exists
247-
template <typename Lambda> static void toVector(const SerialisedValue &node, std::string key, Lambda action)
248-
{
249-
if (!node.contains(key))
250-
return;
21+
namespace Deserialisable
22+
{
25123

252-
toVector(node.at(key), action);
253-
}
254-
};
24+
void deserialiseOnto(bool &a, const SerialisedValue &target);
25+
void deserialiseOnto(int &a, const SerialisedValue &target);
26+
void deserialiseOnto(long &a, const SerialisedValue &target);
27+
void deserialiseOnto(float &a, const SerialisedValue &target);
28+
void deserialiseOnto(double &a, const SerialisedValue &target);
29+
void deserialiseOnto(std::string &a, const SerialisedValue &target);
30+
} // namespace Deserialisable

0 commit comments

Comments
 (0)