Skip to content

Commit 47f4820

Browse files
authored
1407 Introduce a naming scheme for concepts (#1412)
1 parent 6233316 commit 47f4820

File tree

10 files changed

+60
-137
lines changed

10 files changed

+60
-137
lines changed

cpp/memilio/compartments/compartmental_model.h

Lines changed: 22 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -22,46 +22,27 @@
2222

2323
#include "memilio/config.h"
2424
#include "memilio/math/eigen.h"
25-
#include "memilio/utils/custom_index_array.h"
26-
#include "memilio/utils/metaprogramming.h"
27-
#include <cstddef>
28-
#include <type_traits>
29-
#include <vector>
30-
#include <functional>
25+
#include <concepts>
3126

3227
namespace mio
3328
{
3429

35-
namespace details
36-
{
37-
//helpers for check_constraint
38-
template <class T>
39-
using check_constraints_expr_t = decltype(std::declval<T>().check_constraints());
40-
41-
//helpers for apply_constraints
30+
/// @brief Check that the given type has a check_constraints member function.
4231
template <class T>
43-
using apply_constraints_expr_t = decltype(std::declval<T>().apply_constraints());
44-
45-
} //namespace details
46-
47-
/**
48-
* @brief Check whether a check_constraints function exists.
49-
* @tparam The type to check for the existence of the member function.
50-
*/
51-
template <class T>
52-
using has_check_constraints_member_function = is_expression_valid<details::check_constraints_expr_t, T>;
32+
concept HasCheckConstraints = requires(T t) {
33+
{ t.check_constraints() } -> std::same_as<bool>;
34+
};
5335

54-
/**
55-
* @brief Check whether a apply_constraints function exists.
56-
* @tparam The type to check for the existence of the member function.
57-
*/
36+
/// @brief Check that the given type has an apply_constraints member function.
5837
template <class T>
59-
using has_apply_constraints_member_function = is_expression_valid<details::apply_constraints_expr_t, T>;
38+
concept HasApplyConstraints = requires(T t) {
39+
{ t.apply_constraints() } -> std::same_as<bool>;
40+
};
6041

6142
/**
6243
* @brief CompartmentalModel is a template for a compartmental model for an
6344
* array of initial populations and a parameter set.
64-
* @tparam FP floating point type, e.g., double.
45+
* @tparam FP A floating point type, e.g., double.
6546
*
6647
* The Populations must be a concrete class derived from the Populations template,
6748
* i.e. a multi-dimensional array of compartment populations where each dimension
@@ -148,7 +129,7 @@ struct CompartmentalModel {
148129
*/
149130
bool apply_constraints()
150131
{
151-
if constexpr (has_apply_constraints_member_function<ParameterSet>::value) {
132+
if constexpr (HasApplyConstraints<ParameterSet>) {
152133
// use bitwise instead of logical or, so that both apply_constraint functions are called
153134
return ((int)parameters.apply_constraints() | (int)populations.apply_constraints());
154135
}
@@ -163,7 +144,7 @@ struct CompartmentalModel {
163144
*/
164145
bool check_constraints() const
165146
{
166-
if constexpr (has_check_constraints_member_function<ParameterSet>::value) {
147+
if constexpr (HasCheckConstraints<ParameterSet>) {
167148
return (parameters.check_constraints() || populations.check_constraints());
168149
}
169150
else {
@@ -176,42 +157,17 @@ struct CompartmentalModel {
176157
};
177158

178159
/**
179-
* Detect the eval_right_hand_side member function of a compartment model.
180-
* If the eval_right_hand_side member function exists in the type M, this template when instatiated
181-
* will be equal to the return type of the function.
182-
* Otherwise the template is invalid.
183-
* @tparam FP, floating point type, e.g., double.
184-
* @tparam M a type that has a eval_right_hand_side member function, e.g. a compartment model type.
185-
*/
186-
template <typename FP, class M>
187-
using eval_right_hand_side_expr_t = decltype(std::declval<const M&>().eval_right_hand_side(
188-
std::declval<Eigen::Ref<const Eigen::VectorX<FP>>>(), std::declval<Eigen::Ref<const Eigen::VectorX<FP>>>(),
189-
std::declval<FP>(), std::declval<Eigen::Ref<Eigen::VectorX<FP>>>()));
190-
191-
/**
192-
* Detect the get_initial_values member function of a compartment model.
193-
* If the detect_initial_values member function exists in the type M, this template when instatiated
194-
* will be equal to the return type of the function.
195-
* Otherwise the template is invalid.
196-
* @tparam FP, floating point type, e.g., double.
197-
* @tparam M a type that has a get_initial_values member function, e.g. a compartment model type.
198-
*/
199-
template <typename FP, class M>
200-
using get_initial_values_expr_t =
201-
decltype(std::declval<Eigen::VectorX<FP>&>() = std::declval<const M&>().get_initial_values());
202-
203-
/**
204-
* Template meta function to check if a type is a valid compartment model.
205-
* Defines a static constant of name `value`.
206-
* The constant `value` will be equal to true if M is a valid compartment model type.
207-
* Otherwise, `value` will be equal to false.
208-
* @tparam FP, floating point type, e.g., double.
209-
* @tparam M a type that may or may not be a compartment model.
160+
* @brief Concept to check if a type is a valid compartment model.
161+
* Note that Model must be the first template argument
162+
* @tparam Model A type that may or may not be a compartment model.
163+
* @tparam FP A floating point type, e.g. double.
210164
*/
211-
template <typename FP, class M>
212-
using is_compartment_model =
213-
std::integral_constant<bool, (is_expression_valid<eval_right_hand_side_expr_t, FP, M>::value &&
214-
is_expression_valid<get_initial_values_expr_t, FP, M>::value)>;
165+
template <class Model, typename FP>
166+
concept IsCompartmentalModel =
167+
requires(Model m, Eigen::Ref<const Eigen::VectorX<FP>> pop_y, Eigen::Ref<Eigen::VectorX<FP>> dydt, FP t) {
168+
{ m.get_initial_values() } -> std::convertible_to<Eigen::VectorX<FP>>;
169+
m.eval_right_hand_side(pop_y, pop_y, t, dydt);
170+
};
215171

216172
} // namespace mio
217173

cpp/memilio/compartments/flow_model.h

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -235,41 +235,19 @@ class FlowModel : public CompartmentalModel<FP, Comp, Pop, Params>
235235
};
236236

237237
/**
238-
* Detect whether certain member functions (the name before '_expr_t') exist.
239-
* If the member function exists in the type M, this template when instatiated
240-
* will be equal to the return type of the function. Otherwise the template is invalid.
241-
* @tparam FP floating point type, e.g. double.
242-
* @tparam M Any class, e.g. FlowModel.
243-
* @{
244-
*/
245-
template <typename FP, class M>
246-
using get_derivatives_expr_t = decltype(std::declval<const M&>().get_derivatives(
247-
std::declval<Eigen::Ref<const Eigen::VectorX<FP>>>(), std::declval<Eigen::Ref<Eigen::VectorX<FP>>>()));
248-
249-
template <typename FP, class M>
250-
using get_flows_expr_t =
251-
decltype(std::declval<const M&>().get_flows(std::declval<Eigen::Ref<const Eigen::VectorX<FP>>>(),
252-
std::declval<Eigen::Ref<const Eigen::VectorX<FP>>>(),
253-
std::declval<FP>(), std::declval<Eigen::Ref<Eigen::VectorX<FP>>>()));
254-
255-
template <typename FP, class M>
256-
using get_initial_flows_expr_t =
257-
decltype(std::declval<Eigen::VectorX<FP>>() = std::declval<const M&>().get_initial_flows());
258-
/** @} */
259-
260-
/**
261-
* Template meta function to check if a type is a valid flow model.
262-
* Defines a static constant of name `value`.
263-
* The constant `value` will be equal to true if M is a valid flow model type.
264-
* Otherwise, `value` will be equal to false.
265-
* @tparam FP floating point type, e.g. double.
238+
* @brief Concept to check if a type is a valid flow model.
239+
* Note that Model must be the first template argument
266240
* @tparam Model A type that may or may not be a flow model.
241+
* @tparam FP A floating point type, e.g. double.
267242
*/
268-
template <typename FP, class Model>
269-
using is_flow_model = std::integral_constant<bool, (is_expression_valid<get_derivatives_expr_t, FP, Model>::value &&
270-
is_expression_valid<get_flows_expr_t, FP, Model>::value &&
271-
is_expression_valid<get_initial_flows_expr_t, FP, Model>::value &&
272-
is_compartment_model<FP, Model>::value)>;
243+
template <class Model, typename FP>
244+
concept IsFlowModel =
245+
requires(Model m, Eigen::Ref<const Eigen::VectorX<FP>> const_vref, Eigen::Ref<Eigen::VectorX<FP>> vref, FP t) {
246+
requires IsCompartmentalModel<Model, FP>;
247+
{ m.get_initial_flows() } -> std::convertible_to<Eigen::VectorX<FP>>;
248+
m.get_flows(const_vref, const_vref, t, vref);
249+
m.get_derivatives(const_vref, vref);
250+
};
273251

274252
} // namespace mio
275253

cpp/memilio/compartments/flow_simulation.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#ifndef MIO_COMPARTMENTS_FLOW_SIMULATION_H
2121
#define MIO_COMPARTMENTS_FLOW_SIMULATION_H
2222

23+
#include "memilio/compartments/flow_model.h"
2324
#include "memilio/compartments/flow_simulation_base.h"
2425
#include "memilio/compartments/simulation.h"
2526
#include "memilio/math/integrator.h"
@@ -32,7 +33,7 @@ namespace mio
3233
* @tparam FP A floating point type, e.g. double.
3334
* @tparam M An implementation of a FlowModel.
3435
*/
35-
template <typename FP, class M>
36+
template <typename FP, IsFlowModel<FP> M>
3637
class FlowSimulation : public details::FlowSimulationBase<FP, M, OdeIntegrator<FP>>
3738
{
3839
public:

cpp/memilio/compartments/flow_simulation_base.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,9 @@ namespace details
3737
* @tparam M An implementation of FlowModel.
3838
* @tparam Integrands One or more function types used for defining the right hand side of a system of equations.
3939
*/
40-
template <typename FP, class M, class... Integrands>
40+
template <typename FP, IsFlowModel<FP> M, class... Integrands>
4141
class FlowSimulationBase : public SimulationBase<FP, M, Integrands...>
4242
{
43-
static_assert(is_flow_model<FP, M>::value, "Template parameter must be a flow model.");
4443

4544
public:
4645
using Base = SimulationBase<FP, M, Integrands...>;

cpp/memilio/compartments/simulation.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ using DefaultIntegratorCore = mio::ControlledStepperWrapper<FP, boost::numeric::
3939
* @tparam FP A floating point type, e.g. double.
4040
* @tparam M An implementation of a CompartmentalModel.
4141
*/
42-
template <typename FP, class M>
42+
template <typename FP, IsCompartmentalModel<FP> M>
4343
class Simulation : public details::SimulationBase<FP, M, OdeIntegrator<FP>>
4444
{
4545
public:
@@ -95,7 +95,7 @@ using advance_expr_t = decltype(std::declval<Sim>().advance(std::declval<FP>()))
9595
template <typename FP, class Sim>
9696
using is_compartment_model_simulation =
9797
std::integral_constant<bool, (is_expression_valid<advance_expr_t, FP, Sim>::value &&
98-
is_compartment_model<FP, typename Sim::Model>::value)>;
98+
IsCompartmentalModel<typename Sim::Model, FP>)>;
9999

100100
/**
101101
* @brief Run a Simulation of a CompartmentalModel.

cpp/memilio/compartments/simulation_base.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,9 @@ namespace details
3737
* @tparam M An implementation of CompartmentalModel.
3838
* @tparam Integrands One or more function types used for defining the right hand side of a system of equations.
3939
*/
40-
template <typename FP, class M, class... Integrands>
40+
template <typename FP, IsCompartmentalModel<FP> M, class... Integrands>
4141
class SimulationBase
4242
{
43-
static_assert(is_compartment_model<FP, M>::value, "Template parameter must be a compartment model.");
4443

4544
public:
4645
using Model = M;

cpp/memilio/compartments/stochastic_model.h

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -92,27 +92,18 @@ class StochasticModel
9292
mutable RandomNumberGenerator m_rng; ///< RNG for generating white noise in the get_noise implementation.
9393
};
9494

95-
namespace details
96-
{
97-
template <typename FP, class M>
98-
using get_noise_expr_t =
99-
decltype(std::declval<const M&>().get_noise(std::declval<Eigen::Ref<const Eigen::VectorX<FP>>>(),
100-
std::declval<Eigen::Ref<const Eigen::VectorX<FP>>>(),
101-
std::declval<FP>(), std::declval<Eigen::Ref<Eigen::VectorX<FP>>>()));
102-
}
103-
10495
/**
105-
* Template meta function to check if a type is a valid stochastic model.
106-
* Defines a static constant of name `value`.
107-
* The constant `value` will be equal to true if M is a valid stochastic model type.
108-
* Otherwise, `value` will be equal to false.
109-
* @tparam FP A floating point type, e.g. double.
96+
* @brief Concept to check if a type is a valid stochastic model.
97+
* Note that Model must be the first template argument
11098
* @tparam Model A type that may or may not be a stochastic model.
99+
* @tparam FP A floating point type, e.g. double.
111100
*/
112-
template <typename FP, class Model>
113-
using is_stochastic_model =
114-
std::integral_constant<bool, (is_expression_valid<details::get_noise_expr_t, FP, Model>::value &&
115-
is_compartment_model<FP, Model>::value)>;
101+
template <class Model, typename FP>
102+
concept IsStochasticModel =
103+
requires(Model m, Eigen::Ref<const Eigen::VectorX<FP>> const_vref, Eigen::Ref<Eigen::VectorX<FP>> vref, FP t) {
104+
requires IsCompartmentalModel<Model, FP>;
105+
m.get_noise(const_vref, const_vref, t, vref);
106+
};
116107

117108
} // namespace mio
118109

cpp/memilio/compartments/stochastic_simulation.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,9 @@ namespace mio
3434
* @tparam FP A floating point type, e.g. double.
3535
* @tparam M An implementation of a StochasticModel.
3636
*/
37-
template <typename FP, class M>
37+
template <typename FP, IsStochasticModel<FP> M>
3838
class StochasticSimulation : public details::SimulationBase<FP, M, SdeIntegrator<FP>>
3939
{
40-
static_assert(is_stochastic_model<FP, M>::value, "Template parameter must be a stochastic model.");
41-
4240
public:
4341
using Base = details::SimulationBase<FP, M, SdeIntegrator<FP>>;
4442
using Model = M;

cpp/memilio/geography/rtree.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ namespace geo
4242
* The location type needs to provide get_latitude() and get_longitude() methods returning floating point types.
4343
*/
4444
template <class Location>
45-
concept SphericalLocationType = requires(const Location& loc) {
45+
concept IsSphericalLocation = requires(const Location& loc) {
4646
std::is_floating_point_v<decltype(loc.get_latitude())> && std::is_floating_point_v<decltype(loc.get_longitude())>;
4747
};
4848

@@ -69,7 +69,7 @@ class RTree
6969
*
7070
* @param locations A vector of geographical locations, they need to provide get_latitude() and get_longitude().
7171
*/
72-
template <SphericalLocationType Location>
72+
template <IsSphericalLocation Location>
7373
RTree(const std::vector<Location>& locations)
7474
: rtree{}
7575
{
@@ -96,7 +96,7 @@ class RTree
9696
* @param number The number of nearest neighbours to find.
9797
* @return Vector with indices of the nearest neighbours.
9898
*/
99-
std::vector<size_t> nearest_neighbor_indices(const SphericalLocationType auto& location, size_t number) const
99+
std::vector<size_t> nearest_neighbor_indices(const IsSphericalLocation auto& location, size_t number) const
100100
{
101101
using boost::geometry::index::nearest;
102102
using boost::geometry::index::query;
@@ -114,7 +114,7 @@ class RTree
114114
* @param radius The radius of the query.
115115
* @return Vector with indices of the points found.
116116
*/
117-
std::vector<size_t> in_range_indices_approximate(const SphericalLocationType auto& location, Distance radius) const
117+
std::vector<size_t> in_range_indices_approximate(const IsSphericalLocation auto& location, Distance radius) const
118118
{
119119
using boost::geometry::index::covered_by;
120120
using boost::geometry::index::query;
@@ -132,7 +132,7 @@ class RTree
132132
* @param radii Vector containing the radii of the query.
133133
* @return Vector of vectors with indices of the points found.
134134
*/
135-
std::vector<std::vector<size_t>> in_range_indices_query(const SphericalLocationType auto& location,
135+
std::vector<std::vector<size_t>> in_range_indices_query(const IsSphericalLocation auto& location,
136136
const std::vector<Distance>& radii) const
137137
{
138138
using boost::geometry::distance;
@@ -168,7 +168,7 @@ class RTree
168168
*
169169
* Basically the same as \ref in_range_indices_approximate, but filters the result to make sure the points are within the radius.
170170
*/
171-
std::vector<size_t> in_range_indices(const SphericalLocationType auto& location, Distance radius) const
171+
std::vector<size_t> in_range_indices(const IsSphericalLocation auto& location, Distance radius) const
172172
{
173173
using boost::geometry::distance;
174174
using boost::geometry::index::covered_by;
@@ -204,7 +204,7 @@ class RTree
204204
* @param approximation_points Number of points used to approximate the circle. Default is 36, i.e. we build a 36-gon.
205205
* @return multi_polygon.
206206
*/
207-
MultiPolygon create_circle_approximation(const SphericalLocationType auto& location, Distance radius,
207+
MultiPolygon create_circle_approximation(const IsSphericalLocation auto& location, Distance radius,
208208
size_t approximation_points = 36) const
209209
{
210210
using namespace boost::geometry::strategy::buffer;

docs/source/development.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,17 @@ Namespaces:
3838

3939
Naming rules:
4040

41-
- Classes begin with large Letters , e.g. ``class MyClass``.
41+
- classes begin with large letters , e.g. ``class MyClass``.
4242
- functions, methods, variables use small letters + underscore, e.g. ``my_awesome_function``.
43+
- concepts begin with a boolean prefix (like is, has, can) and use large letters, e.g. ``IsMyClass`` or ``HasMyAwesomeFunction``.
4344
- member variables should be generally private (we allow exceptions from this rule) and should be named with a leading ``m_``, e.g. ``m_my_member``.
4445

4546
Return Values:
4647

4748
- If only one object is output, use return, for multiple objects, pass by reference (we still have to check ``std::expected``).
4849
- The semantics of return value arguments have to make clear, how the ownership is handled.
4950

50-
- If the function creates an object (allocates), pass it as ``std::unique_ptr<T>&``
51+
- If the function creates an object (allocates), pass it as ``T``
5152
- If the function simply changes an object, pass is as ``T&``
5253

5354
- Avoid producing unnecessarily long outputs. Ensure that all output is concise and limited to relevant information.
@@ -64,7 +65,7 @@ Logging:
6465

6566
Includes:
6667

67-
- Please use include guards with capitalized name of the header file (``test.h -> #ifndefine TEST_H``).
68+
- Please use include guards with capitalized name of the header file (``memilio/utils/test.h -> #ifndefine MIO_UTILS_TEST_H``).
6869
- Sort includes according to
6970

7071
1. own header

0 commit comments

Comments
 (0)