Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a6a3cef
Stop inheriting from Serialisable
rprospero Jul 9, 2026
5c1f9ee
Get atom.h to compile serialisation
rprospero Jul 9, 2026
42152dd
Get a bunch more compile working
rprospero Jul 9, 2026
b792f44
Fix some type issues with SerialisableClass
rprospero Jul 9, 2026
76d5a94
More minor fixes
rprospero Jul 9, 2026
6c253e5
Create serialiser and deserialisers for base types
rprospero Jul 10, 2026
b75f0da
Convert replace toMap and fromMap with just map
rprospero Jul 21, 2026
711de22
Add concept requirements on fromVectorToMap
rprospero Jul 21, 2026
9701f0a
Remove unneeded fromVectorToTable instance
rprospero Jul 21, 2026
7e1d9bb
Move serialiser library into own file
rprospero Jul 23, 2026
6c187e7
Start cleaning last fromVector functions
rprospero Jul 23, 2026
455d3d1
Make serialising a non-serialisable type a runtime error
rprospero Jul 23, 2026
bff3757
Fix de_or bug
rprospero Jul 23, 2026
248be6f
Refactor SerialisableData
rprospero Jul 23, 2026
b2d72fc
Remove serialiserLibrary.h import in atom.h
rprospero Jul 24, 2026
ec39f79
Remove toml::find instances
rprospero Jul 24, 2026
4fabced
Remove SerialisableCast concept
rprospero Jul 27, 2026
c49d785
Remove library import from speciesIntra.h
rprospero Jul 27, 2026
d1476f1
Stop using "using namespace" for serialisation
rprospero Jul 28, 2026
dc7453a
Remove inline declarations
rprospero Jul 29, 2026
65f7a7c
Uniform Serialisable spellings
rprospero Jul 29, 2026
00bb48d
Remove dead code comments
rprospero Aug 3, 2026
c22dae7
Clean up `map` function
rprospero Aug 3, 2026
439efcf
Rename `de` to `deser` for better clarity
rprospero Aug 3, 2026
ceb43a0
Fix up box serialiser
rprospero Aug 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/base/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ add_library(
outputHandler.cpp
outputHandler.h
parserLibrary.cpp
serialiser.h
serialiser.cpp
sysFunc.cpp
sysFunc.h
timer.cpp
Expand Down
36 changes: 36 additions & 0 deletions src/base/geometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,39 @@ int Geometry::indices(int i) const { return indices_[i]; }

bool Geometry::operator==(const Geometry &rhs) const { return value_ == rhs.value_ && indices_ == rhs.indices_; }
bool Geometry::operator!=(const Geometry &rhs) const { return !(rhs == *this); }

namespace Serialisable
{
void serialiseOnto(const Geometry::GeometryType &e, std::string tag, SerialisedValue &node)
{
switch (e)
{
case Geometry::GeometryType::AngleType:
node["tag"] = "angle";
case Geometry::GeometryType::DistanceType:
node["tag"] = "distance";
case Geometry::GeometryType::TorsionType:
node["tag"] = "torsion";
default:
throw std::runtime_error("Unhandled geometry type - can't convert to TOML value.\n");
}
}
}; // namespace Serialisable

namespace Deserialisable
{
void deserialiseOnto(Geometry::GeometryType &e, const SerialisedValue &target)
{
auto typeString = target.as_string();
if (typeString == "angle")
e = Geometry::GeometryType::AngleType;
else if (typeString == "distance")
e = Geometry::GeometryType::DistanceType;
else if (typeString == "torsion")
e = Geometry::GeometryType::TorsionType;
else
throw toml::type_error(
std::format("Unhandled geometry type '{}' - can't convert from TOML value.\n", std::string(typeString)),
target.location());
}
} // namespace Deserialisable
41 changes: 6 additions & 35 deletions src/base/geometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

#pragma once

#include "base/serialiser.h"
#include <format>
#include <map>
#include <toml11/toml.hpp>

// Geometry Definition
class Geometry
Expand Down Expand Up @@ -42,41 +42,12 @@ class Geometry
};

// TOML Conversion
namespace toml
namespace Serialisable
{
template <> struct from<Geometry::GeometryType>
{
static Geometry::GeometryType from_toml(const toml::value &node)
{
auto typeString = node.as_string();
if (typeString == "angle")
return Geometry::GeometryType::AngleType;
else if (typeString == "distance")
return Geometry::GeometryType::DistanceType;
else if (typeString == "torsion")
return Geometry::GeometryType::TorsionType;
else
throw toml::type_error(
std::format("Unhandled geometry type '{}' - can't convert from TOML value.\n", std::string(typeString)),
node.location());
}
void serialiseOnto(const Geometry::GeometryType &e, std::string tag, SerialisedValue &node);
};

template <> struct into<Geometry::GeometryType>
namespace Deserialisable
{
static toml::basic_value<toml::preserve_comments> into_toml(const Geometry::GeometryType &e)
{
switch (e)
{
case Geometry::GeometryType::AngleType:
return "angle";
case Geometry::GeometryType::DistanceType:
return "distance";
case Geometry::GeometryType::TorsionType:
return "torsion";
default:
throw std::runtime_error("Unhandled geometry type - can't convert to TOML value.\n");
}
}
};
} // namespace toml
void deserialiseOnto(Geometry::GeometryType &e, const SerialisedValue &target);
}
21 changes: 21 additions & 0 deletions src/base/serialiser.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026 Team Dissolve and contributors

#include "base/serialiser.h"

namespace Serialisable
{
void serialiseOnto(int a, std::string tag, SerialisedValue &target) { target[tag] = a; }
void serialiseOnto(double a, std::string tag, SerialisedValue &target) { target[tag] = a; }
void serialiseOnto(std::string a, std::string tag, SerialisedValue &target) { target[tag] = a; }
} // namespace Serialisable

namespace Deserialisable
{
void deserialiseOnto(bool &a, const SerialisedValue &target) { a = target.as_boolean(); }
void deserialiseOnto(int &a, const SerialisedValue &target) { a = target.as_integer(); }
void deserialiseOnto(long &a, const SerialisedValue &target) { a = target.as_integer(); }
void deserialiseOnto(float &a, const SerialisedValue &target) { a = target.as_floating(); }
void deserialiseOnto(double &a, const SerialisedValue &target) { a = target.as_floating(); }
void deserialiseOnto(std::string &a, const SerialisedValue &target) { a = target.as_string(); }
} // namespace Deserialisable
252 changes: 14 additions & 238 deletions src/base/serialiser.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,252 +3,28 @@

#pragma once

#include "templates/keyedVector.h"
#include "templates/orderedMap.h"
#include "templates/resolvableKeyedVector.h"
#include <map>
#include <toml11/toml.hpp>
#include <vector>

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

// We need a way at compile time to detect all the types of smart
// pointers for things that can be serialised
template <typename T>
concept serialisablePointer = requires(T a, std::string tag, SerialisedValue target) { a->serialise(tag, target); };

// An interface for classes that can be serialised into an input file
class Serialisable
namespace Serialisable
{
public:
Serialisable() = default;
virtual ~Serialisable() = default;
// Express as a serialisable value
virtual void serialise(std::string tag, SerialisedValue &target) const = 0;
// Read values from a serialisable value
virtual void deserialise(const SerialisedValue &node) {}

/* Functions that hook into the toml11 library */
// Wrapper for deserialise that toml11 will check for
void from_toml(const toml::value &node) { deserialise(node); }
// Wrapper for serialise that toml11 will check for
SerialisedValue into_toml() const
{
SerialisedValue result;
serialise("inner", result);
return result["inner"];
}

// Perform an action on a child node in a table if the node exists.
// This cuts out quite a bit of boilerplate.
template <typename Lambda> static bool optionalOn(const SerialisedValue &node, std::string name, Lambda action)
{
if (node.contains(name))
{
auto child = toml::find(node, name);
if (!node.is_uninitialized())
action(child);
return true;
}

return false;
}
// Place the named value into the supplied object, but only if it exists
template <typename T, typename U> bool getIfPresent(const SerialisedValue &node, std::string name, U &destination)
{
if (!node.contains(name))
return false;
destination = toml::find<T>(node, name);
return true;
}
// A helper function to add elements of a vector to a node under the named heading
template <serialisablePointer T>
static void fromVectorToTable(const std::vector<T> &vector, std::string name, SerialisedValue &node)
{
fromVectorToTable(vector, name, node, [](const auto &item) { return item->name().data(); });
}
// A helper function to add elements of a vector to a node
template <typename T, typename Lambda>
static SerialisedValue fromVectorToTable(const std::vector<T> &vector, Lambda getName)
{
SerialisedValue group;
for (const auto &value : vector)
value->serialise(getName(value), group);
return group;
};
// A helper function to add elements of a KeyedVector to a node
template <typename KeyClass, typename ValueClass, typename Lambda>
static SerialisedValue fromVectorToTable(const KeyedVector<KeyClass, ValueClass> &keyedVector, Lambda getName)
{
SerialisedValue group;
for (const auto &[key, value] : keyedVector)
group[std::string(getName(key))] = value;
return group;
};
// A helper function to add elements of a ResolvableKeyedVector to a node
template <typename KeyClass, typename ValueClass>
static SerialisedValue fromVectorToTable(const ResolvableKeyedVector<KeyClass, ValueClass> &keyedVector)
{
SerialisedValue group;
for (const auto &[resolvable, value] : keyedVector)
group[std::string(resolvable.name())] = value;
return group;
}
template <typename KeyClass, typename ValueClass, typename Lambda>
static SerialisedValue fromVectorToTable(const ResolvableKeyedVector<KeyClass, ValueClass> &keyedVector, Lambda getInner)
{
SerialisedValue group;
for (const auto &[resolvable, value] : keyedVector)
group[std::string(resolvable.name())] = getInner(value);
return group;
}
// A helper function to add elements of a vector to a node under the named heading
template <typename T, typename Lambda>
static void fromVectorToTable(const std::vector<T> &vector, std::string name, SerialisedValue &node, Lambda getName)
{
if (vector.empty())
return;
node[name] = fromVectorToTable(vector, getName);
};
// A helper function to add elements of a vector to a node. This
// is more generic than fromVectorToTable and the later could be
// be implemented in terms of this function, but the two template
// types conflict with the resolution of other overloads. While
// this could be solved with C++20 Concepts, it's probably better
// to just remove the other overloads. That should be another
// issue before TOML is merged.
template <typename T, typename Lambda, typename Lambda2>
static SerialisedValue fromVectorToMap(const std::vector<T> &vector, Lambda getName, Lambda2 getValue)
{
SerialisedValue group;
for (auto &value : vector)
group[getName(value)] = getValue(value);
return group;
};
// A helper function to add the elements of a vector to a node under a name
template <typename T>
static void fromVector(const std::vector<std::unique_ptr<T>> &vector, std::string name, SerialisedValue &node)
{
fromVector(vector, name, node,
[](const auto &item)
{
SerialisedValue outer;
item->serialise("inner", outer);
return outer["inner"];
});
}
// A helper function to add the elements of a vector to a node under a name
template <typename T>
static void fromVector(const std::vector<std::shared_ptr<T>> &vector, std::string name, SerialisedValue &node)
{
fromVector(vector, name, node, [](const auto &item) { return item->serialise(); });
}
// A helper function to add the elements of a vector to a node under a name
template <typename T> static void fromVector(const std::vector<T> &vector, std::string name, SerialisedValue &node)
{
fromVector(vector, name, node,
[](const auto &item)
{
SerialisedValue outer;
item.serialise("inner", outer);
return outer["inner"];
});
}
// A helper function to add the elements of a vector to a node under a name
template <typename T, typename Lambda>
static void fromVector(const std::vector<T> &vector, std::string name, SerialisedValue &node, Lambda toSerial)
{
if (vector.empty())
return;
node[name] = fromVector(vector, toSerial);
}
// A helper function to add the elements of a vector to a node under a name
template <typename T, typename Lambda> static SerialisedValue fromVector(const std::vector<T> &vector, Lambda toSerial)
{
SerialisedValue result = SerialisedValue::array_type{};
std::transform(vector.begin(), vector.end(), std::back_inserter(result), toSerial);
return result;
}
// A helper function to add the elements of a ranged object to a node under a name
template <std::ranges::input_range Range, typename Lambda>
static SerialisedValue fromRange(const Range &range, Lambda toSerial)
{
SerialisedValue result = SerialisedValue::array_type{};
std::ranges::transform(range, std::back_inserter(result), toSerial);
return result;
}
// A helper function to add the elements of a map to a node under a name
template <typename K, typename V> static void fromMap(const std::map<K, V> &map, std::string name, SerialisedValue &node)
{
SerialisedValue result;
for (auto &[key, value] : map)
if constexpr (serialisablePointer<V>)
value->serialise(std::format("{}", key), result);
else if constexpr (std::is_base_of_v<Serialisable, V>)
value.serialise(std::format("{}", key), result);
else
// We use the direct value (with casting) instead of
// value.serialise() to handle the case where the value
// is a raw type (e.g. int)
result[std::format("{}", key)] = value;
if (!map.empty())
node[name] = result;
}
// A helper function to add the elements of a map to a node under a name
// Only add values that pass the test lambda
template <typename K, typename V, typename Lambda>
static void fromMap(const std::map<K, V> &map, std::string name, SerialisedValue &node, Lambda filter)
{
SerialisedValue result;
bool changed = false;
for (auto &[key, value] : map)
{
if (!filter(key, value))
continue;
changed = true;
if constexpr (serialisablePointer<V>)
value->serialise(std::string(key), result);
else
// We use the direct value (with casting) instead of
// value.serialise() to handle the case where the value
// is a raw type (e.g. int)
result[std::string(key)] = value;
}
if (changed)
node[name] = result;
}

// Act over each value in a node table, if the key exists
template <typename Lambda> static void toMap(const SerialisedValue &node, Lambda action)
{
for (auto &[key, value] : node.as_table())
action(key, value);
}
void serialiseOnto(const int a, std::string tag, SerialisedValue &target);
void serialiseOnto(const double a, std::string tag, SerialisedValue &target);
void serialiseOnto(const std::string a, std::string tag, SerialisedValue &target);
} // namespace Serialisable

// Act over each value in a node table, if the key exists
template <typename Lambda> static void toMap(const SerialisedValue &node, std::string key, Lambda action)
{
if (!node.contains(key))
return;

for (auto &[subKey, value] : toml::find<SerialisedValue::table_type>(node, key))
action(subKey, value);
}

// Act over each value in a node array
template <typename Lambda> static void toVector(const SerialisedValue &node, Lambda action)
{
for (auto &item : node.as_array())
action(item);
}

// Act over each value in a node table, if the key exists
template <typename Lambda> static void toVector(const SerialisedValue &node, std::string key, Lambda action)
{
if (!node.contains(key))
return;
namespace Deserialisable
{

toVector(node.at(key), action);
}
};
void deserialiseOnto(bool &a, const SerialisedValue &target);
void deserialiseOnto(int &a, const SerialisedValue &target);
void deserialiseOnto(long &a, const SerialisedValue &target);
void deserialiseOnto(float &a, const SerialisedValue &target);
void deserialiseOnto(double &a, const SerialisedValue &target);
void deserialiseOnto(std::string &a, const SerialisedValue &target);
} // namespace Deserialisable
Loading
Loading