diff --git a/CMakeLists.txt b/CMakeLists.txt index b4fa18f..4b1d877 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,10 @@ target_sources( include/SHiP/detectors/detector_id.hpp ) target_link_libraries(SHiPDataModel PUBLIC ROOT::Core) +# Propagate the C++ standard to installed consumers: the ROOT dictionary is +# built at C++23, so consumers streaming these classes must match it. This is +# not carried by CMAKE_CXX_STANDARD, which does not export. +target_compile_features(SHiPDataModel PUBLIC cxx_std_23) set_target_properties(SHiPDataModel PROPERTIES LINKER_LANGUAGE CXX) # Canonical unit vocabulary (mp-units quantity types + views over the data @@ -49,6 +53,9 @@ target_sources( FILES include/SHiP/Units.hpp include/SHiP/QuantityView.hpp ) target_link_libraries(SHiPUnits INTERFACE mp-units::mp-units) +# Header-only and ROOT-free: C++20 is the true minimum (concepts, defaulted +# comparisons), matching what mp-units already requires. +target_compile_features(SHiPUnits INTERFACE cxx_std_20) root_generate_dictionary( G__SHiPDataModel diff --git a/include/SHiP/MCParticle.hpp b/include/SHiP/MCParticle.hpp index d94d6f2..ff1a49a 100644 --- a/include/SHiP/MCParticle.hpp +++ b/include/SHiP/MCParticle.hpp @@ -14,6 +14,8 @@ struct MCParticle { double time{0}; ///< Production time [ns] std::int32_t motherId{-1}; ///< Index of mother (-1 = primary) std::int32_t status{1}; ///< Status code (1 = stable) + + bool operator==(MCParticle const&) const = default; }; } // namespace SHiP diff --git a/include/SHiP/RecHit.hpp b/include/SHiP/RecHit.hpp index 3f99262..c89a624 100644 --- a/include/SHiP/RecHit.hpp +++ b/include/SHiP/RecHit.hpp @@ -17,6 +17,8 @@ struct RecHit { double energyDeposit{0}; ///< Energy deposited [GeV] double time{0}; ///< Global time [ns] double pathLength{0}; ///< Step length [mm] + + bool operator==(RecHit const&) const = default; }; inline RecHit fromSimHit(SimHit const& sp) { diff --git a/include/SHiP/RecParticle.hpp b/include/SHiP/RecParticle.hpp index 2b979a7..c71a34a 100644 --- a/include/SHiP/RecParticle.hpp +++ b/include/SHiP/RecParticle.hpp @@ -22,6 +22,8 @@ struct RecParticle { double time{0}; ///< Production time [ns] std::int32_t creatorProcess{0}; double ipPV{0}; ///< IP wrt to the PV (at 0,0,0) [mm] + + bool operator==(RecParticle const&) const = default; }; inline RecParticle fromSimParticle(SimParticle const& sp) { diff --git a/include/SHiP/SimHit.hpp b/include/SHiP/SimHit.hpp index d6f49e8..32fb39e 100644 --- a/include/SHiP/SimHit.hpp +++ b/include/SHiP/SimHit.hpp @@ -16,6 +16,8 @@ struct SimHit { double energyDeposit{0}; ///< Energy deposited [GeV] double time{0}; ///< Global time [ns] double pathLength{0}; ///< Step length [mm] + + bool operator==(SimHit const&) const = default; }; } // namespace SHiP diff --git a/include/SHiP/SimParticle.hpp b/include/SHiP/SimParticle.hpp index 7977f1c..f2c0375 100644 --- a/include/SHiP/SimParticle.hpp +++ b/include/SHiP/SimParticle.hpp @@ -16,6 +16,8 @@ struct SimParticle { double energy{0}; ///< Initial kinetic energy [GeV] double time{0}; ///< Production time [ns] std::int32_t creatorProcess{0}; + + bool operator==(SimParticle const&) const = default; }; } // namespace SHiP diff --git a/include/SHiP/SimResult.hpp b/include/SHiP/SimResult.hpp index 11ee9c7..87af186 100644 --- a/include/SHiP/SimResult.hpp +++ b/include/SHiP/SimResult.hpp @@ -11,6 +11,8 @@ namespace SHiP { struct SimResult { std::vector hits; std::vector particles; + + bool operator==(SimResult const&) const = default; }; } // namespace SHiP diff --git a/tests/test_utils.hpp b/tests/test_utils.hpp index 16963f9..25c477a 100644 --- a/tests/test_utils.hpp +++ b/tests/test_utils.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -85,52 +86,14 @@ inline SimResult makeSimResult(int offset) { .particles = makeSimParticles(offset + 5)}; } -inline bool equal(EventHeader const& a, EventHeader const& b) { return a == b; } - -inline bool equal(MCParticle const& a, MCParticle const& b) { - return a.pdgCode == b.pdgCode && a.vertex == b.vertex && - a.momentum == b.momentum && a.energy == b.energy && a.time == b.time && - a.motherId == b.motherId && a.status == b.status; -} - -inline bool equal(SimHit const& a, SimHit const& b) { - return a.detectorId == b.detectorId && a.trackId == b.trackId && - a.pdgCode == b.pdgCode && a.position == b.position && - a.momentum == b.momentum && a.energyDeposit == b.energyDeposit && - a.time == b.time && a.pathLength == b.pathLength; -} - -inline bool equal(SimParticle const& a, SimParticle const& b) { - return a.trackId == b.trackId && a.parentId == b.parentId && - a.pdgCode == b.pdgCode && a.vertex == b.vertex && - a.endpoint == b.endpoint && a.momentum == b.momentum && - a.energy == b.energy && a.time == b.time && - a.creatorProcess == b.creatorProcess; -} - -inline bool equal(RecParticle const& a, RecParticle const& b) { - return a.trackId == b.trackId && a.parentId == b.parentId && - a.pdgCode == b.pdgCode && a.vertex == b.vertex && - a.endpoint == b.endpoint && a.momentum == b.momentum && - a.energy == b.energy && a.time == b.time && - a.creatorProcess == b.creatorProcess && a.ipPV == b.ipPV; -} - +/// Default comparison used by check(): any type with operator== works as-is. +/// A type without operator== can still opt in by defining its own +/// equal(T const&, T const&) overload, which is preferred via ADL / overload +/// resolution (a non-template overload beats this constrained template). template -bool equal(std::vector const& a, std::vector const& b) { - if (a.size() != b.size()) { - return false; - } - for (std::size_t i = 0; i < a.size(); ++i) { - if (!equal(a[i], b[i])) { - return false; - } - } - return true; -} - -inline bool equal(SimResult const& a, SimResult const& b) { - return equal(a.hits, b.hits) && equal(a.particles, b.particles); + requires std::equality_comparable +bool equal(T const& a, T const& b) { + return a == b; } /// Compare expected vs. read-back values, reporting PASS/FAIL.