SlideShare a Scribd company logo
1 of 168
Download to read offline
Mateusz Pusz
September 17, 2019
Rethinking the Way We Do
Templates in C++
1 User experience
2 New toys in a toolbox
3 Performance
CppCon 2019 | Rethinking the Way We Do Templates in C++
Plan for the talk
2
CppCon 2019 | Rethinking the Way We Do Templates in C++
USER EXPERIENCE
3
// simple numeric operations
static_assert(10km / 2 == 5km);
CppCon 2019 | Rethinking the Way We Do Templates in C++
Physical Units library in a nutshell
4
// simple numeric operations
static_assert(10km / 2 == 5km);
// unit conversions
static_assert(1h == 3600s);
static_assert(1km + 1m == 1001m);
CppCon 2019 | Rethinking the Way We Do Templates in C++
Physical Units library in a nutshell
4
// simple numeric operations
static_assert(10km / 2 == 5km);
// unit conversions
static_assert(1h == 3600s);
static_assert(1km + 1m == 1001m);
// dimension conversions
static_assert(1km / 1s == 1000mps);
static_assert(2kmph * 2h == 4km);
static_assert(2km / 2kmph == 1h);
static_assert(1000 / 1s == 1kHz);
static_assert(10km / 5km == 2);
CppCon 2019 | Rethinking the Way We Do Templates in C++
Physical Units library in a nutshell
4
/* velocity */ avg_speed(/* length */ distance, /* time */ duration)
{
return distance / duration;
}
CppCon 2019 | Rethinking the Way We Do Templates in C++
Toy example
5
/* velocity */ avg_speed(/* length */ distance, /* time */ duration)
{
return distance / duration;
}
const auto kmph = avg_speed(/* 220 km */, /* 2 hours */);
std::cout << /* kmph */ << " km/hn";
CppCon 2019 | Rethinking the Way We Do Templates in C++
Toy example
5
/* velocity */ avg_speed(/* length */ distance, /* time */ duration)
{
return distance / duration;
}
const auto kmph = avg_speed(/* 220 km */, /* 2 hours */);
std::cout << /* kmph */ << " km/hn";
const auto mph = avg_speed(/* 140 miles */, /* 2 hours */);
std::cout << /* mph */ << " mphn";
CppCon 2019 | Rethinking the Way We Do Templates in C++
Toy example
5
/* velocity */ avg_speed(/* length */ distance, /* time */ duration)
{
return distance / duration;
}
const auto kmph = avg_speed(/* 220 km */, /* 2 hours */);
std::cout << /* kmph */ << " km/hn";
const auto mph = avg_speed(/* 140 miles */, /* 2 hours */);
std::cout << /* mph */ << " mphn";
• Compile time safety to make sure that the result is of a correct dimension
CppCon 2019 | Rethinking the Way We Do Templates in C++
Toy example
5
/* velocity */ avg_speed(/* length */ distance, /* time */ duration)
{
return distance / duration;
}
const auto kmph = avg_speed(/* 220 km */, /* 2 hours */);
std::cout << /* kmph */ << " km/hn";
const auto mph = avg_speed(/* 140 miles */, /* 2 hours */);
std::cout << /* mph */ << " mphn";
• Compile time safety to make sure that the result is of a correct dimension
• Support for multiple units and unit prefixes
CppCon 2019 | Rethinking the Way We Do Templates in C++
Toy example
5
/* velocity */ avg_speed(/* length */ distance, /* time */ duration)
{
return distance / duration;
}
const auto kmph = avg_speed(/* 220 km */, /* 2 hours */);
std::cout << /* kmph */ << " km/hn";
const auto mph = avg_speed(/* 140 miles */, /* 2 hours */);
std::cout << /* mph */ << " mphn";
• Compile time safety to make sure that the result is of a correct dimension
• Support for multiple units and unit prefixes
• No runtime overhead
– no additional intermediate conversions
– as fast as a custom code implemented with doubles
CppCon 2019 | Rethinking the Way We Do Templates in C++
Toy example
5
constexpr bu::quantity<bu::si::velocity> avg_speed(bu::quantity<bu::si::length> d, bu::quantity<bu::si::time> t)
{ return d * t; }
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Compilation: Boost.Units
6
constexpr bu::quantity<bu::si::velocity> avg_speed(bu::quantity<bu::si::length> d, bu::quantity<bu::si::time> t)
{ return d * t; }
error: could not convert ‘boost::units::operator*(const boost::units::quantity<Unit1, X>&,
const boost::units::quantity<Unit2, Y>&) [with Unit1 = boost::units::unit<boost::units::list<boost::units::dim
<boost::units::length_base_dimension, boost::units::static_rational<1> >, boost::units::dimensionless_type>,
boost::units::homogeneous_system<boost::units::list<boost::units::si::meter_base_unit,
boost::units::list<boost::units::scaled_base_unit<boost::units::cgs::gram_base_unit, boost::units::scale<10,
boost::units::static_rational<3> > >, boost::units::list<boost::units::si::second_base_unit,
boost::units::list<boost::units::si::ampere_base_unit, boost::units::list<boost::units::si::kelvin_base_unit,
boost::units::list<boost::units::si::mole_base_unit, boost::units::list<boost::units::si::candela_base_unit,
boost::units::list<boost::units::angle::radian_base_unit, boost::units::list<boost::units::angle::steradian_base_unit,
boost::units::dimensionless_type> > > > > > > > > > >; Unit2 = boost::units::unit<boost::units::list<boost::units::dim
<boost::units::time_base_dimension, boost::units::static_rational<1> >, boost::units::dimensionless_type>,
boost::units::homogeneous_system<boost::units::list<boost::units::si::meter_base_unit,
boost::units::list<boost::units::scaled_base_unit<boost::units::cgs::gram_base_unit, boost::units::scale<10,
boost::units::static_rational<3> > >, boost::units::list<boost::units::si::second_base_unit, boost::units::list
<boost::units::si::ampere_base_unit, boost::units::list<boost::units::si::kelvin_base_unit, boost::units::list
<boost::units::si::mole_base_unit, boost::units::list<boost::units::si::candela_base_unit, boost::units::list
<boost::units::angle::radian_base_unit, boost::units::list<boost::units::angle::steradian_base_unit,
boost::units::dimensionless_type> > > > > > > > > > >; X = double; Y = double; typename
boost::units::multiply_typeof_helper<boost::units::quantity<Unit1, X>, boost::units::quantity<Unit2, Y> >::type =
...
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Compilation: Boost.Units
GCC-8
6
constexpr bu::quantity<bu::si::velocity> avg_speed(bu::quantity<bu::si::length> d, bu::quantity<bu::si::time> t)
{ return d * t; }
...
boost::units::quantity<boost::units::unit<boost::units::list<boost::units::dim<boost::units::length_base_dimension,
boost::units::static_rational<1> >, boost::units::list<boost::units::dim<boost::units::time_base_dimension,
boost::units::static_rational<1> >, boost::units::dimensionless_type> >, boost::units::homogeneous_system
<boost::units::list<boost::units::si::meter_base_unit, boost::units::list<boost::units::scaled_base_unit
<boost::units::cgs::gram_base_unit, boost::units::scale<10, boost::units::static_rational<3> > >,
boost::units::list<boost::units::si::second_base_unit, boost::units::list<boost::units::si::ampere_base_unit,
boost::units::list<boost::units::si::kelvin_base_unit, boost::units::list<boost::units::si::mole_base_unit,
boost::units::list<boost::units::si::candela_base_unit, boost::units::list<boost::units::angle::radian_base_unit,
boost::units::list<boost::units::angle::steradian_base_unit, boost::units::dimensionless_type> > > > > > > > > >,
void>, double>](t)’ from ‘quantity<unit<list<[...],list<dim<[...],static_rational<1>>,[...]>>,[...],[...]>,[...]>’
to ‘quantity<unit<list<[...],list<dim<[...],static_rational<-1>>,[...]>>,[...],[...]>,[...]>’
return d * t;
~~^~~
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Compilation: Boost.Units
GCC-8 (CONTINUED)
7
constexpr bu::quantity<bu::si::velocity> avg_speed(bu::quantity<bu::si::length> d, bu::quantity<bu::si::time> t)
{ return d * t; }
error: no viable conversion from returned value of type 'quantity<unit<list<[...], list<dim<[...],
static_rational<1, [...]>>, [...]>>, [...]>, [...]>' to function return type 'quantity<unit<list<[...], list<dim<[...],
static_rational<-1, [...]>>, [...]>>, [...]>, [...]>'
return d * t;
^~~~~
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Compilation: Boost.Units
CLANG-7
8
constexpr bu::quantity<bu::si::velocity> avg_speed(bu::quantity<bu::si::length> d, bu::quantity<bu::si::time> t)
{ return d * t; }
error: no viable conversion from returned value of type 'quantity<unit<list<[...], list<dim<[...],
static_rational<1, [...]>>, [...]>>, [...]>, [...]>' to function return type 'quantity<unit<list<[...], list<dim<[...],
static_rational<-1, [...]>>, [...]>>, [...]>, [...]>'
return d * t;
^~~~~
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Compilation: Boost.Units
CLANG-7
Sometimes a shorter error message is not necessarily better ;-)
8
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Debugging: Boost.Units
9
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Debugging: Boost.Units
9
Breakpoint 1, avg_speed<boost::units::heterogeneous_system<boost::units::heterogeneous_system_impl
<boost::units::list<boost::units::heterogeneous_system_dim<boost::units::si::meter_base_unit, boost::units::static_rational<1> >,
boost::units::dimensionless_type>, boost::units::list<boost::units::dim<boost::units::length_base_dimension,
boost::units::static_rational<1> >, boost::units::dimensionless_type>, boost::units::list<boost::units::scale_list_dim
<boost::units::scale<10, boost::units::static_rational<3> > >, boost::units::dimensionless_type> > >,
boost::units::heterogeneous_system<boost::units::heterogeneous_system_impl<boost::units::list
<boost::units::heterogeneous_system_dim<boost::units::scaled_base_unit<boost::units::si::second_base_unit,
boost::units::scale<60, boost::units::static_rational<2> > >, boost::units::static_rational<1> >,
boost::units::dimensionless_type>, boost::units::list<boost::units::dim<boost::units::time_base_dimension,
boost::units::static_rational<1> >, boost::units::dimensionless_type>, boost::units::dimensionless_type> > > (d=..., t=...) at
velocity_2.cpp:39
39 return d / t;
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Debugging: Boost.Units
10
(gdb) ptype d
type = class boost::units::quantity<boost::units::unit<boost::units::list<boost::units::dim<boost::units::length_base_dimension,
boost::units::static_rational<1, 1> >, boost::units::dimensionless_type>, boost::units::heterogeneous_system
<boost::units::heterogeneous_system_impl<boost::units::list<boost::units::heterogeneous_system_dim
<boost::units::si::meter_base_unit, boost::units::static_rational<1, 1> >, boost::units::dimensionless_type>,
boost::units::list<boost::units::dim<boost::units::length_base_dimension, boost::units::static_rational<1, 1> >,
boost::units::dimensionless_type>, boost::units::list<boost::units::scale_list_dim<boost::units::scale<10, static_rational<3>>>,
boost::units::dimensionless_type> > >, void>, double> [with Unit = boost::units::unit<boost::units::list<boost::units::dim
<boost::units::length_base_dimension, boost::units::static_rational<1, 1> >, boost::units::dimensionless_type>,
boost::units::heterogeneous_system<boost::units::heterogeneous_system_impl<boost::units::list
<boost::units::heterogeneous_system_dim<boost::units::si::meter_base_unit, boost::units::static_rational<1, 1> >,
boost::units::dimensionless_type>, boost::units::list<boost::units::dim<boost::units::length_base_dimension,
boost::units::static_rational<1, 1> >, boost::units::dimensionless_type>, boost::units::list<boost::units::scale_list_dim
<boost::units::scale<10, static_rational<3> > >, boost::units::dimensionless_type> > >, void>, Y = double] {
...
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Debugging: Boost.Units
11
template<typename Dim, typename System, typename Enable = void>
class unit;
template<typename Unit, typename Rep = double>
class quantity;
CppCon 2019 | Rethinking the Way We Do Templates in C++
Boost.Units: Design
12
template<typename Dim, typename System, typename Enable = void>
class unit;
template<typename Unit, typename Rep = double>
class quantity;
struct length_base_dimension : base_dimension<length_base_dimension, -9> {};
struct time_base_dimension : base_dimension<time_base_dimension, -7> {};
using velocity_dimension = derived_dimension<length_base_dimension, 1, time_base_dimension, -1>::type;
CppCon 2019 | Rethinking the Way We Do Templates in C++
Boost.Units: Design
12
template<typename Dim, typename System, typename Enable = void>
class unit;
template<typename Unit, typename Rep = double>
class quantity;
struct length_base_dimension : base_dimension<length_base_dimension, -9> {};
struct time_base_dimension : base_dimension<time_base_dimension, -7> {};
using velocity_dimension = derived_dimension<length_base_dimension, 1, time_base_dimension, -1>::type;
namespace si {
using system = make_system<meter_base_unit, kilogram_base_unit, second_base_unit, ampere_base_unit,
kelvin_base_unit, mole_base_unit, candela_base_unit,
angle::radian_base_unit, angle::steradian_base_unit>::type;
}
CppCon 2019 | Rethinking the Way We Do Templates in C++
Boost.Units: Design
12
template<typename Dim, typename System, typename Enable = void>
class unit;
template<typename Unit, typename Rep = double>
class quantity;
struct length_base_dimension : base_dimension<length_base_dimension, -9> {};
struct time_base_dimension : base_dimension<time_base_dimension, -7> {};
using velocity_dimension = derived_dimension<length_base_dimension, 1, time_base_dimension, -1>::type;
namespace si {
using system = make_system<meter_base_unit, kilogram_base_unit, second_base_unit, ampere_base_unit,
kelvin_base_unit, mole_base_unit, candela_base_unit,
angle::radian_base_unit, angle::steradian_base_unit>::type;
}
using velocity = unit<velocity_dimension, si::system>;
CppCon 2019 | Rethinking the Way We Do Templates in C++
Boost.Units: Design
12
constexpr units::velocity::meters_per_second_t avg_speed(units::length::meter_t d, units::time::second_t t)
{ return d * t; }
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Compilation: NHolthaus Units
13
constexpr units::velocity::meters_per_second_t avg_speed(units::length::meter_t d, units::time::second_t t)
{ return d * t; }
error: static assertion failed: Units are not compatible.
static_assert(traits::is_convertible_unit<UnitFrom, UnitTo>::value, "Units are not compatible.");
^~~~~~
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Compilation: NHolthaus Units
GCC-8
13
constexpr units::velocity::meters_per_second_t avg_speed(units::length::meter_t d, units::time::second_t t)
{ return d * t; }
error: static assertion failed: Units are not compatible.
static_assert(traits::is_convertible_unit<UnitFrom, UnitTo>::value, "Units are not compatible.");
^~~~~~
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Compilation: NHolthaus Units
GCC-8
static_assert is o en not the best solution
• does not influence the overload resolution process
• for some compilers does not provide enough context
13
constexpr units::velocity::meters_per_second_t avg_speed(units::length::meter_t d, units::time::second_t t)
{ return d * t; }
error: static_assert failed due to requirement 'traits::is_convertible_unit<unit<ratio<1, 1>, base_unit<ratio<1, 1>,
ratio<0, 1>, ratio<1, 1>, ratio<0, 1>, ratio<0, 1>, ratio<0, 1>, ratio<0, 1>, ratio<0, 1>, ratio<0, 1> >, ratio<0, 1>,
ratio<0, 1> >, unit<ratio<1, 1>, base_unit<ratio<1, 1>, ratio<0, 1>, ratio<-1, 1>, ratio<0, 1>, ratio<0, 1>, ratio<0, 1>,
ratio<0, 1>, ratio<0, 1>, ratio<0, 1> >, ratio<0, 1>, ratio<0, 1> > >::value' "Units are not compatible."
static_assert(traits::is_convertible_unit<UnitFrom, UnitTo>::value, "Units are not compatible.");
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Compilation: NHolthaus Units
CLANG-7
14
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Debugging: NHolthaus Units
15
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Debugging: NHolthaus Units
15
Breakpoint 1, avg_speed<units::unit_t<units::unit<std::ratio<1000, 1>, units::unit<std::ratio<1>,
units::base_unit<std::ratio<1> > >, std::ratio<0, 1>, std::ratio<0, 1> > >, units::unit_t<units::unit<std::ratio<60>,
units::unit<std::ratio<60>, units::unit<std::ratio<1>, units::base_unit<std::ratio<0, 1>, std::ratio<0, 1>,
std::ratio<1> > > > > > >
(d=..., t=...) at velocity.cpp:28
28 const auto v = d / t;
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Debugging: NHolthaus Units
16
(gdb) ptype d
type = class units::unit_t<units::unit<std::ratio<1000, 1>, units::unit<std::ratio<1, 1>, units::base_unit<std::ratio<1, 1>,
std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>,
std::ratio<0, 1> >, std::ratio<0, 1>, std::ratio<0, 1> >, std::ratio<0, 1>, std::ratio<0, 1> >, double, units::linear_scale>
[with Units = units::unit<std::ratio<1000, 1>, units::unit<std::ratio<1, 1>, units::base_unit<std::ratio<1, 1>, std::ratio<0, 1>,
std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1> >,
std::ratio<0, 1>, std::ratio<0, 1> >, std::ratio<0, 1>, std::ratio<0, 1> >, T = double] : public units::linear_scale<T>,
private units::detail::_unit_t {
...
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Debugging: NHolthaus Units
17
template<class Meter = detail::meter_ratio<0>,
class Kilogram = std::ratio<0>,
class Second = std::ratio<0>,
class Radian = std::ratio<0>,
class Ampere = std::ratio<0>,
class Kelvin = std::ratio<0>,
class Mole = std::ratio<0>,
class Candela = std::ratio<0>,
class Byte = std::ratio<0>>
struct base_unit;
template<class Conversion, class BaseUnit, class PiExponent = std::ratio<0>, class Translation = std::ratio<0>>
struct unit;
#ifndef UNIT_LIB_DEFAULT_TYPE
# define UNIT_LIB_DEFAULT_TYPE double
#endif
template<class Units, typename T = UNIT_LIB_DEFAULT_TYPE, template<typename> class NonLinearScale = linear_scale>
class unit_t;
CppCon 2019 | Rethinking the Way We Do Templates in C++
NHolthaus Units: Design
18
template<class Meter = detail::meter_ratio<0>,
class Kilogram = std::ratio<0>,
class Second = std::ratio<0>,
class Radian = std::ratio<0>,
class Ampere = std::ratio<0>,
class Kelvin = std::ratio<0>,
class Mole = std::ratio<0>,
class Candela = std::ratio<0>,
class Byte = std::ratio<0>>
struct base_unit;
template<class Conversion, class BaseUnit, class PiExponent = std::ratio<0>, class Translation = std::ratio<0>>
struct unit;
#ifndef UNIT_LIB_DEFAULT_TYPE
# define UNIT_LIB_DEFAULT_TYPE double
#endif
template<class Units, typename T = UNIT_LIB_DEFAULT_TYPE, template<typename> class NonLinearScale = linear_scale>
class unit_t;
using velocity_t = units::unit<std::ratio<1>,
units::base_unit<std::ratio<1>, std::ratio<0>, std::ratio<1>, std::ratio<0>,
std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>,
std::ratio<0>>,
std::ratio<0>, std::ratio<0>>;
CppCon 2019 | Rethinking the Way We Do Templates in C++
NHolthaus Units: Design
18
• For most template metaprogramming libraries compile-time errors are rare
CppCon 2019 | Rethinking the Way We Do Templates in C++
A need to modernize our toolbox
19
• For most template metaprogramming libraries compile-time errors are rare
• It is expected that engineers working with a physical units library will experience compile-time errors
very o en
– generating compile-time errors for invalid calculation is the main reason to create such a library
CppCon 2019 | Rethinking the Way We Do Templates in C++
A need to modernize our toolbox
19
• For most template metaprogramming libraries compile-time errors are rare
• It is expected that engineers working with a physical units library will experience compile-time errors
very o en
– generating compile-time errors for invalid calculation is the main reason to create such a library
In case of the physical units library we have to rethink the way we
do template metaprogramming!
CppCon 2019 | Rethinking the Way We Do Templates in C++
A need to modernize our toolbox
19
• Developers cannot live without aliases as they hugely simplify code development and its maintenance
CppCon 2019 | Rethinking the Way We Do Templates in C++
Type aliases are great for developers but not for end users
20
• Developers cannot live without aliases as they hugely simplify code development and its maintenance
• Type aliases names are lost quickly during compilation process
CppCon 2019 | Rethinking the Way We Do Templates in C++
Type aliases are great for developers but not for end users
20
• Developers cannot live without aliases as they hugely simplify code development and its maintenance
• Type aliases names are lost quickly during compilation process
• As a result end users get huge types in error messages
CppCon 2019 | Rethinking the Way We Do Templates in C++
Type aliases are great for developers but not for end users
20
• Developers cannot live without aliases as they hugely simplify code development and its maintenance
• Type aliases names are lost quickly during compilation process
• As a result end users get huge types in error messages
using velocity = make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -1>>;
using kilometre_per_hour = derived_unit<velocity, kilometre, hour>;
CppCon 2019 | Rethinking the Way We Do Templates in C++
Type aliases are great for developers but not for end users
EXAMPLE
20
• Developers cannot live without aliases as they hugely simplify code development and its maintenance
• Type aliases names are lost quickly during compilation process
• As a result end users get huge types in error messages
using velocity = make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -1>>;
using kilometre_per_hour = derived_unit<velocity, kilometre, hour>;
void foo(quantity<kilometre_per_hour>(90));
CppCon 2019 | Rethinking the Way We Do Templates in C++
Type aliases are great for developers but not for end users
EXAMPLE
20
• Developers cannot live without aliases as they hugely simplify code development and its maintenance
• Type aliases names are lost quickly during compilation process
• As a result end users get huge types in error messages
using velocity = make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -1>>;
using kilometre_per_hour = derived_unit<velocity, kilometre, hour>;
void foo(quantity<kilometre_per_hour>(90));
[with Q = units::quantity<units::unit<units::dimension<units::exp<units::base_dim_length, 1, 1>,
units::exp<units::base_dim_time, 1, -1> >, units::ratio<5, 18> >, double>]
CppCon 2019 | Rethinking the Way We Do Templates in C++
Type aliases are great for developers but not for end users
EXAMPLE
20
• Developers cannot live without aliases as they hugely simplify code development and its maintenance
• Type aliases names are lost quickly during compilation process
• As a result end users get huge types in error messages
using velocity = make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -1>>;
using kilometre_per_hour = derived_unit<velocity, kilometre, hour>;
void foo(quantity<kilometre_per_hour>(90));
[with Q = units::quantity<units::unit<units::dimension<units::exp<units::base_dim_length, 1, 1>,
units::exp<units::base_dim_time, 1, -1> >, units::ratio<5, 18> >, double>]
CppCon 2019 | Rethinking the Way We Do Templates in C++
Type aliases are great for developers but not for end users
EXAMPLE
It is a pity that we still do not have strong typedefs in the C++ language :-(
20
struct velocity : make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -1>> {};
CppCon 2019 | Rethinking the Way We Do Templates in C++
Inheritance as a workaround
21
struct velocity : make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -1>> {};
• Similarly to strong typedefs
– strong types that do not vanish during compilation process
– member functions of a base class taking the base class type as an argument will still work with a
child class type provided instead (i.e. op==)
CppCon 2019 | Rethinking the Way We Do Templates in C++
Inheritance as a workaround
21
struct velocity : make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -1>> {};
• Similarly to strong typedefs
– strong types that do not vanish during compilation process
– member functions of a base class taking the base class type as an argument will still work with a
child class type provided instead (i.e. op==)
• Alternatively to strong typedefs
– do not automatically inherit constructors and assignment operators
– member functions of a base class returning the base class type will still return the same base type
for a child class instance
CppCon 2019 | Rethinking the Way We Do Templates in C++
Inheritance as a workaround
21
Velocity auto v = 10m / 2s;
CppCon 2019 | Rethinking the Way We Do Templates in C++
Type substitution problem
22
Velocity auto v = 10m / 2s;
template<typename U1, typename Rep1, typename U2, typename Rep2>
[[nodiscard]] constexpr Quantity operator/(const quantity<U1, Rep1>& lhs,
const quantity<U2, Rep2>& rhs);
CppCon 2019 | Rethinking the Way We Do Templates in C++
Type substitution problem
22
Velocity auto v = 10m / 2s;
template<typename U1, typename Rep1, typename U2, typename Rep2>
[[nodiscard]] constexpr Quantity operator/(const quantity<U1, Rep1>& lhs,
const quantity<U2, Rep2>& rhs);
CppCon 2019 | Rethinking the Way We Do Templates in C++
Type substitution problem
How to form a velocity dimension child class type from division of length
by time?
22
template<typename BaseType>
struct downcast_base {
using base_type = BaseType;
};
template<Exponent... Es>
struct dimension : downcast_base<dimension<Es...>> {};
CppCon 2019 | Rethinking the Way We Do Templates in C++
Downcasting facility
BASE CLASS
23
template<typename BaseType>
struct downcast_base {
using base_type = BaseType;
};
template<Exponent... Es>
struct dimension : downcast_base<dimension<Es...>> {};
template<typename T>
concept Downcastable =
requires {
typename T::base_type;
} &&
std::derived_from<T, downcast_base<typename T::base_type>>;
CppCon 2019 | Rethinking the Way We Do Templates in C++
Downcasting facility
BASE CLASS
DOWNCASTABLE
23
template<Downcastable T>
using downcast_from = T::base_type;
template<Downcastable T>
using downcast_to = std::type_identity<T>;
CppCon 2019 | Rethinking the Way We Do Templates in C++
Downcasting facility
HELPER ALIASES
24
template<Downcastable T>
using downcast_from = T::base_type;
template<Downcastable T>
using downcast_to = std::type_identity<T>;
template<Downcastable T>
struct downcasting_traits : downcast_to<T> {};
template<Downcastable T>
using downcasting_traits_t = downcasting_traits<T>::type;
CppCon 2019 | Rethinking the Way We Do Templates in C++
Downcasting facility
HELPER ALIASES
DOWNCASTING TRAITS
24
template<Exponent... Es>
struct dimension : downcast_base<dimension<Es...>> {};
CppCon 2019 | Rethinking the Way We Do Templates in C++
Downcasting facility
EXAMPLE
25
template<Exponent... Es>
struct dimension : downcast_base<dimension<Es...>> {};
struct velocity : make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -1>> {};
template<>
struct downcasting_traits<downcast_from<velocity>> : downcast_to<velocity> {};
CppCon 2019 | Rethinking the Way We Do Templates in C++
Downcasting facility
EXAMPLE
25
[with D = units::quantity<units::unit<units::dimension<units::exp<units::base_dim_length, 1, 1>,
units::exp<units::base_dim_time, 1, -1> >, units::ratio<5, 18> >, double>]
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Compilation
BEFORE
26
[with D = units::quantity<units::unit<units::dimension<units::exp<units::base_dim_length, 1, 1>,
units::exp<units::base_dim_time, 1, -1> >, units::ratio<5, 18> >, double>]
[with D = units::quantity<units::kilometre_per_hour, double>]
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Compilation
BEFORE
AFTER
26
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Debugging
27
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Debugging
27
Breakpoint 1, avg_speed<units::quantity<units::kilometre, double>,
units::quantity<units::hour, double> >
(d=..., t=...) at velocity.cpp:31
31 return d / t;
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Debugging
28
(gdb) ptype d
type = class units::quantity<units::kilometre, double>
[with U = units::kilometre, Rep = double] {
...
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Debugging
29
CppCon 2019 | Rethinking the Way We Do Templates in C++
NEW TOYS IN A TOOLBOX
30
template<intmax_t Num, intmax_t Den = 1>
struct ratio {
static constexpr intmax_t num = Num * static_sign<Den>::value / static_gcd<Num, Den>::value;
static constexpr intmax_t den = static_abs<Den>::value / static_gcd<Num, Den>::value;
using type = ratio<num, den>;
};
CppCon 2019 | Rethinking the Way We Do Templates in C++
Traditional implementation of std::ratio
31
template<intmax_t Num, intmax_t Den = 1>
struct ratio {
static constexpr intmax_t num = Num * static_sign<Den>::value / static_gcd<Num, Den>::value;
static constexpr intmax_t den = static_abs<Den>::value / static_gcd<Num, Den>::value;
using type = ratio<num, den>;
};
template<intmax_t Pn>
struct static_sign : integral_constant<intmax_t, (Pn < 0) ? -1 : 1> {};
CppCon 2019 | Rethinking the Way We Do Templates in C++
Traditional implementation of std::ratio
31
template<intmax_t Num, intmax_t Den = 1>
struct ratio {
static constexpr intmax_t num = Num * static_sign<Den>::value / static_gcd<Num, Den>::value;
static constexpr intmax_t den = static_abs<Den>::value / static_gcd<Num, Den>::value;
using type = ratio<num, den>;
};
template<intmax_t Pn>
struct static_sign : integral_constant<intmax_t, (Pn < 0) ? -1 : 1> {};
template<intmax_t Pn>
struct static_abs : integral_constant<intmax_t, Pn * static_sign<Pn>::value> {};
CppCon 2019 | Rethinking the Way We Do Templates in C++
Traditional implementation of std::ratio
31
template<intmax_t Num, intmax_t Den = 1>
struct ratio {
static constexpr intmax_t num = Num * static_sign<Den>::value / static_gcd<Num, Den>::value;
static constexpr intmax_t den = static_abs<Den>::value / static_gcd<Num, Den>::value;
using type = ratio<num, den>;
};
template<intmax_t Pn, intmax_t Qn>
struct static_gcd : static_gcd<Qn, (Pn % Qn)> {};
template<intmax_t Pn>
struct static_gcd<Pn, 0> : integral_constant<intmax_t, static_abs<Pn>::value> {};
template<intmax_t Qn>
struct static_gcd<0, Qn> : integral_constant<intmax_t, static_abs<Qn>::value> {};
CppCon 2019 | Rethinking the Way We Do Templates in C++
Traditional implementation of std::ratio
32
namespace detail {
template<typename R1, typename R2>
struct ratio_multiply_impl {
private:
static constexpr intmax_t gcd1 = static_gcd<R1::num, R2::den>::value;
static constexpr intmax_t gcd2 = static_gcd<R2::num, R1::den>::value;
public:
using type = ratio<safe_multiply<(R1::num / gcd1), (R2::num / gcd2)>::value,
safe_multiply<(R1::den / gcd2), (R2::den / gcd1)>::value>;
static constexpr intmax_t num = type::num;
static constexpr intmax_t den = type::den;
};
}
template<typename R1, typename R2>
using ratio_multiply = detail::ratio_multiply_impl<R1, R2>::type;
CppCon 2019 | Rethinking the Way We Do Templates in C++
Traditional implementation of std::ratio_multiply
33
template<typename T>
[[nodiscard]] constexpr T abs(T v) noexcept { return v < 0 ? -v : v; }
template<std::intmax_t Num, std::intmax_t Den = 1>
struct ratio {
static constexpr std::intmax_t num = Num * (Den < 0 ? -1 : 1) / std::gcd(Num, Den);
static constexpr std::intmax_t den = abs(Den) / std::gcd(Num, Den);
using type = ratio<num, den>;
};
CppCon 2019 | Rethinking the Way We Do Templates in C++
constexpr-based implementation of ratio
34
template<typename T>
[[nodiscard]] constexpr T abs(T v) noexcept { return v < 0 ? -v : v; }
template<std::intmax_t Num, std::intmax_t Den = 1>
struct ratio {
static constexpr std::intmax_t num = Num * (Den < 0 ? -1 : 1) / std::gcd(Num, Den);
static constexpr std::intmax_t den = abs(Den) / std::gcd(Num, Den);
using type = ratio<num, den>;
};
• Better code reuse between run-time and compile-time programming
• Less instantiations of class templates
CppCon 2019 | Rethinking the Way We Do Templates in C++
constexpr-based implementation of ratio
34
namespace detail {
template<typename R1, typename R2>
struct ratio_multiply_impl {
private:
static constexpr std::intmax_t gcd1 = std::gcd(R1::num, R2::den);
static constexpr std::intmax_t gcd2 = std::gcd(R2::num, R1::den);
public:
using type = ratio<safe_multiply(R1::num / gcd1, R2::num / gcd2),
safe_multiply(R1::den / gcd2, R2::den / gcd1)>;
static constexpr std::intmax_t num = type::num;
static constexpr std::intmax_t den = type::den;
};
}
template<Ratio R1, Ratio R2>
using ratio_multiply = detail::ratio_multiply_impl<R1, R2>::type;
CppCon 2019 | Rethinking the Way We Do Templates in C++
constexpr-based implementation of ratio_multiply
35
• Allow non-union class types to appear in non-type template parameters (NTTP)
• Require that types used as such, have strong structural equality
– a type T is having strong structural equality if each subobject recursively has defaulted == and none
of the subobjects are floating point types
CppCon 2019 | Rethinking the Way We Do Templates in C++
P0732 P1185 Class Types in Non-Type Template Parameters
36
template<size_t seconds>
class fixed_timer { /* ... */ };
template<std::chrono::seconds seconds>
class fixed_timer { /* ... */ };
• Allow non-union class types to appear in non-type template parameters (NTTP)
• Require that types used as such, have strong structural equality
– a type T is having strong structural equality if each subobject recursively has defaulted == and none
of the subobjects are floating point types
CppCon 2019 | Rethinking the Way We Do Templates in C++
P0732 P1185 Class Types in Non-Type Template Parameters
C++17 C++20
36
• gcc is the only compiler supporting NTTP for now
– standard compliant support in gcc-9.2
• QoI and compile-time performance might improve over time
CppCon 2019 | Rethinking the Way We Do Templates in C++
NTTP support and controversies
37
• gcc is the only compiler supporting NTTP for now
– standard compliant support in gcc-9.2
• QoI and compile-time performance might improve over time
• There are some controversies regarding the NTTP feature (P1837)
– Identity and Equality are not the same thing
CppCon 2019 | Rethinking the Way We Do Templates in C++
NTTP support and controversies
37
• gcc is the only compiler supporting NTTP for now
– standard compliant support in gcc-9.2
• QoI and compile-time performance might improve over time
• There are some controversies regarding the NTTP feature (P1837)
– Identity and Equality are not the same thing
CppCon 2019 | Rethinking the Way We Do Templates in C++
NTTP support and controversies
NTTP support in the Physical Units library will not be merged to a master
branch before the ISO C++ Committee meeting in Belfast
37
struct ratio {
std::intmax_t num;
std::intmax_t den;
explicit constexpr ratio(std::intmax_t n, std::intmax_t d = 1) :
num(n * (d < 0 ? -1 : 1) / std::gcd(n, d)),
den(abs(d) / std::gcd(n, d))
{
}
[[nodiscard]] constexpr bool operator==(const ratio&) = default;
// ...
};
CppCon 2019 | Rethinking the Way We Do Templates in C++
NTTP-based implementation of ratio
38
struct ratio {
// ...
[[nodiscard]] friend constexpr ratio operator*(const ratio& lhs, const ratio& rhs)
{
const std::intmax_t gcd1 = std::gcd(lhs.num, rhs.den);
const std::intmax_t gcd2 = std::gcd(rhs.num, lhs.den);
return ratio(safe_multiply(lhs.num / gcd1, rhs.num / gcd2),
safe_multiply(lhs.den / gcd2, rhs.den / gcd1));
}
};
CppCon 2019 | Rethinking the Way We Do Templates in C++
NTTP-based implementation of ratio_multiply
39
struct ratio {
// ...
[[nodiscard]] friend constexpr ratio operator*(const ratio& lhs, const ratio& rhs)
{
const std::intmax_t gcd1 = std::gcd(lhs.num, rhs.den);
const std::intmax_t gcd2 = std::gcd(rhs.num, lhs.den);
return ratio(safe_multiply(lhs.num / gcd1, rhs.num / gcd2),
safe_multiply(lhs.den / gcd2, rhs.den / gcd1));
}
[[nodiscard]] friend consteval ratio operator*(std::intmax_t n, const ratio& rhs)
{
return ratio(n) * rhs;
}
[[nodiscard]] friend consteval ratio operator*(const ratio& lhs, std::intmax_t n)
{
return lhs * ratio(n);
}
};
CppCon 2019 | Rethinking the Way We Do Templates in C++
NTTP-based implementation of ratio_multiply
39
// US customary units
struct yard : unit<length, ratio(9'144, 10'000)> {};
template<> struct downcasting_traits<downcast_from<yard>> : downcast_to<yard> {};
struct foot : unit<length, yard::ratio / 3> {};
template<> struct downcasting_traits<downcast_from<foot>> : downcast_to<foot> {};
struct inch : unit<length, foot::ratio / 12> {};
template<> struct downcasting_traits<downcast_from<inch>> : downcast_to<inch> {};
struct mile : unit<length, 1'760 * yard::ratio> {};
template<> struct downcasting_traits<downcast_from<mile>> : downcast_to<mile> {};
CppCon 2019 | Rethinking the Way We Do Templates in C++
NTTP in action
40
template<typename U1, typename Rep1, typename U2, typename Rep2>
requires (std::ratio_divide<typename U1::ratio, typename U2::ratio>::den == 1)
[[nodiscard]] quantity<downcasting_traits_t<unit<dimension_divide_t<typename U1::dimension, typename U2::dimension>,
std::ratio_divide<typename U1::ratio, typename U2::ratio>>>,
std::common_type_t<Rep1, Rep2>>
constexpr operator/(const quantity<U1, Rep1>& lhs,
const quantity<U2, Rep2>& rhs);
CppCon 2019 | Rethinking the Way We Do Templates in C++
NTTP in action
BEFORE
41
template<typename U1, typename Rep1, typename U2, typename Rep2>
requires (std::ratio_divide<typename U1::ratio, typename U2::ratio>::den == 1)
[[nodiscard]] quantity<downcasting_traits_t<unit<dimension_divide_t<typename U1::dimension, typename U2::dimension>,
std::ratio_divide<typename U1::ratio, typename U2::ratio>>>,
std::common_type_t<Rep1, Rep2>>
constexpr operator/(const quantity<U1, Rep1>& lhs,
const quantity<U2, Rep2>& rhs);
template<typename U1, typename Rep1, typename U2, typename Rep2>
requires ((U1::ratio / U2::ratio).den == 1)
[[nodiscard]] quantity<downcasting_traits_t<unit<dimension_divide_t<typename U1::dimension, typename U2::dimension>,
U1::ratio / U2::ratio>>,
std::common_type_t<Rep1, Rep2>>
constexpr operator/(const quantity<U1, Rep1>& lhs,
const quantity<U2, Rep2>& rhs);
CppCon 2019 | Rethinking the Way We Do Templates in C++
NTTP in action
BEFORE
AFTER
41
template<typename U1, typename Rep1, typename U2, typename Rep2>
requires (std::ratio_divide<typename U1::ratio, typename U2::ratio>::den == 1)
[[nodiscard]] quantity<downcasting_traits_t<unit<dimension_divide_t<typename U1::dimension, typename U2::dimension>,
std::ratio_divide<typename U1::ratio, typename U2::ratio>>>,
std::common_type_t<Rep1, Rep2>>
constexpr operator/(const quantity<U1, Rep1>& lhs,
const quantity<U2, Rep2>& rhs);
CppCon 2019 | Rethinking the Way We Do Templates in C++
NTTP in action
BEFORE
42
template<typename U1, typename Rep1, typename U2, typename Rep2>
requires (std::ratio_divide<typename U1::ratio, typename U2::ratio>::den == 1)
[[nodiscard]] quantity<downcasting_traits_t<unit<dimension_divide_t<typename U1::dimension, typename U2::dimension>,
std::ratio_divide<typename U1::ratio, typename U2::ratio>>>,
std::common_type_t<Rep1, Rep2>>
constexpr operator/(const quantity<U1, Rep1>& lhs,
const quantity<U2, Rep2>& rhs);
template<typename U1, typename Rep1, typename U2, typename Rep2>
requires ((U1::ratio / U2::ratio).den == 1)
[[nodiscard]] quantity<downcasting_traits_t<unit<U1::dimension / U2::dimension, U1::ratio / U2::ratio>>,
std::common_type_t<Rep1, Rep2>>
constexpr operator/(const quantity<U1, Rep1>& lhs,
const quantity<U2, Rep2>& rhs);
CppCon 2019 | Rethinking the Way We Do Templates in C++
NTTP in action
BEFORE
AFTER
42
struct exp {
base_dimension base;
int num;
int den;
constexpr exp(base_dimension b, int n, int d = 1): base(b), num(n), den(d) {}
};
CppCon 2019 | Rethinking the Way We Do Templates in C++
NTTP in action
43
struct exp {
base_dimension base;
int num;
int den;
constexpr exp(base_dimension b, int n, int d = 1): base(b), num(n), den(d) {}
};
struct dimension {
std::vector<exp> exponents; // compile-time vector
constexpr dimension(std::initializer_list<exp> init);
[[nodiscard]] friend constexpr dimension operator*(const dimension& lhs, const dimension& rhs);
[[nodiscard]] friend constexpr dimension operator/(const dimension& lhs, const dimension& rhs);
};
CppCon 2019 | Rethinking the Way We Do Templates in C++
NTTP in action
43
struct exp {
base_dimension base;
int num;
int den;
constexpr exp(base_dimension b, int n, int d = 1): base(b), num(n), den(d) {}
};
struct dimension {
std::vector<exp> exponents; // compile-time vector
constexpr dimension(std::initializer_list<exp> init);
[[nodiscard]] friend constexpr dimension operator*(const dimension& lhs, const dimension& rhs);
[[nodiscard]] friend constexpr dimension operator/(const dimension& lhs, const dimension& rhs);
};
inline constexpr dimension velocity = { exp(base_dim_length, 1), exp(base_dim_time, -1) };
CppCon 2019 | Rethinking the Way We Do Templates in C++
NTTP in action
43
struct exp {
base_dimension base;
int num;
int den;
constexpr exp(base_dimension b, int n, int d = 1): base(b), num(n), den(d) {}
};
struct dimension {
std::vector<exp> exponents; // compile-time vector
constexpr dimension(std::initializer_list<exp> init);
[[nodiscard]] friend constexpr dimension operator*(const dimension& lhs, const dimension& rhs);
[[nodiscard]] friend constexpr dimension operator/(const dimension& lhs, const dimension& rhs);
};
inline constexpr dimension velocity = { exp(base_dim_length, 1), exp(base_dim_time, -1) };
CppCon 2019 | Rethinking the Way We Do Templates in C++
NTTP in action
exp and dimension values are only used as NTTP so those types and their
implementation should exist only during compile-time
43
struct exp {
base_dimension base;
int num;
int den;
consteval exp(base_dimension b, int n, int d = 1): base(b), num(n), den(d) {}
};
struct dimension {
std::vector<exp> exponents; // compile-time vector
consteval dimension(std::initializer_list<exp> init);
[[nodiscard]] friend consteval dimension operator*(const dimension& lhs, const dimension& rhs);
[[nodiscard]] friend consteval dimension operator/(const dimension& lhs, const dimension& rhs);
};
inline consteval dimension velocity = { exp(base_dim_length, 1), exp(base_dim_time, -1) };
CppCon 2019 | Rethinking the Way We Do Templates in C++
P1073 Immediate functions
44
struct exp {
base_dimension base;
int num;
int den;
consteval exp(base_dimension b, int n, int d = 1): base(b), num(n), den(d) {}
};
struct dimension {
std::vector<exp> exponents; // compile-time vector
consteval dimension(std::initializer_list<exp> init);
[[nodiscard]] friend consteval dimension operator*(const dimension& lhs, const dimension& rhs);
[[nodiscard]] friend consteval dimension operator/(const dimension& lhs, const dimension& rhs);
};
inline consteval dimension velocity = { exp(base_dim_length, 1), exp(base_dim_time, -1) };
CppCon 2019 | Rethinking the Way We Do Templates in C++
P1073 Immediate functions
C++20 does not allow us yet to create consteval objects :-(
44
Usage of class types as non-type template parameters (NTTP)
might be one of the most significant C++ improvements in
template metaprogramming during the last decade
CppCon 2019 | Rethinking the Way We Do Templates in C++
Class Types in Non-Type Template Parameters
45
void* foo(void* t);
CppCon 2019 | Rethinking the Way We Do Templates in C++
How do you feel about such an interface?
46
void* foo(void* t);
template<typename T>
auto foo(T&& t);
CppCon 2019 | Rethinking the Way We Do Templates in C++
How do you feel about such an interface?
46
void* foo(void* t);
template<typename T>
auto foo(T&& t);
auto foo = [](auto&& t){ /* ... */ };
CppCon 2019 | Rethinking the Way We Do Templates in C++
How do you feel about such an interface?
46
void* foo(void* t);
template<typename T>
auto foo(T&& t);
auto foo = [](auto&& t){ /* ... */ };
Unconstrained template parameters are a void* of C++
CppCon 2019 | Rethinking the Way We Do Templates in C++
How do you feel about such an interface?
46
• Class/Function/Variable/Alias templates, and non-template functions (typically members of class
templates) may be associated with a constraint
CppCon 2019 | Rethinking the Way We Do Templates in C++
Concepts
47
• Class/Function/Variable/Alias templates, and non-template functions (typically members of class
templates) may be associated with a constraint
• Constraint specifies the requirements on template arguments
– can be used to select the most appropriate function overloads and template specializations
CppCon 2019 | Rethinking the Way We Do Templates in C++
Concepts
47
• Class/Function/Variable/Alias templates, and non-template functions (typically members of class
templates) may be associated with a constraint
• Constraint specifies the requirements on template arguments
– can be used to select the most appropriate function overloads and template specializations
• Named sets of such requirements are called concepts
CppCon 2019 | Rethinking the Way We Do Templates in C++
Concepts
47
• Class/Function/Variable/Alias templates, and non-template functions (typically members of class
templates) may be associated with a constraint
• Constraint specifies the requirements on template arguments
– can be used to select the most appropriate function overloads and template specializations
• Named sets of such requirements are called concepts
• Concept
– is a named predicate
– evaluated at compile time
– a part of the interface of a template
CppCon 2019 | Rethinking the Way We Do Templates in C++
Concepts
47
template<typename T>
concept Velocity = QuantityOf<T, velocity>;
template<Velocity V>
constexpr price calc_fine(V speed);
CppCon 2019 | Rethinking the Way We Do Templates in C++
Concept example
48
template<typename T, typename D>
concept QuantityOf = Quantity<T> && Dimension<D> && std::same_as<typename T::dimension, D>;
template<typename T>
concept Velocity = QuantityOf<T, velocity>;
template<Velocity V>
constexpr price calc_fine(V speed);
CppCon 2019 | Rethinking the Way We Do Templates in C++
Concept example
48
template<typename T>
concept Dimension =
std::is_empty_v<T> &&
detail::is_dimension<downcast_from<T>>;
template<typename T, typename D>
concept QuantityOf = Quantity<T> && Dimension<D> && std::same_as<typename T::dimension, D>;
template<typename T>
concept Velocity = QuantityOf<T, velocity>;
template<Velocity V>
constexpr price calc_fine(V speed);
CppCon 2019 | Rethinking the Way We Do Templates in C++
Concept example
48
template<typename T>
concept Quantity = detail::is_quantity<T>;
template<typename T>
concept Dimension =
std::is_empty_v<T> &&
detail::is_dimension<downcast_from<T>>;
template<typename T, typename D>
concept QuantityOf = Quantity<T> && Dimension<D> && std::same_as<typename T::dimension, D>;
template<typename T>
concept Velocity = QuantityOf<T, velocity>;
template<Velocity V>
constexpr price calc_fine(V speed);
CppCon 2019 | Rethinking the Way We Do Templates in C++
Concept example
48
CppCon 2019 | Rethinking the Way We Do Templates in C++
Not just a syntactic sugar for enable_if and void_t
49
• Constraining function template return types
constexpr Velocity auto avg_speed(Length auto d, Time auto t)
{
return d / t;
}
CppCon 2019 | Rethinking the Way We Do Templates in C++
Not just a syntactic sugar for enable_if and void_t
49
• Constraining function template return types
constexpr Velocity auto avg_speed(Length auto d, Time auto t)
{
return d / t;
}
• Constraining the deduced types of user's variables
const Velocity auto speed = avg_speed(220.km, 2.h);
CppCon 2019 | Rethinking the Way We Do Templates in C++
Not just a syntactic sugar for enable_if and void_t
49
• Constraining function template return types
constexpr Velocity auto avg_speed(Length auto d, Time auto t)
{
return d / t;
}
• Constraining the deduced types of user's variables
const Velocity auto speed = avg_speed(220.km, 2.h);
• Constraining class template parameters without the need to introduce new class template parameters
template<Dimension D, Ratio R = ratio<1>>
requires (R::num * R::den > 0)
struct unit;
CppCon 2019 | Rethinking the Way We Do Templates in C++
Not just a syntactic sugar for enable_if and void_t
49
CppCon 2019 | Rethinking the Way We Do Templates in C++
Concept categories
50
template<class T>
concept signed_integral =
integral<T> &&
is_signed_v<T>;
CppCon 2019 | Rethinking the Way We Do Templates in C++
Concept categories
PREDICATES
50
template<class T>
concept signed_integral =
integral<T> &&
is_signed_v<T>;
template<class T>
concept swappable =
requires(T& a, T& b) {
ranges::swap(a, b);
};
CppCon 2019 | Rethinking the Way We Do Templates in C++
Concept categories
PREDICATES
CAPABILITIES
50
template<class I>
concept bidirectional_iterator =
forward_iterator<I> &&
derived_from<ITER_CONCEPT(I), bidirectional_iterator_tag> &&
requires(I i) {
{ --i } -> same_as<I&>;
{ i-- } -> same_as<I>;
};
CppCon 2019 | Rethinking the Way We Do Templates in C++
Concept categories
ABSTRACTIONS
51
template<typename T>
concept Ratio = is_ratio<T>;
CppCon 2019 | Rethinking the Way We Do Templates in C++
Concept categories
FAMILY OF INSTANTIATIONS
52
template<typename T>
inline constexpr bool is_ratio = false;
template<intmax_t Num, intmax_t Den>
inline constexpr bool is_ratio<ratio<Num, Den>> = true;
template<typename T>
concept Ratio = is_ratio<T>;
CppCon 2019 | Rethinking the Way We Do Templates in C++
Concept categories
FAMILY OF INSTANTIATIONS
52
template<typename T>
inline constexpr bool is_ratio = false;
template<intmax_t Num, intmax_t Den>
inline constexpr bool is_ratio<ratio<Num, Den>> = true;
template<typename T>
concept Ratio = is_ratio<T>;
template<Ratio R1, Ratio R2>
using ratio_multiply = detail::ratio_multiply_impl<R1, R2>::type;
CppCon 2019 | Rethinking the Way We Do Templates in C++
Concept categories
FAMILY OF INSTANTIATIONS
USAGE EXAMPLES
52
template<typename T>
inline constexpr bool is_ratio = false;
template<intmax_t Num, intmax_t Den>
inline constexpr bool is_ratio<ratio<Num, Den>> = true;
template<typename T>
concept Ratio = is_ratio<T>;
template<Ratio R1, Ratio R2>
using ratio_multiply = detail::ratio_multiply_impl<R1, R2>::type;
template<Unit U, Scalar Rep = double>
class quantity;
CppCon 2019 | Rethinking the Way We Do Templates in C++
Concept categories
FAMILY OF INSTANTIATIONS
USAGE EXAMPLES
52
constexpr units::Velocity auto avg_speed(units::Length auto d, units::Time auto t)
{
return d * t;
}
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Compilation
53
constexpr units::Velocity auto avg_speed(units::Length auto d, units::Time auto t)
{
return d * t;
}
example.cpp: In instantiation of ‘constexpr units::Velocity avg_speed(D, T)
[with D = units::quantity<units::kilometre>; T = units::quantity<units::hour>]’:
example.cpp:49:49: required from here
example.cpp:34:14: error: placeholder constraints not satisfied
34 | return d * t;
| ^
include/units/dimensions/velocity.h:34:16: note: within ‘template<class T> concept units::Velocity<T>
[with T = units::quantity<units::unit<units::dimension<units::exp<units::base_dim_length, 1, 1>,
units::exp<units::base_dim_time, 1, 1> >, units::ratio<3600000, 1> >, double>]’
34 | concept Velocity = Quantity<T> && std::same_as<typename T::dimension, velocity>;
| ^~~~~~~~
include/stl2/detail/concepts/core.hpp:37:15: note: within ‘template<class T, class U> concept std::same_as<T, U>
[with T = units::dimension<units::exp<units::base_dim_length, 1, 1>, units::exp<units::base_dim_time, 1, 1> >;
U = units::velocity]’
37 | META_CONCEPT same_as = meta::Same<T, U> && meta::Same<U, T>;
| ^~~~~~~
...
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Compilation
53
constexpr units::Velocity auto avg_speed(units::Length auto d, units::Time auto t)
{
return d * t;
}
include/meta/meta_fwd.hpp:224:18: note: within ‘template<class T, class U> concept meta::Same<T, U>
[with T = units::dimension<units::exp<units::base_dim_length, 1, 1>, units::exp<units::base_dim_time, 1, 1> >;
U = units::velocity]’
224 | META_CONCEPT Same =
| ^~~~
include/meta/meta_fwd.hpp:224:18: note: ‘meta::detail::barrier’ evaluated to false
include/meta/meta_fwd.hpp:224:18: note: within ‘template<class T, class U> concept meta::Same<T, U>
[with T = units::velocity;
U = units::dimension<units::exp<units::base_dim_length, 1, 1>, units::exp<units::base_dim_time, 1, 1> >]’
include/meta/meta_fwd.hpp:224:18: note: ‘meta::detail::barrier’ evaluated to false
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Compilation
54
constexpr units::Velocity auto avg_speed(units::Length auto d, units::Time auto t)
{
return d * t;
}
include/meta/meta_fwd.hpp:224:18: note: within ‘template<class T, class U> concept meta::Same<T, U>
[with T = units::dimension<units::exp<units::base_dim_length, 1, 1>, units::exp<units::base_dim_time, 1, 1> >;
U = units::velocity]’
224 | META_CONCEPT Same =
| ^~~~
include/meta/meta_fwd.hpp:224:18: note: ‘meta::detail::barrier’ evaluated to false
include/meta/meta_fwd.hpp:224:18: note: within ‘template<class T, class U> concept meta::Same<T, U>
[with T = units::velocity;
U = units::dimension<units::exp<units::base_dim_length, 1, 1>, units::exp<units::base_dim_time, 1, 1> >]’
include/meta/meta_fwd.hpp:224:18: note: ‘meta::detail::barrier’ evaluated to false
CppCon 2019 | Rethinking the Way We Do Templates in C++
User experience: Compilation
Concepts support in C++ compilers is still experimental and largely based on
Concepts TS. We can expect even better diagnostics in the future.
54
CppCon 2019 | Rethinking the Way We Do Templates in C++
Bene ts of using C++ Concepts
55
1 Clearly state the design intent of the interface of a class/function template
CppCon 2019 | Rethinking the Way We Do Templates in C++
Bene ts of using C++ Concepts
55
1 Clearly state the design intent of the interface of a class/function template
2 Embedded in a template signature
CppCon 2019 | Rethinking the Way We Do Templates in C++
Bene ts of using C++ Concepts
55
1 Clearly state the design intent of the interface of a class/function template
2 Embedded in a template signature
3 Simplify and extend SFINAE
• disabling specific overloads for specific constraints (compared to std::enable_if)
• constraints based on dependent member types/functions existence (compared to void_t)
• no dummy template parameters allocated for SFINAE needs
• constraining function return types and deduced types of user's variables
CppCon 2019 | Rethinking the Way We Do Templates in C++
Bene ts of using C++ Concepts
55
1 Clearly state the design intent of the interface of a class/function template
2 Embedded in a template signature
3 Simplify and extend SFINAE
• disabling specific overloads for specific constraints (compared to std::enable_if)
• constraints based on dependent member types/functions existence (compared to void_t)
• no dummy template parameters allocated for SFINAE needs
• constraining function return types and deduced types of user's variables
4 Greatly improve error messages
• raise compilation error about failed compile-time contract before instantiating a template
• no more errors from deeply nested implementation details of a function template
CppCon 2019 | Rethinking the Way We Do Templates in C++
Bene ts of using C++ Concepts
55
CppCon 2019 | Rethinking the Way We Do Templates in C++
PERFORMANCE
56
• Started in 2016 by Louis Dionne
CppCon 2019 | Rethinking the Way We Do Templates in C++
Compile-time benchmarking with Metabench
57
• Started in 2016 by Louis Dionne
• CMake module that simplifies compile-time
microbenchmarking
CppCon 2019 | Rethinking the Way We Do Templates in C++
Compile-time benchmarking with Metabench
57
• Started in 2016 by Louis Dionne
• CMake module that simplifies compile-time
microbenchmarking
• Can be used to benchmark precise parts of a
C++ file, such as the instantiation of a single
function
CppCon 2019 | Rethinking the Way We Do Templates in C++
Compile-time benchmarking with Metabench
57
• Started in 2016 by Louis Dionne
• CMake module that simplifies compile-time
microbenchmarking
• Can be used to benchmark precise parts of a
C++ file, such as the instantiation of a single
function
• http://metaben.ch compares the
performance of several MPL algorithms
implemented in various libraries
CppCon 2019 | Rethinking the Way We Do Templates in C++
Compile-time benchmarking with Metabench
57
metabench_add_dataset(metabench.data.ratio.create.std_ratio
all_std_ratio.cpp.erb "[10, 50, 100, 250, 500, 750, 1000, 1500, 2000]"
NAME "std::ratio"
)
metabench_add_dataset(metabench.data.ratio.create.ratio_type_constexpr
all_ratio_type_constexpr.cpp.erb "[10, 50, 100, 250, 500, 750, 1000, 1500, 2000]"
NAME "ratio constexpr"
)
metabench_add_dataset(metabench.data.ratio.create.ratio_nttp
all_ratio_nttp.cpp.erb "[10, 50, 100, 250, 500, 750, 1000, 1500, 2000]"
NAME "ratio NTTP"
)
metabench_add_chart(metabench.chart.ratio.all
TITLE "Creation of 2*N ratios"
SUBTITLE "(smaller is better)"
DATASETS
metabench.data.ratio.create.std_ratio
metabench.data.ratio.create.ratio_type_constexpr
metabench.data.ratio.create.ratio_nttp
)
CppCon 2019 | Rethinking the Way We Do Templates in C++
A short intro to Metabench: CMake
58
#include "ratio_type_constexpr.h"
<% (1..n).each do |i| %>
struct test<%= i %> {
#if defined(METABENCH)
using r1 = units::ratio<<%= 2 * i - 1 %>, <%= 2 * n %>>;
using r2 = units::ratio<<%= 2 * i %>, <%= 2 * n %>>;
#else
using r1 = void;
using r2 = void;
#endif
};
<% end %>
int main() {}
CppCon 2019 | Rethinking the Way We Do Templates in C++
A short intro to Metabench: ERB le
CREATE_RATIO_TYPE_CONSTEXPR.CPP.ERB
59
#include "ratio_type_constexpr.h"
struct test1 {
#if defined(METABENCH)
using r1 = std::ratio<1, 20>;
using r2 = std::ratio<2, 20>;
#else
using r1 = void;
using r2 = void;
#endif
};
struct test2 {
#if defined(METABENCH)
using r1 = std::ratio<3, 20>;
using r2 = std::ratio<4, 20>;
#else
using r1 = void;
using r2 = void;
#endif
};
// ...
int main() {}
CppCon 2019 | Rethinking the Way We Do Templates in C++
A short intro to Metabench: Generated C++ code
60
#include "ratio_type_constexpr.h"
struct test1 {
#if defined(METABENCH)
using r1 = std::ratio<1, 20>;
using r2 = std::ratio<2, 20>;
#else
using r1 = void;
using r2 = void;
#endif
};
struct test2 {
#if defined(METABENCH)
using r1 = std::ratio<3, 20>;
using r2 = std::ratio<4, 20>;
#else
using r1 = void;
using r2 = void;
#endif
};
// ...
int main() {}
#include "ratio_nttp.h"
struct test1 {
#if defined(METABENCH)
static constexpr units::ratio r1{1, 20};
static constexpr units::ratio r2{2, 20};
#else
static constexpr bool r1 = false;
static constexpr bool r2 = false;
#endif
};
struct test2 {
#if defined(METABENCH)
static constexpr units::ratio r1{3, 20};
static constexpr units::ratio r2{4, 20};
#else
static constexpr bool r1 = false;
static constexpr bool r2 = false;
#endif
};
// ...
int main() {}
CppCon 2019 | Rethinking the Way We Do Templates in C++
A short intro to Metabench: Generated C++ code
60
CppCon 2019 | Rethinking the Way We Do Templates in C++
ratio performance
61
CppCon 2019 | Rethinking the Way We Do Templates in C++
ratio performance
62
CppCon 2019 | Rethinking the Way We Do Templates in C++
Current constexpr QoI is slow
63
CppCon 2019 | Rethinking the Way We Do Templates in C++
The Rule of Chiel
64
CppCon 2019 | Rethinking the Way We Do Templates in C++
The Rule of Chiel
64
CppCon 2019 | Rethinking the Way We Do Templates in C++
Cost of operations: The Rule of Chiel
65
1 Looking up a memoized type
CppCon 2019 | Rethinking the Way We Do Templates in C++
Cost of operations: The Rule of ChielCost of operations: The Rule of Chiel
65
1 Looking up a memoized type
2 Adding a parameter to an alias call
CppCon 2019 | Rethinking the Way We Do Templates in C++
Cost of operations: The Rule of Chiel
65
1 Looking up a memoized type
2 Adding a parameter to an alias call
3 Adding a parameter to a type
CppCon 2019 | Rethinking the Way We Do Templates in C++
Cost of operations: The Rule of Chiel
65
1 Looking up a memoized type
2 Adding a parameter to an alias call
3 Adding a parameter to a type
4 Calling an alias
CppCon 2019 | Rethinking the Way We Do Templates in C++
Cost of operations: The Rule of Chiel
65
1 Looking up a memoized type
2 Adding a parameter to an alias call
3 Adding a parameter to a type
4 Calling an alias
5 Instantiating a class
CppCon 2019 | Rethinking the Way We Do Templates in C++
Cost of operations: The Rule of Chiel
65
1 Looking up a memoized type
2 Adding a parameter to an alias call
3 Adding a parameter to a type
4 Calling an alias
5 Instantiating a class
6 Instantiating a function template
CppCon 2019 | Rethinking the Way We Do Templates in C++
Cost of operations: The Rule of Chiel
65
1 Looking up a memoized type
2 Adding a parameter to an alias call
3 Adding a parameter to a type
4 Calling an alias
5 Instantiating a class
6 Instantiating a function template
7 SFINAE
CppCon 2019 | Rethinking the Way We Do Templates in C++
Cost of operations: The Rule of Chiel
65
template<bool B, class T, class F>
struct conditional {
using type = T;
};
template<class T, class F>
struct conditional<false, T, F> {
using type = F;
};
template<bool B, class T, class F>
using conditional_t = conditional<B,T,F>::type;
CppCon 2019 | Rethinking the Way We Do Templates in C++
std::conditional<B, T, F>
TRADITIONAL
66
template<bool B, class T, class F>
struct conditional {
using type = T;
};
template<class T, class F>
struct conditional<false, T, F> {
using type = F;
};
template<bool B, class T, class F>
using conditional_t = conditional<B,T,F>::type;
template<bool>
struct conditional {
template<typename T, typename F>
using type = F;
};
template<>
struct conditional<true> {
template<typename T, typename F>
using type = T;
};
template<bool B, typename T, typename F>
using conditional_t =
conditional<B>::template type<T, F>;
CppCon 2019 | Rethinking the Way We Do Templates in C++
std::conditional<B, T, F>
TRADITIONAL WITH ALIAS TEMPLATE
66
CppCon 2019 | Rethinking the Way We Do Templates in C++
conditional performance (gcc-9)
67
CppCon 2019 | Rethinking the Way We Do Templates in C++
conditional performance (clang-8)
68
template<class T, class U>
struct is_same : std::false_type {};
template<class T>
struct is_same<T, T> : std::true_type {};
template<class T, class U>
inline constexpr bool is_same_v =
is_same<T, U>::value;
CppCon 2019 | Rethinking the Way We Do Templates in C++
std::is_same<T, U>
TRADITIONAL
69
template<class T, class U>
struct is_same : std::false_type {};
template<class T>
struct is_same<T, T> : std::true_type {};
template<class T, class U>
inline constexpr bool is_same_v =
is_same<T, U>::value;
template<class T, class U>
inline constexpr bool is_same = false;
template<class T>
inline constexpr bool is_same<T, T> = true;
CppCon 2019 | Rethinking the Way We Do Templates in C++
std::is_same<T, U>
TRADITIONAL USING VARIABLE TEMPLATES
69
CppCon 2019 | Rethinking the Way We Do Templates in C++
is_same performance (gcc-9)
70
CppCon 2019 | Rethinking the Way We Do Templates in C++
is_same performance (clang-8)
71
CppCon 2019 | Rethinking the Way We Do Templates in C++
What about C++ Concepts?
72
• C++ Concepts are great and not too expensive at compile-time
CppCon 2019 | Rethinking the Way We Do Templates in C++
What about C++ Concepts?
72
• C++ Concepts are great and not too expensive at compile-time
• Compile-time performance impact might be limited by using concepts only in the user interfaces
template<Exponent... Es>
struct dimension : downcast_base<dimension<Es...>> {};
namespace detail {
template<typename D1, typename D2>
struct dimension_divide;
template<typename... E1, typename... E2>
struct dimension_divide<dimension<E1...>, dimension<E2...>>
: dimension_multiply<dimension<E1...>, dimension<exp_invert_t<E2>...>> {
};
}
template<Dimension D1, Dimension D2>
using dimension_divide_t = detail::dimension_divide<typename D1::base_type,
typename D2::base_type>::type;
CppCon 2019 | Rethinking the Way We Do Templates in C++
What about C++ Concepts?
72
CppCon 2019 | Rethinking the Way We Do Templates in C++
C++ Concepts performance
73
CppCon 2019 | Rethinking the Way We Do Templates in C++
More info on MPL performance on http://metaben.ch
74
CppCon 2019 | Rethinking the Way We Do Templates in C++
TAKEAWAYS
75
CppCon 2019 | Rethinking the Way We Do Templates in C++
Rethinking C++ templates
76
1 Think about end users' experience and not only about your convenience as a developer
CppCon 2019 | Rethinking the Way We Do Templates in C++
Rethinking C++ templates
76
1 Think about end users' experience and not only about your convenience as a developer
2 Use C++ Concepts to
• express compile-time contracts
• improve productivity
• improve compile-time errors
CppCon 2019 | Rethinking the Way We Do Templates in C++
Rethinking C++ templates
76
1 Think about end users' experience and not only about your convenience as a developer
2 Use C++ Concepts to
• express compile-time contracts
• improve productivity
• improve compile-time errors
3 Use NTTPs when a template parameter represents a value rather than a type
CppCon 2019 | Rethinking the Way We Do Templates in C++
Rethinking C++ templates
76
1 Think about end users' experience and not only about your convenience as a developer
2 Use C++ Concepts to
• express compile-time contracts
• improve productivity
• improve compile-time errors
3 Use NTTPs when a template parameter represents a value rather than a type
4 Optimize compile-time performance
• MPL is free at run-time but can be really expensive at compile-time
• the same MPL algorithm may be implemented using di erent techniques and take di erent
amount of time to compile
CppCon 2019 | Rethinking the Way We Do Templates in C++
Rethinking C++ templates
76
Rethinking the Way We do Templates in C++
Rethinking the Way We do Templates in C++

More Related Content

Similar to Rethinking the Way We do Templates in C++

Performance measurement and tuning
Performance measurement and tuningPerformance measurement and tuning
Performance measurement and tuning
AOE
 
Cs2312 OOPS LAB MANUAL
Cs2312 OOPS LAB MANUALCs2312 OOPS LAB MANUAL
Cs2312 OOPS LAB MANUAL
Prabhu D
 
Estimation Of Production And Cost Function
Estimation Of Production And Cost FunctionEstimation Of Production And Cost Function
Estimation Of Production And Cost Function
Pradeep Awasare
 
Formal Verification of Web Service Interaction Contracts
Formal Verification of Web Service Interaction ContractsFormal Verification of Web Service Interaction Contracts
Formal Verification of Web Service Interaction Contracts
Gera Shegalov
 
Mid term sem 2 1415 sol
Mid term sem 2 1415 solMid term sem 2 1415 sol
Mid term sem 2 1415 sol
IIUM
 

Similar to Rethinking the Way We do Templates in C++ (20)

C++ chapter 2
C++ chapter 2C++ chapter 2
C++ chapter 2
 
Performance measurement and tuning
Performance measurement and tuningPerformance measurement and tuning
Performance measurement and tuning
 
cbse 12 computer science IP
cbse 12 computer science IPcbse 12 computer science IP
cbse 12 computer science IP
 
Cs2312 OOPS LAB MANUAL
Cs2312 OOPS LAB MANUALCs2312 OOPS LAB MANUAL
Cs2312 OOPS LAB MANUAL
 
Remote Config REST API and Versioning
Remote Config REST API and VersioningRemote Config REST API and Versioning
Remote Config REST API and Versioning
 
Deductive verification of unmodified Linux kernel library functions
Deductive verification of unmodified Linux kernel library functionsDeductive verification of unmodified Linux kernel library functions
Deductive verification of unmodified Linux kernel library functions
 
cbse 12 computer science investigatory project
cbse 12 computer science investigatory project  cbse 12 computer science investigatory project
cbse 12 computer science investigatory project
 
cbse 12 computer science investigatory project
cbse 12 computer science investigatory project  cbse 12 computer science investigatory project
cbse 12 computer science investigatory project
 
computerscience-170129081612.pdf
computerscience-170129081612.pdfcomputerscience-170129081612.pdf
computerscience-170129081612.pdf
 
Two C++ Tools: Compiler Explorer and Cpp Insights
Two C++ Tools: Compiler Explorer and Cpp InsightsTwo C++ Tools: Compiler Explorer and Cpp Insights
Two C++ Tools: Compiler Explorer and Cpp Insights
 
Tool sdl2pml
Tool sdl2pmlTool sdl2pml
Tool sdl2pml
 
Estimation Of Production And Cost Function
Estimation Of Production And Cost FunctionEstimation Of Production And Cost Function
Estimation Of Production And Cost Function
 
Computer Science Project on Management System
Computer Science Project on Management System Computer Science Project on Management System
Computer Science Project on Management System
 
Formal Verification of Web Service Interaction Contracts
Formal Verification of Web Service Interaction ContractsFormal Verification of Web Service Interaction Contracts
Formal Verification of Web Service Interaction Contracts
 
Python Manuel-R2021.pdf
Python Manuel-R2021.pdfPython Manuel-R2021.pdf
Python Manuel-R2021.pdf
 
Implementing Physical Units Library for C++
Implementing Physical Units Library for C++Implementing Physical Units Library for C++
Implementing Physical Units Library for C++
 
Mini Project- Dual Processor Computation
Mini Project- Dual Processor ComputationMini Project- Dual Processor Computation
Mini Project- Dual Processor Computation
 
Generative Programming In The Large - Applied C++ meta-programming
Generative Programming In The Large - Applied C++ meta-programmingGenerative Programming In The Large - Applied C++ meta-programming
Generative Programming In The Large - Applied C++ meta-programming
 
Virtual training Intro to Kapacitor
Virtual training  Intro to Kapacitor Virtual training  Intro to Kapacitor
Virtual training Intro to Kapacitor
 
Mid term sem 2 1415 sol
Mid term sem 2 1415 solMid term sem 2 1415 sol
Mid term sem 2 1415 sol
 

More from Mateusz Pusz

More from Mateusz Pusz (13)

Free Lunch is Over: Why is C++ so Important in the Modern World?
Free Lunch is Over: Why is C++ so Important in the Modern World?Free Lunch is Over: Why is C++ so Important in the Modern World?
Free Lunch is Over: Why is C++ so Important in the Modern World?
 
Striving for ultimate Low Latency
Striving for ultimate Low LatencyStriving for ultimate Low Latency
Striving for ultimate Low Latency
 
C++11 Was Only the Beginning
C++11 Was Only the BeginningC++11 Was Only the Beginning
C++11 Was Only the Beginning
 
Implementing a Physical Units Library for C++
Implementing a Physical Units Library for C++Implementing a Physical Units Library for C++
Implementing a Physical Units Library for C++
 
Effective replacement of dynamic polymorphism with std::variant
Effective replacement of dynamic polymorphism with std::variantEffective replacement of dynamic polymorphism with std::variant
Effective replacement of dynamic polymorphism with std::variant
 
C++ Concepts and Ranges - How to use them?
C++ Concepts and Ranges - How to use them?C++ Concepts and Ranges - How to use them?
C++ Concepts and Ranges - How to use them?
 
Effective replacement of dynamic polymorphism with std::variant
Effective replacement of dynamic polymorphism with std::variantEffective replacement of dynamic polymorphism with std::variant
Effective replacement of dynamic polymorphism with std::variant
 
Git, CMake, Conan - How to ship and reuse our C++ projects?
Git, CMake, Conan - How to ship and reuse our C++ projects?Git, CMake, Conan - How to ship and reuse our C++ projects?
Git, CMake, Conan - How to ship and reuse our C++ projects?
 
Beyond C++17
Beyond C++17Beyond C++17
Beyond C++17
 
Pointless Pointers - How to make our interfaces efficient?
Pointless Pointers - How to make our interfaces efficient?Pointless Pointers - How to make our interfaces efficient?
Pointless Pointers - How to make our interfaces efficient?
 
Striving for ultimate Low Latency
Striving for ultimate Low LatencyStriving for ultimate Low Latency
Striving for ultimate Low Latency
 
Small Lie in Big O
Small Lie in Big OSmall Lie in Big O
Small Lie in Big O
 
std::shared_ptr<T> - (not so) Smart hammer for every pointy nail
std::shared_ptr<T> - (not so) Smart hammer for every pointy nailstd::shared_ptr<T> - (not so) Smart hammer for every pointy nail
std::shared_ptr<T> - (not so) Smart hammer for every pointy nail
 

Recently uploaded

Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 

Recently uploaded (20)

Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot ModelMcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontology
 
API Governance and Monetization - The evolution of API governance
API Governance and Monetization -  The evolution of API governanceAPI Governance and Monetization -  The evolution of API governance
API Governance and Monetization - The evolution of API governance
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
Introduction to use of FHIR Documents in ABDM
Introduction to use of FHIR Documents in ABDMIntroduction to use of FHIR Documents in ABDM
Introduction to use of FHIR Documents in ABDM
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Vector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxVector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptx
 
Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
AI in Action: Real World Use Cases by Anitaraj
AI in Action: Real World Use Cases by AnitarajAI in Action: Real World Use Cases by Anitaraj
AI in Action: Real World Use Cases by Anitaraj
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with Milvus
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Simplifying Mobile A11y Presentation.pptx
Simplifying Mobile A11y Presentation.pptxSimplifying Mobile A11y Presentation.pptx
Simplifying Mobile A11y Presentation.pptx
 
Navigating Identity and Access Management in the Modern Enterprise
Navigating Identity and Access Management in the Modern EnterpriseNavigating Identity and Access Management in the Modern Enterprise
Navigating Identity and Access Management in the Modern Enterprise
 

Rethinking the Way We do Templates in C++

  • 1. Mateusz Pusz September 17, 2019 Rethinking the Way We Do Templates in C++
  • 2. 1 User experience 2 New toys in a toolbox 3 Performance CppCon 2019 | Rethinking the Way We Do Templates in C++ Plan for the talk 2
  • 3. CppCon 2019 | Rethinking the Way We Do Templates in C++ USER EXPERIENCE 3
  • 4. // simple numeric operations static_assert(10km / 2 == 5km); CppCon 2019 | Rethinking the Way We Do Templates in C++ Physical Units library in a nutshell 4
  • 5. // simple numeric operations static_assert(10km / 2 == 5km); // unit conversions static_assert(1h == 3600s); static_assert(1km + 1m == 1001m); CppCon 2019 | Rethinking the Way We Do Templates in C++ Physical Units library in a nutshell 4
  • 6. // simple numeric operations static_assert(10km / 2 == 5km); // unit conversions static_assert(1h == 3600s); static_assert(1km + 1m == 1001m); // dimension conversions static_assert(1km / 1s == 1000mps); static_assert(2kmph * 2h == 4km); static_assert(2km / 2kmph == 1h); static_assert(1000 / 1s == 1kHz); static_assert(10km / 5km == 2); CppCon 2019 | Rethinking the Way We Do Templates in C++ Physical Units library in a nutshell 4
  • 7. /* velocity */ avg_speed(/* length */ distance, /* time */ duration) { return distance / duration; } CppCon 2019 | Rethinking the Way We Do Templates in C++ Toy example 5
  • 8. /* velocity */ avg_speed(/* length */ distance, /* time */ duration) { return distance / duration; } const auto kmph = avg_speed(/* 220 km */, /* 2 hours */); std::cout << /* kmph */ << " km/hn"; CppCon 2019 | Rethinking the Way We Do Templates in C++ Toy example 5
  • 9. /* velocity */ avg_speed(/* length */ distance, /* time */ duration) { return distance / duration; } const auto kmph = avg_speed(/* 220 km */, /* 2 hours */); std::cout << /* kmph */ << " km/hn"; const auto mph = avg_speed(/* 140 miles */, /* 2 hours */); std::cout << /* mph */ << " mphn"; CppCon 2019 | Rethinking the Way We Do Templates in C++ Toy example 5
  • 10. /* velocity */ avg_speed(/* length */ distance, /* time */ duration) { return distance / duration; } const auto kmph = avg_speed(/* 220 km */, /* 2 hours */); std::cout << /* kmph */ << " km/hn"; const auto mph = avg_speed(/* 140 miles */, /* 2 hours */); std::cout << /* mph */ << " mphn"; • Compile time safety to make sure that the result is of a correct dimension CppCon 2019 | Rethinking the Way We Do Templates in C++ Toy example 5
  • 11. /* velocity */ avg_speed(/* length */ distance, /* time */ duration) { return distance / duration; } const auto kmph = avg_speed(/* 220 km */, /* 2 hours */); std::cout << /* kmph */ << " km/hn"; const auto mph = avg_speed(/* 140 miles */, /* 2 hours */); std::cout << /* mph */ << " mphn"; • Compile time safety to make sure that the result is of a correct dimension • Support for multiple units and unit prefixes CppCon 2019 | Rethinking the Way We Do Templates in C++ Toy example 5
  • 12. /* velocity */ avg_speed(/* length */ distance, /* time */ duration) { return distance / duration; } const auto kmph = avg_speed(/* 220 km */, /* 2 hours */); std::cout << /* kmph */ << " km/hn"; const auto mph = avg_speed(/* 140 miles */, /* 2 hours */); std::cout << /* mph */ << " mphn"; • Compile time safety to make sure that the result is of a correct dimension • Support for multiple units and unit prefixes • No runtime overhead – no additional intermediate conversions – as fast as a custom code implemented with doubles CppCon 2019 | Rethinking the Way We Do Templates in C++ Toy example 5
  • 13. constexpr bu::quantity<bu::si::velocity> avg_speed(bu::quantity<bu::si::length> d, bu::quantity<bu::si::time> t) { return d * t; } CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Compilation: Boost.Units 6
  • 14. constexpr bu::quantity<bu::si::velocity> avg_speed(bu::quantity<bu::si::length> d, bu::quantity<bu::si::time> t) { return d * t; } error: could not convert ‘boost::units::operator*(const boost::units::quantity<Unit1, X>&, const boost::units::quantity<Unit2, Y>&) [with Unit1 = boost::units::unit<boost::units::list<boost::units::dim <boost::units::length_base_dimension, boost::units::static_rational<1> >, boost::units::dimensionless_type>, boost::units::homogeneous_system<boost::units::list<boost::units::si::meter_base_unit, boost::units::list<boost::units::scaled_base_unit<boost::units::cgs::gram_base_unit, boost::units::scale<10, boost::units::static_rational<3> > >, boost::units::list<boost::units::si::second_base_unit, boost::units::list<boost::units::si::ampere_base_unit, boost::units::list<boost::units::si::kelvin_base_unit, boost::units::list<boost::units::si::mole_base_unit, boost::units::list<boost::units::si::candela_base_unit, boost::units::list<boost::units::angle::radian_base_unit, boost::units::list<boost::units::angle::steradian_base_unit, boost::units::dimensionless_type> > > > > > > > > > >; Unit2 = boost::units::unit<boost::units::list<boost::units::dim <boost::units::time_base_dimension, boost::units::static_rational<1> >, boost::units::dimensionless_type>, boost::units::homogeneous_system<boost::units::list<boost::units::si::meter_base_unit, boost::units::list<boost::units::scaled_base_unit<boost::units::cgs::gram_base_unit, boost::units::scale<10, boost::units::static_rational<3> > >, boost::units::list<boost::units::si::second_base_unit, boost::units::list <boost::units::si::ampere_base_unit, boost::units::list<boost::units::si::kelvin_base_unit, boost::units::list <boost::units::si::mole_base_unit, boost::units::list<boost::units::si::candela_base_unit, boost::units::list <boost::units::angle::radian_base_unit, boost::units::list<boost::units::angle::steradian_base_unit, boost::units::dimensionless_type> > > > > > > > > > >; X = double; Y = double; typename boost::units::multiply_typeof_helper<boost::units::quantity<Unit1, X>, boost::units::quantity<Unit2, Y> >::type = ... CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Compilation: Boost.Units GCC-8 6
  • 15. constexpr bu::quantity<bu::si::velocity> avg_speed(bu::quantity<bu::si::length> d, bu::quantity<bu::si::time> t) { return d * t; } ... boost::units::quantity<boost::units::unit<boost::units::list<boost::units::dim<boost::units::length_base_dimension, boost::units::static_rational<1> >, boost::units::list<boost::units::dim<boost::units::time_base_dimension, boost::units::static_rational<1> >, boost::units::dimensionless_type> >, boost::units::homogeneous_system <boost::units::list<boost::units::si::meter_base_unit, boost::units::list<boost::units::scaled_base_unit <boost::units::cgs::gram_base_unit, boost::units::scale<10, boost::units::static_rational<3> > >, boost::units::list<boost::units::si::second_base_unit, boost::units::list<boost::units::si::ampere_base_unit, boost::units::list<boost::units::si::kelvin_base_unit, boost::units::list<boost::units::si::mole_base_unit, boost::units::list<boost::units::si::candela_base_unit, boost::units::list<boost::units::angle::radian_base_unit, boost::units::list<boost::units::angle::steradian_base_unit, boost::units::dimensionless_type> > > > > > > > > >, void>, double>](t)’ from ‘quantity<unit<list<[...],list<dim<[...],static_rational<1>>,[...]>>,[...],[...]>,[...]>’ to ‘quantity<unit<list<[...],list<dim<[...],static_rational<-1>>,[...]>>,[...],[...]>,[...]>’ return d * t; ~~^~~ CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Compilation: Boost.Units GCC-8 (CONTINUED) 7
  • 16. constexpr bu::quantity<bu::si::velocity> avg_speed(bu::quantity<bu::si::length> d, bu::quantity<bu::si::time> t) { return d * t; } error: no viable conversion from returned value of type 'quantity<unit<list<[...], list<dim<[...], static_rational<1, [...]>>, [...]>>, [...]>, [...]>' to function return type 'quantity<unit<list<[...], list<dim<[...], static_rational<-1, [...]>>, [...]>>, [...]>, [...]>' return d * t; ^~~~~ CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Compilation: Boost.Units CLANG-7 8
  • 17. constexpr bu::quantity<bu::si::velocity> avg_speed(bu::quantity<bu::si::length> d, bu::quantity<bu::si::time> t) { return d * t; } error: no viable conversion from returned value of type 'quantity<unit<list<[...], list<dim<[...], static_rational<1, [...]>>, [...]>>, [...]>, [...]>' to function return type 'quantity<unit<list<[...], list<dim<[...], static_rational<-1, [...]>>, [...]>>, [...]>, [...]>' return d * t; ^~~~~ CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Compilation: Boost.Units CLANG-7 Sometimes a shorter error message is not necessarily better ;-) 8
  • 18. CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Debugging: Boost.Units 9
  • 19. CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Debugging: Boost.Units 9
  • 20. Breakpoint 1, avg_speed<boost::units::heterogeneous_system<boost::units::heterogeneous_system_impl <boost::units::list<boost::units::heterogeneous_system_dim<boost::units::si::meter_base_unit, boost::units::static_rational<1> >, boost::units::dimensionless_type>, boost::units::list<boost::units::dim<boost::units::length_base_dimension, boost::units::static_rational<1> >, boost::units::dimensionless_type>, boost::units::list<boost::units::scale_list_dim <boost::units::scale<10, boost::units::static_rational<3> > >, boost::units::dimensionless_type> > >, boost::units::heterogeneous_system<boost::units::heterogeneous_system_impl<boost::units::list <boost::units::heterogeneous_system_dim<boost::units::scaled_base_unit<boost::units::si::second_base_unit, boost::units::scale<60, boost::units::static_rational<2> > >, boost::units::static_rational<1> >, boost::units::dimensionless_type>, boost::units::list<boost::units::dim<boost::units::time_base_dimension, boost::units::static_rational<1> >, boost::units::dimensionless_type>, boost::units::dimensionless_type> > > (d=..., t=...) at velocity_2.cpp:39 39 return d / t; CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Debugging: Boost.Units 10
  • 21. (gdb) ptype d type = class boost::units::quantity<boost::units::unit<boost::units::list<boost::units::dim<boost::units::length_base_dimension, boost::units::static_rational<1, 1> >, boost::units::dimensionless_type>, boost::units::heterogeneous_system <boost::units::heterogeneous_system_impl<boost::units::list<boost::units::heterogeneous_system_dim <boost::units::si::meter_base_unit, boost::units::static_rational<1, 1> >, boost::units::dimensionless_type>, boost::units::list<boost::units::dim<boost::units::length_base_dimension, boost::units::static_rational<1, 1> >, boost::units::dimensionless_type>, boost::units::list<boost::units::scale_list_dim<boost::units::scale<10, static_rational<3>>>, boost::units::dimensionless_type> > >, void>, double> [with Unit = boost::units::unit<boost::units::list<boost::units::dim <boost::units::length_base_dimension, boost::units::static_rational<1, 1> >, boost::units::dimensionless_type>, boost::units::heterogeneous_system<boost::units::heterogeneous_system_impl<boost::units::list <boost::units::heterogeneous_system_dim<boost::units::si::meter_base_unit, boost::units::static_rational<1, 1> >, boost::units::dimensionless_type>, boost::units::list<boost::units::dim<boost::units::length_base_dimension, boost::units::static_rational<1, 1> >, boost::units::dimensionless_type>, boost::units::list<boost::units::scale_list_dim <boost::units::scale<10, static_rational<3> > >, boost::units::dimensionless_type> > >, void>, Y = double] { ... CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Debugging: Boost.Units 11
  • 22. template<typename Dim, typename System, typename Enable = void> class unit; template<typename Unit, typename Rep = double> class quantity; CppCon 2019 | Rethinking the Way We Do Templates in C++ Boost.Units: Design 12
  • 23. template<typename Dim, typename System, typename Enable = void> class unit; template<typename Unit, typename Rep = double> class quantity; struct length_base_dimension : base_dimension<length_base_dimension, -9> {}; struct time_base_dimension : base_dimension<time_base_dimension, -7> {}; using velocity_dimension = derived_dimension<length_base_dimension, 1, time_base_dimension, -1>::type; CppCon 2019 | Rethinking the Way We Do Templates in C++ Boost.Units: Design 12
  • 24. template<typename Dim, typename System, typename Enable = void> class unit; template<typename Unit, typename Rep = double> class quantity; struct length_base_dimension : base_dimension<length_base_dimension, -9> {}; struct time_base_dimension : base_dimension<time_base_dimension, -7> {}; using velocity_dimension = derived_dimension<length_base_dimension, 1, time_base_dimension, -1>::type; namespace si { using system = make_system<meter_base_unit, kilogram_base_unit, second_base_unit, ampere_base_unit, kelvin_base_unit, mole_base_unit, candela_base_unit, angle::radian_base_unit, angle::steradian_base_unit>::type; } CppCon 2019 | Rethinking the Way We Do Templates in C++ Boost.Units: Design 12
  • 25. template<typename Dim, typename System, typename Enable = void> class unit; template<typename Unit, typename Rep = double> class quantity; struct length_base_dimension : base_dimension<length_base_dimension, -9> {}; struct time_base_dimension : base_dimension<time_base_dimension, -7> {}; using velocity_dimension = derived_dimension<length_base_dimension, 1, time_base_dimension, -1>::type; namespace si { using system = make_system<meter_base_unit, kilogram_base_unit, second_base_unit, ampere_base_unit, kelvin_base_unit, mole_base_unit, candela_base_unit, angle::radian_base_unit, angle::steradian_base_unit>::type; } using velocity = unit<velocity_dimension, si::system>; CppCon 2019 | Rethinking the Way We Do Templates in C++ Boost.Units: Design 12
  • 26. constexpr units::velocity::meters_per_second_t avg_speed(units::length::meter_t d, units::time::second_t t) { return d * t; } CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Compilation: NHolthaus Units 13
  • 27. constexpr units::velocity::meters_per_second_t avg_speed(units::length::meter_t d, units::time::second_t t) { return d * t; } error: static assertion failed: Units are not compatible. static_assert(traits::is_convertible_unit<UnitFrom, UnitTo>::value, "Units are not compatible."); ^~~~~~ CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Compilation: NHolthaus Units GCC-8 13
  • 28. constexpr units::velocity::meters_per_second_t avg_speed(units::length::meter_t d, units::time::second_t t) { return d * t; } error: static assertion failed: Units are not compatible. static_assert(traits::is_convertible_unit<UnitFrom, UnitTo>::value, "Units are not compatible."); ^~~~~~ CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Compilation: NHolthaus Units GCC-8 static_assert is o en not the best solution • does not influence the overload resolution process • for some compilers does not provide enough context 13
  • 29. constexpr units::velocity::meters_per_second_t avg_speed(units::length::meter_t d, units::time::second_t t) { return d * t; } error: static_assert failed due to requirement 'traits::is_convertible_unit<unit<ratio<1, 1>, base_unit<ratio<1, 1>, ratio<0, 1>, ratio<1, 1>, ratio<0, 1>, ratio<0, 1>, ratio<0, 1>, ratio<0, 1>, ratio<0, 1>, ratio<0, 1> >, ratio<0, 1>, ratio<0, 1> >, unit<ratio<1, 1>, base_unit<ratio<1, 1>, ratio<0, 1>, ratio<-1, 1>, ratio<0, 1>, ratio<0, 1>, ratio<0, 1>, ratio<0, 1>, ratio<0, 1>, ratio<0, 1> >, ratio<0, 1>, ratio<0, 1> > >::value' "Units are not compatible." static_assert(traits::is_convertible_unit<UnitFrom, UnitTo>::value, "Units are not compatible."); ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Compilation: NHolthaus Units CLANG-7 14
  • 30. CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Debugging: NHolthaus Units 15
  • 31. CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Debugging: NHolthaus Units 15
  • 32. Breakpoint 1, avg_speed<units::unit_t<units::unit<std::ratio<1000, 1>, units::unit<std::ratio<1>, units::base_unit<std::ratio<1> > >, std::ratio<0, 1>, std::ratio<0, 1> > >, units::unit_t<units::unit<std::ratio<60>, units::unit<std::ratio<60>, units::unit<std::ratio<1>, units::base_unit<std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<1> > > > > > > (d=..., t=...) at velocity.cpp:28 28 const auto v = d / t; CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Debugging: NHolthaus Units 16
  • 33. (gdb) ptype d type = class units::unit_t<units::unit<std::ratio<1000, 1>, units::unit<std::ratio<1, 1>, units::base_unit<std::ratio<1, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1> >, std::ratio<0, 1>, std::ratio<0, 1> >, std::ratio<0, 1>, std::ratio<0, 1> >, double, units::linear_scale> [with Units = units::unit<std::ratio<1000, 1>, units::unit<std::ratio<1, 1>, units::base_unit<std::ratio<1, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1>, std::ratio<0, 1> >, std::ratio<0, 1>, std::ratio<0, 1> >, std::ratio<0, 1>, std::ratio<0, 1> >, T = double] : public units::linear_scale<T>, private units::detail::_unit_t { ... CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Debugging: NHolthaus Units 17
  • 34. template<class Meter = detail::meter_ratio<0>, class Kilogram = std::ratio<0>, class Second = std::ratio<0>, class Radian = std::ratio<0>, class Ampere = std::ratio<0>, class Kelvin = std::ratio<0>, class Mole = std::ratio<0>, class Candela = std::ratio<0>, class Byte = std::ratio<0>> struct base_unit; template<class Conversion, class BaseUnit, class PiExponent = std::ratio<0>, class Translation = std::ratio<0>> struct unit; #ifndef UNIT_LIB_DEFAULT_TYPE # define UNIT_LIB_DEFAULT_TYPE double #endif template<class Units, typename T = UNIT_LIB_DEFAULT_TYPE, template<typename> class NonLinearScale = linear_scale> class unit_t; CppCon 2019 | Rethinking the Way We Do Templates in C++ NHolthaus Units: Design 18
  • 35. template<class Meter = detail::meter_ratio<0>, class Kilogram = std::ratio<0>, class Second = std::ratio<0>, class Radian = std::ratio<0>, class Ampere = std::ratio<0>, class Kelvin = std::ratio<0>, class Mole = std::ratio<0>, class Candela = std::ratio<0>, class Byte = std::ratio<0>> struct base_unit; template<class Conversion, class BaseUnit, class PiExponent = std::ratio<0>, class Translation = std::ratio<0>> struct unit; #ifndef UNIT_LIB_DEFAULT_TYPE # define UNIT_LIB_DEFAULT_TYPE double #endif template<class Units, typename T = UNIT_LIB_DEFAULT_TYPE, template<typename> class NonLinearScale = linear_scale> class unit_t; using velocity_t = units::unit<std::ratio<1>, units::base_unit<std::ratio<1>, std::ratio<0>, std::ratio<1>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>, std::ratio<0>>, std::ratio<0>, std::ratio<0>>; CppCon 2019 | Rethinking the Way We Do Templates in C++ NHolthaus Units: Design 18
  • 36. • For most template metaprogramming libraries compile-time errors are rare CppCon 2019 | Rethinking the Way We Do Templates in C++ A need to modernize our toolbox 19
  • 37. • For most template metaprogramming libraries compile-time errors are rare • It is expected that engineers working with a physical units library will experience compile-time errors very o en – generating compile-time errors for invalid calculation is the main reason to create such a library CppCon 2019 | Rethinking the Way We Do Templates in C++ A need to modernize our toolbox 19
  • 38. • For most template metaprogramming libraries compile-time errors are rare • It is expected that engineers working with a physical units library will experience compile-time errors very o en – generating compile-time errors for invalid calculation is the main reason to create such a library In case of the physical units library we have to rethink the way we do template metaprogramming! CppCon 2019 | Rethinking the Way We Do Templates in C++ A need to modernize our toolbox 19
  • 39. • Developers cannot live without aliases as they hugely simplify code development and its maintenance CppCon 2019 | Rethinking the Way We Do Templates in C++ Type aliases are great for developers but not for end users 20
  • 40. • Developers cannot live without aliases as they hugely simplify code development and its maintenance • Type aliases names are lost quickly during compilation process CppCon 2019 | Rethinking the Way We Do Templates in C++ Type aliases are great for developers but not for end users 20
  • 41. • Developers cannot live without aliases as they hugely simplify code development and its maintenance • Type aliases names are lost quickly during compilation process • As a result end users get huge types in error messages CppCon 2019 | Rethinking the Way We Do Templates in C++ Type aliases are great for developers but not for end users 20
  • 42. • Developers cannot live without aliases as they hugely simplify code development and its maintenance • Type aliases names are lost quickly during compilation process • As a result end users get huge types in error messages using velocity = make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -1>>; using kilometre_per_hour = derived_unit<velocity, kilometre, hour>; CppCon 2019 | Rethinking the Way We Do Templates in C++ Type aliases are great for developers but not for end users EXAMPLE 20
  • 43. • Developers cannot live without aliases as they hugely simplify code development and its maintenance • Type aliases names are lost quickly during compilation process • As a result end users get huge types in error messages using velocity = make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -1>>; using kilometre_per_hour = derived_unit<velocity, kilometre, hour>; void foo(quantity<kilometre_per_hour>(90)); CppCon 2019 | Rethinking the Way We Do Templates in C++ Type aliases are great for developers but not for end users EXAMPLE 20
  • 44. • Developers cannot live without aliases as they hugely simplify code development and its maintenance • Type aliases names are lost quickly during compilation process • As a result end users get huge types in error messages using velocity = make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -1>>; using kilometre_per_hour = derived_unit<velocity, kilometre, hour>; void foo(quantity<kilometre_per_hour>(90)); [with Q = units::quantity<units::unit<units::dimension<units::exp<units::base_dim_length, 1, 1>, units::exp<units::base_dim_time, 1, -1> >, units::ratio<5, 18> >, double>] CppCon 2019 | Rethinking the Way We Do Templates in C++ Type aliases are great for developers but not for end users EXAMPLE 20
  • 45. • Developers cannot live without aliases as they hugely simplify code development and its maintenance • Type aliases names are lost quickly during compilation process • As a result end users get huge types in error messages using velocity = make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -1>>; using kilometre_per_hour = derived_unit<velocity, kilometre, hour>; void foo(quantity<kilometre_per_hour>(90)); [with Q = units::quantity<units::unit<units::dimension<units::exp<units::base_dim_length, 1, 1>, units::exp<units::base_dim_time, 1, -1> >, units::ratio<5, 18> >, double>] CppCon 2019 | Rethinking the Way We Do Templates in C++ Type aliases are great for developers but not for end users EXAMPLE It is a pity that we still do not have strong typedefs in the C++ language :-( 20
  • 46. struct velocity : make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -1>> {}; CppCon 2019 | Rethinking the Way We Do Templates in C++ Inheritance as a workaround 21
  • 47. struct velocity : make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -1>> {}; • Similarly to strong typedefs – strong types that do not vanish during compilation process – member functions of a base class taking the base class type as an argument will still work with a child class type provided instead (i.e. op==) CppCon 2019 | Rethinking the Way We Do Templates in C++ Inheritance as a workaround 21
  • 48. struct velocity : make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -1>> {}; • Similarly to strong typedefs – strong types that do not vanish during compilation process – member functions of a base class taking the base class type as an argument will still work with a child class type provided instead (i.e. op==) • Alternatively to strong typedefs – do not automatically inherit constructors and assignment operators – member functions of a base class returning the base class type will still return the same base type for a child class instance CppCon 2019 | Rethinking the Way We Do Templates in C++ Inheritance as a workaround 21
  • 49. Velocity auto v = 10m / 2s; CppCon 2019 | Rethinking the Way We Do Templates in C++ Type substitution problem 22
  • 50. Velocity auto v = 10m / 2s; template<typename U1, typename Rep1, typename U2, typename Rep2> [[nodiscard]] constexpr Quantity operator/(const quantity<U1, Rep1>& lhs, const quantity<U2, Rep2>& rhs); CppCon 2019 | Rethinking the Way We Do Templates in C++ Type substitution problem 22
  • 51. Velocity auto v = 10m / 2s; template<typename U1, typename Rep1, typename U2, typename Rep2> [[nodiscard]] constexpr Quantity operator/(const quantity<U1, Rep1>& lhs, const quantity<U2, Rep2>& rhs); CppCon 2019 | Rethinking the Way We Do Templates in C++ Type substitution problem How to form a velocity dimension child class type from division of length by time? 22
  • 52. template<typename BaseType> struct downcast_base { using base_type = BaseType; }; template<Exponent... Es> struct dimension : downcast_base<dimension<Es...>> {}; CppCon 2019 | Rethinking the Way We Do Templates in C++ Downcasting facility BASE CLASS 23
  • 53. template<typename BaseType> struct downcast_base { using base_type = BaseType; }; template<Exponent... Es> struct dimension : downcast_base<dimension<Es...>> {}; template<typename T> concept Downcastable = requires { typename T::base_type; } && std::derived_from<T, downcast_base<typename T::base_type>>; CppCon 2019 | Rethinking the Way We Do Templates in C++ Downcasting facility BASE CLASS DOWNCASTABLE 23
  • 54. template<Downcastable T> using downcast_from = T::base_type; template<Downcastable T> using downcast_to = std::type_identity<T>; CppCon 2019 | Rethinking the Way We Do Templates in C++ Downcasting facility HELPER ALIASES 24
  • 55. template<Downcastable T> using downcast_from = T::base_type; template<Downcastable T> using downcast_to = std::type_identity<T>; template<Downcastable T> struct downcasting_traits : downcast_to<T> {}; template<Downcastable T> using downcasting_traits_t = downcasting_traits<T>::type; CppCon 2019 | Rethinking the Way We Do Templates in C++ Downcasting facility HELPER ALIASES DOWNCASTING TRAITS 24
  • 56. template<Exponent... Es> struct dimension : downcast_base<dimension<Es...>> {}; CppCon 2019 | Rethinking the Way We Do Templates in C++ Downcasting facility EXAMPLE 25
  • 57. template<Exponent... Es> struct dimension : downcast_base<dimension<Es...>> {}; struct velocity : make_dimension_t<exp<base_dim_length, 1>, exp<base_dim_time, -1>> {}; template<> struct downcasting_traits<downcast_from<velocity>> : downcast_to<velocity> {}; CppCon 2019 | Rethinking the Way We Do Templates in C++ Downcasting facility EXAMPLE 25
  • 58. [with D = units::quantity<units::unit<units::dimension<units::exp<units::base_dim_length, 1, 1>, units::exp<units::base_dim_time, 1, -1> >, units::ratio<5, 18> >, double>] CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Compilation BEFORE 26
  • 59. [with D = units::quantity<units::unit<units::dimension<units::exp<units::base_dim_length, 1, 1>, units::exp<units::base_dim_time, 1, -1> >, units::ratio<5, 18> >, double>] [with D = units::quantity<units::kilometre_per_hour, double>] CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Compilation BEFORE AFTER 26
  • 60. CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Debugging 27
  • 61. CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Debugging 27
  • 62. Breakpoint 1, avg_speed<units::quantity<units::kilometre, double>, units::quantity<units::hour, double> > (d=..., t=...) at velocity.cpp:31 31 return d / t; CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Debugging 28
  • 63. (gdb) ptype d type = class units::quantity<units::kilometre, double> [with U = units::kilometre, Rep = double] { ... CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Debugging 29
  • 64. CppCon 2019 | Rethinking the Way We Do Templates in C++ NEW TOYS IN A TOOLBOX 30
  • 65. template<intmax_t Num, intmax_t Den = 1> struct ratio { static constexpr intmax_t num = Num * static_sign<Den>::value / static_gcd<Num, Den>::value; static constexpr intmax_t den = static_abs<Den>::value / static_gcd<Num, Den>::value; using type = ratio<num, den>; }; CppCon 2019 | Rethinking the Way We Do Templates in C++ Traditional implementation of std::ratio 31
  • 66. template<intmax_t Num, intmax_t Den = 1> struct ratio { static constexpr intmax_t num = Num * static_sign<Den>::value / static_gcd<Num, Den>::value; static constexpr intmax_t den = static_abs<Den>::value / static_gcd<Num, Den>::value; using type = ratio<num, den>; }; template<intmax_t Pn> struct static_sign : integral_constant<intmax_t, (Pn < 0) ? -1 : 1> {}; CppCon 2019 | Rethinking the Way We Do Templates in C++ Traditional implementation of std::ratio 31
  • 67. template<intmax_t Num, intmax_t Den = 1> struct ratio { static constexpr intmax_t num = Num * static_sign<Den>::value / static_gcd<Num, Den>::value; static constexpr intmax_t den = static_abs<Den>::value / static_gcd<Num, Den>::value; using type = ratio<num, den>; }; template<intmax_t Pn> struct static_sign : integral_constant<intmax_t, (Pn < 0) ? -1 : 1> {}; template<intmax_t Pn> struct static_abs : integral_constant<intmax_t, Pn * static_sign<Pn>::value> {}; CppCon 2019 | Rethinking the Way We Do Templates in C++ Traditional implementation of std::ratio 31
  • 68. template<intmax_t Num, intmax_t Den = 1> struct ratio { static constexpr intmax_t num = Num * static_sign<Den>::value / static_gcd<Num, Den>::value; static constexpr intmax_t den = static_abs<Den>::value / static_gcd<Num, Den>::value; using type = ratio<num, den>; }; template<intmax_t Pn, intmax_t Qn> struct static_gcd : static_gcd<Qn, (Pn % Qn)> {}; template<intmax_t Pn> struct static_gcd<Pn, 0> : integral_constant<intmax_t, static_abs<Pn>::value> {}; template<intmax_t Qn> struct static_gcd<0, Qn> : integral_constant<intmax_t, static_abs<Qn>::value> {}; CppCon 2019 | Rethinking the Way We Do Templates in C++ Traditional implementation of std::ratio 32
  • 69. namespace detail { template<typename R1, typename R2> struct ratio_multiply_impl { private: static constexpr intmax_t gcd1 = static_gcd<R1::num, R2::den>::value; static constexpr intmax_t gcd2 = static_gcd<R2::num, R1::den>::value; public: using type = ratio<safe_multiply<(R1::num / gcd1), (R2::num / gcd2)>::value, safe_multiply<(R1::den / gcd2), (R2::den / gcd1)>::value>; static constexpr intmax_t num = type::num; static constexpr intmax_t den = type::den; }; } template<typename R1, typename R2> using ratio_multiply = detail::ratio_multiply_impl<R1, R2>::type; CppCon 2019 | Rethinking the Way We Do Templates in C++ Traditional implementation of std::ratio_multiply 33
  • 70. template<typename T> [[nodiscard]] constexpr T abs(T v) noexcept { return v < 0 ? -v : v; } template<std::intmax_t Num, std::intmax_t Den = 1> struct ratio { static constexpr std::intmax_t num = Num * (Den < 0 ? -1 : 1) / std::gcd(Num, Den); static constexpr std::intmax_t den = abs(Den) / std::gcd(Num, Den); using type = ratio<num, den>; }; CppCon 2019 | Rethinking the Way We Do Templates in C++ constexpr-based implementation of ratio 34
  • 71. template<typename T> [[nodiscard]] constexpr T abs(T v) noexcept { return v < 0 ? -v : v; } template<std::intmax_t Num, std::intmax_t Den = 1> struct ratio { static constexpr std::intmax_t num = Num * (Den < 0 ? -1 : 1) / std::gcd(Num, Den); static constexpr std::intmax_t den = abs(Den) / std::gcd(Num, Den); using type = ratio<num, den>; }; • Better code reuse between run-time and compile-time programming • Less instantiations of class templates CppCon 2019 | Rethinking the Way We Do Templates in C++ constexpr-based implementation of ratio 34
  • 72. namespace detail { template<typename R1, typename R2> struct ratio_multiply_impl { private: static constexpr std::intmax_t gcd1 = std::gcd(R1::num, R2::den); static constexpr std::intmax_t gcd2 = std::gcd(R2::num, R1::den); public: using type = ratio<safe_multiply(R1::num / gcd1, R2::num / gcd2), safe_multiply(R1::den / gcd2, R2::den / gcd1)>; static constexpr std::intmax_t num = type::num; static constexpr std::intmax_t den = type::den; }; } template<Ratio R1, Ratio R2> using ratio_multiply = detail::ratio_multiply_impl<R1, R2>::type; CppCon 2019 | Rethinking the Way We Do Templates in C++ constexpr-based implementation of ratio_multiply 35
  • 73. • Allow non-union class types to appear in non-type template parameters (NTTP) • Require that types used as such, have strong structural equality – a type T is having strong structural equality if each subobject recursively has defaulted == and none of the subobjects are floating point types CppCon 2019 | Rethinking the Way We Do Templates in C++ P0732 P1185 Class Types in Non-Type Template Parameters 36
  • 74. template<size_t seconds> class fixed_timer { /* ... */ }; template<std::chrono::seconds seconds> class fixed_timer { /* ... */ }; • Allow non-union class types to appear in non-type template parameters (NTTP) • Require that types used as such, have strong structural equality – a type T is having strong structural equality if each subobject recursively has defaulted == and none of the subobjects are floating point types CppCon 2019 | Rethinking the Way We Do Templates in C++ P0732 P1185 Class Types in Non-Type Template Parameters C++17 C++20 36
  • 75. • gcc is the only compiler supporting NTTP for now – standard compliant support in gcc-9.2 • QoI and compile-time performance might improve over time CppCon 2019 | Rethinking the Way We Do Templates in C++ NTTP support and controversies 37
  • 76. • gcc is the only compiler supporting NTTP for now – standard compliant support in gcc-9.2 • QoI and compile-time performance might improve over time • There are some controversies regarding the NTTP feature (P1837) – Identity and Equality are not the same thing CppCon 2019 | Rethinking the Way We Do Templates in C++ NTTP support and controversies 37
  • 77. • gcc is the only compiler supporting NTTP for now – standard compliant support in gcc-9.2 • QoI and compile-time performance might improve over time • There are some controversies regarding the NTTP feature (P1837) – Identity and Equality are not the same thing CppCon 2019 | Rethinking the Way We Do Templates in C++ NTTP support and controversies NTTP support in the Physical Units library will not be merged to a master branch before the ISO C++ Committee meeting in Belfast 37
  • 78. struct ratio { std::intmax_t num; std::intmax_t den; explicit constexpr ratio(std::intmax_t n, std::intmax_t d = 1) : num(n * (d < 0 ? -1 : 1) / std::gcd(n, d)), den(abs(d) / std::gcd(n, d)) { } [[nodiscard]] constexpr bool operator==(const ratio&) = default; // ... }; CppCon 2019 | Rethinking the Way We Do Templates in C++ NTTP-based implementation of ratio 38
  • 79. struct ratio { // ... [[nodiscard]] friend constexpr ratio operator*(const ratio& lhs, const ratio& rhs) { const std::intmax_t gcd1 = std::gcd(lhs.num, rhs.den); const std::intmax_t gcd2 = std::gcd(rhs.num, lhs.den); return ratio(safe_multiply(lhs.num / gcd1, rhs.num / gcd2), safe_multiply(lhs.den / gcd2, rhs.den / gcd1)); } }; CppCon 2019 | Rethinking the Way We Do Templates in C++ NTTP-based implementation of ratio_multiply 39
  • 80. struct ratio { // ... [[nodiscard]] friend constexpr ratio operator*(const ratio& lhs, const ratio& rhs) { const std::intmax_t gcd1 = std::gcd(lhs.num, rhs.den); const std::intmax_t gcd2 = std::gcd(rhs.num, lhs.den); return ratio(safe_multiply(lhs.num / gcd1, rhs.num / gcd2), safe_multiply(lhs.den / gcd2, rhs.den / gcd1)); } [[nodiscard]] friend consteval ratio operator*(std::intmax_t n, const ratio& rhs) { return ratio(n) * rhs; } [[nodiscard]] friend consteval ratio operator*(const ratio& lhs, std::intmax_t n) { return lhs * ratio(n); } }; CppCon 2019 | Rethinking the Way We Do Templates in C++ NTTP-based implementation of ratio_multiply 39
  • 81. // US customary units struct yard : unit<length, ratio(9'144, 10'000)> {}; template<> struct downcasting_traits<downcast_from<yard>> : downcast_to<yard> {}; struct foot : unit<length, yard::ratio / 3> {}; template<> struct downcasting_traits<downcast_from<foot>> : downcast_to<foot> {}; struct inch : unit<length, foot::ratio / 12> {}; template<> struct downcasting_traits<downcast_from<inch>> : downcast_to<inch> {}; struct mile : unit<length, 1'760 * yard::ratio> {}; template<> struct downcasting_traits<downcast_from<mile>> : downcast_to<mile> {}; CppCon 2019 | Rethinking the Way We Do Templates in C++ NTTP in action 40
  • 82. template<typename U1, typename Rep1, typename U2, typename Rep2> requires (std::ratio_divide<typename U1::ratio, typename U2::ratio>::den == 1) [[nodiscard]] quantity<downcasting_traits_t<unit<dimension_divide_t<typename U1::dimension, typename U2::dimension>, std::ratio_divide<typename U1::ratio, typename U2::ratio>>>, std::common_type_t<Rep1, Rep2>> constexpr operator/(const quantity<U1, Rep1>& lhs, const quantity<U2, Rep2>& rhs); CppCon 2019 | Rethinking the Way We Do Templates in C++ NTTP in action BEFORE 41
  • 83. template<typename U1, typename Rep1, typename U2, typename Rep2> requires (std::ratio_divide<typename U1::ratio, typename U2::ratio>::den == 1) [[nodiscard]] quantity<downcasting_traits_t<unit<dimension_divide_t<typename U1::dimension, typename U2::dimension>, std::ratio_divide<typename U1::ratio, typename U2::ratio>>>, std::common_type_t<Rep1, Rep2>> constexpr operator/(const quantity<U1, Rep1>& lhs, const quantity<U2, Rep2>& rhs); template<typename U1, typename Rep1, typename U2, typename Rep2> requires ((U1::ratio / U2::ratio).den == 1) [[nodiscard]] quantity<downcasting_traits_t<unit<dimension_divide_t<typename U1::dimension, typename U2::dimension>, U1::ratio / U2::ratio>>, std::common_type_t<Rep1, Rep2>> constexpr operator/(const quantity<U1, Rep1>& lhs, const quantity<U2, Rep2>& rhs); CppCon 2019 | Rethinking the Way We Do Templates in C++ NTTP in action BEFORE AFTER 41
  • 84. template<typename U1, typename Rep1, typename U2, typename Rep2> requires (std::ratio_divide<typename U1::ratio, typename U2::ratio>::den == 1) [[nodiscard]] quantity<downcasting_traits_t<unit<dimension_divide_t<typename U1::dimension, typename U2::dimension>, std::ratio_divide<typename U1::ratio, typename U2::ratio>>>, std::common_type_t<Rep1, Rep2>> constexpr operator/(const quantity<U1, Rep1>& lhs, const quantity<U2, Rep2>& rhs); CppCon 2019 | Rethinking the Way We Do Templates in C++ NTTP in action BEFORE 42
  • 85. template<typename U1, typename Rep1, typename U2, typename Rep2> requires (std::ratio_divide<typename U1::ratio, typename U2::ratio>::den == 1) [[nodiscard]] quantity<downcasting_traits_t<unit<dimension_divide_t<typename U1::dimension, typename U2::dimension>, std::ratio_divide<typename U1::ratio, typename U2::ratio>>>, std::common_type_t<Rep1, Rep2>> constexpr operator/(const quantity<U1, Rep1>& lhs, const quantity<U2, Rep2>& rhs); template<typename U1, typename Rep1, typename U2, typename Rep2> requires ((U1::ratio / U2::ratio).den == 1) [[nodiscard]] quantity<downcasting_traits_t<unit<U1::dimension / U2::dimension, U1::ratio / U2::ratio>>, std::common_type_t<Rep1, Rep2>> constexpr operator/(const quantity<U1, Rep1>& lhs, const quantity<U2, Rep2>& rhs); CppCon 2019 | Rethinking the Way We Do Templates in C++ NTTP in action BEFORE AFTER 42
  • 86. struct exp { base_dimension base; int num; int den; constexpr exp(base_dimension b, int n, int d = 1): base(b), num(n), den(d) {} }; CppCon 2019 | Rethinking the Way We Do Templates in C++ NTTP in action 43
  • 87. struct exp { base_dimension base; int num; int den; constexpr exp(base_dimension b, int n, int d = 1): base(b), num(n), den(d) {} }; struct dimension { std::vector<exp> exponents; // compile-time vector constexpr dimension(std::initializer_list<exp> init); [[nodiscard]] friend constexpr dimension operator*(const dimension& lhs, const dimension& rhs); [[nodiscard]] friend constexpr dimension operator/(const dimension& lhs, const dimension& rhs); }; CppCon 2019 | Rethinking the Way We Do Templates in C++ NTTP in action 43
  • 88. struct exp { base_dimension base; int num; int den; constexpr exp(base_dimension b, int n, int d = 1): base(b), num(n), den(d) {} }; struct dimension { std::vector<exp> exponents; // compile-time vector constexpr dimension(std::initializer_list<exp> init); [[nodiscard]] friend constexpr dimension operator*(const dimension& lhs, const dimension& rhs); [[nodiscard]] friend constexpr dimension operator/(const dimension& lhs, const dimension& rhs); }; inline constexpr dimension velocity = { exp(base_dim_length, 1), exp(base_dim_time, -1) }; CppCon 2019 | Rethinking the Way We Do Templates in C++ NTTP in action 43
  • 89. struct exp { base_dimension base; int num; int den; constexpr exp(base_dimension b, int n, int d = 1): base(b), num(n), den(d) {} }; struct dimension { std::vector<exp> exponents; // compile-time vector constexpr dimension(std::initializer_list<exp> init); [[nodiscard]] friend constexpr dimension operator*(const dimension& lhs, const dimension& rhs); [[nodiscard]] friend constexpr dimension operator/(const dimension& lhs, const dimension& rhs); }; inline constexpr dimension velocity = { exp(base_dim_length, 1), exp(base_dim_time, -1) }; CppCon 2019 | Rethinking the Way We Do Templates in C++ NTTP in action exp and dimension values are only used as NTTP so those types and their implementation should exist only during compile-time 43
  • 90. struct exp { base_dimension base; int num; int den; consteval exp(base_dimension b, int n, int d = 1): base(b), num(n), den(d) {} }; struct dimension { std::vector<exp> exponents; // compile-time vector consteval dimension(std::initializer_list<exp> init); [[nodiscard]] friend consteval dimension operator*(const dimension& lhs, const dimension& rhs); [[nodiscard]] friend consteval dimension operator/(const dimension& lhs, const dimension& rhs); }; inline consteval dimension velocity = { exp(base_dim_length, 1), exp(base_dim_time, -1) }; CppCon 2019 | Rethinking the Way We Do Templates in C++ P1073 Immediate functions 44
  • 91. struct exp { base_dimension base; int num; int den; consteval exp(base_dimension b, int n, int d = 1): base(b), num(n), den(d) {} }; struct dimension { std::vector<exp> exponents; // compile-time vector consteval dimension(std::initializer_list<exp> init); [[nodiscard]] friend consteval dimension operator*(const dimension& lhs, const dimension& rhs); [[nodiscard]] friend consteval dimension operator/(const dimension& lhs, const dimension& rhs); }; inline consteval dimension velocity = { exp(base_dim_length, 1), exp(base_dim_time, -1) }; CppCon 2019 | Rethinking the Way We Do Templates in C++ P1073 Immediate functions C++20 does not allow us yet to create consteval objects :-( 44
  • 92. Usage of class types as non-type template parameters (NTTP) might be one of the most significant C++ improvements in template metaprogramming during the last decade CppCon 2019 | Rethinking the Way We Do Templates in C++ Class Types in Non-Type Template Parameters 45
  • 93. void* foo(void* t); CppCon 2019 | Rethinking the Way We Do Templates in C++ How do you feel about such an interface? 46
  • 94. void* foo(void* t); template<typename T> auto foo(T&& t); CppCon 2019 | Rethinking the Way We Do Templates in C++ How do you feel about such an interface? 46
  • 95. void* foo(void* t); template<typename T> auto foo(T&& t); auto foo = [](auto&& t){ /* ... */ }; CppCon 2019 | Rethinking the Way We Do Templates in C++ How do you feel about such an interface? 46
  • 96. void* foo(void* t); template<typename T> auto foo(T&& t); auto foo = [](auto&& t){ /* ... */ }; Unconstrained template parameters are a void* of C++ CppCon 2019 | Rethinking the Way We Do Templates in C++ How do you feel about such an interface? 46
  • 97. • Class/Function/Variable/Alias templates, and non-template functions (typically members of class templates) may be associated with a constraint CppCon 2019 | Rethinking the Way We Do Templates in C++ Concepts 47
  • 98. • Class/Function/Variable/Alias templates, and non-template functions (typically members of class templates) may be associated with a constraint • Constraint specifies the requirements on template arguments – can be used to select the most appropriate function overloads and template specializations CppCon 2019 | Rethinking the Way We Do Templates in C++ Concepts 47
  • 99. • Class/Function/Variable/Alias templates, and non-template functions (typically members of class templates) may be associated with a constraint • Constraint specifies the requirements on template arguments – can be used to select the most appropriate function overloads and template specializations • Named sets of such requirements are called concepts CppCon 2019 | Rethinking the Way We Do Templates in C++ Concepts 47
  • 100. • Class/Function/Variable/Alias templates, and non-template functions (typically members of class templates) may be associated with a constraint • Constraint specifies the requirements on template arguments – can be used to select the most appropriate function overloads and template specializations • Named sets of such requirements are called concepts • Concept – is a named predicate – evaluated at compile time – a part of the interface of a template CppCon 2019 | Rethinking the Way We Do Templates in C++ Concepts 47
  • 101. template<typename T> concept Velocity = QuantityOf<T, velocity>; template<Velocity V> constexpr price calc_fine(V speed); CppCon 2019 | Rethinking the Way We Do Templates in C++ Concept example 48
  • 102. template<typename T, typename D> concept QuantityOf = Quantity<T> && Dimension<D> && std::same_as<typename T::dimension, D>; template<typename T> concept Velocity = QuantityOf<T, velocity>; template<Velocity V> constexpr price calc_fine(V speed); CppCon 2019 | Rethinking the Way We Do Templates in C++ Concept example 48
  • 103. template<typename T> concept Dimension = std::is_empty_v<T> && detail::is_dimension<downcast_from<T>>; template<typename T, typename D> concept QuantityOf = Quantity<T> && Dimension<D> && std::same_as<typename T::dimension, D>; template<typename T> concept Velocity = QuantityOf<T, velocity>; template<Velocity V> constexpr price calc_fine(V speed); CppCon 2019 | Rethinking the Way We Do Templates in C++ Concept example 48
  • 104. template<typename T> concept Quantity = detail::is_quantity<T>; template<typename T> concept Dimension = std::is_empty_v<T> && detail::is_dimension<downcast_from<T>>; template<typename T, typename D> concept QuantityOf = Quantity<T> && Dimension<D> && std::same_as<typename T::dimension, D>; template<typename T> concept Velocity = QuantityOf<T, velocity>; template<Velocity V> constexpr price calc_fine(V speed); CppCon 2019 | Rethinking the Way We Do Templates in C++ Concept example 48
  • 105. CppCon 2019 | Rethinking the Way We Do Templates in C++ Not just a syntactic sugar for enable_if and void_t 49
  • 106. • Constraining function template return types constexpr Velocity auto avg_speed(Length auto d, Time auto t) { return d / t; } CppCon 2019 | Rethinking the Way We Do Templates in C++ Not just a syntactic sugar for enable_if and void_t 49
  • 107. • Constraining function template return types constexpr Velocity auto avg_speed(Length auto d, Time auto t) { return d / t; } • Constraining the deduced types of user's variables const Velocity auto speed = avg_speed(220.km, 2.h); CppCon 2019 | Rethinking the Way We Do Templates in C++ Not just a syntactic sugar for enable_if and void_t 49
  • 108. • Constraining function template return types constexpr Velocity auto avg_speed(Length auto d, Time auto t) { return d / t; } • Constraining the deduced types of user's variables const Velocity auto speed = avg_speed(220.km, 2.h); • Constraining class template parameters without the need to introduce new class template parameters template<Dimension D, Ratio R = ratio<1>> requires (R::num * R::den > 0) struct unit; CppCon 2019 | Rethinking the Way We Do Templates in C++ Not just a syntactic sugar for enable_if and void_t 49
  • 109. CppCon 2019 | Rethinking the Way We Do Templates in C++ Concept categories 50
  • 110. template<class T> concept signed_integral = integral<T> && is_signed_v<T>; CppCon 2019 | Rethinking the Way We Do Templates in C++ Concept categories PREDICATES 50
  • 111. template<class T> concept signed_integral = integral<T> && is_signed_v<T>; template<class T> concept swappable = requires(T& a, T& b) { ranges::swap(a, b); }; CppCon 2019 | Rethinking the Way We Do Templates in C++ Concept categories PREDICATES CAPABILITIES 50
  • 112. template<class I> concept bidirectional_iterator = forward_iterator<I> && derived_from<ITER_CONCEPT(I), bidirectional_iterator_tag> && requires(I i) { { --i } -> same_as<I&>; { i-- } -> same_as<I>; }; CppCon 2019 | Rethinking the Way We Do Templates in C++ Concept categories ABSTRACTIONS 51
  • 113. template<typename T> concept Ratio = is_ratio<T>; CppCon 2019 | Rethinking the Way We Do Templates in C++ Concept categories FAMILY OF INSTANTIATIONS 52
  • 114. template<typename T> inline constexpr bool is_ratio = false; template<intmax_t Num, intmax_t Den> inline constexpr bool is_ratio<ratio<Num, Den>> = true; template<typename T> concept Ratio = is_ratio<T>; CppCon 2019 | Rethinking the Way We Do Templates in C++ Concept categories FAMILY OF INSTANTIATIONS 52
  • 115. template<typename T> inline constexpr bool is_ratio = false; template<intmax_t Num, intmax_t Den> inline constexpr bool is_ratio<ratio<Num, Den>> = true; template<typename T> concept Ratio = is_ratio<T>; template<Ratio R1, Ratio R2> using ratio_multiply = detail::ratio_multiply_impl<R1, R2>::type; CppCon 2019 | Rethinking the Way We Do Templates in C++ Concept categories FAMILY OF INSTANTIATIONS USAGE EXAMPLES 52
  • 116. template<typename T> inline constexpr bool is_ratio = false; template<intmax_t Num, intmax_t Den> inline constexpr bool is_ratio<ratio<Num, Den>> = true; template<typename T> concept Ratio = is_ratio<T>; template<Ratio R1, Ratio R2> using ratio_multiply = detail::ratio_multiply_impl<R1, R2>::type; template<Unit U, Scalar Rep = double> class quantity; CppCon 2019 | Rethinking the Way We Do Templates in C++ Concept categories FAMILY OF INSTANTIATIONS USAGE EXAMPLES 52
  • 117. constexpr units::Velocity auto avg_speed(units::Length auto d, units::Time auto t) { return d * t; } CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Compilation 53
  • 118. constexpr units::Velocity auto avg_speed(units::Length auto d, units::Time auto t) { return d * t; } example.cpp: In instantiation of ‘constexpr units::Velocity avg_speed(D, T) [with D = units::quantity<units::kilometre>; T = units::quantity<units::hour>]’: example.cpp:49:49: required from here example.cpp:34:14: error: placeholder constraints not satisfied 34 | return d * t; | ^ include/units/dimensions/velocity.h:34:16: note: within ‘template<class T> concept units::Velocity<T> [with T = units::quantity<units::unit<units::dimension<units::exp<units::base_dim_length, 1, 1>, units::exp<units::base_dim_time, 1, 1> >, units::ratio<3600000, 1> >, double>]’ 34 | concept Velocity = Quantity<T> && std::same_as<typename T::dimension, velocity>; | ^~~~~~~~ include/stl2/detail/concepts/core.hpp:37:15: note: within ‘template<class T, class U> concept std::same_as<T, U> [with T = units::dimension<units::exp<units::base_dim_length, 1, 1>, units::exp<units::base_dim_time, 1, 1> >; U = units::velocity]’ 37 | META_CONCEPT same_as = meta::Same<T, U> && meta::Same<U, T>; | ^~~~~~~ ... CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Compilation 53
  • 119. constexpr units::Velocity auto avg_speed(units::Length auto d, units::Time auto t) { return d * t; } include/meta/meta_fwd.hpp:224:18: note: within ‘template<class T, class U> concept meta::Same<T, U> [with T = units::dimension<units::exp<units::base_dim_length, 1, 1>, units::exp<units::base_dim_time, 1, 1> >; U = units::velocity]’ 224 | META_CONCEPT Same = | ^~~~ include/meta/meta_fwd.hpp:224:18: note: ‘meta::detail::barrier’ evaluated to false include/meta/meta_fwd.hpp:224:18: note: within ‘template<class T, class U> concept meta::Same<T, U> [with T = units::velocity; U = units::dimension<units::exp<units::base_dim_length, 1, 1>, units::exp<units::base_dim_time, 1, 1> >]’ include/meta/meta_fwd.hpp:224:18: note: ‘meta::detail::barrier’ evaluated to false CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Compilation 54
  • 120. constexpr units::Velocity auto avg_speed(units::Length auto d, units::Time auto t) { return d * t; } include/meta/meta_fwd.hpp:224:18: note: within ‘template<class T, class U> concept meta::Same<T, U> [with T = units::dimension<units::exp<units::base_dim_length, 1, 1>, units::exp<units::base_dim_time, 1, 1> >; U = units::velocity]’ 224 | META_CONCEPT Same = | ^~~~ include/meta/meta_fwd.hpp:224:18: note: ‘meta::detail::barrier’ evaluated to false include/meta/meta_fwd.hpp:224:18: note: within ‘template<class T, class U> concept meta::Same<T, U> [with T = units::velocity; U = units::dimension<units::exp<units::base_dim_length, 1, 1>, units::exp<units::base_dim_time, 1, 1> >]’ include/meta/meta_fwd.hpp:224:18: note: ‘meta::detail::barrier’ evaluated to false CppCon 2019 | Rethinking the Way We Do Templates in C++ User experience: Compilation Concepts support in C++ compilers is still experimental and largely based on Concepts TS. We can expect even better diagnostics in the future. 54
  • 121. CppCon 2019 | Rethinking the Way We Do Templates in C++ Bene ts of using C++ Concepts 55
  • 122. 1 Clearly state the design intent of the interface of a class/function template CppCon 2019 | Rethinking the Way We Do Templates in C++ Bene ts of using C++ Concepts 55
  • 123. 1 Clearly state the design intent of the interface of a class/function template 2 Embedded in a template signature CppCon 2019 | Rethinking the Way We Do Templates in C++ Bene ts of using C++ Concepts 55
  • 124. 1 Clearly state the design intent of the interface of a class/function template 2 Embedded in a template signature 3 Simplify and extend SFINAE • disabling specific overloads for specific constraints (compared to std::enable_if) • constraints based on dependent member types/functions existence (compared to void_t) • no dummy template parameters allocated for SFINAE needs • constraining function return types and deduced types of user's variables CppCon 2019 | Rethinking the Way We Do Templates in C++ Bene ts of using C++ Concepts 55
  • 125. 1 Clearly state the design intent of the interface of a class/function template 2 Embedded in a template signature 3 Simplify and extend SFINAE • disabling specific overloads for specific constraints (compared to std::enable_if) • constraints based on dependent member types/functions existence (compared to void_t) • no dummy template parameters allocated for SFINAE needs • constraining function return types and deduced types of user's variables 4 Greatly improve error messages • raise compilation error about failed compile-time contract before instantiating a template • no more errors from deeply nested implementation details of a function template CppCon 2019 | Rethinking the Way We Do Templates in C++ Bene ts of using C++ Concepts 55
  • 126. CppCon 2019 | Rethinking the Way We Do Templates in C++ PERFORMANCE 56
  • 127. • Started in 2016 by Louis Dionne CppCon 2019 | Rethinking the Way We Do Templates in C++ Compile-time benchmarking with Metabench 57
  • 128. • Started in 2016 by Louis Dionne • CMake module that simplifies compile-time microbenchmarking CppCon 2019 | Rethinking the Way We Do Templates in C++ Compile-time benchmarking with Metabench 57
  • 129. • Started in 2016 by Louis Dionne • CMake module that simplifies compile-time microbenchmarking • Can be used to benchmark precise parts of a C++ file, such as the instantiation of a single function CppCon 2019 | Rethinking the Way We Do Templates in C++ Compile-time benchmarking with Metabench 57
  • 130. • Started in 2016 by Louis Dionne • CMake module that simplifies compile-time microbenchmarking • Can be used to benchmark precise parts of a C++ file, such as the instantiation of a single function • http://metaben.ch compares the performance of several MPL algorithms implemented in various libraries CppCon 2019 | Rethinking the Way We Do Templates in C++ Compile-time benchmarking with Metabench 57
  • 131. metabench_add_dataset(metabench.data.ratio.create.std_ratio all_std_ratio.cpp.erb "[10, 50, 100, 250, 500, 750, 1000, 1500, 2000]" NAME "std::ratio" ) metabench_add_dataset(metabench.data.ratio.create.ratio_type_constexpr all_ratio_type_constexpr.cpp.erb "[10, 50, 100, 250, 500, 750, 1000, 1500, 2000]" NAME "ratio constexpr" ) metabench_add_dataset(metabench.data.ratio.create.ratio_nttp all_ratio_nttp.cpp.erb "[10, 50, 100, 250, 500, 750, 1000, 1500, 2000]" NAME "ratio NTTP" ) metabench_add_chart(metabench.chart.ratio.all TITLE "Creation of 2*N ratios" SUBTITLE "(smaller is better)" DATASETS metabench.data.ratio.create.std_ratio metabench.data.ratio.create.ratio_type_constexpr metabench.data.ratio.create.ratio_nttp ) CppCon 2019 | Rethinking the Way We Do Templates in C++ A short intro to Metabench: CMake 58
  • 132. #include "ratio_type_constexpr.h" <% (1..n).each do |i| %> struct test<%= i %> { #if defined(METABENCH) using r1 = units::ratio<<%= 2 * i - 1 %>, <%= 2 * n %>>; using r2 = units::ratio<<%= 2 * i %>, <%= 2 * n %>>; #else using r1 = void; using r2 = void; #endif }; <% end %> int main() {} CppCon 2019 | Rethinking the Way We Do Templates in C++ A short intro to Metabench: ERB le CREATE_RATIO_TYPE_CONSTEXPR.CPP.ERB 59
  • 133. #include "ratio_type_constexpr.h" struct test1 { #if defined(METABENCH) using r1 = std::ratio<1, 20>; using r2 = std::ratio<2, 20>; #else using r1 = void; using r2 = void; #endif }; struct test2 { #if defined(METABENCH) using r1 = std::ratio<3, 20>; using r2 = std::ratio<4, 20>; #else using r1 = void; using r2 = void; #endif }; // ... int main() {} CppCon 2019 | Rethinking the Way We Do Templates in C++ A short intro to Metabench: Generated C++ code 60
  • 134. #include "ratio_type_constexpr.h" struct test1 { #if defined(METABENCH) using r1 = std::ratio<1, 20>; using r2 = std::ratio<2, 20>; #else using r1 = void; using r2 = void; #endif }; struct test2 { #if defined(METABENCH) using r1 = std::ratio<3, 20>; using r2 = std::ratio<4, 20>; #else using r1 = void; using r2 = void; #endif }; // ... int main() {} #include "ratio_nttp.h" struct test1 { #if defined(METABENCH) static constexpr units::ratio r1{1, 20}; static constexpr units::ratio r2{2, 20}; #else static constexpr bool r1 = false; static constexpr bool r2 = false; #endif }; struct test2 { #if defined(METABENCH) static constexpr units::ratio r1{3, 20}; static constexpr units::ratio r2{4, 20}; #else static constexpr bool r1 = false; static constexpr bool r2 = false; #endif }; // ... int main() {} CppCon 2019 | Rethinking the Way We Do Templates in C++ A short intro to Metabench: Generated C++ code 60
  • 135. CppCon 2019 | Rethinking the Way We Do Templates in C++ ratio performance 61
  • 136. CppCon 2019 | Rethinking the Way We Do Templates in C++ ratio performance 62
  • 137. CppCon 2019 | Rethinking the Way We Do Templates in C++ Current constexpr QoI is slow 63
  • 138. CppCon 2019 | Rethinking the Way We Do Templates in C++ The Rule of Chiel 64
  • 139. CppCon 2019 | Rethinking the Way We Do Templates in C++ The Rule of Chiel 64
  • 140. CppCon 2019 | Rethinking the Way We Do Templates in C++ Cost of operations: The Rule of Chiel 65
  • 141. 1 Looking up a memoized type CppCon 2019 | Rethinking the Way We Do Templates in C++ Cost of operations: The Rule of ChielCost of operations: The Rule of Chiel 65
  • 142. 1 Looking up a memoized type 2 Adding a parameter to an alias call CppCon 2019 | Rethinking the Way We Do Templates in C++ Cost of operations: The Rule of Chiel 65
  • 143. 1 Looking up a memoized type 2 Adding a parameter to an alias call 3 Adding a parameter to a type CppCon 2019 | Rethinking the Way We Do Templates in C++ Cost of operations: The Rule of Chiel 65
  • 144. 1 Looking up a memoized type 2 Adding a parameter to an alias call 3 Adding a parameter to a type 4 Calling an alias CppCon 2019 | Rethinking the Way We Do Templates in C++ Cost of operations: The Rule of Chiel 65
  • 145. 1 Looking up a memoized type 2 Adding a parameter to an alias call 3 Adding a parameter to a type 4 Calling an alias 5 Instantiating a class CppCon 2019 | Rethinking the Way We Do Templates in C++ Cost of operations: The Rule of Chiel 65
  • 146. 1 Looking up a memoized type 2 Adding a parameter to an alias call 3 Adding a parameter to a type 4 Calling an alias 5 Instantiating a class 6 Instantiating a function template CppCon 2019 | Rethinking the Way We Do Templates in C++ Cost of operations: The Rule of Chiel 65
  • 147. 1 Looking up a memoized type 2 Adding a parameter to an alias call 3 Adding a parameter to a type 4 Calling an alias 5 Instantiating a class 6 Instantiating a function template 7 SFINAE CppCon 2019 | Rethinking the Way We Do Templates in C++ Cost of operations: The Rule of Chiel 65
  • 148. template<bool B, class T, class F> struct conditional { using type = T; }; template<class T, class F> struct conditional<false, T, F> { using type = F; }; template<bool B, class T, class F> using conditional_t = conditional<B,T,F>::type; CppCon 2019 | Rethinking the Way We Do Templates in C++ std::conditional<B, T, F> TRADITIONAL 66
  • 149. template<bool B, class T, class F> struct conditional { using type = T; }; template<class T, class F> struct conditional<false, T, F> { using type = F; }; template<bool B, class T, class F> using conditional_t = conditional<B,T,F>::type; template<bool> struct conditional { template<typename T, typename F> using type = F; }; template<> struct conditional<true> { template<typename T, typename F> using type = T; }; template<bool B, typename T, typename F> using conditional_t = conditional<B>::template type<T, F>; CppCon 2019 | Rethinking the Way We Do Templates in C++ std::conditional<B, T, F> TRADITIONAL WITH ALIAS TEMPLATE 66
  • 150. CppCon 2019 | Rethinking the Way We Do Templates in C++ conditional performance (gcc-9) 67
  • 151. CppCon 2019 | Rethinking the Way We Do Templates in C++ conditional performance (clang-8) 68
  • 152. template<class T, class U> struct is_same : std::false_type {}; template<class T> struct is_same<T, T> : std::true_type {}; template<class T, class U> inline constexpr bool is_same_v = is_same<T, U>::value; CppCon 2019 | Rethinking the Way We Do Templates in C++ std::is_same<T, U> TRADITIONAL 69
  • 153. template<class T, class U> struct is_same : std::false_type {}; template<class T> struct is_same<T, T> : std::true_type {}; template<class T, class U> inline constexpr bool is_same_v = is_same<T, U>::value; template<class T, class U> inline constexpr bool is_same = false; template<class T> inline constexpr bool is_same<T, T> = true; CppCon 2019 | Rethinking the Way We Do Templates in C++ std::is_same<T, U> TRADITIONAL USING VARIABLE TEMPLATES 69
  • 154. CppCon 2019 | Rethinking the Way We Do Templates in C++ is_same performance (gcc-9) 70
  • 155. CppCon 2019 | Rethinking the Way We Do Templates in C++ is_same performance (clang-8) 71
  • 156. CppCon 2019 | Rethinking the Way We Do Templates in C++ What about C++ Concepts? 72
  • 157. • C++ Concepts are great and not too expensive at compile-time CppCon 2019 | Rethinking the Way We Do Templates in C++ What about C++ Concepts? 72
  • 158. • C++ Concepts are great and not too expensive at compile-time • Compile-time performance impact might be limited by using concepts only in the user interfaces template<Exponent... Es> struct dimension : downcast_base<dimension<Es...>> {}; namespace detail { template<typename D1, typename D2> struct dimension_divide; template<typename... E1, typename... E2> struct dimension_divide<dimension<E1...>, dimension<E2...>> : dimension_multiply<dimension<E1...>, dimension<exp_invert_t<E2>...>> { }; } template<Dimension D1, Dimension D2> using dimension_divide_t = detail::dimension_divide<typename D1::base_type, typename D2::base_type>::type; CppCon 2019 | Rethinking the Way We Do Templates in C++ What about C++ Concepts? 72
  • 159. CppCon 2019 | Rethinking the Way We Do Templates in C++ C++ Concepts performance 73
  • 160. CppCon 2019 | Rethinking the Way We Do Templates in C++ More info on MPL performance on http://metaben.ch 74
  • 161. CppCon 2019 | Rethinking the Way We Do Templates in C++ TAKEAWAYS 75
  • 162. CppCon 2019 | Rethinking the Way We Do Templates in C++ Rethinking C++ templates 76
  • 163. 1 Think about end users' experience and not only about your convenience as a developer CppCon 2019 | Rethinking the Way We Do Templates in C++ Rethinking C++ templates 76
  • 164. 1 Think about end users' experience and not only about your convenience as a developer 2 Use C++ Concepts to • express compile-time contracts • improve productivity • improve compile-time errors CppCon 2019 | Rethinking the Way We Do Templates in C++ Rethinking C++ templates 76
  • 165. 1 Think about end users' experience and not only about your convenience as a developer 2 Use C++ Concepts to • express compile-time contracts • improve productivity • improve compile-time errors 3 Use NTTPs when a template parameter represents a value rather than a type CppCon 2019 | Rethinking the Way We Do Templates in C++ Rethinking C++ templates 76
  • 166. 1 Think about end users' experience and not only about your convenience as a developer 2 Use C++ Concepts to • express compile-time contracts • improve productivity • improve compile-time errors 3 Use NTTPs when a template parameter represents a value rather than a type 4 Optimize compile-time performance • MPL is free at run-time but can be really expensive at compile-time • the same MPL algorithm may be implemented using di erent techniques and take di erent amount of time to compile CppCon 2019 | Rethinking the Way We Do Templates in C++ Rethinking C++ templates 76