Units for types in C++
Clash Royale CLAN TAG#URR8PPP
up vote
13
down vote
favorite
In the C++ Core Guidlines P.1 change_speed
example, it shows a Speed
type that is used as shown below:
change_speed(Speed s); // better: the meaning of s is specified
// ...
change_speed(2.3); // error: no unit
change_speed(23m / 10s); // meters per second
I am particularly interested in the last two lines of this example. The first seems to suggest that if you provide no units with the argument to change_speed
it will throw an error. The last line shows the units defined using some the m
and s
literals. Are both of these new features in modern versions of C++? If so, how would something like this be implemented, and what version of C++ is required?
c++ cpp-core-guidelines
add a comment |Â
up vote
13
down vote
favorite
In the C++ Core Guidlines P.1 change_speed
example, it shows a Speed
type that is used as shown below:
change_speed(Speed s); // better: the meaning of s is specified
// ...
change_speed(2.3); // error: no unit
change_speed(23m / 10s); // meters per second
I am particularly interested in the last two lines of this example. The first seems to suggest that if you provide no units with the argument to change_speed
it will throw an error. The last line shows the units defined using some the m
and s
literals. Are both of these new features in modern versions of C++? If so, how would something like this be implemented, and what version of C++ is required?
c++ cpp-core-guidelines
12
cppreference
â Tyker
Aug 21 at 7:15
Perhaps this is what you ment: It won't throw an error, It won't compile, becausechange_speed
takes a Speed and not a floating point literal.
â hetepeperfan
Aug 21 at 7:48
add a comment |Â
up vote
13
down vote
favorite
up vote
13
down vote
favorite
In the C++ Core Guidlines P.1 change_speed
example, it shows a Speed
type that is used as shown below:
change_speed(Speed s); // better: the meaning of s is specified
// ...
change_speed(2.3); // error: no unit
change_speed(23m / 10s); // meters per second
I am particularly interested in the last two lines of this example. The first seems to suggest that if you provide no units with the argument to change_speed
it will throw an error. The last line shows the units defined using some the m
and s
literals. Are both of these new features in modern versions of C++? If so, how would something like this be implemented, and what version of C++ is required?
c++ cpp-core-guidelines
In the C++ Core Guidlines P.1 change_speed
example, it shows a Speed
type that is used as shown below:
change_speed(Speed s); // better: the meaning of s is specified
// ...
change_speed(2.3); // error: no unit
change_speed(23m / 10s); // meters per second
I am particularly interested in the last two lines of this example. The first seems to suggest that if you provide no units with the argument to change_speed
it will throw an error. The last line shows the units defined using some the m
and s
literals. Are both of these new features in modern versions of C++? If so, how would something like this be implemented, and what version of C++ is required?
c++ cpp-core-guidelines
c++ cpp-core-guidelines
asked Aug 21 at 7:14
rozzy
1,09731328
1,09731328
12
cppreference
â Tyker
Aug 21 at 7:15
Perhaps this is what you ment: It won't throw an error, It won't compile, becausechange_speed
takes a Speed and not a floating point literal.
â hetepeperfan
Aug 21 at 7:48
add a comment |Â
12
cppreference
â Tyker
Aug 21 at 7:15
Perhaps this is what you ment: It won't throw an error, It won't compile, becausechange_speed
takes a Speed and not a floating point literal.
â hetepeperfan
Aug 21 at 7:48
12
12
cppreference
â Tyker
Aug 21 at 7:15
cppreference
â Tyker
Aug 21 at 7:15
Perhaps this is what you ment: It won't throw an error, It won't compile, because
change_speed
takes a Speed and not a floating point literal.â hetepeperfan
Aug 21 at 7:48
Perhaps this is what you ment: It won't throw an error, It won't compile, because
change_speed
takes a Speed and not a floating point literal.â hetepeperfan
Aug 21 at 7:48
add a comment |Â
2 Answers
2
active
oldest
votes
up vote
13
down vote
accepted
As mentioned in the comments, the example from the core guidelines uses user-defined literals to construct application-specific types that intuitively represent physical quantities. To illustrate them for the specific example, consider these types:
/* "Strong" speed type, unit is always [m/s]. */
struct Speed
long double value;
;
/* "Strong" length type, parameterized by a unit as multiples of [m]. */
template <class Period = std::ratio<1>> struct Length
unsigned long long value;
;
It probably doesn't make much sense to track the unit of Length
objects, but not for Speed
instances, but let's consider the simplest possible example here. Now, let's look at two user-defined literals:
#include <ratio>
auto operator ""_m(unsigned long long n)
return Length<>n;
auto operator ""_km(unsigned long long n)
return Length<std::kilo>n;
They let you instantiate Length
objects like this:
/* We use auto here, because the suffix is so crystal clear: */
const auto lengthInMeter = 23_m;
const auto lengthInKilometer = 23_km;
In order to cosntruct a Speed
instance, let's define an appropriate operator for dividing a Length
by a duration
:
#include <chrono>
template <class LengthRatio, class Rep, class DurationRatio>
auto operator / (const Length<LengthRatio>& lhs,
const std::chrono::duration<Rep, DurationRatio>& rhs)
const auto lengthFactor = static_cast<double>(LengthRatio::num)/LengthRatio::den;
const auto rhsInSeconds = std::chrono::duration_cast<std::chrono::seconds>(rhs);
return SpeedlengthFactor*lhs.value/rhsInSeconds.count();
Now, let's look at the example from the core guidelines again,
void change_speed(const Speed& s)
/* Complicated stuff... */
but most importantly, how you can call such a function:
using namespace std::chrono_literals;
int main(int, char **)
change_speed(23_m/1s);
change_speed(42_km/3600s);
change_speed(42_km/1h);
return 0;
As @KillzoneKid mentioned in the comments, C++11 is required for this to work.
add a comment |Â
up vote
3
down vote
There are two different things involved in your code:
The use of strong/unit types to make your code more robust, i.e., you differentiate two integer types. This is built-in in some languages (e.g., Ada), but not in C++, but you can create classes that wraps integer types to mimic such behavior (see below).
The use of operator literals to create instances of such classes in user-friendly way, i.e., you write
1s
instead ofseconds1
. This is simply a convenience feature, which can be useful in some places.
Using strong integer types is very useful because it makes your code much less error prone*:
- You cannot convert between types representing durations and lengths, as in real life.
- Within types representing the same kind of things (e.g.,
seconds
andhours
), there are not implicit conversions if you lose precision, e.g., you cannot convertseconds
tohours
, unless you are representing our with floating point type (float
/double
). - The (implicit and non-implicit) conversions takes care of the scaling for you: you can convert
hours
toseconds
without having to manually multiply by 3600. - You can provide realistic operators that will takes care of conversions for you, e.g., in your example, there is division operator between length and duration that gives speeds. The exact type of speed is automatically deduced from the type of the length and the type of duration:
auto speed = 70km / 1h; // Don't bother deducing the type of speed, let the compiler do it for you.
- Unit types are self-documented: If a function returns
microseconds
, you know what this is, you do not have to hope that the guy documenting the function returningunsigned long long
mentioned that this represents microseconds...
* I am only talking about implicit conversion here, of course, you can do conversion explicitly, e.g., using duration_cast
(loss of precision).
Unit types
Wrapping integer types in "unit" classes has always been available, but C++11 brought one standard wrapped integer type: std::chrono::duration
.
A "unit" class can be defined by:
- the type of thing it represents: time, length, weight, speed, ...
- the C++ type it uses to represent these data:
int
,double
, ... - the ratio between this type and the "1-type" of the same unit.
Currently, only duration-like types are provided by the standard, but there have been discussion (maybe a proposal?) for providing a more generic basic unit type such as:
template <class Unit, class Rep, class Ratio = std::ratio<1>> class unit;
...where Unit
would be a placeholder indicating the kind of thing represented, e.g.:
struct length_t ;
template <class Rep, class Ratio = std::ratio<1>>
using length = unit<length_t, Rep, Ratio>;
But this is not standard yet, so let's have a look at std::chrono::duration
:
template <class Rep, class Period = std::ratio<1>> class duration;
The Rep
template parameters is the C++ type:
- Standard defined durations types have integer representations (implementation defined).
- The underlying type defines the type of conversions that can be implicitly made:
- You can convert integer hours to integer seconds implicitly (multiply by 3600).
- You can convert integer seconds to integer hours implicitly because you would lose precision.
- You can convert integer seconds to
double
hours.
The Period
template parameters defines the ratio between the duration
type and one second (which is the basic duration chosen):
std::ratio
is a very convenient standard-defined type that simply represent a ratio between two integers, with corresponding operations (*
,/
, ...).- The standad provides multiple duration types with various ratios:
std::chrono::seconds
,std::chrono::minutes
, ...
Operator literals
These have been introduced in C++11 and are literals operators.
The s
one is standard and is included in the chrono
standard library:
using namespace std::chrono_literals;
auto one_second = 1s;
auto one_hour = 1h;
The m
one is not standard, and thus should be prefixed by _
(since it is user-defined), like 23_m
. You can define your own operator like so:
constexpr auto operator "" _m(unsigned long long ull)
return metersull;
Once we will have units in C++ we will understand how good practice is to perform dimensional analysis and how computing simulation using dimensionless equation is such a good solution! ;)
â Oliv
Aug 21 at 16:41
@Oliv It's already quite easy to implements units in C++, especially if you simply wraps astd::chrono::duration
. ;)
â Holt
Aug 21 at 18:56
That lacks the concept of Dimension (the Units template parameter), and the possibility to multiply/divide numbers with different dimension: 1 meter / 1 second = 1 meter/second => dimensionaly meter/second = speed. See boost units.
â Oliv
Aug 22 at 8:24
add a comment |Â
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
13
down vote
accepted
As mentioned in the comments, the example from the core guidelines uses user-defined literals to construct application-specific types that intuitively represent physical quantities. To illustrate them for the specific example, consider these types:
/* "Strong" speed type, unit is always [m/s]. */
struct Speed
long double value;
;
/* "Strong" length type, parameterized by a unit as multiples of [m]. */
template <class Period = std::ratio<1>> struct Length
unsigned long long value;
;
It probably doesn't make much sense to track the unit of Length
objects, but not for Speed
instances, but let's consider the simplest possible example here. Now, let's look at two user-defined literals:
#include <ratio>
auto operator ""_m(unsigned long long n)
return Length<>n;
auto operator ""_km(unsigned long long n)
return Length<std::kilo>n;
They let you instantiate Length
objects like this:
/* We use auto here, because the suffix is so crystal clear: */
const auto lengthInMeter = 23_m;
const auto lengthInKilometer = 23_km;
In order to cosntruct a Speed
instance, let's define an appropriate operator for dividing a Length
by a duration
:
#include <chrono>
template <class LengthRatio, class Rep, class DurationRatio>
auto operator / (const Length<LengthRatio>& lhs,
const std::chrono::duration<Rep, DurationRatio>& rhs)
const auto lengthFactor = static_cast<double>(LengthRatio::num)/LengthRatio::den;
const auto rhsInSeconds = std::chrono::duration_cast<std::chrono::seconds>(rhs);
return SpeedlengthFactor*lhs.value/rhsInSeconds.count();
Now, let's look at the example from the core guidelines again,
void change_speed(const Speed& s)
/* Complicated stuff... */
but most importantly, how you can call such a function:
using namespace std::chrono_literals;
int main(int, char **)
change_speed(23_m/1s);
change_speed(42_km/3600s);
change_speed(42_km/1h);
return 0;
As @KillzoneKid mentioned in the comments, C++11 is required for this to work.
add a comment |Â
up vote
13
down vote
accepted
As mentioned in the comments, the example from the core guidelines uses user-defined literals to construct application-specific types that intuitively represent physical quantities. To illustrate them for the specific example, consider these types:
/* "Strong" speed type, unit is always [m/s]. */
struct Speed
long double value;
;
/* "Strong" length type, parameterized by a unit as multiples of [m]. */
template <class Period = std::ratio<1>> struct Length
unsigned long long value;
;
It probably doesn't make much sense to track the unit of Length
objects, but not for Speed
instances, but let's consider the simplest possible example here. Now, let's look at two user-defined literals:
#include <ratio>
auto operator ""_m(unsigned long long n)
return Length<>n;
auto operator ""_km(unsigned long long n)
return Length<std::kilo>n;
They let you instantiate Length
objects like this:
/* We use auto here, because the suffix is so crystal clear: */
const auto lengthInMeter = 23_m;
const auto lengthInKilometer = 23_km;
In order to cosntruct a Speed
instance, let's define an appropriate operator for dividing a Length
by a duration
:
#include <chrono>
template <class LengthRatio, class Rep, class DurationRatio>
auto operator / (const Length<LengthRatio>& lhs,
const std::chrono::duration<Rep, DurationRatio>& rhs)
const auto lengthFactor = static_cast<double>(LengthRatio::num)/LengthRatio::den;
const auto rhsInSeconds = std::chrono::duration_cast<std::chrono::seconds>(rhs);
return SpeedlengthFactor*lhs.value/rhsInSeconds.count();
Now, let's look at the example from the core guidelines again,
void change_speed(const Speed& s)
/* Complicated stuff... */
but most importantly, how you can call such a function:
using namespace std::chrono_literals;
int main(int, char **)
change_speed(23_m/1s);
change_speed(42_km/3600s);
change_speed(42_km/1h);
return 0;
As @KillzoneKid mentioned in the comments, C++11 is required for this to work.
add a comment |Â
up vote
13
down vote
accepted
up vote
13
down vote
accepted
As mentioned in the comments, the example from the core guidelines uses user-defined literals to construct application-specific types that intuitively represent physical quantities. To illustrate them for the specific example, consider these types:
/* "Strong" speed type, unit is always [m/s]. */
struct Speed
long double value;
;
/* "Strong" length type, parameterized by a unit as multiples of [m]. */
template <class Period = std::ratio<1>> struct Length
unsigned long long value;
;
It probably doesn't make much sense to track the unit of Length
objects, but not for Speed
instances, but let's consider the simplest possible example here. Now, let's look at two user-defined literals:
#include <ratio>
auto operator ""_m(unsigned long long n)
return Length<>n;
auto operator ""_km(unsigned long long n)
return Length<std::kilo>n;
They let you instantiate Length
objects like this:
/* We use auto here, because the suffix is so crystal clear: */
const auto lengthInMeter = 23_m;
const auto lengthInKilometer = 23_km;
In order to cosntruct a Speed
instance, let's define an appropriate operator for dividing a Length
by a duration
:
#include <chrono>
template <class LengthRatio, class Rep, class DurationRatio>
auto operator / (const Length<LengthRatio>& lhs,
const std::chrono::duration<Rep, DurationRatio>& rhs)
const auto lengthFactor = static_cast<double>(LengthRatio::num)/LengthRatio::den;
const auto rhsInSeconds = std::chrono::duration_cast<std::chrono::seconds>(rhs);
return SpeedlengthFactor*lhs.value/rhsInSeconds.count();
Now, let's look at the example from the core guidelines again,
void change_speed(const Speed& s)
/* Complicated stuff... */
but most importantly, how you can call such a function:
using namespace std::chrono_literals;
int main(int, char **)
change_speed(23_m/1s);
change_speed(42_km/3600s);
change_speed(42_km/1h);
return 0;
As @KillzoneKid mentioned in the comments, C++11 is required for this to work.
As mentioned in the comments, the example from the core guidelines uses user-defined literals to construct application-specific types that intuitively represent physical quantities. To illustrate them for the specific example, consider these types:
/* "Strong" speed type, unit is always [m/s]. */
struct Speed
long double value;
;
/* "Strong" length type, parameterized by a unit as multiples of [m]. */
template <class Period = std::ratio<1>> struct Length
unsigned long long value;
;
It probably doesn't make much sense to track the unit of Length
objects, but not for Speed
instances, but let's consider the simplest possible example here. Now, let's look at two user-defined literals:
#include <ratio>
auto operator ""_m(unsigned long long n)
return Length<>n;
auto operator ""_km(unsigned long long n)
return Length<std::kilo>n;
They let you instantiate Length
objects like this:
/* We use auto here, because the suffix is so crystal clear: */
const auto lengthInMeter = 23_m;
const auto lengthInKilometer = 23_km;
In order to cosntruct a Speed
instance, let's define an appropriate operator for dividing a Length
by a duration
:
#include <chrono>
template <class LengthRatio, class Rep, class DurationRatio>
auto operator / (const Length<LengthRatio>& lhs,
const std::chrono::duration<Rep, DurationRatio>& rhs)
const auto lengthFactor = static_cast<double>(LengthRatio::num)/LengthRatio::den;
const auto rhsInSeconds = std::chrono::duration_cast<std::chrono::seconds>(rhs);
return SpeedlengthFactor*lhs.value/rhsInSeconds.count();
Now, let's look at the example from the core guidelines again,
void change_speed(const Speed& s)
/* Complicated stuff... */
but most importantly, how you can call such a function:
using namespace std::chrono_literals;
int main(int, char **)
change_speed(23_m/1s);
change_speed(42_km/3600s);
change_speed(42_km/1h);
return 0;
As @KillzoneKid mentioned in the comments, C++11 is required for this to work.
edited Aug 21 at 8:32
answered Aug 21 at 8:01
lubgr
7,08521441
7,08521441
add a comment |Â
add a comment |Â
up vote
3
down vote
There are two different things involved in your code:
The use of strong/unit types to make your code more robust, i.e., you differentiate two integer types. This is built-in in some languages (e.g., Ada), but not in C++, but you can create classes that wraps integer types to mimic such behavior (see below).
The use of operator literals to create instances of such classes in user-friendly way, i.e., you write
1s
instead ofseconds1
. This is simply a convenience feature, which can be useful in some places.
Using strong integer types is very useful because it makes your code much less error prone*:
- You cannot convert between types representing durations and lengths, as in real life.
- Within types representing the same kind of things (e.g.,
seconds
andhours
), there are not implicit conversions if you lose precision, e.g., you cannot convertseconds
tohours
, unless you are representing our with floating point type (float
/double
). - The (implicit and non-implicit) conversions takes care of the scaling for you: you can convert
hours
toseconds
without having to manually multiply by 3600. - You can provide realistic operators that will takes care of conversions for you, e.g., in your example, there is division operator between length and duration that gives speeds. The exact type of speed is automatically deduced from the type of the length and the type of duration:
auto speed = 70km / 1h; // Don't bother deducing the type of speed, let the compiler do it for you.
- Unit types are self-documented: If a function returns
microseconds
, you know what this is, you do not have to hope that the guy documenting the function returningunsigned long long
mentioned that this represents microseconds...
* I am only talking about implicit conversion here, of course, you can do conversion explicitly, e.g., using duration_cast
(loss of precision).
Unit types
Wrapping integer types in "unit" classes has always been available, but C++11 brought one standard wrapped integer type: std::chrono::duration
.
A "unit" class can be defined by:
- the type of thing it represents: time, length, weight, speed, ...
- the C++ type it uses to represent these data:
int
,double
, ... - the ratio between this type and the "1-type" of the same unit.
Currently, only duration-like types are provided by the standard, but there have been discussion (maybe a proposal?) for providing a more generic basic unit type such as:
template <class Unit, class Rep, class Ratio = std::ratio<1>> class unit;
...where Unit
would be a placeholder indicating the kind of thing represented, e.g.:
struct length_t ;
template <class Rep, class Ratio = std::ratio<1>>
using length = unit<length_t, Rep, Ratio>;
But this is not standard yet, so let's have a look at std::chrono::duration
:
template <class Rep, class Period = std::ratio<1>> class duration;
The Rep
template parameters is the C++ type:
- Standard defined durations types have integer representations (implementation defined).
- The underlying type defines the type of conversions that can be implicitly made:
- You can convert integer hours to integer seconds implicitly (multiply by 3600).
- You can convert integer seconds to integer hours implicitly because you would lose precision.
- You can convert integer seconds to
double
hours.
The Period
template parameters defines the ratio between the duration
type and one second (which is the basic duration chosen):
std::ratio
is a very convenient standard-defined type that simply represent a ratio between two integers, with corresponding operations (*
,/
, ...).- The standad provides multiple duration types with various ratios:
std::chrono::seconds
,std::chrono::minutes
, ...
Operator literals
These have been introduced in C++11 and are literals operators.
The s
one is standard and is included in the chrono
standard library:
using namespace std::chrono_literals;
auto one_second = 1s;
auto one_hour = 1h;
The m
one is not standard, and thus should be prefixed by _
(since it is user-defined), like 23_m
. You can define your own operator like so:
constexpr auto operator "" _m(unsigned long long ull)
return metersull;
Once we will have units in C++ we will understand how good practice is to perform dimensional analysis and how computing simulation using dimensionless equation is such a good solution! ;)
â Oliv
Aug 21 at 16:41
@Oliv It's already quite easy to implements units in C++, especially if you simply wraps astd::chrono::duration
. ;)
â Holt
Aug 21 at 18:56
That lacks the concept of Dimension (the Units template parameter), and the possibility to multiply/divide numbers with different dimension: 1 meter / 1 second = 1 meter/second => dimensionaly meter/second = speed. See boost units.
â Oliv
Aug 22 at 8:24
add a comment |Â
up vote
3
down vote
There are two different things involved in your code:
The use of strong/unit types to make your code more robust, i.e., you differentiate two integer types. This is built-in in some languages (e.g., Ada), but not in C++, but you can create classes that wraps integer types to mimic such behavior (see below).
The use of operator literals to create instances of such classes in user-friendly way, i.e., you write
1s
instead ofseconds1
. This is simply a convenience feature, which can be useful in some places.
Using strong integer types is very useful because it makes your code much less error prone*:
- You cannot convert between types representing durations and lengths, as in real life.
- Within types representing the same kind of things (e.g.,
seconds
andhours
), there are not implicit conversions if you lose precision, e.g., you cannot convertseconds
tohours
, unless you are representing our with floating point type (float
/double
). - The (implicit and non-implicit) conversions takes care of the scaling for you: you can convert
hours
toseconds
without having to manually multiply by 3600. - You can provide realistic operators that will takes care of conversions for you, e.g., in your example, there is division operator between length and duration that gives speeds. The exact type of speed is automatically deduced from the type of the length and the type of duration:
auto speed = 70km / 1h; // Don't bother deducing the type of speed, let the compiler do it for you.
- Unit types are self-documented: If a function returns
microseconds
, you know what this is, you do not have to hope that the guy documenting the function returningunsigned long long
mentioned that this represents microseconds...
* I am only talking about implicit conversion here, of course, you can do conversion explicitly, e.g., using duration_cast
(loss of precision).
Unit types
Wrapping integer types in "unit" classes has always been available, but C++11 brought one standard wrapped integer type: std::chrono::duration
.
A "unit" class can be defined by:
- the type of thing it represents: time, length, weight, speed, ...
- the C++ type it uses to represent these data:
int
,double
, ... - the ratio between this type and the "1-type" of the same unit.
Currently, only duration-like types are provided by the standard, but there have been discussion (maybe a proposal?) for providing a more generic basic unit type such as:
template <class Unit, class Rep, class Ratio = std::ratio<1>> class unit;
...where Unit
would be a placeholder indicating the kind of thing represented, e.g.:
struct length_t ;
template <class Rep, class Ratio = std::ratio<1>>
using length = unit<length_t, Rep, Ratio>;
But this is not standard yet, so let's have a look at std::chrono::duration
:
template <class Rep, class Period = std::ratio<1>> class duration;
The Rep
template parameters is the C++ type:
- Standard defined durations types have integer representations (implementation defined).
- The underlying type defines the type of conversions that can be implicitly made:
- You can convert integer hours to integer seconds implicitly (multiply by 3600).
- You can convert integer seconds to integer hours implicitly because you would lose precision.
- You can convert integer seconds to
double
hours.
The Period
template parameters defines the ratio between the duration
type and one second (which is the basic duration chosen):
std::ratio
is a very convenient standard-defined type that simply represent a ratio between two integers, with corresponding operations (*
,/
, ...).- The standad provides multiple duration types with various ratios:
std::chrono::seconds
,std::chrono::minutes
, ...
Operator literals
These have been introduced in C++11 and are literals operators.
The s
one is standard and is included in the chrono
standard library:
using namespace std::chrono_literals;
auto one_second = 1s;
auto one_hour = 1h;
The m
one is not standard, and thus should be prefixed by _
(since it is user-defined), like 23_m
. You can define your own operator like so:
constexpr auto operator "" _m(unsigned long long ull)
return metersull;
Once we will have units in C++ we will understand how good practice is to perform dimensional analysis and how computing simulation using dimensionless equation is such a good solution! ;)
â Oliv
Aug 21 at 16:41
@Oliv It's already quite easy to implements units in C++, especially if you simply wraps astd::chrono::duration
. ;)
â Holt
Aug 21 at 18:56
That lacks the concept of Dimension (the Units template parameter), and the possibility to multiply/divide numbers with different dimension: 1 meter / 1 second = 1 meter/second => dimensionaly meter/second = speed. See boost units.
â Oliv
Aug 22 at 8:24
add a comment |Â
up vote
3
down vote
up vote
3
down vote
There are two different things involved in your code:
The use of strong/unit types to make your code more robust, i.e., you differentiate two integer types. This is built-in in some languages (e.g., Ada), but not in C++, but you can create classes that wraps integer types to mimic such behavior (see below).
The use of operator literals to create instances of such classes in user-friendly way, i.e., you write
1s
instead ofseconds1
. This is simply a convenience feature, which can be useful in some places.
Using strong integer types is very useful because it makes your code much less error prone*:
- You cannot convert between types representing durations and lengths, as in real life.
- Within types representing the same kind of things (e.g.,
seconds
andhours
), there are not implicit conversions if you lose precision, e.g., you cannot convertseconds
tohours
, unless you are representing our with floating point type (float
/double
). - The (implicit and non-implicit) conversions takes care of the scaling for you: you can convert
hours
toseconds
without having to manually multiply by 3600. - You can provide realistic operators that will takes care of conversions for you, e.g., in your example, there is division operator between length and duration that gives speeds. The exact type of speed is automatically deduced from the type of the length and the type of duration:
auto speed = 70km / 1h; // Don't bother deducing the type of speed, let the compiler do it for you.
- Unit types are self-documented: If a function returns
microseconds
, you know what this is, you do not have to hope that the guy documenting the function returningunsigned long long
mentioned that this represents microseconds...
* I am only talking about implicit conversion here, of course, you can do conversion explicitly, e.g., using duration_cast
(loss of precision).
Unit types
Wrapping integer types in "unit" classes has always been available, but C++11 brought one standard wrapped integer type: std::chrono::duration
.
A "unit" class can be defined by:
- the type of thing it represents: time, length, weight, speed, ...
- the C++ type it uses to represent these data:
int
,double
, ... - the ratio between this type and the "1-type" of the same unit.
Currently, only duration-like types are provided by the standard, but there have been discussion (maybe a proposal?) for providing a more generic basic unit type such as:
template <class Unit, class Rep, class Ratio = std::ratio<1>> class unit;
...where Unit
would be a placeholder indicating the kind of thing represented, e.g.:
struct length_t ;
template <class Rep, class Ratio = std::ratio<1>>
using length = unit<length_t, Rep, Ratio>;
But this is not standard yet, so let's have a look at std::chrono::duration
:
template <class Rep, class Period = std::ratio<1>> class duration;
The Rep
template parameters is the C++ type:
- Standard defined durations types have integer representations (implementation defined).
- The underlying type defines the type of conversions that can be implicitly made:
- You can convert integer hours to integer seconds implicitly (multiply by 3600).
- You can convert integer seconds to integer hours implicitly because you would lose precision.
- You can convert integer seconds to
double
hours.
The Period
template parameters defines the ratio between the duration
type and one second (which is the basic duration chosen):
std::ratio
is a very convenient standard-defined type that simply represent a ratio between two integers, with corresponding operations (*
,/
, ...).- The standad provides multiple duration types with various ratios:
std::chrono::seconds
,std::chrono::minutes
, ...
Operator literals
These have been introduced in C++11 and are literals operators.
The s
one is standard and is included in the chrono
standard library:
using namespace std::chrono_literals;
auto one_second = 1s;
auto one_hour = 1h;
The m
one is not standard, and thus should be prefixed by _
(since it is user-defined), like 23_m
. You can define your own operator like so:
constexpr auto operator "" _m(unsigned long long ull)
return metersull;
There are two different things involved in your code:
The use of strong/unit types to make your code more robust, i.e., you differentiate two integer types. This is built-in in some languages (e.g., Ada), but not in C++, but you can create classes that wraps integer types to mimic such behavior (see below).
The use of operator literals to create instances of such classes in user-friendly way, i.e., you write
1s
instead ofseconds1
. This is simply a convenience feature, which can be useful in some places.
Using strong integer types is very useful because it makes your code much less error prone*:
- You cannot convert between types representing durations and lengths, as in real life.
- Within types representing the same kind of things (e.g.,
seconds
andhours
), there are not implicit conversions if you lose precision, e.g., you cannot convertseconds
tohours
, unless you are representing our with floating point type (float
/double
). - The (implicit and non-implicit) conversions takes care of the scaling for you: you can convert
hours
toseconds
without having to manually multiply by 3600. - You can provide realistic operators that will takes care of conversions for you, e.g., in your example, there is division operator between length and duration that gives speeds. The exact type of speed is automatically deduced from the type of the length and the type of duration:
auto speed = 70km / 1h; // Don't bother deducing the type of speed, let the compiler do it for you.
- Unit types are self-documented: If a function returns
microseconds
, you know what this is, you do not have to hope that the guy documenting the function returningunsigned long long
mentioned that this represents microseconds...
* I am only talking about implicit conversion here, of course, you can do conversion explicitly, e.g., using duration_cast
(loss of precision).
Unit types
Wrapping integer types in "unit" classes has always been available, but C++11 brought one standard wrapped integer type: std::chrono::duration
.
A "unit" class can be defined by:
- the type of thing it represents: time, length, weight, speed, ...
- the C++ type it uses to represent these data:
int
,double
, ... - the ratio between this type and the "1-type" of the same unit.
Currently, only duration-like types are provided by the standard, but there have been discussion (maybe a proposal?) for providing a more generic basic unit type such as:
template <class Unit, class Rep, class Ratio = std::ratio<1>> class unit;
...where Unit
would be a placeholder indicating the kind of thing represented, e.g.:
struct length_t ;
template <class Rep, class Ratio = std::ratio<1>>
using length = unit<length_t, Rep, Ratio>;
But this is not standard yet, so let's have a look at std::chrono::duration
:
template <class Rep, class Period = std::ratio<1>> class duration;
The Rep
template parameters is the C++ type:
- Standard defined durations types have integer representations (implementation defined).
- The underlying type defines the type of conversions that can be implicitly made:
- You can convert integer hours to integer seconds implicitly (multiply by 3600).
- You can convert integer seconds to integer hours implicitly because you would lose precision.
- You can convert integer seconds to
double
hours.
The Period
template parameters defines the ratio between the duration
type and one second (which is the basic duration chosen):
std::ratio
is a very convenient standard-defined type that simply represent a ratio between two integers, with corresponding operations (*
,/
, ...).- The standad provides multiple duration types with various ratios:
std::chrono::seconds
,std::chrono::minutes
, ...
Operator literals
These have been introduced in C++11 and are literals operators.
The s
one is standard and is included in the chrono
standard library:
using namespace std::chrono_literals;
auto one_second = 1s;
auto one_hour = 1h;
The m
one is not standard, and thus should be prefixed by _
(since it is user-defined), like 23_m
. You can define your own operator like so:
constexpr auto operator "" _m(unsigned long long ull)
return metersull;
answered Aug 21 at 8:32
Holt
24.2k64989
24.2k64989
Once we will have units in C++ we will understand how good practice is to perform dimensional analysis and how computing simulation using dimensionless equation is such a good solution! ;)
â Oliv
Aug 21 at 16:41
@Oliv It's already quite easy to implements units in C++, especially if you simply wraps astd::chrono::duration
. ;)
â Holt
Aug 21 at 18:56
That lacks the concept of Dimension (the Units template parameter), and the possibility to multiply/divide numbers with different dimension: 1 meter / 1 second = 1 meter/second => dimensionaly meter/second = speed. See boost units.
â Oliv
Aug 22 at 8:24
add a comment |Â
Once we will have units in C++ we will understand how good practice is to perform dimensional analysis and how computing simulation using dimensionless equation is such a good solution! ;)
â Oliv
Aug 21 at 16:41
@Oliv It's already quite easy to implements units in C++, especially if you simply wraps astd::chrono::duration
. ;)
â Holt
Aug 21 at 18:56
That lacks the concept of Dimension (the Units template parameter), and the possibility to multiply/divide numbers with different dimension: 1 meter / 1 second = 1 meter/second => dimensionaly meter/second = speed. See boost units.
â Oliv
Aug 22 at 8:24
Once we will have units in C++ we will understand how good practice is to perform dimensional analysis and how computing simulation using dimensionless equation is such a good solution! ;)
â Oliv
Aug 21 at 16:41
Once we will have units in C++ we will understand how good practice is to perform dimensional analysis and how computing simulation using dimensionless equation is such a good solution! ;)
â Oliv
Aug 21 at 16:41
@Oliv It's already quite easy to implements units in C++, especially if you simply wraps a
std::chrono::duration
. ;)â Holt
Aug 21 at 18:56
@Oliv It's already quite easy to implements units in C++, especially if you simply wraps a
std::chrono::duration
. ;)â Holt
Aug 21 at 18:56
That lacks the concept of Dimension (the Units template parameter), and the possibility to multiply/divide numbers with different dimension: 1 meter / 1 second = 1 meter/second => dimensionaly meter/second = speed. See boost units.
â Oliv
Aug 22 at 8:24
That lacks the concept of Dimension (the Units template parameter), and the possibility to multiply/divide numbers with different dimension: 1 meter / 1 second = 1 meter/second => dimensionaly meter/second = speed. See boost units.
â Oliv
Aug 22 at 8:24
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f51943434%2funits-for-types-in-c%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
12
cppreference
â Tyker
Aug 21 at 7:15
Perhaps this is what you ment: It won't throw an error, It won't compile, because
change_speed
takes a Speed and not a floating point literal.â hetepeperfan
Aug 21 at 7:48