From 34d0e0efbbcc1307cc311326585106d8a9277bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 1 Aug 2025 21:06:24 +0200 Subject: [PATCH 1/2] Replace boost::variant by std::variant --- CMakeLists.txt | 5 - src/core/bond_breakage/actions.hpp | 5 +- src/core/bond_breakage/bond_breakage.cpp | 8 +- .../bonded_interaction_data.cpp | 37 +++---- .../bonded_interaction_data.hpp | 29 ++---- .../bonded_interactions.dox | 13 +-- src/core/cell_system/CellStructure.cpp | 4 +- .../cell_system/ParticleDecomposition.hpp | 5 +- src/core/communication.hpp | 4 +- src/core/energy_inline.hpp | 39 ++++---- src/core/forces.cpp | 2 - src/core/forces_inline.hpp | 41 ++++---- .../immersed_boundary/ImmersedBoundaries.cpp | 13 +-- .../object-in-fluid/oif_global_forces.cpp | 5 +- src/core/pressure_inline.hpp | 15 ++- src/core/rattle.cpp | 3 +- src/core/thermostat.cpp | 4 +- .../EspressoSystemStandAlone_test.cpp | 6 +- src/core/unit_tests/random_test.hpp | 37 ++++--- src/python/espressomd/script_interface.pxd | 2 +- src/script_interface/GlobalContext.cpp | 8 +- src/script_interface/GlobalContext.hpp | 3 +- src/script_interface/ObjectHandle.cpp | 14 +-- src/script_interface/ObjectId.hpp | 70 +++++++++++++ src/script_interface/ObjectState.hpp | 6 +- src/script_interface/Variant.hpp | 97 +++++++++++-------- .../bond_breakage/BreakageSpec.hpp | 3 +- .../cell_system/CellSystem.cpp | 8 +- .../ElectrostaticLayerCorrection.hpp | 14 ++- src/script_interface/get_value.hpp | 56 ++++++----- .../interactions/BondedInteraction.hpp | 4 +- .../interactions/NonBondedInteraction.hpp | 5 +- .../magnetostatics/DipolarLayerCorrection.hpp | 14 ++- src/script_interface/packed_variant.hpp | 79 +++++++-------- src/script_interface/scafacos/scafacos.cpp | 35 +++---- .../tests/Accumulators_test.cpp | 3 +- .../tests/AutoParameter_test.cpp | 28 +++--- .../tests/AutoParameters_test.cpp | 22 ++--- .../tests/GlobalContext_test.cpp | 11 ++- .../tests/LocalContext_test.cpp | 11 ++- .../tests/ObjectHandle_test.cpp | 14 +-- src/script_interface/tests/ObjectMap_test.cpp | 19 ++-- src/script_interface/tests/get_value_test.cpp | 2 +- .../tests/packed_variant_test.cpp | 26 +++-- .../walberla/LBFluidSlice.cpp | 5 +- .../include/utils/serialization/pack.hpp | 6 +- .../include/utils/serialization/variant.hpp | 65 +++++++++++++ src/utils/tests/serialization_test.cpp | 27 ++++++ 48 files changed, 531 insertions(+), 401 deletions(-) create mode 100644 src/script_interface/ObjectId.hpp create mode 100644 src/utils/include/utils/serialization/variant.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ba01178bf3..204fc5b1775 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -805,11 +805,6 @@ endif() find_package(Boost 1.83.0 REQUIRED ${ESPRESSO_BOOST_COMPONENTS}) -# enable boost::variant with more than 20 types -target_compile_options( - espresso_cpp_flags INTERFACE -DBOOST_MPL_CFG_NO_PREPROCESSED_HEADERS - -DBOOST_MPL_LIMIT_LIST_SIZE=30) - # # Paths # diff --git a/src/core/bond_breakage/actions.hpp b/src/core/bond_breakage/actions.hpp index cfdabc9f36e..041509592e1 100644 --- a/src/core/bond_breakage/actions.hpp +++ b/src/core/bond_breakage/actions.hpp @@ -20,7 +20,6 @@ #pragma once #include -#include #include #include @@ -74,7 +73,7 @@ struct DeleteAllBonds { } // namespace BondBreakage // Hash support for std::unordered_set -namespace boost { +namespace std { template <> struct hash { std::size_t operator()(BondBreakage::DeleteBond const &t) const noexcept { return t.hash_value(); @@ -91,4 +90,4 @@ template <> struct hash { return t.hash_value(); } }; -} // namespace boost +} // namespace std diff --git a/src/core/bond_breakage/bond_breakage.cpp b/src/core/bond_breakage/bond_breakage.cpp index 7feb52844df..7fa8d0aeb24 100644 --- a/src/core/bond_breakage/bond_breakage.cpp +++ b/src/core/bond_breakage/bond_breakage.cpp @@ -30,18 +30,18 @@ #include #include -#include #include #include #include #include +#include #include namespace BondBreakage { // Variant holding any of the actions -using Action = boost::variant; +using Action = std::variant; // Set of actions using ActionSet = std::unordered_set; @@ -160,7 +160,7 @@ static void remove_pair_bonds_to(Particle &p, int other_pid) { } // Handler for the different delete events -class execute : public boost::static_visitor<> { +class execute { CellStructure &cell_structure; public: @@ -199,7 +199,7 @@ void BondBreakage::process_queue_impl(System::System &system) { // Execute actions for (auto const &a : actions) { - boost::apply_visitor(execute(cell_structure), a); + std::visit(execute(cell_structure), a); system.on_particle_change(); } } diff --git a/src/core/bonded_interactions/bonded_interaction_data.cpp b/src/core/bonded_interactions/bonded_interaction_data.cpp index 3373e53c6e7..e9f1f1ff7a1 100644 --- a/src/core/bonded_interactions/bonded_interaction_data.cpp +++ b/src/core/bonded_interactions/bonded_interaction_data.cpp @@ -27,31 +27,22 @@ #include "thermalized_bond.hpp" #include "thermostat.hpp" -#include - #include #include #include - -/** Visitor to get the bond cutoff from the bond parameter variant */ -class BondCutoff : public boost::static_visitor { -public: - template double operator()(T const &bond) const { - return bond.cutoff(); - } -}; +#include double BondedInteractionsMap::maximal_cutoff() const { auto const max_cut_bonded = std::accumulate( begin(), end(), BONDED_INACTIVE_CUTOFF, [](auto max_cut, auto const &kv) { - return std::max(max_cut, - boost::apply_visitor(BondCutoff(), *kv.second)); + auto constexpr visitor = [](auto const &bond) { return bond.cutoff(); }; + return std::max(max_cut, std::visit(visitor, *kv.second)); }); /* Check if there are dihedrals */ auto const any_dihedrals = std::ranges::any_of(*this, [](auto const &kv) { - return (boost::get(&(*kv.second)) || - boost::get(&(*kv.second))); + return (std::holds_alternative(*kv.second) or + std::holds_alternative(*kv.second)); }); /* dihedrals: the central particle is indirectly connected to the fourth @@ -65,11 +56,11 @@ void BondedInteractionsMap::on_ia_change() { n_rigid_bonds = 0; #endif for (auto const &bond : std::views::elements<1>(*this)) { - if (boost::get(&(*bond)) != nullptr) { + if (std::holds_alternative(*bond)) { ++n_thermalized_bonds; } #ifdef BOND_CONSTRAINT - if (boost::get(&(*bond)) != nullptr) { + if (std::holds_alternative(*bond)) { ++n_rigid_bonds; } #endif @@ -82,30 +73,30 @@ void BondedInteractionsMap::on_ia_change() { void BondedInteractionsMap::activate_bond(mapped_type const &ptr) { auto &system = get_system(); - if (auto bond = boost::get(ptr.get())) { + if (auto bond = std::get_if(ptr.get())) { bond->set_thermostat_view(system.thermostat); } - if (auto bond = boost::get(ptr.get())) { + if (auto bond = std::get_if(ptr.get())) { system.immersed_boundaries->register_softID(*bond); } - if (auto bond = boost::get(ptr.get())) { + if (auto bond = std::get_if(ptr.get())) { bond->initialize(*system.box_geo, *system.cell_structure); } - if (auto bond = boost::get(ptr.get())) { + if (auto bond = std::get_if(ptr.get())) { bond->initialize(*system.box_geo, *system.cell_structure); } } void BondedInteractionsMap::deactivate_bond(mapped_type const &ptr) { - if (auto bond = boost::get(ptr.get())) { + if (auto bond = std::get_if(ptr.get())) { bond->unset_thermostat_view(); n_thermalized_bonds = -1; } - if (auto bond = boost::get(ptr.get())) { + if (auto bond = std::get_if(ptr.get())) { bond->unset_volumes_view(); } #ifdef BOND_CONSTRAINT - if (boost::get(ptr.get()) != nullptr) { + if (std::get_if(ptr.get())) { n_rigid_bonds = -1; } #endif diff --git a/src/core/bonded_interactions/bonded_interaction_data.hpp b/src/core/bonded_interactions/bonded_interaction_data.hpp index 1bf20278191..a2a969bef71 100644 --- a/src/core/bonded_interactions/bonded_interaction_data.hpp +++ b/src/core/bonded_interactions/bonded_interaction_data.hpp @@ -47,14 +47,13 @@ #include "TabulatedPotential.hpp" #include "system/Leaf.hpp" -#include - #include #include #include #include #include #include +#include #include /* Special cutoff value for a disabled bond. @@ -74,24 +73,16 @@ struct VirtualBond { double cutoff() const { return BONDED_INACTIVE_CUTOFF; } }; -/** Visitor to get the number of bound partners from the bond parameter - * variant. - */ -class BondNumPartners : public boost::static_visitor { -public: - template int operator()(T const &) const { return T::num; } -}; - /** Variant in which to store the parameters of an individual bonded * interaction */ using Bonded_IA_Parameters = - boost::variant; + std::variant; /** * @brief container for bonded interactions. @@ -147,7 +138,7 @@ class BondedInteractionsMap : public System::Leaf { auto size() const { return m_params.size(); } auto get_next_key() const { return next_key; } auto get_zero_based_type(int bond_id) const { - return contains(bond_id) ? at(bond_id)->which() : 0; + return contains(bond_id) ? static_cast(at(bond_id)->index()) : 0; } auto get_n_thermalized_bonds() const { assert(n_thermalized_bonds >= 0); @@ -198,7 +189,7 @@ class BondedInteractionsMap : public System::Leaf { bonds.begin(), bonds.end(), [this, partner_id = p_partner.id()](BondView const &bond) { auto const &bond_ptr = at(bond.bond_id()); - return (boost::get(bond_ptr.get()) != nullptr) and + return std::holds_alternative(*bond_ptr.get()) and (bond.partner_ids()[0] == partner_id); }); } @@ -234,5 +225,5 @@ class BondedInteractionsMap : public System::Leaf { /** @brief Get the number of bonded partners for the specified bond. */ inline int number_of_partners(Bonded_IA_Parameters const &iaparams) { - return boost::apply_visitor(BondNumPartners(), iaparams); + return std::visit([](T const &) { return T::num; }, iaparams); } diff --git a/src/core/bonded_interactions/bonded_interactions.dox b/src/core/bonded_interactions/bonded_interactions.dox index 7883f96be6a..44151471fff 100644 --- a/src/core/bonded_interactions/bonded_interactions.dox +++ b/src/core/bonded_interactions/bonded_interactions.dox @@ -110,15 +110,6 @@ * - Include the header file containing the new bond type. * - Add the new bond type to @ref Bonded_IA_Parameters at the end of the * types list. - * - If by doing this, the length of the list in Bonded_IA_Parameters passes - * over a multiple of 10, you may have to update ESPResSo's top level - * CMakeLists.txt: - * @code - * # enable boost::variant with more than 20 types - * target_compile_options( - * espresso_cpp_flags INTERFACE -DBOOST_MPL_CFG_NO_PREPROCESSED_HEADERS - * -DBOOST_MPL_LIMIT_LIST_SIZE=40) - * @endcode * * In forces_inline.hpp: * - A call to the new bond's force calculation needs to be placed in either * of the functions @ref calc_bond_pair_force(), @ref @@ -128,7 +119,7 @@ * example * @code{.cpp} * // ... - * else if (auto const *iap = boost::get(&iaparams)) { + * else if (auto const *iap = std::get_if(&iaparams)) { * return iap->force(dx); * } * // ... @@ -141,7 +132,7 @@ * example * @code{.cpp} * // ... - * else if (auto const *iap = boost::get(&iaparams)) { + * else if (auto const *iap = std::get_if(&iaparams)) { * return iap->energy(dx); * } * // ... diff --git a/src/core/cell_system/CellStructure.cpp b/src/core/cell_system/CellStructure.cpp index 83888d2fd2c..5b7b67842a9 100644 --- a/src/core/cell_system/CellStructure.cpp +++ b/src/core/cell_system/CellStructure.cpp @@ -40,7 +40,6 @@ #include #include -#include #include #include @@ -53,6 +52,7 @@ #include #include #include +#include #include #ifdef SHARED_MEMORY_PARALLELISM @@ -231,7 +231,7 @@ void CellStructure::resort_particles(bool global_flag) { m_decomposition->resort(global_flag, diff); for (auto d : diff) { - boost::apply_visitor(UpdateParticleIndexVisitor{this}, d); + std::visit(UpdateParticleIndexVisitor{this}, d); } auto const &lebc = get_system().box_geo->lees_edwards_bc(); diff --git a/src/core/cell_system/ParticleDecomposition.hpp b/src/core/cell_system/ParticleDecomposition.hpp index b2f50e253e1..f99b74b0a71 100644 --- a/src/core/cell_system/ParticleDecomposition.hpp +++ b/src/core/cell_system/ParticleDecomposition.hpp @@ -26,10 +26,9 @@ #include -#include - #include #include +#include #include struct RemovedParticle { @@ -43,7 +42,7 @@ struct ModifiedList { /** * @brief Change of Particle Address. */ -using ParticleChange = boost::variant; +using ParticleChange = std::variant; /** * @brief A distributed particle decomposition. diff --git a/src/core/communication.hpp b/src/core/communication.hpp index ef2d0818962..905074ed002 100644 --- a/src/core/communication.hpp +++ b/src/core/communication.hpp @@ -28,9 +28,9 @@ * The asynchronous MPI communication is used during the script * evaluation. Except for the head node that interprets the interface * script, all other nodes wait in @ref mpi_loop() for the head node to - * issue an action using @ref mpi_call(). @ref mpi_loop() immediately + * issue an action using @c MpiCallbacks::call(). @ref mpi_loop() immediately * executes an @c MPI_Bcast and therefore waits for the head node to - * broadcast a command, which is done by @ref mpi_call(). The request + * broadcast a command, which is done by @c MpiCallbacks::call(). The request * consists of a callback function with an arbitrary number of arguments. * * To add new actions (e.g. to implement new interface functionality), do the diff --git a/src/core/energy_inline.hpp b/src/core/energy_inline.hpp index c63399208a3..f18a8b8b314 100644 --- a/src/core/energy_inline.hpp +++ b/src/core/energy_inline.hpp @@ -57,11 +57,10 @@ #include -#include - #include #include #include +#include /** Calculate non-bonded energies between a pair of particles. * @param p1 particle 1. @@ -219,34 +218,34 @@ calc_bonded_energy(Bonded_IA_Parameters const &iaparams, Particle const &p1, if (n_partners == 1) { auto const dx = box_geo.get_mi_vector(p1.pos(), p2->pos()); - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->energy(dx); } - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->energy(dx); } - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->energy(dx); } #ifdef ELECTROSTATICS - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->energy(p1.q() * p2->q(), dx); } - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->energy(p1, *p2, dx, *kernel); } #endif #ifdef BOND_CONSTRAINT - if (boost::get(&iaparams)) { + if (std::get_if(&iaparams)) { return {0.}; } #endif #ifdef TABULATED - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->energy(dx); } #endif - if (boost::get(&iaparams)) { + if (std::get_if(&iaparams)) { return {0.}; } throw BondUnknownTypeError(); @@ -254,21 +253,21 @@ calc_bonded_energy(Bonded_IA_Parameters const &iaparams, Particle const &p1, if (n_partners == 2) { auto const vec1 = box_geo.get_mi_vector(p2->pos(), p1.pos()); auto const vec2 = box_geo.get_mi_vector(p3->pos(), p1.pos()); - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->energy(vec1, vec2); } - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->energy(vec1, vec2); } - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->energy(vec1, vec2); } - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->energy(vec1, vec2); } - if (boost::get(&iaparams)) { + if (std::get_if(&iaparams)) { runtimeWarningMsg() << "Unsupported bond type " + - std::to_string(iaparams.which()) + + std::to_string(iaparams.index()) + " in energy calculation."; return 0.; } @@ -279,15 +278,15 @@ calc_bonded_energy(Bonded_IA_Parameters const &iaparams, Particle const &p1, auto const v12 = box_geo.get_mi_vector(p1.pos(), p2->pos()); auto const v23 = box_geo.get_mi_vector(p3->pos(), p1.pos()); auto const v34 = box_geo.get_mi_vector(p4->pos(), p3->pos()); - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->energy(v12, v23, v34); } - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->energy(v12, v23, v34); } - if (boost::get(&iaparams)) { + if (std::get_if(&iaparams)) { runtimeWarningMsg() << "Unsupported bond type " + - std::to_string(iaparams.which()) + + std::to_string(iaparams.index()) + " in energy calculation."; return 0.; } diff --git a/src/core/forces.cpp b/src/core/forces.cpp index b843e812656..475825f5819 100644 --- a/src/core/forces.cpp +++ b/src/core/forces.cpp @@ -54,8 +54,6 @@ #include #include -#include - #ifdef CALIPER #include #endif diff --git a/src/core/forces_inline.hpp b/src/core/forces_inline.hpp index 8bddbc8bbf2..9dcd726c511 100644 --- a/src/core/forces_inline.hpp +++ b/src/core/forces_inline.hpp @@ -70,11 +70,10 @@ #include -#include - #include #include #include +#include inline ParticleForce calc_central_radial_force(IA_parameters const &ia_params, Utils::Vector3d const &d, @@ -292,34 +291,34 @@ inline std::optional calc_bond_pair_force( Bonded_IA_Parameters const &iaparams, Particle const &p1, Particle const &p2, Utils::Vector3d const &dx, Coulomb::ShortRangeForceKernel::kernel_type const *kernel) { - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->force(dx); } - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->force(dx); } - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->force(dx); } #ifdef ELECTROSTATICS - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->force(p1.q() * p2.q(), dx); } - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->force(dx, *kernel); } #endif #ifdef BOND_CONSTRAINT - if (boost::get(&iaparams)) { + if (std::get_if(&iaparams)) { return Utils::Vector3d{}; } #endif #ifdef TABULATED - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->force(dx); } #endif - if (boost::get(&iaparams)) { + if (std::get_if(&iaparams)) { return Utils::Vector3d{}; } throw BondUnknownTypeError(); @@ -331,7 +330,7 @@ inline bool add_bonded_two_body_force( Coulomb::ShortRangeForceKernel::kernel_type const *kernel) { auto const dx = box_geo.get_mi_vector(p1.pos(), p2.pos()); - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { auto result = iap->forces(p1, p2, dx); if (result) { auto const &forces = result.value(); @@ -363,21 +362,21 @@ calc_bonded_three_body_force(Bonded_IA_Parameters const &iaparams, Particle const &p2, Particle const &p3) { auto const vec1 = box_geo.get_mi_vector(p2.pos(), p1.pos()); auto const vec2 = box_geo.get_mi_vector(p3.pos(), p1.pos()); - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->forces(vec1, vec2); } - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->forces(vec1, vec2); } - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->forces(vec1, vec2); } #ifdef TABULATED - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->forces(vec1, vec2); } #endif - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->calc_forces(vec1, vec2); } throw BondUnknownTypeError(); @@ -387,7 +386,7 @@ inline bool add_bonded_three_body_force(Bonded_IA_Parameters const &iaparams, BoxGeometry const &box_geo, Particle &p1, Particle &p2, Particle &p3) { - if (boost::get(&iaparams)) { + if (std::get_if(&iaparams)) { return false; } auto const result = @@ -410,21 +409,21 @@ calc_bonded_four_body_force(Bonded_IA_Parameters const &iaparams, BoxGeometry const &box_geo, Particle const &p1, Particle const &p2, Particle const &p3, Particle const &p4) { - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->calc_forces(box_geo, p1, p2, p3, p4); } - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->calc_forces(box_geo, p1, p2, p3, p4); } // note: particles in a dihedral bond are ordered as p2-p1-p3-p4 auto const v12 = box_geo.get_mi_vector(p1.pos(), p2.pos()); auto const v23 = box_geo.get_mi_vector(p3.pos(), p1.pos()); auto const v34 = box_geo.get_mi_vector(p4.pos(), p3.pos()); - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->forces(v12, v23, v34); } #ifdef TABULATED - if (auto const *iap = boost::get(&iaparams)) { + if (auto const *iap = std::get_if(&iaparams)) { return iap->forces(v12, v23, v34); } #endif diff --git a/src/core/immersed_boundary/ImmersedBoundaries.cpp b/src/core/immersed_boundary/ImmersedBoundaries.cpp index 0bc8f7b06b5..7f3576ac0c0 100644 --- a/src/core/immersed_boundary/ImmersedBoundaries.cpp +++ b/src/core/immersed_boundary/ImmersedBoundaries.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include /** Calculate volumes, volume force and add it to each virtual particle. */ @@ -55,7 +56,7 @@ void ImmersedBoundaries::init_volume_conservation(CellStructure &cs) { if (not BoundariesFound) { BoundariesFound = std::ranges::any_of( std::views::elements<1>(bonded_ias), [](auto const &handle) { - return boost::get(handle.get()) != nullptr; + return std::holds_alternative(*handle); }); } @@ -66,7 +67,7 @@ void ImmersedBoundaries::init_volume_conservation(CellStructure &cs) { // Loop through all bonded interactions and check if we need to set the // reference volume for (auto &handle : std::views::elements<1>(bonded_ias)) { - if (auto *v = boost::get(handle.get())) { + if (auto *v = std::get_if(handle.get())) { // This check is important because InitVolumeConservation may be called // accidentally during the integration. Then we must not reset the // reference @@ -85,11 +86,11 @@ static IBMVolCons const * vol_cons_parameters(BondedInteractionsMap const &bonded_ias, Particle const &p1) { auto const it = boost::find_if(p1.bonds(), [&](auto const &bond) -> bool { - return boost::get(bonded_ias.at(bond.bond_id()).get()); + return std::holds_alternative(*bonded_ias.at(bond.bond_id())); }); return (it != p1.bonds().end()) - ? boost::get(bonded_ias.at(it->bond_id()).get()) + ? std::get_if(bonded_ias.at(it->bond_id()).get()) : nullptr; } @@ -113,7 +114,7 @@ void ImmersedBoundaries::calc_volumes(CellStructure &cs) { auto const vol_cons_params = vol_cons_parameters(bonded_ias, p1); if (vol_cons_params && - boost::get(bonded_ias.at(bond_id).get()) != nullptr) { + std::holds_alternative(*bonded_ias.at(bond_id).get())) { // Our particle is the leading particle of a triel // Get second and third particle of the triangle Particle &p2 = *partners[0]; @@ -165,7 +166,7 @@ void ImmersedBoundaries::calc_volume_force(CellStructure &cs) { cs.bond_loop([this, &box_geo, &bonded_ias](Particle &p1, int bond_id, std::span partners) { - if (boost::get(bonded_ias.at(bond_id).get()) != nullptr) { + if (std::holds_alternative(*bonded_ias.at(bond_id).get())) { // Check if particle has an IBM Triel bonded interaction and an // IBM VolCons bonded interaction. Basically this loops over all // triangles, not all particles. First round to check for volume diff --git a/src/core/object-in-fluid/oif_global_forces.cpp b/src/core/object-in-fluid/oif_global_forces.cpp index 6ac1465b622..74d1fa45473 100644 --- a/src/core/object-in-fluid/oif_global_forces.cpp +++ b/src/core/object-in-fluid/oif_global_forces.cpp @@ -37,6 +37,7 @@ #include #include #include +#include /** Calculate the mesh volume and area. */ static auto calc_oif_mesh(int molType, BoxGeometry const &box_geo, @@ -51,7 +52,7 @@ static auto calc_oif_mesh(int molType, BoxGeometry const &box_geo, if (p1.mol_id() != molType) return false; - if (boost::get(bonded_ias.at(bond_id).get())) { + if (std::holds_alternative(*bonded_ias.at(bond_id))) { auto const p11 = box_geo.unfolded_position(p1.pos(), p1.image_box()); auto const p22 = p11 + box_geo.get_mi_vector(partners[0]->pos(), p11); auto const p33 = p11 + box_geo.get_mi_vector(partners[1]->pos(), p11); @@ -82,7 +83,7 @@ static void add_oif_global_forces(double area, double volume, int molType, return false; auto const *bond_ptr = bonded_ias.at(bond_id).get(); - if (auto const *bond = boost::get(bond_ptr)) { + if (auto const *bond = std::get_if(bond_ptr)) { auto const p11 = box_geo.unfolded_position(p1.pos(), p1.image_box()); auto const p22 = p11 + box_geo.get_mi_vector(partners[0]->pos(), p11); auto const p33 = p11 + box_geo.get_mi_vector(partners[1]->pos(), p11); diff --git a/src/core/pressure_inline.hpp b/src/core/pressure_inline.hpp index 3368d562bc3..a038f50de97 100644 --- a/src/core/pressure_inline.hpp +++ b/src/core/pressure_inline.hpp @@ -37,13 +37,12 @@ #include #include -#include - #include #include #include #include #include +#include /** Calculate non-bonded energies between a pair of particles. * @param p1 pointer to particle 1. @@ -119,12 +118,12 @@ calc_bonded_three_body_pressure_tensor(Bonded_IA_Parameters const &iaparams, Particle const &p1, Particle const &p2, Particle const &p3, BoxGeometry const &box_geo) { - if ((boost::get(&iaparams) != nullptr) || - (boost::get(&iaparams) != nullptr) || + if (std::holds_alternative(iaparams) or + std::holds_alternative(iaparams) or #ifdef TABULATED - (boost::get(&iaparams) != nullptr) || + std::holds_alternative(iaparams) or #endif - (boost::get(&iaparams) != nullptr)) { + std::holds_alternative(iaparams)) { auto const dx21 = -box_geo.get_mi_vector(p1.pos(), p2.pos()); auto const dx31 = box_geo.get_mi_vector(p3.pos(), p1.pos()); @@ -139,7 +138,7 @@ calc_bonded_three_body_pressure_tensor(Bonded_IA_Parameters const &iaparams, } } else { runtimeWarningMsg() << "Unsupported bond type " + - std::to_string(iaparams.which()) + + std::to_string(iaparams.index()) + " in pressure calculation."; return Utils::Matrix{}; } @@ -160,7 +159,7 @@ inline std::optional> calc_bonded_pressure_tensor( *partners[1], box_geo); default: runtimeWarningMsg() << "Unsupported bond type " + - std::to_string(iaparams.which()) + + std::to_string(iaparams.index()) + " in pressure calculation."; return Utils::Matrix{}; } diff --git a/src/core/rattle.cpp b/src/core/rattle.cpp index b7e23516b4a..53cffbf5c04 100644 --- a/src/core/rattle.cpp +++ b/src/core/rattle.cpp @@ -38,6 +38,7 @@ #include #include #include +#include static void check_convergence(int cnt, char const *const name) { static constexpr char const *const msg = " failed to converge after "; @@ -125,7 +126,7 @@ static bool compute_correction_vector(CellStructure &cs, Particle &p1, int bond_id, std::span partners) { auto const &iaparams = *bonded_ias.at(bond_id); - if (auto const *bond = boost::get(&iaparams)) { + if (auto const *bond = std::get_if(&iaparams)) { auto const corrected = kernel(*bond, box_geo, p1, *partners[0]); if (corrected) correction = true; diff --git a/src/core/thermostat.cpp b/src/core/thermostat.cpp index 7499c2d0cf8..7ebcd3512fd 100644 --- a/src/core/thermostat.cpp +++ b/src/core/thermostat.cpp @@ -33,7 +33,7 @@ #include "system/System.hpp" #include "thermostat.hpp" -#include +#include #include @@ -103,7 +103,7 @@ void Thermostat::Thermostat::lb_coupling_deactivate() { void ThermalizedBondThermostat::recalc_prefactors( double time_step, BondedInteractionsMap &bonded_ias) { for (auto &handle : std::views::elements<1>(bonded_ias)) { - if (auto *bond = boost::get(&(*handle))) { + if (auto *bond = std::get_if(handle.get())) { bond->recalc_prefactors(time_step); } } diff --git a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp index e09bfe7f737..8fa1e605b10 100644 --- a/src/core/unit_tests/EspressoSystemStandAlone_test.cpp +++ b/src/core/unit_tests/EspressoSystemStandAlone_test.cpp @@ -68,7 +68,6 @@ namespace utf = boost::unit_test; #include #include -#include #include #include @@ -82,6 +81,7 @@ namespace utf = boost::unit_test; #include #include #include +#include #include namespace espresso { @@ -440,9 +440,9 @@ BOOST_FIXTURE_TEST_CASE(espresso_system_stand_alone, ParticleFactory) { system.bonded_ias->insert(fene_bond_id, bond_ia); } auto const &harm_bond = - *boost::get(system.bonded_ias->at(harm_bond_id).get()); + std::get(*system.bonded_ias->at(harm_bond_id)); auto const &fene_bond = - *boost::get(system.bonded_ias->at(fene_bond_id).get()); + std::get(*system.bonded_ias->at(fene_bond_id)); insert_particle_bond(pid2, harm_bond_id, {pid1}); insert_particle_bond(pid2, fene_bond_id, {pid3}); diff --git a/src/core/unit_tests/random_test.hpp b/src/core/unit_tests/random_test.hpp index 9c20202205d..db7165e935d 100644 --- a/src/core/unit_tests/random_test.hpp +++ b/src/core/unit_tests/random_test.hpp @@ -21,15 +21,12 @@ #include -/* Helper functions to compute random numbers covariance in a single pass */ - #include #include #include #include #include -#include #include #include @@ -40,51 +37,51 @@ #include #include #include +#include #include namespace Utils { using VariantVectorXd = - boost::variant>; + std::variant>; } // namespace Utils using Utils::VariantVectorXd; namespace { -using Utils::Vector; - -class visitor_size : public boost::static_visitor { -public: +struct visitor_get_size { template - std::size_t operator()(Vector const &v) const { + std::size_t operator()(Utils::Vector const &v) const { return v.size(); } std::size_t operator()(Utils::Quaternion const &) const { return 4u; } std::size_t operator()(double) const { return 1u; } }; -class visitor_get : public boost::static_visitor { -public: +struct visitor_get_at { + std::size_t m_i; + template - double operator()(Vector const &v, std::size_t i) const { - return v[i]; + double operator()(Utils::Vector const &v) const { + assert(m_i < N); + return v[m_i]; } - double operator()(Utils::Quaternion const &q, std::size_t i) const { - return q[i]; + double operator()(Utils::Quaternion const &q) const { + assert(m_i < 4u); + return q[m_i]; } - double operator()(double v, std::size_t i) const { - assert(i == 0u); + double operator()(double v) const { + assert(m_i == 0u); return v; } }; std::size_t get_size(VariantVectorXd const &vec) { - return boost::apply_visitor(visitor_size(), vec); + return std::visit(visitor_get_size(), vec); } double get_value(VariantVectorXd const &vec, std::size_t i) { - return boost::apply_visitor(visitor_get(), vec, - boost::variant(i)); + return std::visit(visitor_get_at(i), vec); } template auto square_matrix(std::size_t N) { diff --git a/src/python/espressomd/script_interface.pxd b/src/python/espressomd/script_interface.pxd index 936c5737ef3..e5d6f13bff1 100644 --- a/src/python/espressomd/script_interface.pxd +++ b/src/python/espressomd/script_interface.pxd @@ -73,7 +73,7 @@ cdef extern from "script_interface/initialize.hpp" namespace "ScriptInterface": void initialize(Factory[ObjectHandle] *) cdef extern from "script_interface/get_value.hpp" namespace "ScriptInterface": - T get_value[T](const Variant T) except + + T get_value[T](const Variant &) except + cdef extern from "script_interface/code_info/CodeInfo.hpp" namespace "ScriptInterface::CodeInfo": void check_features(const vector[string] & features) except + diff --git a/src/script_interface/GlobalContext.cpp b/src/script_interface/GlobalContext.cpp index 1c327846e5d..cb4219a3167 100644 --- a/src/script_interface/GlobalContext.cpp +++ b/src/script_interface/GlobalContext.cpp @@ -70,7 +70,7 @@ void GlobalContext::set_parameter(ObjectId id, std::string const &name, void GlobalContext::notify_set_parameter(const ObjectHandle *o, std::string const &name, Variant const &value) { - cb_set_parameter(object_id(o), name, pack(value)); + cb_set_parameter(ObjectId(o), name, pack(value)); } void GlobalContext::call_method(ObjectId id, std::string const &name, @@ -85,7 +85,7 @@ void GlobalContext::call_method(ObjectId id, std::string const &name, void GlobalContext::notify_call_method(const ObjectHandle *o, std::string const &name, VariantMap const &arguments) { - cb_call_method(object_id(o), name, pack(arguments)); + cb_call_method(ObjectId(o), name, pack(arguments)); } std::shared_ptr @@ -95,7 +95,7 @@ GlobalContext::make_shared(std::string const &name, auto sp = m_node_local_context->factory().make(name); set_context(sp.get()); - auto const id = object_id(sp.get()); + auto const id = ObjectId(sp.get()); remote_make_handle(id, name, parameters); sp->construct(parameters); @@ -108,7 +108,7 @@ GlobalContext::make_shared(std::string const &name, * required * to have synchronous destructors, which is needed by some client * code. */ - global_context->cb_delete_handle(object_id(o)); + global_context->cb_delete_handle(ObjectId(o)); /* Locally destroy the object. */ deleter(o); diff --git a/src/script_interface/GlobalContext.hpp b/src/script_interface/GlobalContext.hpp index 66464632ba8..24f21d3b364 100644 --- a/src/script_interface/GlobalContext.hpp +++ b/src/script_interface/GlobalContext.hpp @@ -30,6 +30,7 @@ #include "Context.hpp" #include "LocalContext.hpp" #include "ObjectHandle.hpp" +#include "ObjectId.hpp" #include "ParallelExceptionHandler.hpp" #include "packed_variant.hpp" @@ -64,7 +65,7 @@ namespace ScriptInterface { * the remote copies are also destroyed. */ class GlobalContext : public Context { - using ObjectId = std::size_t; + using ObjectId = ScriptInterface::ObjectId; /* Instances on this node that are managed by the * head node. */ diff --git a/src/script_interface/ObjectHandle.cpp b/src/script_interface/ObjectHandle.cpp index fe9e6d137a7..ec5e2e76cb6 100644 --- a/src/script_interface/ObjectHandle.cpp +++ b/src/script_interface/ObjectHandle.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -37,6 +38,7 @@ #include #include #include +#include namespace ScriptInterface { void ObjectHandle::set_parameter(const std::string &name, @@ -67,18 +69,18 @@ std::string ObjectHandle::serialize() const { auto const params = serialize_parameters(); state.params.reserve(params.size()); - PackVisitor visit; + PackVisitor visitor; /* Pack parameters and keep track of ObjectRef parameters */ std::ranges::transform(params, std::back_inserter(state.params), - [&visit](auto const &kv) -> PackedMap::value_type { + [&visitor](auto const &kv) -> PackedMap::value_type { auto const &[name, value] = kv; - return {name, boost::apply_visitor(visit, value)}; + return {name, std::visit(visitor, value)}; }); /* Packed Object parameters */ - state.objects.reserve(visit.objects().size()); - std::ranges::transform(visit.objects(), std::back_inserter(state.objects), + state.objects.reserve(visitor.objects().size()); + std::ranges::transform(visitor.objects(), std::back_inserter(state.objects), [](auto const &kv) { auto const &[name, obj] = kv; return std::make_pair(name, obj->serialize()); @@ -103,7 +105,7 @@ ObjectRef ObjectHandle::deserialize(const std::string &packed_state, VariantMap params; for (auto const &[name, variant] : state.params) { - params[name] = boost::apply_visitor(UnpackVisitor(objects), variant); + params[name] = std::visit(UnpackVisitor(objects), variant); } auto o = ctx.make_shared(state.name, params); diff --git a/src/script_interface/ObjectId.hpp b/src/script_interface/ObjectId.hpp new file mode 100644 index 00000000000..6720278cff7 --- /dev/null +++ b/src/script_interface/ObjectId.hpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2025 The ESPResSo project + * + * This file is part of ESPResSo. + * + * ESPResSo is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ESPResSo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "ObjectHandle.hpp" + +#include + +#include +#include + +namespace ScriptInterface { + +/** + * @brief Strongly typed integer type to hold a unique identifier + * for a @ref ObjectHandle object, using its memory address. + * Therefore, this object can only be constructed from a + * @ref ObjectHandle object on the head node. + */ +struct ObjectId { +#ifdef UINTPTR_MAX + using value_type = std::uintptr_t; +#else + using value_type = std::size_t; + static_assert(sizeof(void *) <= sizeof(value_type)); +#endif + + ObjectId() = default; + ObjectId(ObjectHandle const *p) : m_id{reinterpret_cast(p)} {} + + constexpr bool operator==(ObjectId const &) const = default; + constexpr bool operator!=(ObjectId const &) const = default; + + value_type m_id; + +private: + friend class boost::serialization::access; + + template + void serialize(Archive &ar, unsigned const /*version*/) { + ar & m_id; + } +}; + +} // namespace ScriptInterface + +namespace std { +template <> struct hash { + std::size_t operator()(ScriptInterface::ObjectId const &oid) const noexcept { + return std::hash{}(oid.m_id); + } +}; +} // namespace std diff --git a/src/script_interface/ObjectState.hpp b/src/script_interface/ObjectState.hpp index 0bf5e81ad9c..1579e9d8994 100644 --- a/src/script_interface/ObjectState.hpp +++ b/src/script_interface/ObjectState.hpp @@ -21,10 +21,12 @@ #include "packed_variant.hpp" +#include +#include + +#include #include -#include #include -#include #include #include diff --git a/src/script_interface/Variant.hpp b/src/script_interface/Variant.hpp index 5af43ee2761..0ac35b6533e 100644 --- a/src/script_interface/Variant.hpp +++ b/src/script_interface/Variant.hpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2022 The ESPResSo project + * Copyright (C) 2010-2025 The ESPResSo project * * This file is part of ESPResSo. * @@ -24,12 +24,12 @@ #include #include #include +#include +#include #include #include -#include #include -#include #include #include @@ -37,6 +37,7 @@ #include #include #include +#include #include namespace Utils { @@ -44,8 +45,56 @@ using Vector3b = Utils::Vector; } namespace ScriptInterface { + +template struct recursive_variant; + +namespace detail { +// CRTP helper class; declare base class of @ref recursive_variant here, +// such that `using BaseClass = detail::recursive_variant_base` +// can be used in the derived class to avoid writing the entire typelist again +template +using recursive_variant_add_containers = + std::variant>, + std::unordered_map>, + std::unordered_map>>; +} // namespace detail + +template +struct recursive_variant : detail::recursive_variant_add_containers { + using BaseClass = detail::recursive_variant_add_containers; + using BaseClass::BaseClass; + +private: + friend class boost::serialization::access; + + template + void serialize(Archive &ar, unsigned const /*version*/) { + BaseClass &self = *this; + ar & self; + } +}; + +/** + * @brief Helper typedef to generate consistent variant types. + * + * This is a custom recursive variant type designed specifically for the script + * interface. It features all basic types required to interface with the core. + * Recursive types are added by @c detail::recursive_variant_add_containers. + * The template parameter @c ObjectType is used to ensure instantiations like + * @ref Variant and @ref PackedVariant hold the same types in the same order. + * + * @tparam ObjectType Type of the script interface object handle or reference. + */ +template +using make_recursive_variant = + recursive_variant, std::vector>; + class ObjectHandle; using ObjectRef = std::shared_ptr; + /** * @brief None-"literal". */ @@ -53,20 +102,8 @@ constexpr const None none{}; /** * @brief Possible types for parameters. - * - * The visitors and packing functions need to be adapted accordingly when - * extending this variant with new types. For the exact details, see commit - * b48ab62. - * The number of types is limited by macro @c BOOST_MPL_LIMIT_LIST_SIZE - * (defaults to 20). */ -using Variant = boost::make_recursive_variant< - None, bool, int, std::size_t, double, std::string, ObjectRef, - Utils::Vector3b, Utils::Vector3i, Utils::Vector2d, Utils::Vector3d, - Utils::Vector4d, std::vector, std::vector, - std::vector, std::filesystem::path, - std::unordered_map, - std::unordered_map>::type; +using Variant = make_recursive_variant; using VariantMap = std::unordered_map; @@ -74,35 +111,19 @@ using VariantMap = std::unordered_map; * @brief Make a Variant from argument. * * This is a convenience function, so that rather involved constructors from - * boost::variant are not needed in the script interfaces. + * @ref Variant are not needed in the script interface. */ template Variant make_variant(const T &x) { return Variant(x); } template auto make_unordered_map_of_variants(std::unordered_map const &v) { - std::unordered_map ret; - for (auto const &[key, value] : v) { - ret.emplace(key, value); - } - return ret; + return std::unordered_map{v.begin(), v.end()}; } template auto make_vector_of_variants(std::vector const &v) { - std::vector ret; - for (auto const &item : v) { - ret.emplace_back(item); - } - return ret; + return std::vector{v.begin(), v.end()}; } -namespace detail { -template struct is_type_visitor : boost::static_visitor { - template constexpr bool operator()(const U &) const { - return std::is_same_v; - } -}; -} // namespace detail - /** * @brief Check is a Variant holds a specific type. * @@ -110,9 +131,9 @@ template struct is_type_visitor : boost::static_visitor { * @param v Variant to check in * @return true, if v holds a T. */ -template bool is_type(Variant const &v) { - return boost::apply_visitor(detail::is_type_visitor(), v); +template constexpr bool is_type(Variant const &v) { + return std::holds_alternative(v); } -inline bool is_none(Variant const &v) { return is_type(v); } +constexpr bool is_none(Variant const &v) { return is_type(v); } } // namespace ScriptInterface diff --git a/src/script_interface/bond_breakage/BreakageSpec.hpp b/src/script_interface/bond_breakage/BreakageSpec.hpp index 07e17db3743..fdd7cc40c74 100644 --- a/src/script_interface/bond_breakage/BreakageSpec.hpp +++ b/src/script_interface/bond_breakage/BreakageSpec.hpp @@ -24,6 +24,7 @@ #include "script_interface/ScriptInterface.hpp" #include +#include namespace ScriptInterface { namespace BondBreakage { @@ -37,7 +38,7 @@ class BreakageSpec : public AutoParameters { {"action_type", [this](const Variant &v) { m_breakage_spec->action_type = ::BondBreakage::ActionType{ - m_breakage_str_to_enum.at(boost::get(v))}; + m_breakage_str_to_enum.at(std::get(v))}; }, [this]() { return Variant( diff --git a/src/script_interface/cell_system/CellSystem.cpp b/src/script_interface/cell_system/CellSystem.cpp index a95427e01d9..ea1bcada22b 100644 --- a/src/script_interface/cell_system/CellSystem.cpp +++ b/src/script_interface/cell_system/CellSystem.cpp @@ -39,7 +39,6 @@ #include #include -#include #include #include @@ -51,6 +50,7 @@ #include #include #include +#include #include static int coord(std::string const &s) { @@ -205,7 +205,7 @@ Variant CellSystem::do_call_method(std::string const &name, system.on_observable_calc(); std::vector> pair_list; auto const distance = get_value(params, "distance"); - if (boost::get(¶ms.at("types")) != nullptr) { + if (std::get_if(¶ms.at("types"))) { auto const key = get_value(params, "types"); if (key != "all") { throw std::invalid_argument("Unknown argument types='" + key + "'"); @@ -304,8 +304,8 @@ void CellSystem::initialize(CellStructureType const &cs_type, auto const variant = get_value(params, "fully_connected_boundary"); context()->parallel_try_catch([&fcb_pair, &variant]() { - fcb_pair = {{coord(boost::get(variant.at("boundary"))), - coord(boost::get(variant.at("direction")))}}; + fcb_pair = {{coord(std::get(variant.at("boundary"))), + coord(std::get(variant.at("direction")))}}; }); } context()->parallel_try_catch([this, &fcb_pair]() { diff --git a/src/script_interface/electrostatics/ElectrostaticLayerCorrection.hpp b/src/script_interface/electrostatics/ElectrostaticLayerCorrection.hpp index 02fc52ed4b8..031fe8a8357 100644 --- a/src/script_interface/electrostatics/ElectrostaticLayerCorrection.hpp +++ b/src/script_interface/electrostatics/ElectrostaticLayerCorrection.hpp @@ -31,10 +31,9 @@ #include "script_interface/get_value.hpp" -#include "boost/variant.hpp" - #include #include +#include namespace ScriptInterface { namespace Coulomb { @@ -43,7 +42,7 @@ class ElectrostaticLayerCorrection : public Actor { - using BaseSolver = boost::variant< + using BaseSolver = std::variant< #ifdef CUDA std::shared_ptr>, #endif // CUDA @@ -51,9 +50,8 @@ class ElectrostaticLayerCorrection BaseSolver m_solver; void on_bind_system(::System::System &system) override { - boost::apply_visitor( - [this](auto &solver) { solver->bind_system(m_system.lock()); }, - m_solver); + std::visit([this](auto &solver) { solver->bind_system(m_system.lock()); }, + m_solver); } public: @@ -77,8 +75,8 @@ class ElectrostaticLayerCorrection [this]() { return actor()->elc.pot_diff; }}, {"actor", AutoParameter::read_only, [this]() { - return boost::apply_visitor( - [](auto &solver) { return Variant{solver}; }, m_solver); + return std::visit([](auto &solver) { return Variant{solver}; }, + m_solver); }}, }); } diff --git a/src/script_interface/get_value.hpp b/src/script_interface/get_value.hpp index 6bf31ec9575..f6779938ce4 100644 --- a/src/script_interface/get_value.hpp +++ b/src/script_interface/get_value.hpp @@ -36,6 +36,7 @@ #include #include #include +#include #include namespace ScriptInterface { @@ -122,7 +123,7 @@ auto simplify_symbol(std::unordered_map const *map) { return "std::unordered_map<" + name_key + ", " + value_type_name + ">"; } -struct simplify_symbol_visitor : boost::static_visitor { +struct simplify_symbol_visitor { template std::string operator()(T const &t) const { return simplify_symbol(&t); } @@ -130,7 +131,7 @@ struct simplify_symbol_visitor : boost::static_visitor { /** @brief Simplify the demangled symbol of an object wrapped in a variant. */ inline std::string simplify_symbol_variant(Variant const &v) { - return boost::apply_visitor(simplify_symbol_visitor(), v); + return std::visit(simplify_symbol_visitor(), v); } /** @brief Simplify the demangled symbol of a container @c value_type. */ @@ -152,7 +153,7 @@ auto simplify_symbol_containee(std::unordered_map const *) { return name_key + "' or '" + name_val; } -struct simplify_symbol_containee_visitor : boost::static_visitor { +struct simplify_symbol_containee_visitor { template std::string operator()(const T &) const { return simplify_symbol_containee(static_cast(nullptr)); } @@ -163,7 +164,7 @@ struct simplify_symbol_containee_visitor : boost::static_visitor { * in a variant. */ inline auto simplify_symbol_containee_variant(Variant const &v) { - return boost::apply_visitor(simplify_symbol_containee_visitor(), v); + return std::visit(simplify_symbol_containee_visitor(), v); } } // namespace demangle @@ -181,12 +182,12 @@ using allow_conversion = std::is_floating_point_v && std::is_arithmetic_v)>; -template struct conversion_visitor : boost::static_visitor { +template struct conversion_visitor { template To operator()(const From &value) const { if constexpr (allow_conversion::value) { return To(value); } - throw boost::bad_get{}; + throw std::bad_variant_access{}; } }; @@ -198,15 +199,14 @@ template struct conversion_visitor : boost::static_visitor { */ template struct get_value_helper { T operator()(Variant const &v) const { - return boost::apply_visitor(detail::conversion_visitor(), v); + return std::visit(detail::conversion_visitor(), v); } }; -template -struct vector_conversion_visitor : boost::static_visitor> { +template struct vector_conversion_visitor { /* Catch all case -> wrong type. */ template Utils::Vector operator()(U const &) const { - throw boost::bad_get{}; + throw std::bad_variant_access{}; } template @@ -219,7 +219,7 @@ struct vector_conversion_visitor : boost::static_visitor> { requires(std::is_same_v or allow_conversion::value) Utils::Vector operator()(std::vector const &vector) const { if (vector.size() != N) { - throw boost::bad_get{}; + throw std::bad_variant_access{}; } if constexpr (std::is_same_v) { Utils::Vector ret{}; @@ -235,15 +235,14 @@ struct vector_conversion_visitor : boost::static_visitor> { template struct get_value_helper> { Utils::Vector operator()(Variant const &v) const { - return boost::apply_visitor(detail::vector_conversion_visitor(), v); + return std::visit(detail::vector_conversion_visitor(), v); } }; -template -struct VisitorVector : boost::static_visitor> { +template struct VisitorVector { /* Catch all case -> wrong type. */ template std::vector operator()(U const &) const { - throw boost::bad_get{}; + throw std::bad_variant_access{}; } /* Standard case, correct type */ @@ -263,15 +262,14 @@ struct VisitorVector : boost::static_visitor> { /* std::vector cases */ template struct get_value_helper> { std::vector operator()(Variant const &v) const { - return boost::apply_visitor(VisitorVector(), v); + return std::visit(VisitorVector(), v); } }; -template -struct VisitorMap : boost::static_visitor> { +template struct VisitorMap { /* Catch all case -> wrong type. */ template std::unordered_map operator()(U const &) const { - throw boost::bad_get{}; + throw std::bad_variant_access{}; } /* Standard case, correct type */ @@ -291,28 +289,28 @@ struct VisitorMap : boost::static_visitor> { /* std::unordered_map cases */ template struct get_value_helper> { std::unordered_map operator()(Variant const &v) const { - return boost::apply_visitor(VisitorMap(), v); + return std::visit(VisitorMap(), v); } }; template struct get_value_helper> { std::unordered_map operator()(Variant const &v) const { - return boost::apply_visitor(VisitorMap(), v); + return std::visit(VisitorMap(), v); } }; /* std::filesystem::path case */ template <> struct get_value_helper { auto operator()(Variant const &v) const { - if (auto const *source = boost::get(&v)) { + if (auto const *source = std::get_if(&v)) { return std::filesystem::path(*source); } - return boost::get(v); + return std::get(v); } }; /** Custom error for a conversion that fails when the value is a nullptr. */ -class bad_get_nullptr : public boost::bad_get {}; +class bad_get_nullptr : public std::bad_variant_access {}; /* This allows direct retrieval of a shared_ptr to the object from * an ObjectRef variant. If the type is a derived type, the type is @@ -322,7 +320,7 @@ template requires(std::is_base_of_v) struct get_value_helper> { std::shared_ptr operator()(Variant const &v) const { - auto so_ptr = boost::get(v); + auto so_ptr = std::get(v); if (!so_ptr) { throw bad_get_nullptr{}; } @@ -331,12 +329,12 @@ struct get_value_helper> { return t_ptr; } - throw boost::bad_get{}; + throw std::bad_variant_access{}; } }; /** - * @brief Re-throw a @c boost::bad_get exception wrapped in an @ref Exception. + * @brief Re-throw a @c std::bad_variant_access wrapped in an @ref Exception. * Write a custom error message for invalid conversions due to type mismatch * and due to nullptr values, possibly with context information if the variant * is a container. @@ -359,7 +357,7 @@ inline void handle_bad_get(Variant const &v, std::string const &name) { } catch (bad_get_nullptr const &) { auto const item_error = (to_container) ? " contains a value that" : ""; throw Exception(what + item_error + " is a null pointer"); - } catch (boost::bad_get const &) { + } catch (std::bad_variant_access const &) { auto const non_convertible = std::string(" is not convertible to "); auto item_error = std::string(""); if (from_container and to_container) { @@ -385,7 +383,7 @@ template T get_value(Variant const &v, std::string const &name) { /** * @brief Extract value of specific type T from a Variant. * - * This is a wrapper around boost::get that allows us to + * This is a wrapper around std::get that allows us to * customize the behavior for different types. This is * needed e.g. to deal with containers whose elements * have mixed types that are implicitly convertible diff --git a/src/script_interface/interactions/BondedInteraction.hpp b/src/script_interface/interactions/BondedInteraction.hpp index 6dbd23f0993..76d8fc78e6c 100644 --- a/src/script_interface/interactions/BondedInteraction.hpp +++ b/src/script_interface/interactions/BondedInteraction.hpp @@ -34,7 +34,6 @@ #include "script_interface/get_value.hpp" #include -#include #include #include @@ -49,6 +48,7 @@ #include #include #include +#include #include namespace ScriptInterface { @@ -122,7 +122,7 @@ template class BondedInteractionImpl : public BondedInteraction { public: using CoreBondedInteraction = CoreIA; CoreBondedInteraction &get_struct() { - return boost::get(*bonded_ia()); + return std::get(*bonded_ia()); } }; diff --git a/src/script_interface/interactions/NonBondedInteraction.hpp b/src/script_interface/interactions/NonBondedInteraction.hpp index 2c200c8d97a..a7d3c79530e 100644 --- a/src/script_interface/interactions/NonBondedInteraction.hpp +++ b/src/script_interface/interactions/NonBondedInteraction.hpp @@ -41,6 +41,7 @@ #include #include #include +#include #include namespace ScriptInterface { @@ -206,7 +207,7 @@ class InteractionLJ : public InteractionPotentialInterface<::LJ_Parameters> { private: void make_new_instance(VariantMap const ¶ms) override { auto new_params = params; - auto const *shift_string = boost::get(¶ms.at("shift")); + auto const *shift_string = std::get_if(¶ms.at("shift")); if (shift_string != nullptr) { if (*shift_string != "auto") { throw std::invalid_argument( @@ -254,7 +255,7 @@ class InteractionLJGen private: void make_new_instance(VariantMap const ¶ms) override { auto new_params = params; - auto const *shift_string = boost::get(¶ms.at("shift")); + auto const *shift_string = std::get_if(¶ms.at("shift")); if (shift_string != nullptr) { if (*shift_string != "auto") { throw std::invalid_argument( diff --git a/src/script_interface/magnetostatics/DipolarLayerCorrection.hpp b/src/script_interface/magnetostatics/DipolarLayerCorrection.hpp index ac3b1878cc8..2e0bd339733 100644 --- a/src/script_interface/magnetostatics/DipolarLayerCorrection.hpp +++ b/src/script_interface/magnetostatics/DipolarLayerCorrection.hpp @@ -32,10 +32,9 @@ #include "script_interface/get_value.hpp" -#include "boost/variant.hpp" - #include #include +#include namespace ScriptInterface { namespace Dipoles { @@ -43,7 +42,7 @@ namespace Dipoles { class DipolarLayerCorrection : public Actor { using DipolarDSR = DipolarDirectSum; - using BaseSolver = boost::variant< + using BaseSolver = std::variant< #ifdef DP3M std::shared_ptr>, #endif @@ -51,9 +50,8 @@ class DipolarLayerCorrection BaseSolver m_solver; void on_bind_system(::System::System &system) override { - boost::apply_visitor( - [this](auto &solver) { solver->bind_system(m_system.lock()); }, - m_solver); + std::visit([this](auto &solver) { solver->bind_system(m_system.lock()); }, + m_solver); } public: @@ -67,8 +65,8 @@ class DipolarLayerCorrection [this]() { return actor()->dlc.far_cut; }}, {"actor", AutoParameter::read_only, [this]() { - return boost::apply_visitor( - [](auto &solver) { return Variant{solver}; }, m_solver); + return std::visit([](auto &solver) { return Variant{solver}; }, + m_solver); }}, }); } diff --git a/src/script_interface/packed_variant.hpp b/src/script_interface/packed_variant.hpp index 47900b2257e..55b03d754fe 100644 --- a/src/script_interface/packed_variant.hpp +++ b/src/script_interface/packed_variant.hpp @@ -19,8 +19,17 @@ #pragma once +#include "ObjectId.hpp" #include "Variant.hpp" +#include +#include +#include + +#include +#include +#include + #include #include #include @@ -28,49 +37,29 @@ #include #include #include +#include #include namespace ScriptInterface { -using ObjectId = std::size_t; - -/** - * @brief Id for object. - * - * This assigns every ObjectHandle a unique id. - */ -inline ObjectId object_id(const ObjectHandle *p) { - // NOLINTNEXTLINE(bugprone-sizeof-expression) - static_assert(sizeof(const ObjectHandle *) <= sizeof(ObjectId)); - // Use the pointer value as the unique identifier. - // This function is only called on the head node. - return reinterpret_cast(p); -} - /** * @brief Packed version of @ref Variant. * * When packing variants by @ref PackVisitor, objects of type * @ref ObjectRef are packed as @ref ObjectId. Other than that, - * all other types allowed in @ref Variant must appear here. + * all other types allowed in @ref Variant also appear here. */ -using PackedVariant = boost::make_recursive_variant< - None, bool, int, std::size_t, double, std::string, ObjectId, - Utils::Vector3b, Utils::Vector3i, Utils::Vector2d, Utils::Vector3d, - Utils::Vector4d, std::vector, std::vector, - std::vector, std::filesystem::path, - std::unordered_map, - std::unordered_map>::type; +using PackedVariant = make_recursive_variant; using PackedMap = std::vector>; /** - * @brief Visitor that converts a Variant to a PackedVariant. + * @brief Visitor that converts a Variant to a @ref PackedVariant. * - * While packing, keeps track of all the ObjectRef values that + * While packing, keeps track of all the @ref ObjectRef values that * were encountered and stores them. This also keeps the * referees alive if there are no other owners. */ -struct PackVisitor : boost::static_visitor { +struct PackVisitor { private: mutable std::unordered_map m_objects; @@ -79,11 +68,11 @@ struct PackVisitor : boost::static_visitor { auto const &objects() const { return m_objects; } /* For the vector, we recurse into each element. */ - auto operator()(const std::vector &vec) const { + PackedVariant operator()(const std::vector &vec) const { std::vector ret(vec.size()); std::ranges::transform(vec, ret.begin(), [this](const Variant &v) { - return boost::apply_visitor(*this, v); + return std::visit(*this, v); }); return ret; @@ -91,11 +80,11 @@ struct PackVisitor : boost::static_visitor { /* For the map, we recurse into each element. */ template - auto operator()(const std::unordered_map &map) const { + PackedVariant operator()(const std::unordered_map &map) const { std::unordered_map ret{}; for (auto const &[key, variant] : map) { - ret.emplace(key, boost::apply_visitor(*this, variant)); + ret.emplace(key, std::visit(*this, variant)); } return ret; @@ -104,7 +93,7 @@ struct PackVisitor : boost::static_visitor { /* For object references we store the object reference, and * replace it by just an id. */ PackedVariant operator()(const ObjectRef &so_ptr) const { - auto const oid = object_id(so_ptr.get()); + auto const oid = ObjectId(so_ptr.get()); m_objects[oid] = so_ptr; return oid; @@ -117,22 +106,22 @@ struct PackVisitor : boost::static_visitor { }; /** - * @brief Visitor that converts a PackedVariant to a Variant. + * @brief Visitor that converts a @ref PackedVariant to a @ref Variant. * * ObjectId are replaced according to the provided object map. */ -struct UnpackVisitor : boost::static_visitor { +struct UnpackVisitor { std::unordered_map const &objects; explicit UnpackVisitor(std::unordered_map const &objects) : objects(objects) {} /* For the vector, we recurse into each element. */ - auto operator()(const std::vector &vec) const { + Variant operator()(const std::vector &vec) const { std::vector ret(vec.size()); std::ranges::transform(vec, ret.begin(), [this](const PackedVariant &v) { - return boost::apply_visitor(*this, v); + return std::visit(*this, v); }); return ret; @@ -140,11 +129,11 @@ struct UnpackVisitor : boost::static_visitor { /* For the map, we recurse into each element. */ template - auto operator()(const std::unordered_map &map) const { + Variant operator()(const std::unordered_map &map) const { std::unordered_map ret{}; for (auto const &[key, packed_variant] : map) { - ret.emplace(key, boost::apply_visitor(*this, packed_variant)); + ret.emplace(key, std::visit(*this, packed_variant)); } return ret; @@ -160,33 +149,33 @@ struct UnpackVisitor : boost::static_visitor { }; /** - * @brief Transform a Variant to a PackedVariant + * @brief Transform a Variant to a @ref PackedVariant * * Applies @ref PackVisitor to a @ref Variant. * - * @param v Input Variant + * @param v Input @ref Variant * @return Packed variant. */ inline PackedVariant pack(const Variant &v) { - return boost::apply_visitor(PackVisitor(), v); + return std::visit(PackVisitor(), v); } /** - * @brief Unpack a PackedVariant. + * @brief Unpack a @ref PackedVariant. * * Applies @ref UnpackVisitor to a @ref Variant. * - * @param v Packed Variant. + * @param v Packed @ref Variant. * @param objects Map of ids to reference. * @return Transformed variant. */ inline Variant unpack(const PackedVariant &v, std::unordered_map const &objects) { - return boost::apply_visitor(UnpackVisitor(objects), v); + return std::visit(UnpackVisitor(objects), v); } /** - * @brief Pack a VariantMap. + * @brief Pack a @ref VariantMap. * * Applies @ref pack to every value in the * input map. @@ -202,7 +191,7 @@ inline PackedMap pack(const VariantMap &v) { } /** - * @brief Unpack a PackedMap. + * @brief Unpack a @ref PackedMap. * * Applies @ref unpack to every value in the * input map. diff --git a/src/script_interface/scafacos/scafacos.cpp b/src/script_interface/scafacos/scafacos.cpp index 1a6ca2138c2..a7d4b1d74b8 100644 --- a/src/script_interface/scafacos/scafacos.cpp +++ b/src/script_interface/scafacos/scafacos.cpp @@ -30,8 +30,6 @@ #include -#include - #include #include #include @@ -41,6 +39,7 @@ #include #include #include +#include #include namespace ScriptInterface { @@ -50,8 +49,9 @@ std::vector available_methods() { return ScafacosContextBase::available_methods(); } -struct ConvertToStringVector - : public boost::static_visitor> { +struct ConvertToStringVector { + using result_type = std::vector; + auto operator()(std::string const &value) const { return result_type{value}; } template result_type operator()(T const &value) const { @@ -63,20 +63,14 @@ struct ConvertToStringVector auto operator()(result_type const &values) const { return values; } - auto operator()(std::vector const &values) const { + template auto operator()(std::vector const &values) const { result_type values_str; for (auto const &v : values) { - values_str.emplace_back(boost::apply_visitor(*this, v).front()); - } - return values_str; - } - - template - requires(std::is_arithmetic_v) - auto operator()(std::vector const &values) const { - result_type values_str; - for (auto const &v : values) { - values_str.emplace_back(to_str(v)); + if constexpr (std::is_same_v) { + values_str.emplace_back(std::visit(*this, v).front()); + } else { + values_str.emplace_back(to_str(v)); + } } return values_str; } @@ -94,8 +88,9 @@ struct ConvertToStringVector } }; -struct GetParameterList - : public boost::static_visitor> { +struct GetParameterList { + using result_type = std::unordered_map; + auto operator()(result_type const &obj) const { return obj; } template @@ -115,7 +110,7 @@ struct GetParameterList }; std::string serialize_parameters(Variant const &pack) { - auto const parameters = boost::apply_visitor(GetParameterList(), pack); + auto const parameters = std::visit(GetParameterList(), pack); if (parameters.empty()) { throw std::invalid_argument( "ScaFaCoS methods require at least 1 parameter"); @@ -124,7 +119,7 @@ std::string serialize_parameters(Variant const &pack) { std::string method_params = ""; for (auto const &[name, values] : parameters) { method_params += "," + name; - for (auto const &value : boost::apply_visitor(visitor, values)) { + for (auto const &value : std::visit(visitor, values)) { method_params += "," + value; } } diff --git a/src/script_interface/tests/Accumulators_test.cpp b/src/script_interface/tests/Accumulators_test.cpp index e2104924f3f..f7ee5f989bd 100644 --- a/src/script_interface/tests/Accumulators_test.cpp +++ b/src/script_interface/tests/Accumulators_test.cpp @@ -21,8 +21,6 @@ #define BOOST_TEST_DYN_LINK #include -#include - #include "script_interface/GlobalContext.hpp" #include "script_interface/accumulators/ContactTimes.hpp" @@ -45,6 +43,7 @@ #include #include #include +#include #include static std::weak_ptr mpi_env; diff --git a/src/script_interface/tests/AutoParameter_test.cpp b/src/script_interface/tests/AutoParameter_test.cpp index ab4d720766d..5ff520b9453 100644 --- a/src/script_interface/tests/AutoParameter_test.cpp +++ b/src/script_interface/tests/AutoParameter_test.cpp @@ -22,15 +22,17 @@ #include "script_interface/auto_parameters/AutoParameter.hpp" +#include + BOOST_AUTO_TEST_CASE(direct_binding) { using namespace ScriptInterface; int i{19}; auto p = AutoParameter("i", i); - BOOST_CHECK(boost::get(p.get()) == 19); + BOOST_CHECK(std::get(p.get()) == 19); p.set(42); - BOOST_CHECK(boost::get(p.get()) == 42); + BOOST_CHECK(std::get(p.get()) == 42); BOOST_CHECK(i == 42); } @@ -41,7 +43,7 @@ BOOST_AUTO_TEST_CASE(read_only) { auto p = AutoParameter("i", i); ; /* Getting should work as usual */ - BOOST_CHECK(boost::get(p.get()) == i); + BOOST_CHECK(std::get(p.get()) == i); /* Setting should throw */ BOOST_CHECK_EXCEPTION(p.set(2), AutoParameter::WriteError, @@ -52,14 +54,14 @@ BOOST_AUTO_TEST_CASE(user_provided) { using namespace ScriptInterface; int i{12}; - auto setter = [&i](Variant const &j) { i = boost::get(j); }; + auto setter = [&i](Variant const &j) { i = std::get(j); }; auto getter = [&i]() { return i; }; auto p = AutoParameter("i", setter, getter); - BOOST_CHECK(boost::get(p.get()) == 12); + BOOST_CHECK(std::get(p.get()) == 12); p.set(42); - BOOST_CHECK(boost::get(p.get()) == 42); + BOOST_CHECK(std::get(p.get()) == 42); BOOST_CHECK(i == 42); } @@ -71,7 +73,7 @@ BOOST_AUTO_TEST_CASE(user_provided_read_only) { auto p = AutoParameter("i", AutoParameter::ReadOnly{}, getter); - BOOST_CHECK(boost::get(p.get()) == 12); + BOOST_CHECK(std::get(p.get()) == 12); BOOST_CHECK_THROW(p.set(42), AutoParameter::WriteError); } @@ -93,7 +95,7 @@ BOOST_AUTO_TEST_CASE(pointer_to_method) { auto p = AutoParameter("name", c_ptr, &C::setter, &C::value_getter); p.set(5); - BOOST_CHECK(5 == boost::get(p.get())); + BOOST_CHECK(5 == std::get(p.get())); } { @@ -101,7 +103,7 @@ BOOST_AUTO_TEST_CASE(pointer_to_method) { auto p = AutoParameter("name", c_ptr, &C::setter, &C::value_getter); p.set(5); - BOOST_CHECK(5 == boost::get(p.get())); + BOOST_CHECK(5 == std::get(p.get())); } { @@ -109,7 +111,7 @@ BOOST_AUTO_TEST_CASE(pointer_to_method) { auto p = AutoParameter("name", c_ptr, &C::setter, &C::ref_getter); p.set(5); - BOOST_CHECK(5 == boost::get(p.get())); + BOOST_CHECK(5 == std::get(p.get())); } { @@ -117,7 +119,7 @@ BOOST_AUTO_TEST_CASE(pointer_to_method) { auto p_setgetter = AutoParameter("name", c_ptr, &C::setter_getter); p_setgetter.set(5); - BOOST_CHECK(5 == boost::get(p_setgetter.get())); + BOOST_CHECK(5 == std::get(p_setgetter.get())); } { @@ -125,7 +127,7 @@ BOOST_AUTO_TEST_CASE(pointer_to_method) { auto p = AutoParameter("name", c_ptr, &C::value_getter); BOOST_CHECK_THROW(p.set(5), AutoParameter::WriteError); - BOOST_CHECK(5 == boost::get(p.get())); + BOOST_CHECK(5 == std::get(p.get())); } { @@ -133,6 +135,6 @@ BOOST_AUTO_TEST_CASE(pointer_to_method) { auto p = AutoParameter("name", c_ptr, &C::ref_getter); BOOST_CHECK_THROW(p.set(5), AutoParameter::WriteError); - BOOST_CHECK(5 == boost::get(p.get())); + BOOST_CHECK(5 == std::get(p.get())); } } diff --git a/src/script_interface/tests/AutoParameters_test.cpp b/src/script_interface/tests/AutoParameters_test.cpp index 3445dca29d7..a2ce6bc5115 100644 --- a/src/script_interface/tests/AutoParameters_test.cpp +++ b/src/script_interface/tests/AutoParameters_test.cpp @@ -20,10 +20,10 @@ #define BOOST_TEST_DYN_LINK #include -#include - #include "script_interface/auto_parameters/AutoParameters.hpp" +#include + using ScriptInterface::AutoParameters; struct A : AutoParameters { @@ -42,13 +42,13 @@ BOOST_AUTO_TEST_CASE(basic) { BOOST_CHECK(std::ranges::find(parameters, "i") != parameters.end()); BOOST_CHECK(std::ranges::find(parameters, "j") != parameters.end()); - BOOST_CHECK(0 == boost::get(a.get_parameter("i"))); - BOOST_CHECK(42 == boost::get(a.get_parameter("j"))); + BOOST_CHECK(0 == std::get(a.get_parameter("i"))); + BOOST_CHECK(42 == std::get(a.get_parameter("j"))); a.set_parameter("i", 12); - BOOST_CHECK(12 == boost::get(a.get_parameter("i"))); - BOOST_CHECK(42 == boost::get(a.get_parameter("j"))); + BOOST_CHECK(12 == std::get(a.get_parameter("i"))); + BOOST_CHECK(42 == std::get(a.get_parameter("j"))); } struct B : public A { @@ -65,12 +65,12 @@ BOOST_AUTO_TEST_CASE(add_parameters) { BOOST_CHECK_EQUAL(a.i, 1); BOOST_CHECK_EQUAL(b.i, 1); - BOOST_CHECK_EQUAL(boost::get(b.get_parameter("j")), 2); - BOOST_CHECK_EQUAL(boost::get(b.get_parameter("k")), 3); - BOOST_CHECK_EQUAL(boost::get(b.get_parameter("i")), 4); - BOOST_CHECK_EQUAL(boost::get(a.get_parameter("i")), 4); + BOOST_CHECK_EQUAL(std::get(b.get_parameter("j")), 2); + BOOST_CHECK_EQUAL(std::get(b.get_parameter("k")), 3); + BOOST_CHECK_EQUAL(std::get(b.get_parameter("i")), 4); + BOOST_CHECK_EQUAL(std::get(a.get_parameter("i")), 4); b.set_parameter("k", 12); - BOOST_CHECK_EQUAL(boost::get(b.get_parameter("k")), 12); + BOOST_CHECK_EQUAL(std::get(b.get_parameter("k")), 12); } BOOST_AUTO_TEST_CASE(exceptions) { diff --git a/src/script_interface/tests/GlobalContext_test.cpp b/src/script_interface/tests/GlobalContext_test.cpp index c700c45da86..1e5bedf6bbd 100644 --- a/src/script_interface/tests/GlobalContext_test.cpp +++ b/src/script_interface/tests/GlobalContext_test.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include static std::weak_ptr mpi_env; @@ -118,13 +119,13 @@ BOOST_AUTO_TEST_CASE(GlobalContext_serialization) { auto d1 = si::ObjectHandle::deserialize(serialized, *ctx); BOOST_REQUIRE(d1); - BOOST_CHECK_EQUAL(boost::get(d1->get_parameter("id")), 1); - auto d2 = boost::get(d1->get_parameter("object_param")); + BOOST_CHECK_EQUAL(std::get(d1->get_parameter("id")), 1); + auto d2 = std::get(d1->get_parameter("object_param")); BOOST_REQUIRE(d2); - BOOST_CHECK_EQUAL(boost::get(d2->get_parameter("id")), 2); - auto d3 = boost::get(d2->get_parameter("object_param")); + BOOST_CHECK_EQUAL(std::get(d2->get_parameter("id")), 2); + auto d3 = std::get(d2->get_parameter("object_param")); BOOST_REQUIRE(d3); - BOOST_CHECK_EQUAL(boost::get(d3->get_parameter("id")), 3); + BOOST_CHECK_EQUAL(std::get(d3->get_parameter("id")), 3); } else { cb->loop(); } diff --git a/src/script_interface/tests/LocalContext_test.cpp b/src/script_interface/tests/LocalContext_test.cpp index 129206329fb..83508ec7f79 100644 --- a/src/script_interface/tests/LocalContext_test.cpp +++ b/src/script_interface/tests/LocalContext_test.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include namespace si = ScriptInterface; @@ -93,13 +94,13 @@ BOOST_AUTO_TEST_CASE(LocalContext_serialization) { { auto d1 = si::ObjectHandle::deserialize(serialized, *ctx); BOOST_REQUIRE(d1); - BOOST_CHECK_EQUAL(boost::get(d1->get_parameter("id")), 1); - auto d2 = boost::get(d1->get_parameter("object_param")); + BOOST_CHECK_EQUAL(std::get(d1->get_parameter("id")), 1); + auto d2 = std::get(d1->get_parameter("object_param")); BOOST_REQUIRE(d2); - BOOST_CHECK_EQUAL(boost::get(d2->get_parameter("id")), 2); - auto d3 = boost::get(d2->get_parameter("object_param")); + BOOST_CHECK_EQUAL(std::get(d2->get_parameter("id")), 2); + auto d3 = std::get(d2->get_parameter("object_param")); BOOST_REQUIRE(d3); - BOOST_CHECK_EQUAL(boost::get(d3->get_parameter("id")), 3); + BOOST_CHECK_EQUAL(std::get(d3->get_parameter("id")), 3); } } diff --git a/src/script_interface/tests/ObjectHandle_test.cpp b/src/script_interface/tests/ObjectHandle_test.cpp index 9db1f752b9f..bb13ae060ec 100644 --- a/src/script_interface/tests/ObjectHandle_test.cpp +++ b/src/script_interface/tests/ObjectHandle_test.cpp @@ -30,7 +30,6 @@ #include #include -#include #include #include @@ -38,6 +37,7 @@ #include #include #include +#include #include using namespace ScriptInterface; @@ -66,7 +66,7 @@ struct CallMethod { } }; -using Info = boost::variant; +using Info = std::variant; } // namespace MockCall /** @@ -133,7 +133,7 @@ BOOST_AUTO_TEST_CASE(do_construct_) { VariantMap test_params; log_handle.construct(test_params); - BOOST_CHECK(boost::get(log_handle.call_log[0]) == + BOOST_CHECK(std::get(log_handle.call_log[0]) == MockCall::Construct{&test_params}); } @@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(do_set_parameter_) { Variant value; log_handle.set_parameter(name, value); - BOOST_CHECK((boost::get(log_handle.call_log[0]) == + BOOST_CHECK((std::get(log_handle.call_log[0]) == MockCall::SetParameter{&name, &value})); } @@ -163,7 +163,7 @@ BOOST_AUTO_TEST_CASE(do_call_method_) { VariantMap params; log_handle.call_method(name, params); - BOOST_CHECK((boost::get(log_handle.call_log[0]) == + BOOST_CHECK((std::get(log_handle.call_log[0]) == MockCall::CallMethod{&name, ¶ms})); } @@ -226,7 +226,7 @@ BOOST_AUTO_TEST_CASE(notify_set_parameter_) { auto const log_entry = log_ctx->call_log.at(0); BOOST_CHECK_EQUAL(log_entry.first, o.get()); - BOOST_CHECK((boost::get(log_entry.second) == + BOOST_CHECK((std::get(log_entry.second) == MockCall::SetParameter{&name, &value})); } @@ -246,7 +246,7 @@ BOOST_AUTO_TEST_CASE(notify_call_method_) { auto const log_entry = log_ctx->call_log.at(0); BOOST_CHECK_EQUAL(log_entry.first, o.get()); - BOOST_CHECK((boost::get(log_entry.second) == + BOOST_CHECK((std::get(log_entry.second) == MockCall::CallMethod{&name, ¶ms})); } diff --git a/src/script_interface/tests/ObjectMap_test.cpp b/src/script_interface/tests/ObjectMap_test.cpp index 0780b706285..6972984bae9 100644 --- a/src/script_interface/tests/ObjectMap_test.cpp +++ b/src/script_interface/tests/ObjectMap_test.cpp @@ -35,6 +35,7 @@ #include #include #include +#include using namespace ScriptInterface; @@ -138,7 +139,7 @@ BOOST_AUTO_TEST_CASE(calling_methods) { auto f = std::make_shared(); VariantMap params{}; ObjectMapImpl map; - BOOST_CHECK(boost::get(map.call_method("empty", params))); + BOOST_CHECK(std::get(map.call_method("empty", params))); // insert an element with key int first_key = 3; @@ -148,32 +149,32 @@ BOOST_AUTO_TEST_CASE(calling_methods) { // insert an element without key params.clear(); params["object"] = make_variant(f); - auto const second_key = boost::get(map.call_method("insert", params)); + auto const second_key = std::get(map.call_method("insert", params)); // Check the returned map - auto const map_ret = boost::get>( + auto const map_ret = std::get>( map.call_method("get_map", params)); - BOOST_CHECK(e == boost::get(map_ret.at(first_key))); - BOOST_CHECK(f == boost::get(map_ret.at(second_key))); + BOOST_CHECK(e == std::get(map_ret.at(first_key))); + BOOST_CHECK(f == std::get(map_ret.at(second_key))); BOOST_REQUIRE_EQUAL(map_ret.size(), 2); // Check contents of the internal map BOOST_CHECK(map.elements().at(first_key) == e); BOOST_CHECK(map.elements().at(second_key) == f); params.clear(); - BOOST_CHECK(!boost::get(map.call_method("empty", params))); - BOOST_REQUIRE_EQUAL(boost::get(map.call_method("size", params)), 2); + BOOST_CHECK(!std::get(map.call_method("empty", params))); + BOOST_REQUIRE_EQUAL(std::get(map.call_method("size", params)), 2); // Erase an element params["key"] = second_key; map.call_method("erase", params); BOOST_CHECK_THROW(map.elements().at(second_key), std::out_of_range); params.clear(); - BOOST_REQUIRE_EQUAL(boost::get(map.call_method("size", params)), 1); + BOOST_REQUIRE_EQUAL(std::get(map.call_method("size", params)), 1); // Clear map map.call_method("clear", params); - BOOST_CHECK(boost::get(map.call_method("empty", params))); + BOOST_CHECK(std::get(map.call_method("empty", params))); } int main(int argc, char **argv) { diff --git a/src/script_interface/tests/get_value_test.cpp b/src/script_interface/tests/get_value_test.cpp index e05b4f68e8a..20c09fdf01d 100644 --- a/src/script_interface/tests/get_value_test.cpp +++ b/src/script_interface/tests/get_value_test.cpp @@ -142,7 +142,7 @@ BOOST_AUTO_TEST_CASE(get_value_from_map) { using ScriptInterface::Variant; using ScriptInterface::VariantMap; - VariantMap map{{"a", 13}, {"e", 3.1}, {"f", "s"}}; + VariantMap map{{"a", 13}, {"e", 3.1}, {"f", std::string("s")}}; BOOST_CHECK(3.1 == get_value(map, "e")); BOOST_CHECK(13 == get_value_or(map, "a", -1)); diff --git a/src/script_interface/tests/packed_variant_test.cpp b/src/script_interface/tests/packed_variant_test.cpp index 025aec9b6bb..88ea8c00ee4 100644 --- a/src/script_interface/tests/packed_variant_test.cpp +++ b/src/script_interface/tests/packed_variant_test.cpp @@ -27,13 +27,13 @@ #include BOOST_AUTO_TEST_CASE(object_id_) { - using ScriptInterface::object_id; + using ScriptInterface::ObjectId; auto const p1 = reinterpret_cast(1); auto const p2 = reinterpret_cast(2); /* Check that different objects have different ids */ - BOOST_CHECK_NE(object_id(p1), object_id(p2)); + BOOST_CHECK(ObjectId(p1) != ObjectId(p2)); } BOOST_AUTO_TEST_CASE(PackVisitor_) { @@ -49,19 +49,18 @@ BOOST_AUTO_TEST_CASE(PackVisitor_) { auto const visitor = ScriptInterface::PackVisitor(); - auto const result = boost::apply_visitor(visitor, v); + auto const result = std::visit(visitor, v); const PackedVariant expected = std::vector{{5, std::vector{ 6, - object_id(ObjectRef{}.get()), + ObjectId(ObjectRef{}.get()), }}}; BOOST_CHECK(result == expected); /* Check that the object hast been captured. */ - BOOST_CHECK(visitor.objects().at(object_id(ObjectRef{}.get())) == - ObjectRef{}); + BOOST_CHECK(visitor.objects().at(ObjectId(ObjectRef{}.get())) == ObjectRef{}); } BOOST_AUTO_TEST_CASE(pack_) { @@ -73,7 +72,7 @@ BOOST_AUTO_TEST_CASE(pack_) { ObjectRef{}, }}}; - auto const expected = boost::apply_visitor(ScriptInterface::PackVisitor(), v); + auto const expected = std::visit(ScriptInterface::PackVisitor(), v); auto const result = ScriptInterface::pack(v); BOOST_CHECK(expected == result); @@ -88,14 +87,14 @@ BOOST_AUTO_TEST_CASE(UnpackVisitor_) { const PackedVariant v = std::vector{{5, std::vector{ 6, - object_id(ObjectRef{}.get()), + ObjectId(ObjectRef{}.get()), }}}; std::unordered_map const &objects{ - {object_id(ObjectRef{}.get()), ObjectRef{}}}; + {ObjectId(ObjectRef{}.get()), ObjectRef{}}}; auto const visitor = ScriptInterface::UnpackVisitor(objects); - auto const result = boost::apply_visitor(visitor, v); + auto const result = std::visit(visitor, v); const Variant expected = std::vector{{5, std::vector{ 6, @@ -113,14 +112,13 @@ BOOST_AUTO_TEST_CASE(unpack_) { const PackedVariant v = std::vector{{5, std::vector{ 6, - object_id(ObjectRef{}.get()), + ObjectId(ObjectRef{}.get()), }}}; std::unordered_map const &objects{ - {object_id(ObjectRef{}.get()), ObjectRef{}}}; + {ObjectId(ObjectRef{}.get()), ObjectRef{}}}; - auto const expected = - boost::apply_visitor(ScriptInterface::UnpackVisitor(objects), v); + auto const expected = std::visit(ScriptInterface::UnpackVisitor(objects), v); auto const result = ScriptInterface::unpack(v, objects); BOOST_CHECK(expected == result); diff --git a/src/script_interface/walberla/LBFluidSlice.cpp b/src/script_interface/walberla/LBFluidSlice.cpp index c2cb79156e3..a8ab37df241 100644 --- a/src/script_interface/walberla/LBFluidSlice.cpp +++ b/src/script_interface/walberla/LBFluidSlice.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include namespace ScriptInterface::walberla { @@ -115,8 +116,8 @@ Variant LBFluidSlice::do_call_method(std::string const &name, auto const density = m_lb_fluid->get_density(); auto const diagonal_term = density * c_sound_sq / m_conv_press; // modify existing variant in-place - auto &vec = *(boost::get>( - &(boost::get>(&variant)->at(0)))); + auto &vec = *(std::get_if>( + &(std::get_if>(&variant)->at(0)))); for (auto it = vec.begin(); it < vec.end(); it += 9) { *(it + 0) -= diagonal_term; *(it + 4) -= diagonal_term; diff --git a/src/utils/include/utils/serialization/pack.hpp b/src/utils/include/utils/serialization/pack.hpp index 0734d29caaa..096867cdbdd 100644 --- a/src/utils/include/utils/serialization/pack.hpp +++ b/src/utils/include/utils/serialization/pack.hpp @@ -16,8 +16,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#ifndef UTILS_SERIALIZATION_PACK_HPP -#define UTILS_SERIALIZATION_PACK_HPP + +#pragma once #include #include @@ -62,5 +62,3 @@ template T unpack(std::string const &state) { return val; } } // namespace Utils - -#endif // UTILS_SERIALIZATION_PACK_HPP diff --git a/src/utils/include/utils/serialization/variant.hpp b/src/utils/include/utils/serialization/variant.hpp new file mode 100644 index 00000000000..f0069e63148 --- /dev/null +++ b/src/utils/include/utils/serialization/variant.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2025 The ESPResSo project + * + * This file is part of ESPResSo. + * + * ESPResSo is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ESPResSo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace boost::serialization { + +namespace detail { +template +void load_impl(Archive &ar, int index, Variant &obj) { + if (index == I) { + std::variant_alternative_t opt{}; + ar >> opt; + obj.template emplace(std::move(opt)); + } else if constexpr (I + 1 < std::variant_size_v) { + load_impl(ar, index, obj); + } +} +} // namespace detail + +template +void save(Archive &ar, std::variant const &obj, unsigned const) { + ar << obj.index(); + std::visit([&](const auto &value) { ar << value; }, obj); +} + +template +void load(Archive &ar, std::variant &obj, unsigned const) { + std::size_t index = 0; + ar >> index; + if (index >= std::variant_size_v>) { + throw std::domain_error("std::variant cannot be reloaded (type mismatch)"); + } + detail::load_impl<0>(ar, index, obj); +} + +template +void serialize(Archive &ar, std::variant &obj, unsigned const version) { + split_free(ar, obj, version); +} + +} // namespace boost::serialization diff --git a/src/utils/tests/serialization_test.cpp b/src/utils/tests/serialization_test.cpp index c61eaf9f950..34e37e8a820 100644 --- a/src/utils/tests/serialization_test.cpp +++ b/src/utils/tests/serialization_test.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -368,6 +369,32 @@ BOOST_AUTO_TEST_CASE(std_optional_test) { } } +BOOST_AUTO_TEST_CASE(std_variant_test) { + boost::mpi::communicator comm; + { + boost::mpi::packed_archive buffer; + std::variant const value_send{-10}; + std::variant value_recv{1.}; + boost::mpi::packed_oarchive oa{comm, buffer}; + oa << value_send; + boost::mpi::packed_iarchive ia{comm, buffer}; + ia >> value_recv; + BOOST_REQUIRE(std::holds_alternative(value_recv)); + BOOST_CHECK_EQUAL(std::get(value_recv), std::get(value_send)); + } + { + boost::mpi::packed_archive buffer; + std::variant const value_send{-2.}; + std::variant value_recv{1}; + boost::mpi::packed_oarchive oa{comm, buffer}; + oa << value_send; + boost::mpi::packed_iarchive ia{comm, buffer}; + BOOST_CHECK_THROW((ia >> value_recv), std::domain_error); + BOOST_REQUIRE(std::holds_alternative(value_recv)); + BOOST_CHECK_EQUAL(std::get(value_recv), 1); + } +} + int main(int argc, char **argv) { boost::mpi::environment mpi_env(argc, argv); From 1b6002c4071686439fe5d3d44e7f22ccd8fa33e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Mon, 4 Aug 2025 20:40:33 +0200 Subject: [PATCH 2/2] Document recursive variant --- src/script_interface/Variant.hpp | 48 +++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/script_interface/Variant.hpp b/src/script_interface/Variant.hpp index 0ac35b6533e..4e747acf9e4 100644 --- a/src/script_interface/Variant.hpp +++ b/src/script_interface/Variant.hpp @@ -45,23 +45,44 @@ using Vector3b = Utils::Vector; } namespace ScriptInterface { +namespace impl { template struct recursive_variant; -namespace detail { -// CRTP helper class; declare base class of @ref recursive_variant here, -// such that `using BaseClass = detail::recursive_variant_base` -// can be used in the derived class to avoid writing the entire typelist again +/** + * @brief Helper class to inject STL containers in a recursive variant typelist. + * CRTP helper class that defines a base class for @ref recursive_variant, + * such that the `using BaseClass = detail::recursive_variant_base` + * syntax can be used in the derived class instead of writing the entire + * typelist again. These STL containers are used to introduce recursion. + */ template using recursive_variant_add_containers = std::variant>, std::unordered_map>, std::unordered_map>>; -} // namespace detail +/** + * @brief Recursive variant implementation. + * + * This boilerplate code is required to emulate the following Boost feature: + * @code{.cpp} + * using Variant = boost::make_recursive_variant< + * int, double, std::string, std::vector, + * std::unordered_map>::type; + * @endcode + * C++ doesn't natively supports the kind of reflections needed to implement + * this behavior. Our implementation splits the definition in two classes: + * a forward-declared @ref recursive_variant template class whose type + * parameters are "basic" types (i.e. non-recursive types), and a helper + * class @c recursive_variant_add_containers that injects carefully chosen + * STL containers into the type list. Since STL containers store a pointer + * to a buffer holding variant instances, the variant size doesn't need to be + * known at the time the variant is defined, and the variant is recursive. + */ template -struct recursive_variant : detail::recursive_variant_add_containers { - using BaseClass = detail::recursive_variant_add_containers; +struct recursive_variant : recursive_variant_add_containers { + using BaseClass = recursive_variant_add_containers; using BaseClass::BaseClass; private: @@ -74,23 +95,24 @@ struct recursive_variant : detail::recursive_variant_add_containers { } }; +} // namespace impl + /** * @brief Helper typedef to generate consistent variant types. * * This is a custom recursive variant type designed specifically for the script * interface. It features all basic types required to interface with the core. - * Recursive types are added by @c detail::recursive_variant_add_containers. + * Recursive types are added by @ref impl::recursive_variant_add_containers. * The template parameter @c ObjectType is used to ensure instantiations like * @ref Variant and @ref PackedVariant hold the same types in the same order. * * @tparam ObjectType Type of the script interface object handle or reference. */ template -using make_recursive_variant = - recursive_variant, std::vector>; +using make_recursive_variant = impl::recursive_variant< + None, bool, int, std::size_t, double, std::string, std::filesystem::path, + ObjectType, Utils::Vector3b, Utils::Vector3i, Utils::Vector2d, + Utils::Vector3d, Utils::Vector4d, std::vector, std::vector>; class ObjectHandle; using ObjectRef = std::shared_ptr;