diff --git a/Core/include/Acts/EventData/ProxyAccessor.hpp b/Core/include/Acts/EventData/ProxyAccessor.hpp index 745c3c076b2..26bdf9c1d69 100644 --- a/Core/include/Acts/EventData/ProxyAccessor.hpp +++ b/Core/include/Acts/EventData/ProxyAccessor.hpp @@ -80,9 +80,8 @@ struct ProxyAccessorBase { /// @tparam proxy_t the type of the proxy /// @param proxy the proxy object to access /// @return mutable reference to the column behind the key - template > - T& operator()(proxy_t proxy) const { + template + requires(!RO) T& operator()(proxy_t proxy) const { static_assert(!proxy_t::ReadOnly, "Cannot get mutable ref for const track proxy"); return proxy.template component(key); @@ -92,9 +91,8 @@ struct ProxyAccessorBase { /// @tparam proxy_t the type of the track proxy /// @param proxy the proxy to access /// @return const reference to the column behind the key - template > - const T& operator()(proxy_t proxy) const { + template + requires(RO) const T& operator()(proxy_t proxy) const { if constexpr (proxy_t::ReadOnly) { return proxy.template component(key); diff --git a/Core/include/Acts/EventData/SourceLink.hpp b/Core/include/Acts/EventData/SourceLink.hpp index 7ba41fce008..ea5c8ec4309 100644 --- a/Core/include/Acts/EventData/SourceLink.hpp +++ b/Core/include/Acts/EventData/SourceLink.hpp @@ -14,6 +14,7 @@ #include "Acts/Utilities/TypeTraits.hpp" #include +#include #include #include #include @@ -37,9 +38,9 @@ class SourceLink final { /// Constructor from concrete sourcelink /// @tparam T The source link type /// @param upstream The upstream source link to store - template , SourceLink>>> - explicit SourceLink(T&& upstream) { + template + requires(!std::same_as, SourceLink>) explicit SourceLink( + T&& upstream) { static_assert(!std::is_same_v, SourceLink>, "Cannot wrap SourceLink in SourceLink"); diff --git a/Core/include/Acts/EventData/TrackStateProxy.hpp b/Core/include/Acts/EventData/TrackStateProxy.hpp index f0ebe42cdd9..f078e41cc2c 100644 --- a/Core/include/Acts/EventData/TrackStateProxy.hpp +++ b/Core/include/Acts/EventData/TrackStateProxy.hpp @@ -386,8 +386,8 @@ class TrackStateProxy { /// This overloaded is only enabled if not read-only, and returns a mutable /// reference. /// @return Mutable reference to the pathlength. - template > - double& pathLength() { + template + requires(!RO) double& pathLength() { return component(); } @@ -440,8 +440,8 @@ class TrackStateProxy { component()); } - template > - Parameters predicted() { + template + requires(!RO) Parameters predicted() { assert(has()); return m_traj->self().parameters( component()); diff --git a/Core/include/Acts/Surfaces/SurfaceArray.hpp b/Core/include/Acts/Surfaces/SurfaceArray.hpp index 39f1433ba9f..595bd56314a 100644 --- a/Core/include/Acts/Surfaces/SurfaceArray.hpp +++ b/Core/include/Acts/Surfaces/SurfaceArray.hpp @@ -328,16 +328,18 @@ class SurfaceArray { /// interface stays the same, since we don't care what happens /// here on the callers end /// This is the version for DIM>1 - template = 0> - Vector3 getBinCenterImpl(std::size_t bin) const { + template + requires(D != 1) Vector3 getBinCenterImpl(std::size_t bin) + const { return m_localToGlobal(ActsVector( m_grid.binCenter(m_grid.localBinsFromGlobalBin(bin)).data())); } /// Internal method, see above. /// This is the version for DIM==1 - template = 0> - Vector3 getBinCenterImpl(std::size_t bin) const { + template + requires(D == 1) Vector3 getBinCenterImpl(std::size_t bin) + const { point_t pos = m_grid.binCenter(m_grid.localBinsFromGlobalBin(bin)); return m_localToGlobal(pos); } diff --git a/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp b/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp index 16da26e3d37..571af8e7cc9 100644 --- a/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp +++ b/Core/include/Acts/TrackFitting/GlobalChiSquareFitter.hpp @@ -680,13 +680,13 @@ class Gx2Fitter { typename parameters_t = BoundTrackParameters, typename track_container_t, template class holder_t, bool _isdn = isDirectNavigator> - auto fit(source_link_iterator_t it, source_link_iterator_t end, - const start_parameters_t& sParameters, - const Gx2FitterOptions& gx2fOptions, - TrackContainer& trackContainer) - const -> std::enable_if_t< - !_isdn, Result::TrackProxy>> { + requires(!_isdn) auto fit( + source_link_iterator_t it, source_link_iterator_t end, + const start_parameters_t& sParameters, + const Gx2FitterOptions& gx2fOptions, + TrackContainer& trackContainer) const + -> Result::TrackProxy> { // Preprocess Measurements (SourceLinks -> map) // To be able to find measurements later, we put them into a map // We need to copy input SourceLinks anyway, so the map can own them. diff --git a/Core/include/Acts/TrackFitting/KalmanFitter.hpp b/Core/include/Acts/TrackFitting/KalmanFitter.hpp index 3d2ca45711c..3dbc5cd0f35 100644 --- a/Core/include/Acts/TrackFitting/KalmanFitter.hpp +++ b/Core/include/Acts/TrackFitting/KalmanFitter.hpp @@ -1084,13 +1084,13 @@ class KalmanFitter { typename parameters_t = BoundTrackParameters, typename track_container_t, template class holder_t, bool _isdn = isDirectNavigator> - auto fit(source_link_iterator_t it, source_link_iterator_t end, - const start_parameters_t& sParameters, - const KalmanFitterOptions& kfOptions, - TrackContainer& trackContainer) - const -> std::enable_if_t< - !_isdn, Result::TrackProxy>> { + requires(!_isdn) auto fit( + source_link_iterator_t it, source_link_iterator_t end, + const start_parameters_t& sParameters, + const KalmanFitterOptions& kfOptions, + TrackContainer& trackContainer) const + -> Result::TrackProxy> { // To be able to find measurements later, we put them into a map // We need to copy input SourceLinks anyway, so the map can own them. ACTS_VERBOSE("Preparing " << std::distance(it, end) @@ -1174,14 +1174,14 @@ class KalmanFitter { typename parameters_t = BoundTrackParameters, typename track_container_t, template class holder_t, bool _isdn = isDirectNavigator> - auto fit(source_link_iterator_t it, source_link_iterator_t end, - const start_parameters_t& sParameters, - const KalmanFitterOptions& kfOptions, - const std::vector& sSequence, - TrackContainer& trackContainer) - const -> std::enable_if_t< - _isdn, Result::TrackProxy>> { + requires(_isdn) auto fit( + source_link_iterator_t it, source_link_iterator_t end, + const start_parameters_t& sParameters, + const KalmanFitterOptions& kfOptions, + const std::vector& sSequence, + TrackContainer& trackContainer) const + -> Result::TrackProxy> { // To be able to find measurements later, we put them into a map // We need to copy input SourceLinks anyway, so the map can own them. ACTS_VERBOSE("Preparing " << std::distance(it, end) diff --git a/Core/include/Acts/Utilities/Any.hpp b/Core/include/Acts/Utilities/Any.hpp index 0eac36cf687..e4bcd88a2e3 100644 --- a/Core/include/Acts/Utilities/Any.hpp +++ b/Core/include/Acts/Utilities/Any.hpp @@ -143,9 +143,9 @@ class AnyBase : public AnyBaseAll { AnyBase() = default; #endif - template , AnyBase>>> - explicit AnyBase(T&& value) + template + requires(!std::same_as, AnyBase>) explicit AnyBase( + T&& value) : AnyBase{std::in_place_type, std::forward(value)} {} template diff --git a/Core/include/Acts/Utilities/Axis.hpp b/Core/include/Acts/Utilities/Axis.hpp index fd595a44660..ce4b9e5cafd 100644 --- a/Core/include/Acts/Utilities/Axis.hpp +++ b/Core/include/Acts/Utilities/Axis.hpp @@ -174,11 +174,10 @@ class Axis final : public IAxis { /// @note Open varies given bin and allows 0 and NBins+1 (underflow, /// overflow) /// as neighbors - template = 0> - NeighborHoodIndices neighborHoodIndices(std::size_t idx, - std::pair sizes = { - -1, 1}) const { + template + requires(T == AxisBoundaryType::Open) NeighborHoodIndices + neighborHoodIndices(std::size_t idx, std::pair sizes = {-1, 1}) + const { constexpr int min = 0; const int max = getNBins() + 1; const int itmin = std::clamp(static_cast(idx + sizes.first), min, max); @@ -196,11 +195,10 @@ class Axis final : public IAxis { /// @return Set of neighboring bin indices (global) /// @note Bound varies given bin and allows 1 and NBins (regular bins) /// as neighbors - template = 0> - NeighborHoodIndices neighborHoodIndices(std::size_t idx, - std::pair sizes = { - -1, 1}) const { + template + requires(T == AxisBoundaryType::Bound) NeighborHoodIndices + neighborHoodIndices(std::size_t idx, std::pair sizes = {-1, 1}) + const { if (idx <= 0 || idx >= (getNBins() + 1)) { return NeighborHoodIndices(); } @@ -221,11 +219,10 @@ class Axis final : public IAxis { /// @return Set of neighboring bin indices (global) /// @note Closed varies given bin and allows bins on the opposite /// side of the axis as neighbors. (excludes underflow / overflow) - template = 0> - NeighborHoodIndices neighborHoodIndices(std::size_t idx, - std::pair sizes = { - -1, 1}) const { + template + requires(T == AxisBoundaryType::Closed) NeighborHoodIndices + neighborHoodIndices(std::size_t idx, std::pair sizes = {-1, 1}) + const { // Handle invalid indices if (idx <= 0 || idx >= (getNBins() + 1)) { return NeighborHoodIndices(); @@ -266,11 +263,9 @@ class Axis final : public IAxis { /// /// @param [in] bin The bin to wrap /// @return valid bin index - template = 0> - std::size_t wrapBin(int bin) const { - return std::max(std::min(bin, static_cast(getNBins()) + 1), 0); - } + template + requires(T == AxisBoundaryType::Open) std::size_t wrapBin(int bin) + const { return std::max(std::min(bin, static_cast(getNBins()) + 1), 0); } /// @brief Converts bin index into a valid one for this axis. /// @@ -278,11 +273,9 @@ class Axis final : public IAxis { /// /// @param [in] bin The bin to wrap /// @return valid bin index - template = 0> - std::size_t wrapBin(int bin) const { - return std::max(std::min(bin, static_cast(getNBins())), 1); - } + template + requires(T == AxisBoundaryType::Bound) std::size_t wrapBin(int bin) + const { return std::max(std::min(bin, static_cast(getNBins())), 1); } /// @brief Converts bin index into a valid one for this axis. /// @@ -290,9 +283,9 @@ class Axis final : public IAxis { /// /// @param [in] bin The bin to wrap /// @return valid bin index - template = 0> - std::size_t wrapBin(int bin) const { + template + requires(T == AxisBoundaryType::Closed) std::size_t wrapBin(int bin) + const { const int w = getNBins(); return 1 + (w + ((bin - 1) % w)) % w; // return int(bin<1)*w - int(bin>w)*w + bin; @@ -477,11 +470,10 @@ class Axis final : public IAxis { /// @note Open varies given bin and allows 0 and NBins+1 (underflow, /// overflow) /// as neighbors - template = 0> - NeighborHoodIndices neighborHoodIndices(std::size_t idx, - std::pair sizes = { - -1, 1}) const { + template + requires(T == AxisBoundaryType::Open) NeighborHoodIndices + neighborHoodIndices(std::size_t idx, std::pair sizes = {-1, 1}) + const { constexpr int min = 0; const int max = getNBins() + 1; const int itmin = std::max(min, static_cast(idx) + sizes.first); @@ -498,11 +490,10 @@ class Axis final : public IAxis { /// @return Set of neighboring bin indices (global) /// @note Bound varies given bin and allows 1 and NBins (regular bins) /// as neighbors - template = 0> - NeighborHoodIndices neighborHoodIndices(std::size_t idx, - std::pair sizes = { - -1, 1}) const { + template + requires(T == AxisBoundaryType::Bound) NeighborHoodIndices + neighborHoodIndices(std::size_t idx, std::pair sizes = {-1, 1}) + const { if (idx <= 0 || idx >= (getNBins() + 1)) { return NeighborHoodIndices(); } @@ -522,11 +513,10 @@ class Axis final : public IAxis { /// @return Set of neighboring bin indices (global) /// @note Closed varies given bin and allows bins on the opposite /// side of the axis as neighbors. (excludes underflow / overflow) - template = 0> - NeighborHoodIndices neighborHoodIndices(std::size_t idx, - std::pair sizes = { - -1, 1}) const { + template + requires(T == AxisBoundaryType::Closed) NeighborHoodIndices + neighborHoodIndices(std::size_t idx, std::pair sizes = {-1, 1}) + const { // Handle invalid indices if (idx <= 0 || idx >= (getNBins() + 1)) { return NeighborHoodIndices(); @@ -567,11 +557,9 @@ class Axis final : public IAxis { /// /// @param [in] bin The bin to wrap /// @return valid bin index - template = 0> - std::size_t wrapBin(int bin) const { - return std::max(std::min(bin, static_cast(getNBins()) + 1), 0); - } + template + requires(T == AxisBoundaryType::Open) std::size_t wrapBin(int bin) + const { return std::max(std::min(bin, static_cast(getNBins()) + 1), 0); } /// @brief Converts bin index into a valid one for this axis. /// @@ -579,11 +567,9 @@ class Axis final : public IAxis { /// /// @param [in] bin The bin to wrap /// @return valid bin index - template = 0> - std::size_t wrapBin(int bin) const { - return std::max(std::min(bin, static_cast(getNBins())), 1); - } + template + requires(T == AxisBoundaryType::Bound) std::size_t wrapBin(int bin) + const { return std::max(std::min(bin, static_cast(getNBins())), 1); } /// @brief Converts bin index into a valid one for this axis. /// @@ -591,9 +577,9 @@ class Axis final : public IAxis { /// /// @param [in] bin The bin to wrap /// @return valid bin index - template = 0> - std::size_t wrapBin(int bin) const { + template + requires(T == AxisBoundaryType::Closed) std::size_t wrapBin(int bin) + const { const int w = getNBins(); return 1 + (w + ((bin - 1) % w)) % w; // return int(bin<1)*w - int(bin>w)*w + bin; diff --git a/Core/include/Acts/Utilities/BoundingBox.hpp b/Core/include/Acts/Utilities/BoundingBox.hpp index f0824f9100c..bbb67931a3a 100644 --- a/Core/include/Acts/Utilities/BoundingBox.hpp +++ b/Core/include/Acts/Utilities/BoundingBox.hpp @@ -290,10 +290,10 @@ class AxisAlignedBoundingBox { * @param color The color to use for drawing * @param trf An optional transform to apply first. */ - template = 0> - void draw(IVisualization3D& helper, - std::array color = {120, 120, 120}, - const transform_type& trf = transform_type::Identity()) const; + template + requires(D == 3) void draw( + IVisualization3D& helper, std::array color = {120, 120, 120}, + const transform_type& trf = transform_type::Identity()) const; /** * Draw this bounding box as SVG. This method is only available for the 2D @@ -307,19 +307,23 @@ class AxisAlignedBoundingBox { * @param fillcolor Color to fill the box with. * @return The outstream given in @p os. */ - template = 0> - std::ostream& svg(std::ostream& os, value_type w, value_type h, - value_type unit = 10, const std::string& label = "", - const std::string& fillcolor = "grey") const; + template + requires(D == + 2) std::ostream& svg(std::ostream& os, value_type w, value_type h, + value_type unit = 10, + const std::string& label = "", + const std::string& fillcolor = "grey") const; private: - template = 0> - std::pair transformVertices( - const transform_type& trf) const; - - template = 0> - std::pair transformVertices( - const transform_type& trf) const; + template + requires(D == 2) std::pair transformVertices( + const transform_type& trf) + const; + + template + requires(D == 3) std::pair transformVertices( + const transform_type& trf) + const; const entity_t* m_entity; VertexType m_vmin; diff --git a/Core/include/Acts/Utilities/BoundingBox.ipp b/Core/include/Acts/Utilities/BoundingBox.ipp index 086e288e338..8121c7dc76c 100644 --- a/Core/include/Acts/Utilities/BoundingBox.ipp +++ b/Core/include/Acts/Utilities/BoundingBox.ipp @@ -275,12 +275,12 @@ std::ostream& Acts::AxisAlignedBoundingBox::toStream( } template -template > -std::pair< +template +requires(D == 3) std::pair < typename Acts::AxisAlignedBoundingBox::VertexType, - typename Acts::AxisAlignedBoundingBox::VertexType> -Acts::AxisAlignedBoundingBox::transformVertices( - const transform_type& trf) const { +typename Acts::AxisAlignedBoundingBox::VertexType > + Acts::AxisAlignedBoundingBox::transformVertices( + const transform_type& trf) const { // we need to enumerate all the vertices, transform, // and then recalculate min and max @@ -308,12 +308,12 @@ Acts::AxisAlignedBoundingBox::transformVertices( } template -template > -std::pair< +template +requires(D == 2) std::pair < typename Acts::AxisAlignedBoundingBox::VertexType, - typename Acts::AxisAlignedBoundingBox::VertexType> -Acts::AxisAlignedBoundingBox::transformVertices( - const transform_type& trf) const { +typename Acts::AxisAlignedBoundingBox::VertexType > + Acts::AxisAlignedBoundingBox::transformVertices( + const transform_type& trf) const { // we need to enumerate all the vertices, transform, // and then recalculate min and max @@ -350,10 +350,11 @@ Acts::AxisAlignedBoundingBox::transformed( } template -template > -void Acts::AxisAlignedBoundingBox::draw( - IVisualization3D& helper, std::array color, - const transform_type& trf) const { +template +requires(D == 3) void Acts::AxisAlignedBoundingBox< + entity_t, value_t, DIM>::draw(IVisualization3D& helper, + std::array color, + const transform_type& trf) const { static_assert(DIM == 3, "PLY output only supported in 3D"); const VertexType& vmin = m_vmin; @@ -385,10 +386,11 @@ void Acts::AxisAlignedBoundingBox::draw( } template -template > -std::ostream& Acts::AxisAlignedBoundingBox::svg( - std::ostream& os, value_type w, value_type h, value_type unit, - const std::string& label, const std::string& fillcolor) const { +template +requires(D == 2) + std::ostream& Acts::AxisAlignedBoundingBox::svg( + std::ostream& os, value_type w, value_type h, value_type unit, + const std::string& label, const std::string& fillcolor) const { static_assert(DIM == 2, "SVG is only supported in 2D"); VertexType mid(w / 2., h / 2.); diff --git a/Core/include/Acts/Utilities/Concepts.hpp b/Core/include/Acts/Utilities/Concepts.hpp new file mode 100644 index 00000000000..bf66d5d71ac --- /dev/null +++ b/Core/include/Acts/Utilities/Concepts.hpp @@ -0,0 +1,24 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2024 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include +#include + +namespace Acts { +/// @brief Concept that is true if T is the same as any of Ts. +template +concept same_as_any_of = (std::same_as || ...); + +/// @brief Concept that is equivalent to `is_nothrow_move_constructible`. +/// @todo Convert this to a "real" concept. +template +concept nothrow_move_constructible = + std::is_nothrow_move_constructible::value; +} // namespace Acts diff --git a/Core/include/Acts/Utilities/Delegate.hpp b/Core/include/Acts/Utilities/Delegate.hpp index 201016fc40b..1b5109fb6e1 100644 --- a/Core/include/Acts/Utilities/Delegate.hpp +++ b/Core/include/Acts/Utilities/Delegate.hpp @@ -16,7 +16,6 @@ #include namespace Acts { - /// Ownership enum for @c Delegate enum class DelegateType { Owning, NonOwning }; @@ -65,11 +64,12 @@ class Delegate { Delegate; using NonOwningDelegate = Delegate; + template - using isNoFunPtr = - std::enable_if_t, function_type> && - !std::is_same_v, OwningDelegate> && - !std::is_same_v, NonOwningDelegate>>; + using isNoFunPtr = std::conjunction< + std::negation, function_type>>, + std::negation, OwningDelegate>>, + std::negation, NonOwningDelegate>>>; public: Delegate() = default; @@ -91,8 +91,8 @@ class Delegate { /// @param callable The callable (function object or lambda) /// @note @c Delegate does not assume owner ship over @p callable. You need to ensure /// it's lifetime is longer than that of @c Delegate. - template > - explicit Delegate(Callable &callable) { + template + requires(isNoFunPtr::value) explicit Delegate(Callable &callable) { connect(callable); } @@ -110,16 +110,17 @@ class Delegate { /// @param instance The instance on which the member function pointer should be called on /// @note @c Delegate does not assume owner ship over @p instance. /// @note @c DelegateFuncTag is used to communicate the callable type - template > - Delegate(DelegateFuncTag /*tag*/, const Type *instance) { + template + requires(T == DelegateType::NonOwning) + Delegate(DelegateFuncTag /*tag*/, const Type *instance) { connect(instance); } /// Constructor from rvalue reference is deleted, should catch construction /// with temporary objects and thus invalid pointers - template > - Delegate(Callable &&) = delete; + template + requires(isNoFunPtr::value) Delegate(Callable &&) + = delete; /// Assignment operator with an explicit runtime callable /// @param callable The runtime value of the callable @@ -133,15 +134,15 @@ class Delegate { /// @param callable The callable (function object or lambda) /// @note @c Delegate does not assume owner ship over @p callable. You need to ensure /// it's lifetime is longer than that of @c Delegate. - template > - void operator=(Callable &callable) { + template + requires(isNoFunPtr::value) void operator=(Callable &callable) { connect(callable); } /// Assignment operator from rvalue reference is deleted, should catch /// assignment from temporary objects and thus invalid pointers - template > - void operator=(Callable &&) = delete; + template + requires(isNoFunPtr::value) void operator=(Callable &&) = delete; /// Connect a free function pointer. /// @note The function pointer must be ``constexpr`` for @c Delegate to accept it @@ -167,15 +168,15 @@ class Delegate { /// @param callable The callable (function object or lambda) /// @note @c Delegate does not assume owner ship over @p callable. You need to ensure /// it's lifetime is longer than that of @c Delegate. - template > - void connect(Callable &callable) { + template + requires(isNoFunPtr::value) void connect(Callable &callable) { connect<&Callable::operator(), Callable>(&callable); } /// Connection with rvalue reference is deleted, should catch assignment /// from temporary objects and thus invalid pointers - template > - void connect(Callable &&) = delete; + template + requires(isNoFunPtr::value) void connect(Callable &&) = delete; /// Connect anything that is assignable to the function pointer /// @param callable The runtime value of the callable @@ -195,9 +196,8 @@ class Delegate { /// @param instance The instance on which the member function pointer should be called on /// @note @c Delegate does not assume owner ship over @p instance. You need to ensure /// it's lifetime is longer than that of @c Delegate. - template > - void connect(const Type *instance) { + template + requires(T == DelegateType::NonOwning) void connect(const Type *instance) { using member_ptr_type = return_type (Type::*)(Args...) const; static_assert(Concepts::is_detected { /// @tparam Type The type of the instance the member function should be called on /// @param instance The instance on which the member function pointer should be called on /// @note @c Delegate assumes owner ship over @p instance. - template > - void connect(std::unique_ptr instance) { + template + requires(T == DelegateType::Owning) void connect( + std::unique_ptr instance) { using member_ptr_type = return_type (Type::*)(Args && ...) const; static_assert(Concepts::is_detected::value, @@ -266,9 +266,8 @@ class Delegate { m_function = nullptr; } - template >> - const holder_type *instance() const { + template + requires(!std::same_as) const holder_type *instance() const { return m_payload.ptr(); } diff --git a/Core/include/Acts/Utilities/Frustum.hpp b/Core/include/Acts/Utilities/Frustum.hpp index 007e3fc8b25..74584ba4fe8 100644 --- a/Core/include/Acts/Utilities/Frustum.hpp +++ b/Core/include/Acts/Utilities/Frustum.hpp @@ -51,9 +51,9 @@ class Frustum { /// @param opening_angle The opening angle /// @note The @p opening_angle is defined as the angle between opposing side /// planes. The opening angle needs to be < pi. - template = 0> - Frustum(const VertexType& origin, const VertexType& dir, - value_type opening_angle); + template + requires(D == 2) Frustum(const VertexType& origin, const VertexType& dir, + value_type opening_angle); /// Constructor for the 3D case. /// @param origin The origin of the frustum @@ -61,17 +61,18 @@ class Frustum { /// @param opening_angle The opening angle /// @note The @p opening_angle is defined as the angle between opposing side /// planes. The opening angle needs to be < pi. - template = 0> - Frustum(const VertexType& origin, const VertexType& dir, - value_type opening_angle); + template + requires(D == 3) Frustum(const VertexType& origin, const VertexType& dir, + value_type opening_angle); /// Draw a representation of this frustum using a visualization helper /// @note This is only available for the 3D case. /// @param helper The visualization helper /// @param far_distance The distance to the virtual "far plane" at which point /// the side planes terminate visually. - template = 0> - void draw(IVisualization3D& helper, value_type far_distance = 10) const; + template + requires(D == 3) void draw(IVisualization3D& helper, + value_type far_distance = 10) const; /// Draw a representation of this frustum as an SVG string to an outstream /// @note This is only available for the 2D case. @@ -81,9 +82,10 @@ class Frustum { /// @param far_distance The distance to the virtual "far line" at which point /// the side lines terminate visually. /// @param unit Multiplicative factor to apply to internal distances - template = 0> - std::ostream& svg(std::ostream& os, value_type w, value_type h, - value_type far_distance = 1, value_type unit = 20.) const; + template + requires(D == 2) std::ostream& svg(std::ostream& os, value_type w, + value_type h, value_type far_distance = 1, + value_type unit = 20.) const; /// Getter for the oriogin of the frustum /// @return The origin of the frustum diff --git a/Core/include/Acts/Utilities/Frustum.ipp b/Core/include/Acts/Utilities/Frustum.ipp index 75dbb3377ad..35a78b163e4 100644 --- a/Core/include/Acts/Utilities/Frustum.ipp +++ b/Core/include/Acts/Utilities/Frustum.ipp @@ -9,10 +9,9 @@ #include "Acts/Utilities/VectorHelpers.hpp" template -template > -Acts::Frustum::Frustum(const VertexType& origin, - const VertexType& dir, - value_type opening_angle) +template +requires(D == 2) Acts::Frustum::Frustum( + const VertexType& origin, const VertexType& dir, value_type opening_angle) : m_origin(origin) { using rotation_t = Eigen::Rotation2D; @@ -31,10 +30,9 @@ Acts::Frustum::Frustum(const VertexType& origin, } template -template > -Acts::Frustum::Frustum(const VertexType& origin, - const VertexType& dir, - value_type opening_angle) +template +requires(D == 3) Acts::Frustum::Frustum( + const VertexType& origin, const VertexType& dir, value_type opening_angle) : m_origin(origin) { static_assert(SIDES > 2, "3D frustum must have 3 or more sides"); assert(opening_angle < M_PI); @@ -74,9 +72,9 @@ Acts::Frustum::Frustum(const VertexType& origin, } template -template > -void Acts::Frustum::draw(IVisualization3D& helper, - value_type far_distance) const { +template +requires(D == 3) void Acts::Frustum::draw( + IVisualization3D& helper, value_type far_distance) const { static_assert(DIM == 3, "Drawing is only supported in 3D"); // Iterate around normals, calculate cross with "far" plane @@ -126,12 +124,10 @@ void Acts::Frustum::draw(IVisualization3D& helper, } template -template > -std::ostream& Acts::Frustum::svg(std::ostream& os, - value_type w, - value_type h, - value_type far_distance, - value_type unit) const { +template +requires(D == 2) std::ostream& Acts::Frustum::svg( + std::ostream& os, value_type w, value_type h, value_type far_distance, + value_type unit) const { static_assert(DIM == 2, "SVG is only supported in 2D"); VertexType mid(w / 2., h / 2.); diff --git a/Core/include/Acts/Utilities/Grid.hpp b/Core/include/Acts/Utilities/Grid.hpp index feb6bbaa15f..1269c45f326 100644 --- a/Core/include/Acts/Utilities/Grid.hpp +++ b/Core/include/Acts/Utilities/Grid.hpp @@ -399,11 +399,11 @@ class Grid final : public IGrid { /// indices must start at 0. /// @note Bin values are interpreted as being the field values at the /// lower-left corner of the corresponding hyper-box. - template , - std::array, U>::value>> - T interpolate(const Point& point) const { + template + requires(detail::interpolatable, + std::array>) T + interpolate(const Point& point) + const { // there are 2^DIM corner points used during the interpolation constexpr std::size_t nCorners = 1 << DIM; diff --git a/Core/include/Acts/Utilities/GridBinFinder.hpp b/Core/include/Acts/Utilities/GridBinFinder.hpp index 9d57790459d..d2c75ab7397 100644 --- a/Core/include/Acts/Utilities/GridBinFinder.hpp +++ b/Core/include/Acts/Utilities/GridBinFinder.hpp @@ -8,6 +8,7 @@ #pragma once +#include "Acts/Utilities/Concepts.hpp" #include "Acts/Utilities/Grid.hpp" #include "Acts/Utilities/Holders.hpp" #include "Acts/Utilities/detail/grid_helper.hpp" @@ -37,15 +38,11 @@ class GridBinFinder { /// @pre The provided paramers must be of type 'int', 'std::pair' or 'std::vector>' /// no other type is allowed. The order of these parameters must correspond to /// the same ordering of the axes in the grid - template >, - std::is_same, std::decay_t>, - std::is_same>, - std::decay_t>>...>>> - explicit GridBinFinder(args&&... vals) { + template + requires(sizeof...(args) == DIM && + (same_as_any_of, int, std::pair, + std::vector>> && + ...)) explicit GridBinFinder(args&&... vals) { storeValue(std::forward(vals)...); } diff --git a/Core/include/Acts/Utilities/Interpolation.hpp b/Core/include/Acts/Utilities/Interpolation.hpp index 368104e21b5..6e618227bc6 100644 --- a/Core/include/Acts/Utilities/Interpolation.hpp +++ b/Core/include/Acts/Utilities/Interpolation.hpp @@ -72,12 +72,10 @@ namespace Acts { /// - (4,5,3): 110 = 6 /// - (4,5,6): 111 = 7 template ::value>> -inline T interpolate(const Point1& position, const Point2& lowerCorner, - const Point3& upperCorner, - const std::array& values) { + class Point3 = Point2> +requires(detail::interpolatable) inline T + interpolate(const Point1& position, const Point2& lowerCorner, + const Point3& upperCorner, const std::array& values) { return detail::interpolate_impl::value - 1, N>::run(position, lowerCorner, upperCorner, diff --git a/Core/include/Acts/Utilities/RangeXD.hpp b/Core/include/Acts/Utilities/RangeXD.hpp index d614004234e..484fd6b89d8 100644 --- a/Core/include/Acts/Utilities/RangeXD.hpp +++ b/Core/include/Acts/Utilities/RangeXD.hpp @@ -60,15 +60,15 @@ class RangeXD { /// @note Only available for one-dimensional ranges /// @param minimum The minimum value of the range /// @param maximum The maximum value of the range - template > - RangeXD(Type minimum, Type maximum) + template + requires(I == 1) RangeXD(Type minimum, Type maximum) : m_minima({minimum}), m_maxima({maximum}) {} /// @brief Construct a range from a pair of minimum and maximum values /// @note Only available for one-dimensional ranges /// @param p The pair of minimum and maximum values - template > - RangeXD(const std::pair& p) + template + requires(I == 1) RangeXD(const std::pair& p) : m_minima({p.first}), m_maxima({p.second}) {} /// @brief Determine whether this range is degenerate @@ -430,10 +430,8 @@ class RangeXD { /// range would be smaller than the current range), this is a no-op. /// /// @param v The proposed new minimum for the range - template > - void expandMin(const Type& v) { - min() = std::min(min(), v); - } + template + requires(I == 1) void expandMin(const Type& v) { min() = std::min(min(), v); } /// @brief Expand a range by increasing the maximum value /// @@ -442,10 +440,8 @@ class RangeXD { /// range would be smaller than the current range), this is a no-op. /// /// @param v The proposed new maximum for the range - template > - void expandMax(const Type& v) { - max() = std::max(max(), v); - } + template + requires(I == 1) void expandMax(const Type& v) { max() = std::max(max(), v); } /// @brief Expand a range on both ends /// @@ -459,8 +455,8 @@ class RangeXD { /// /// @param min The proposed new minimum for the range /// @param max The proposed new maximum for the range - template > - void expand(const Type& min, const Type& max) { + template + requires(I == 1) void expand(const Type& min, const Type& max) { expandMin(min); expandMax(max); } @@ -472,10 +468,8 @@ class RangeXD { /// range would be larger than the current range), this is a no-op. /// /// @param v The proposed new minimum for the range - template > - void shrinkMin(const Type& v) { - min() = std::max(min(), v); - } + template + requires(I == 1) void shrinkMin(const Type& v) { min() = std::max(min(), v); } /// @brief Shrink a range by decreasing the maximum value /// @@ -484,10 +478,8 @@ class RangeXD { /// range would be larger than the current range), this is a no-op. /// /// @param v The proposed new maximum for the range - template > - void shrinkMax(const Type& v) { - max() = std::min(max(), v); - } + template + requires(I == 1) void shrinkMax(const Type& v) { max() = std::min(max(), v); } /// @brief Shrink a range on both ends /// @@ -501,8 +493,8 @@ class RangeXD { /// /// @param min The proposed new minimum for the range /// @param max The proposed new maximum for the range - template > - void shrink(const Type& min, const Type& max) { + template + requires(I == 1) void shrink(const Type& min, const Type& max) { shrinkMin(min); shrinkMax(max); } @@ -516,10 +508,8 @@ class RangeXD { /// expand methods. /// /// @param v The value to use as the new minimum - template > - void setMin(const Type& v) { - min() = v; - } + template + requires(I == 1) void setMin(const Type& v) { min() = v; } /// @brief Set the maximum value /// @@ -530,10 +520,8 @@ class RangeXD { /// expand methods. /// /// @param v The value to use as the new maximum - template > - void setMax(const Type& v) { - max() = v; - } + template + requires(I == 1) void setMax(const Type& v) { max() = v; } /// @brief Set the minimum and maximum value /// @@ -548,35 +536,29 @@ class RangeXD { /// /// @param min The new minimum value of the range /// @param max The new maximum value of the range - template > - void set(const Type& min, const Type& max) { + template + requires(I == 1) void set(const Type& min, const Type& max) { setMin(min); setMax(max); } /// @brief Return the minimum value of the range (inclusive) - template > - Type min() const { - return min(0); - } + template + requires(I == 1) Type min() + const { return min(0); } /// @brief Return the minimum value of the range (inclusive) - template > - Type& min() { - return min(0); - } + template + requires(I == 1) Type& min() { return min(0); } /// @brief Return the maximum value of the range (inclusive) - template > - Type max() const { - return max(0); - } + template + requires(I == 1) Type max() + const { return max(0); } /// @brief Return the maximum value of the range (inclusive) - template > - Type& max() { - return max(0); - } + template + requires(I == 1) Type& max() { return max(0); } /// @brief Compute the size of the range /// @@ -590,10 +572,9 @@ class RangeXD { /// this can be awkward when working with integers. /// /// @return The size of the range - template > - Type size() const { - return std::max(static_cast(0), max() - min()); - } + template + requires(I == 1) Type size() + const { return std::max(static_cast(0), max() - min()); } /// @brief Determine if the range contains a given value /// @@ -604,8 +585,8 @@ class RangeXD { /// /// @return true The value is inside the range /// @return false The value is not inside the range - template > - bool contains(const Type& v) const { + template + requires(I == 1) bool contains(const Type& v) const { return min() <= v && v < max(); } diff --git a/Core/include/Acts/Utilities/Ray.hpp b/Core/include/Acts/Utilities/Ray.hpp index f3f7635569d..44ed32e684f 100644 --- a/Core/include/Acts/Utilities/Ray.hpp +++ b/Core/include/Acts/Utilities/Ray.hpp @@ -59,8 +59,9 @@ class Ray { /// Helper to draw this ray using a given visualization helper. /// @param helper The visualization helper /// @param far_distance The "length" of the drawn line representing the ray - template = 0> - void draw(IVisualization3D& helper, value_type far_distance = 10) const; + template + requires(D == 3) void draw(IVisualization3D& helper, + value_type far_distance = 10) const; private: VertexType m_origin; diff --git a/Core/include/Acts/Utilities/Ray.ipp b/Core/include/Acts/Utilities/Ray.ipp index 5d7279f7796..08ba6b8a9b1 100644 --- a/Core/include/Acts/Utilities/Ray.ipp +++ b/Core/include/Acts/Utilities/Ray.ipp @@ -38,9 +38,10 @@ Acts::Ray Acts::Ray::transformed( } template -template > -void Acts::Ray::draw(IVisualization3D& helper, - value_type far_distance) const { +template +requires(D == + 3) void Acts::Ray::draw(IVisualization3D& helper, + value_type far_distance) const { static_assert(DIM == 3, "OBJ is only supported in 3D"); helper.line(m_origin, (m_origin + m_dir * far_distance).eval()); diff --git a/Core/include/Acts/Utilities/Result.hpp b/Core/include/Acts/Utilities/Result.hpp index f865ee1e64c..43342e9e2d4 100644 --- a/Core/include/Acts/Utilities/Result.hpp +++ b/Core/include/Acts/Utilities/Result.hpp @@ -8,6 +8,7 @@ #pragma once +#include #include #include #include @@ -61,14 +62,13 @@ class Result { /// @tparam T2 Type of the potential assignment /// @param value The potential value, could be an actual valid value or an /// error. - template < - typename T2, typename _E = E, typename _T = T, - typename = std::enable_if_t< - (!std::is_same_v<_T, _E> && !std::is_constructible_v<_T, _E> && - !std::is_convertible_v<_T, _E> && !std::is_constructible_v<_E, _T> && - !std::is_convertible_v<_E, _T> && - !(std::is_convertible_v && std::is_convertible_v))>> - Result(T2 value) noexcept + template + requires(!std::same_as<_T, _E> && !std::constructible_from<_T, _E> && + !std::convertible_to<_T, _E> && !std::constructible_from<_E, _T> && + !std::convertible_to<_E, _T> && + !(std::convertible_to && std::convertible_to)) + Result(T2 value) + noexcept : m_var(std::conditional_t, T, E>{ std::move(value)}) {} @@ -79,14 +79,13 @@ class Result { /// @param value The potential value, could be an actual valid value or an /// error. /// @return The assigned instance - template < - typename T2, typename _E = E, typename _T = T, - typename = std::enable_if_t< - (!std::is_same_v<_T, _E> && !std::is_constructible_v<_T, _E> && - !std::is_convertible_v<_T, _E> && !std::is_constructible_v<_E, _T> && - !std::is_convertible_v<_E, _T> && - !(std::is_convertible_v && std::is_convertible_v))>> - Result& operator=(T2 value) noexcept { + template + requires(!std::same_as<_T, _E> && !std::constructible_from<_T, _E> && + !std::convertible_to<_T, _E> && !std::constructible_from<_E, _T> && + !std::convertible_to<_E, _T> && + !(std::convertible_to && + std::convertible_to)) Result + &operator=(T2 value) noexcept { m_var = std::move(std::conditional_t, T, E>{ std::move(value)}); return *this; diff --git a/Core/include/Acts/Utilities/VectorHelpers.hpp b/Core/include/Acts/Utilities/VectorHelpers.hpp index ee0b04d87c8..5c91f316eb3 100644 --- a/Core/include/Acts/Utilities/VectorHelpers.hpp +++ b/Core/include/Acts/Utilities/VectorHelpers.hpp @@ -21,14 +21,6 @@ namespace Acts::VectorHelpers { -namespace detail { -template -using phi_method_t = decltype(std::declval().phi()); - -template -using has_phi_method = Concepts::is_detected; -} // namespace detail - /// Calculate phi (transverse plane angle) from compatible Eigen types /// @tparam Derived Eigen derived concrete type /// @param v Any vector like Eigen type, static or dynamic @@ -55,8 +47,10 @@ double phi(const Eigen::MatrixBase& v) noexcept { /// @tparam T anything that has a phi method /// @param v Any type that implements a phi method /// @return The phi value -template ::value, int> = 0> +template +requires requires(const T& v) { + {v.phi()}; +} double phi(const T& v) noexcept { return v.phi(); } diff --git a/Core/include/Acts/Utilities/detail/interpolation_impl.hpp b/Core/include/Acts/Utilities/detail/interpolation_impl.hpp index 701c1659e26..91308352cd5 100644 --- a/Core/include/Acts/Utilities/detail/interpolation_impl.hpp +++ b/Core/include/Acts/Utilities/detail/interpolation_impl.hpp @@ -29,8 +29,7 @@ namespace Acts::detail { /// /// is @c true if all @c Point types and @c Value fulfill the type /// requirements for being used in the interpolation function, otherwise it is -/// @c false. This expression can be employed in @c std::enable_if_t to use -/// SFINAE patterns to enable/disable (member) functions. +/// @c false. template struct can_interpolate { template @@ -58,6 +57,12 @@ struct can_interpolate { decltype(point_type_test(nullptr))>::value; }; +/// @brief concept equivalent to `can_interpolate` +/// @todo this is a concept based on a traditional type trait, meaning it won't +/// have nice errors; should be rewritten as a real concept! +template +concept interpolatable = detail::can_interpolate::value; + /// @brief determine number of dimension from power of 2 /// /// @tparam N power of 2 diff --git a/Examples/Framework/include/ActsExamples/Framework/WhiteBoard.hpp b/Examples/Framework/include/ActsExamples/Framework/WhiteBoard.hpp index d70d2c00260..6d6fdedf81a 100644 --- a/Examples/Framework/include/ActsExamples/Framework/WhiteBoard.hpp +++ b/Examples/Framework/include/ActsExamples/Framework/WhiteBoard.hpp @@ -8,6 +8,7 @@ #pragma once +#include #include #include @@ -71,9 +72,7 @@ class WhiteBoard { virtual ~IHolder() = default; virtual const std::type_info& type() const = 0; }; - template ::value>> + template struct HolderT : public IHolder { T value; diff --git a/Examples/Python/include/Acts/Plugins/Python/Utilities.hpp b/Examples/Python/include/Acts/Plugins/Python/Utilities.hpp index b0e0e7c51ce..86bdf1ca353 100644 --- a/Examples/Python/include/Acts/Plugins/Python/Utilities.hpp +++ b/Examples/Python/include/Acts/Plugins/Python/Utilities.hpp @@ -24,8 +24,8 @@ struct Context { pybind11::module_& get(const std::string& name) { return modules.at(name); } - template = 2>> - auto get(Args&&... args) { + template + requires(sizeof...(Args) >= 2) auto get(Args&&... args) { return std::make_tuple((modules.at(args))...); } }; diff --git a/Tests/CommonHelpers/Acts/Tests/CommonHelpers/FloatComparisons.hpp b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/FloatComparisons.hpp index e6b9550e728..e8a79fc4318 100644 --- a/Tests/CommonHelpers/Acts/Tests/CommonHelpers/FloatComparisons.hpp +++ b/Tests/CommonHelpers/Acts/Tests/CommonHelpers/FloatComparisons.hpp @@ -155,23 +155,20 @@ predicate_result matrixCompare(const Eigen::DenseBase& val, } template -using has_begin_t = decltype(std::declval().cbegin()); -template -using has_end_t = decltype(std::declval().cend()); -template -using has_eval_t = decltype(std::declval().eval()); +concept is_eigen_type = requires(const T& t) { + {t.eval()}; +}; // STL container frontend // // FIXME: The algorithm only supports ordered containers, so the API should // only accept them. Does someone know a clean way to do that in C++? // -template && - Acts::Concepts::exists && - Acts::Concepts::exists, - int>> +template +requires(!is_eigen_type) && requires(const Container& t) { + {t.cbegin()}; + {t.cend()}; +} predicate_result compare(const Container& val, const Container& ref, ScalarComparison&& compareImpl) { // Make sure that the two input containers have the same number of items