Skip to content

refactor:Serialisible concept - #2560

Merged
rprospero merged 25 commits into
develop2from
serialisible_concept
Aug 5, 2026
Merged

refactor:Serialisible concept#2560
rprospero merged 25 commits into
develop2from
serialisible_concept

Conversation

@rprospero

@rprospero rprospero commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

This PR replaces the old Serialisable class with class with a pair of concepts. This provides the following advantages

  1. The concepts can be in different namespaces. Instead of the old, confusing fromVector and toVector methods, we now have Serialisable::vector and Deserialisable::vector methods
  2. The handling of classes and raw types are now unified. Previously, we had many helper functions calling foo.serialise, which would fail if foo was a string or a double. The new serialiseOnto method works equally well with classes and raw types
  3. There's no need to inherit from a Serialisable class to produce toml. This also creates smaller classes, as none of the virtual member function pointers are needed
  4. The serialisation helper functions are now in a separate header (serialiserLibrary.h), which is often only needed in the .cpp file and not the .h file.

The fundamental theory of this library is that the overloaded serialiseOnto function serialises things and deserialiseOnto deserialises things. Making a type serialisable merely involves adding another overload to these two functions. For simplicity, I've added a templated overload that works on any type that had the old serialise and deserialise methods that we used before, so most of the code is largely unchanged.

I have the following changes I would like to perform before I pull this PR out of draft

  • Remove all direct references to toml outside of the serialiser files. This will make it easier to replace the TOML11 library in the future
  • Remove import of serialiserLibrary.h from an header files. This should prevent a larger library from many downstream files
    • src/classes/atom.h
    • src/classes/speciesIntra.h

As a guide for preliminary reviews, the best files to look at will be:

  • src/base/serialiser.h is the new minimal header and establishes the new namespaces and the serialiseOnto and deserialiseOnto method signatures
  • src/base/serialiserLibrary.h contains both the actual serialisation concepts and the old helper functions
  • src/classes/speciesSite.cpp gives a good example on how the new library works (mostly the same with cleaner names).

@rprospero rprospero changed the title Serialisible concept refactor:Serialisible concept Jul 24, 2026
@rprospero
rprospero force-pushed the serialisible_concept branch from 99234da to 608a471 Compare July 24, 2026 13:58

@trisyoungs trisyoungs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Direction is good. Made a couple of comments.

Comment thread src/classes/speciesSites.cpp Outdated
Comment thread src/classes/speciesSites.cpp Outdated
Comment on lines +75 to +92
value["sites"] =
vector(sites_, [](const auto &sites) { return vector(sites, [](const auto isoWeight) { return isoWeight; }); });
target[tag] = value;
}

// Read values from a serialisable value
void SpeciesSites::deserialise(const SerialisedValue &node)
{
using namespace Deserialisable;
clear();

toMap(node, "set",
[&](const std::string &speciesName, const SerialisedValue &sites)
{
auto &set = sites_[speciesName];
toMap(sites, [&](const std::string &siteName, const SerialisedValue &population)
{ set[siteName] = population.as_floating(); });
});
map(node, "set",
[&](const std::string &speciesName, const SerialisedValue &sites)
{
auto &set = sites_[speciesName];
map(sites, [&](const std::string &siteName, const SerialisedValue &population)
{ set[siteName] = population.as_floating(); });
});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still have a problem with clarity here. I think this refactoring illustrates it pretty well - I'm supposedly serialising a vector, but deserialising a map. Admittedly this might be caused from the underlying type of sites_ being ResolvableKeyedVector, but you see my point!

@rprospero rprospero closed this Jul 28, 2026
@rprospero
rprospero force-pushed the serialisible_concept branch from d0a488a to 3c68748 Compare July 28, 2026 13:16
@rprospero rprospero reopened this Jul 28, 2026
@rprospero
rprospero marked this pull request as ready for review July 29, 2026 13:00
@rprospero
rprospero force-pushed the serialisible_concept branch from a84b581 to eee891d Compare July 31, 2026 08:48
@rprospero rprospero mentioned this pull request Jul 31, 2026

@trisyoungs trisyoungs left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking really good I think. Couple of moderate comments to consider.

Comment on lines +17 to +18
template <typename T>
concept SerialisablePointer = requires(T a, std::string tag, SerialisedValue target) { a->serialise(tag, target); };

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually wonder if this is even required now? In Dissolve1 a module had to write out, e.g., a Species* target which it did by just serialising the name. In Dissolve2, I don't think there are any remaining examples of us needing to serialise a pointer value - all of our data is either a concrete member or passed via an Edge (bypassing the need for direct serialisation in the Dissolve1 style). The only possible example might be in the ForcefieldNode, but this could be factored out (if I haven't done so already).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is still required, but usually indirectly, as in that we're usually serialising concrete objects, but some of those objects then contain pointers. As a quick example, the atomTypes_ vector in Species is a vector of shared_ptr.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, fair comment. Didn't consider the atom type pointers.

Comment thread src/base/serialiserLibrary.h Outdated
Comment on lines +23 to +24
// template <typename T>
// concept SerialisableFromInto = requires(T a,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Old WIP code?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. That was part of an attempt to apply the serialisable concept to vectors of serialisable, but you can't recurse concepts like that.

Comment thread src/base/serialiserLibrary.h Outdated
Comment thread src/base/serialiserLibrary.h Outdated
Comment thread src/classes/atom.cpp Outdated
Comment on lines +148 to +151
index_ = Deserialisable::de<int>(node.at("index"));

set(Deserialisable::de<Elements::Element>(node.at("z")), Deserialisable::de<Vector3>(node.at("r")),
Deserialisable::de_or<double>(node, "q", 0));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, so dumb question - I don't see de or de_or obviously declared anywhere? Presumably the de is for deserialise, but I'm not sure I like the brevity in this case as it harms readability.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

de is defined on line 190 of serialiserLibrary.h. I'd originally went with the shorter name hoping that the brevity might improve legibility, becoming essentially invisible against the structure of the data. However, that only worked while we were importing the namespaces. Without that, the benefits of brevity are lost. I'm going to move to deser and deser_or, as a parallel to ser.

@rprospero
rprospero force-pushed the serialisible_concept branch from eee891d to ceb43a0 Compare August 5, 2026 09:56
@rprospero
rprospero merged commit cf51673 into develop2 Aug 5, 2026
6 of 9 checks passed
@rprospero
rprospero deleted the serialisible_concept branch August 5, 2026 11:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants