Skip to content

Commit

Permalink
Rework TOML backend (#1729)
Browse files Browse the repository at this point in the history
  • Loading branch information
rprospero authored Nov 30, 2023
1 parent c07b8d2 commit 61dc298
Show file tree
Hide file tree
Showing 15 changed files with 150 additions and 41 deletions.
15 changes: 8 additions & 7 deletions src/base/serialiser.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@

#include <toml11/toml.hpp>

#include "templates/orderedMap.h"
#include <map>
#include <vector>

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

// The associated context for type T This type does double duty.
// First, since the struct has not actual members, it is a Unit type
Expand Down Expand Up @@ -84,8 +85,8 @@ template <typename... Contexts> class Serialisable
static SerialisedValue fromVectorToTable(const std::vector<T> &vector, Lambda getName)
{
SerialisedValue group;
for (auto it = vector.rbegin(); it < vector.rend(); it++)
group[getName(*it)] = (*it)->serialise();
for (const auto &value : vector)
group[getName(value)] = value->serialise();
return group;
};
// A helper function to add elements of a vector to a node under the named heading
Expand All @@ -107,8 +108,8 @@ template <typename... Contexts> class Serialisable
static SerialisedValue fromVectorToMap(const std::vector<T> &vector, Lambda getName, Lambda2 getValue)
{
SerialisedValue group;
for (auto it = vector.rbegin(); it < vector.rend(); it++)
group[getName(*it)] = getValue(*it);
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
Expand Down Expand Up @@ -139,7 +140,7 @@ template <typename... Contexts> class Serialisable
// 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 = toml::array{};
SerialisedValue result = SerialisedValue::array_type{};
std::transform(vector.begin(), vector.end(), std::back_inserter(result), toSerial);
return result;
}
Expand All @@ -155,7 +156,7 @@ template <typename... Contexts> class Serialisable
template <typename Lambda> static void toMap(const SerialisedValue &node, std::string key, Lambda action)
{
if (node.contains(key))
for (auto &[key, value] : toml::find<toml::table>(node, key))
for (auto &[key, value] : toml::find<SerialisedValue::table_type>(node, key))
action(key, value);
}

Expand Down
3 changes: 1 addition & 2 deletions src/classes/atomType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ SerialisedValue AtomType::serialise() const
{
SerialisedValue atomType;

atomType["name"] = name_;
atomType["z"] = Z_;
atomType["charge"] = charge_;
atomType["form"] = ShortRangeFunctions::forms().keyword(interactionPotential_.form());
Expand All @@ -111,7 +110,7 @@ SerialisedValue AtomType::serialise() const
}

// Read values from a serialisable value
void AtomType::deserialise(toml::value node)
void AtomType::deserialise(SerialisedValue node)
{
Z_ = toml::find<Elements::Element>(node, "z");
charge_ = toml::find_or<double>(node, "charge", 0.0);
Expand Down
6 changes: 3 additions & 3 deletions src/classes/box.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,8 @@ Vec3<double> Box::scaleFactors(double requestedVolume, Vec3<bool> scalableAxes)
SerialisedValue Box::serialise() const
{
SerialisedValue box;
box["lengths"] = toml::array{a_, b_, c_};
box["angles"] = toml::array{alpha_, beta_, gamma_};
box["nonPeriodic"] = toml::array{!periodic_.x, !periodic_.y, !periodic_.z};
box["lengths"] = {a_, b_, c_};
box["angles"] = {alpha_, beta_, gamma_};
box["nonPeriodic"] = {!periodic_.x, !periodic_.y, !periodic_.z};
return box;
}
2 changes: 1 addition & 1 deletion src/classes/coreData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@ std::string_view CoreData::inputFilename() const { return inputFilename_; }
// Express as a serialisable value
SerialisedValue CoreData::Masters::serialise() const
{
toml::table table;
SerialisedValue::table_type table;
SerialisedValue node = table;
Serialisable::fromVectorToTable<>(bonds, "bonds", node);
Serialisable::fromVectorToTable<>(angles, "angles", node);
Expand Down
4 changes: 1 addition & 3 deletions src/classes/isotopologue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,14 @@ const std::vector<std::tuple<std::shared_ptr<AtomType>, Sears91::Isotope>> &Isot
// Express as a serialisable value
SerialisedValue Isotopologue::serialise() const
{
SerialisedValue result;
result["name"] = name_;
SerialisedValue::table_type result;
for (auto &&[type, isotope] : isotopes_)
result[type->name().data()] = Sears91::A(isotope);
return result;
}

void Isotopologue::deserialise(const SerialisedValue &node, const CoreData &coreData)
{
name_ = toml::find<std::string>(node, "name");
for (auto &[name, value] : node.as_table())
{
if (value.is_string())
Expand Down
11 changes: 7 additions & 4 deletions src/classes/species.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ SerialisedValue Species::serialise() const
Serialisable::fromVector<>(angles_, "angles", result);
Serialisable::fromVector<>(torsions_, "torsions", result);
Serialisable::fromVector<>(impropers_, "impropers", result);
Serialisable::fromVector<>(isotopologues_, "isotopologues", result);
Serialisable::fromVectorToTable<>(isotopologues_, "isotopologues", result);
Serialisable::fromVectorToTable<>(sites_, "sites", result);

return result;
Expand Down Expand Up @@ -266,9 +266,12 @@ void Species::deserialise(const SerialisedValue &node, CoreData &coreData)
.deserialise(torsion, coreData);
});

Serialisable::toVector(node, "isotopologues",
[this, &coreData](const SerialisedValue &iso)
{ isotopologues_.emplace_back(std::make_unique<Isotopologue>())->deserialise(iso, coreData); });
Serialisable::toMap(node, "isotopologues",
[this, &coreData](const std::string &name, const SerialisedValue &iso)
{
isotopologues_.emplace_back(std::make_unique<Isotopologue>())->setName(name);
isotopologues_.back()->deserialise(iso, coreData);
});

Serialisable::toMap(node, "sites",
[this, &coreData](const std::string &name, const SerialisedValue &site)
Expand Down
2 changes: 1 addition & 1 deletion src/classes/valueStore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const std::list<std::tuple<std::string, std::vector<double>, ValueImportFileForm
// Express as a serialisable value
SerialisedValue ValueStore::serialise() const
{
SerialisedValue result = toml::array{};
SerialisedValue result = SerialisedValue::array_type{};
for (auto &[tag, data, format] : data_)
result.push_back({{"tag", tag}, {"values", data}, {"format", format}});
return result;
Expand Down
2 changes: 1 addition & 1 deletion src/keywords/dataSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ template <class DataType> class DataSourceKeyword : public DataSourceKeywordBase
return fromVector(dataSources_,
[](const auto &item) -> SerialisedValue
{
SerialisedValue result = toml::array{};
SerialisedValue result = SerialisedValue::array_type{};
auto &[dataSourceA, dataSourceB] = item;
result.push_back(dataSourceA.serialise());
// If optional second data source exists
Expand Down
12 changes: 4 additions & 8 deletions src/main/io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ SerialisedValue Dissolve::serialisePairPotentials() const
pairPotentials["forceChargeSource"] = true;
if (atomTypeChargeSource_)
pairPotentials["includeCoulomb"] = true;
Serialisable::fromVector(coreData_.atomTypes(), "atomTypes", pairPotentials);
Serialisable::fromVectorToTable(coreData_.atomTypes(), "atomTypes", pairPotentials);
return pairPotentials;
}

Expand Down Expand Up @@ -173,13 +173,9 @@ void Dissolve::deserialisePairPotentials(const SerialisedValue &node)
PairPotential::shortRangeTruncationScheme_ = PairPotential::shortRangeTruncationSchemes().deserialise(
toml::find_or<std::string>(node, "shortRangeTruncation", "Shifted"));

toVector(node, "atomTypes",
[this](const auto &data) {
coreData()
.atomTypes()
.emplace_back(std::make_unique<AtomType>(toml::find<std::string>(data, "name")))
->deserialise(data);
});
toMap(node, "atomTypes",
[this](const std::string &name, const auto &data)
{ coreData().atomTypes().emplace_back(std::make_unique<AtomType>(name))->deserialise(data); });
}

// Read values from a serialisable value
Expand Down
17 changes: 9 additions & 8 deletions src/module/layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ SerialisedValue ModuleLayer::serialise() const
result["requireEnergyStability"] = true;
if (runControlFlags_.isSet(ModuleLayer::RunControlFlag::SizeFactors))
result["requireSizeFactors"] = true;
Serialisable::fromVector(modules_, "modules", result);
Serialisable::fromVectorToTable(modules_, "modules", result);
return result;
}

Expand All @@ -211,11 +211,12 @@ void ModuleLayer::deserialise(const SerialisedValue &node, const CoreData &coreD
runControlFlags_.setFlag(ModuleLayer::RunControlFlag::EnergyStability);
if (toml::find_or<bool>(node, "requireSizeFactors", false))
runControlFlags_.setFlag(ModuleLayer::RunControlFlag::SizeFactors);
Serialisable::toVector(node, "modules",
[&coreData, this](const SerialisedValue &data)
{
auto *module =
append(*ModuleTypes::moduleType(std::string(toml::find<std::string>(data, "type"), {})), {});
module->deserialise(data, coreData);
});
Serialisable::toMap(node, "modules",
[&coreData, this](const std::string &name, const SerialisedValue &data)
{
auto *module =
append(*ModuleTypes::moduleType(std::string(toml::find<std::string>(data, "type"), {})), {});
module->setName(name);
module->deserialise(data, coreData);
});
}
3 changes: 1 addition & 2 deletions src/module/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ std::vector<Module *> Module::allOfType(std::vector<ModuleTypes::ModuleType> typ
// Express as a serialisable value
SerialisedValue Module::serialise() const
{
SerialisedValue result{{"name", name_}, {"type", ModuleTypes::moduleType(type_)}, {"frequency", frequency_}};
SerialisedValue result{{"type", ModuleTypes::moduleType(type_)}, {"frequency", frequency_}};
if (!enabled_)
result["disabled"] = true;
return keywords_.serialiseOnto(result);
Expand All @@ -262,7 +262,6 @@ SerialisedValue Module::serialise() const
// Read values from a serialisable value
void Module::deserialise(const SerialisedValue &node, const CoreData &data)
{
name_ = toml::find<std::string>(node, "name");
enabled_ = !toml::find_or<bool>(node, "disabled", false);
frequency_ = toml::find<int>(node, "frequency");

Expand Down
81 changes: 81 additions & 0 deletions src/templates/orderedMap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2023 Team Dissolve and contributors

#pragma once

#include <algorithm>
#include <map>
#include <vector>

namespace dissolve
{

// This class is similar to std::map, but the values are returned in
// order of insertion, rather than in alphabetical order (like
// std::map) or random order (like std:unordered_map)
//
// Access is still O(1) and insertion is O(log(N)). Erasure hasn't
// been implemented yet and will likely be O(N), unlike the O(log(N))
// of std::map. However, seeing as removal hasn't even been required
// at this point, it seems unlikely that it will ever serve as a true
// bottleneck.
template <typename Key, typename Value> class OrderedMap
{

private:
// A mapping between keys and indices
std::map<Key, int> index_;
// An association array of the keys and values
std::vector<std::pair<Key, Value>> values_;
// If a key does not exist, add it to the array
std::pair<Key, Value> &insistKey(const Key &key)
{
if (index_.find(key) == index_.end())
{
index_[key] = values_.size();
return values_.emplace_back(key, Value{});
}
else
return values_[index_[key]];
}

public:
// Constructors
OrderedMap(){};
// Constructor from an iterator off a std::map or std::unorderedmap
template <typename Iter> OrderedMap(Iter begin, Iter end)
{
for (auto i = begin; i != end; i++)
(*this)[i->first] = i->second;
};

// Iterator access
typename std::vector<std::pair<Key, Value>>::iterator begin() { return values_.begin(); }
typename std::vector<std::pair<Key, Value>>::iterator end() { return values_.end(); }
typename std::vector<std::pair<Key, Value>>::const_iterator begin() const { return values_.begin(); }
typename std::vector<std::pair<Key, Value>>::const_iterator end() const { return values_.end(); }

// Required std::map interface functions
const Value &at(const Key &key) const { return values_[index_.at(key)].second; }
Value &at(const Key &key) { return values_[index_.at(key)].second; }
// The number of times a key is used. This can only be zero or one.
auto count(const Key &key) const { return index_.count(key); }

// Operator overloads from std::map
bool operator==(const OrderedMap<Key, Value> other) const
{
if (values_.size() != other.values_.size())
return false;
for (int i = 0; i < values_.size(); i++)
if (values_[i] != other.values_[i])
return false;
for (auto [k, v] : index_)
if (other.index_.at(k) != v)
return false;
return true;
}
Value &operator[](const Key &key) { return insistKey(key).second; }
Value &operator[](Key &&key) { return insistKey(key).second; }
};

} // namespace dissolve
2 changes: 1 addition & 1 deletion src/templates/vector3.h
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ template <class T> class Vec3 : public Serialisable<typename SerialisableContext
// Express as a serialisable value
SerialisedValue serialise() const override
{
toml::array result;
SerialisedValue::array_type result;
result.push_back(x);
result.push_back(y);
result.push_back(z);
Expand Down
1 change: 1 addition & 0 deletions tests/algorithms/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
dissolve_add_test(SRC aragorn.cpp)
dissolve_add_test(SRC arrayIteration.cpp)
dissolve_add_test(SRC orderedMap.cpp)
dissolve_add_test(SRC pairIterator.cpp)
dissolve_add_test(SRC array3DIterator.cpp)
dissolve_add_test(SRC sysFunc.cpp)
Expand Down
30 changes: 30 additions & 0 deletions tests/algorithms/orderedMap.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2023 Team Dissolve and contributors

#include "templates/orderedMap.h"
#include <gtest/gtest.h>
#include <toml11/toml.hpp>

namespace UnitTest
{

using SerialisedValue = toml::basic_value<toml::discard_comments, dissolve::OrderedMap, std::vector>;
TEST(OrderedMapTest, BasicOrderedMap)
{
SerialisedValue example;
std::vector<std::string> keys = {"foo", "bar", "quux", "xyzzy"};
std::vector<int> values = {2, 7, 5, 3};
for (auto i = 0; i < keys.size(); i++)
example[keys[i]] = values[i];

auto index = 0;
for (auto [k, v] : example.as_table())
{
EXPECT_EQ(keys[index], k);
EXPECT_EQ(values[index], v.as_integer());
index++;
}
EXPECT_EQ(index, keys.size());
}

} // namespace UnitTest

0 comments on commit 61dc298

Please sign in to comment.