Flexible compile-time customization
An example - std::vector
// Declaration of the class template, usable with any
// `T` that fulfills the requirements that `vector` poses on it.
template<typename T, typename A = std::allocator<T>>
class vector;
// Instantiation of a concrete vector - a vector of ints.
// The compiler will define this concrete type for us,
// using the definition of the class template.
std::vector<int> v;
Template implementations can be specialized for concrete types
Using class templates, we can write generic algorithms
For some classes, providing all template parameters can be very cumbersome and error-prone.
A usual way to group template parameters
template<class PV, class FSY, class FST, class SSY,...>
struct TwoPVolumeVariablesTraits
{
using PrimaryVariables = PV;
using FluidSystem = FSY;
using FluidState = FST;
using SolidSystem = SSY;
using SolidState = SST;
using PermeabilityType = PT;
using ModelTraits = MT;
using SaturationReconstruction = SR;
};
Why do we need the type?
// Type trait template declaration
template<typename T> struct ValueType;
// Specialization for vectors of T
template<typename T, typename Allocator>
struct ValueType<std::vector<T, Allocator>> { using type = T; };
// Specialization for Dune::FieldVector
template<typename T, int size>
struct ValueType<Dune::FieldVector<T, size>> { using type = T; };
template <class Traits>
class TwoPVolumeVariables
{
...
using FluidSystem = typename Traits::FluidSystem
...
Usage: these VolumeVariables will work for various FluidSystems:
Inheritance may lead to unexpected results
A simplified example to illustrate the idea
// myproperties.hh
namespace Dumux::Properties {
namespace TTag { struct MyTypeTag {}; }
// some property tag
template<typename TypeTag> struct PropTagA;
// property definition for MyTypeTag
template<>
struct PropTagA<MyTypeTag>
{ using type = /*the actual property*/; };
} // namespace Dumux::Properties
A simplified example to illustrate the idea
template<class TypeTag>
class GenericClass // e.g. LocalResidual
{
using PropA = typename Properties::PropTagA<TypeTag>::type;
using PropB = typename Properties::PropTagB<TypeTag>::type;
// ...
// A property could be, for instance, a fluid system
using FS = typename Properties::FluidSystem<TypeTag>::type;
};
Issue: Inheritance not (easily) possible. All type
traits need to be specialized for MyTypeTag
.
Goal: We would like type tags to be composable via inheritance, while providing a mechanism for customizing any property defined in the hierarchy.
Let’s implement the Vector
example using the property
system
namespace Properties {
namespace TTag { struct BaseTag {}; }
// specialization of the Scalar property for BaseTag
template<class TypeTag>
struct Scalar<TypeTag, TTag::BaseTag> { using type = int; };
// specialization of the Vector property for BaseTag
template<class TypeTag>
struct Vector<TypeTag, TTag::BaseTag> {
using Scalar = GetPropType<TypeTag, Properties::Scalar>;
using type = std::vector<Scalar>;
};
Let’s implement the Vector
example using the property
system
Creating new TypeTag nodes
Creating new property tags (empty, unset properties) \(\leftrightarrow\) Property names are unique!
Usually not needed in user code because all necessary properties are already defined in dumux/common/properties.hh.
Setting type properties for a specific type tag
MyTypeTag
Alternatively, using type alias properties for a
specific type tag MyTypeTag
Setting value properties for a specific type tag
MyTypeTag
Setting value for a alias property for a specific
type tag MyTypeTag
Getting the property type set for a TypeTag
C++ type traits
C++ type traits
Generic algorithm