Reducing casting clutter in C++ - c++

When programming generic classes, I end up with methods cluttered with casts (otherwise I get warnings, which are treated as errors for our projects):
template <typename floatType>
class foo
{
public:
typedef floatType real_type;
real_type bar()
{
real_type a = (real_type)0.5; // should I be using static_cast? Either way, the code becomes cluttered quickly
real_type b = a + 0.6; // warning here for floatType = float
real_type someLongEquation = a + ((real_type)0.5 * (real_type)100) + (real_type)17.0;
return a + b + someLongEquation;
}
};
int main()
{
{
foo<float> z;
z.bar();
}
{
foo<double> z;
z.bar();
}
return 0;
}
Is there any way to reduce that clutter?
Note that I realize I am using magic constants in the someLongEquation. Even if I separate them out, there is increased clutter. Either way, that is not the point of the question :)

A simple way would be to just turn off the warning around the code. With MSVC:
#pragma warning(push) // save warnings state
#pragma warning(disable:4244) // narrowing conversion
// code here ...
#pragma warning(pop) // restore warnings
The better way, however, would be to just use float literals if you don't need the extended precision of double for your magic constants. Just append an f to your floating point literals and you'll be fine. Also, no cast needed for the 100. If you do in fact need the precision, well, fall back to disabling the warning.

You should separate the magic constants out and store them in the right generic type. All the casting will be limited to that location. Now that (I think) C++11 allows in class initialization of non-integral const statics it's easy.
template <typename floatType>
class foo
{
public:
typedef floatType real_type;
static constexpr real_type A = real_type(0.5);
static constexpr real_type B = real_type(0.6);
static constexpr real_type C = real_type(100.0);
static constexpr real_type D = real_type(17.0);
real_type bar()
{
real_type a = A;
real_type b = a + B;
real_type someLongEquation = a + (A * C) + D;
return a + b + someLongEquation;
}
};

Related

How to define C++ struct for configuration?

I have been trying to solve following problem in C++. I would like to define a struct containing a configuration parameters for some software module. The configuration parameters are basically a floating point values and they are of two types:
parameters which are independent i.e. their values are given directly by some floating point numbers
parameters which are dependent i.e. their values are given by some expressions where the operands are the independent parameters
Here is an example
struct Configuration {
float param_independent_01;
float param_independent_02;
float param_dependent_01; // param_independent_01 + param_independent_02
float param_dependent_02; // 1.5f*param_independent_01/(param_independent_01 + param_independent_02)
};
I have been looking for a solution which enables the client code to only set values for the independent parameters and the dependent parameters values will be calculated automatically behind the scene.
Configuration config = {
param_independent_01 = 0.236f,
param_independent_02 = 0.728f
// param_dependent_01 = 0.236f + 0.728f
// param_dependent_02 = 1.5f*0.236f/(0.236f + 0.728f)
};
I suppose that the Configuration structure will be instantiated only once and the values of the parameters are known at compile time. Can anybody give me an advice how to do that in the C++?
One approach to achieve this behavior is to make use of C++'s constructor initialization list.
struct Configuration {
float param_independent_01;
float param_independent_02;
float param_dependent_01;
float param_dependent_02;
Configuration(float p1, float p2) :
param_independent_01(p1),
param_independent_02(p2),
param_dependent_01(p1 + p2),
param_dependent_02(1.5f * p1 / (p1 + p2)
)
{}
};
int main() {
Configuration config(0.236f, 0.728f);
return 0;
}
Or just inline constexpr variables in a namespace (can be put in a header).
This allows you to write some constexpr (consteval) functions to calculate the values too. (Not everything needs to be a class or a struct)
// header file
#pragma once
namespace configuration
{
inline constexpr float get_param_dependent_02(const float p1, const float p2)
{
return (1.5f * p1) / (p1+p2);
}
inline constexpr float param_independent_01{ 0.236f };
inline constexpr float param_independent_02{ 0.728f };
inline constexpr float param_dependent_01 = param_independent_01 + param_independent_02; // direct
inline constexpr float param_dependent_02 = get_param_dependent_02(param_independent_01, param_independent_02); // or through constexpr/consteval function
};
int main()
{
float f = configuration::param_dependent_02;
}
If you know the configuration is not going to change at runtime, you can implement a constexpr constructor for Configuration, and then define a constexpr Configuration variable. The construction will be done at compile time (see the generated assembler code for the godbolt link below).
If you wanted to make sure the configuration is not going to change at runtime, I would change Configuration into a class with private members, and just provide accessors for those members.
Notice also that the constructor may throw (due to a division by zero). If you want to take control of that situation, you may want to try-catch the setting of the dependent parameter 2 in the constructor's body.
[Demo]
#include <fmt/format.h>
#include <iostream>
class Configuration {
float param_independent_01;
float param_independent_02;
float param_dependent_01;
float param_dependent_02;
public:
constexpr Configuration(float p1, float p2)
: param_independent_01{p1}
, param_independent_02{p2}
, param_dependent_01{p1 + p2}
, param_dependent_02{(p1 * 1.5f)/param_dependent_01}
{}
auto get_pi1() { return param_independent_01; }
auto get_pi2() { return param_independent_02; }
auto get_pd1() { return param_dependent_01; }
auto get_pd2() { return param_dependent_02; }
friend std::ostream& operator<<(std::ostream& os, const Configuration& c) {
return os << fmt::format("pi1: {}\npi2: {}\npd1: {}\npd2: {}\n",
c.param_independent_01, c.param_independent_02,
c.param_dependent_01, c.param_dependent_02);
}
};
int main() {
constexpr Configuration c{3.14, 9.8};
std::cout << c;
}
No need for a class with a custom constructor, just do this:
struct Configuration
{
float param_independent_01 = 0; // Always initialize all class members.
float param_independent_02 = 0;
float param_dependent_01() const {return param_independent_01 + param_independent_02;}
float param_dependent_02() const {return 1.5f*param_independent_01/(param_independent_01 + param_independent_02);}
};

How to "duplicate" an existing type instead of just aliasing it? [duplicate]

I'm trying to emulate in C++ a distinct
type from the Nim
programming language. The following example won't
compile in Nim because the compiler catches the variables e and d
having different types (Error: type mismatch: got (Euros, float)) despite
both being a float at the binary level:
type
Euros = distinct float
when isMainModule:
var
e = Euros(12.34)
d = 23.3
echo (e + d)
One way to do this in C++ would be to write a wrapper class for floats. But
this doesn't work well with APIs which export the type because the size won't
be the same as float. Or even if a class' size matches the storage length of a
float, it will never match the size of a char type. That will work if you also implement all possible operators for operations like addition, substraction, etc, but requires much typing and duplication of code.
Older questions like Creating a new primitive
type
have as accepted answer to use boost's strong typedef. However the typedef
seems to work only for function type signatures, the typedef won't prevent two
float-inherited types to be added together and their type completely changed
(well, because there is just the illusion of a new type):
#include <boost/serialization/strong_typedef.hpp>
#include <stdio.h>
BOOST_STRONG_TYPEDEF(float, money);
void test(money a, float b)
{
int t = a + b;
printf("value is %d", t);
}
int main()
{
money a(5.5);
int euros(5);
// This is not caught!
int dollars = a + euros;
printf("dollars %d\n", dollars);
// But the compiler catches this misuse.
test(euros, a);
}
But that's nearly it, the test() call won't work because the signature
doesn't match, but the language still allows other operations to mangle types
at will.
That same answer mentions C++0x to bring strong typedefs, so I looked for this
new support and found that Bjarne Stroustrup himself gave a C++11 style
keynote in
2012.
Around minute 21 he starts talking about these new strong typedefs. If you
download just the slides, page 19 starts talking about SI Units and later
page 22 and 23 mention how this would be done. However, I have been unable to
make the examples work. Here's the patchwork I managed to concoct:
template<int M, int K, int S> struct Unit { // a unit in the MKS system
enum { m=M, kg=K, s=S };
};
template<typename Unit> // a magnitude with a unit
struct Value {
double val; // the magnitude
explicit Value(double d) : val(d) {} // construct a Value from a double
};
using Meter = Unit<1,0,0>; // unit: meter
using Second = Unit<0,0,1>; // unit: sec
using Speed = Value< Unit<1,0,-1> >; // meters/second type
constexpr Value<Second> operator "" _s(long double d)
// a f-p literal suffixed by ‘_s’
{
return Value<Second> (d);
}
constexpr Value<Meter> operator "" _m(long double d)
// a f-p literal suffixed by ‘_m’
{
return Value<Meter> (d);
}
int main(void)
{
Speed sp1 = 100_m / 9.8_s;
return 42;
}
I'm trying to compile this under MacOSX with the latest Xcode 5.1.1 with the command line:
$ g++ unit.cpp -std=c++11
unit.cpp:13:25: error: constexpr function's return type 'Value<Second>' is not a
literal type
constexpr Value<Second> operator "" _s(long double d)
^
unit.cpp:5:8: note: 'Value<Unit<0, 0, 1> >' is not literal because it is not an
aggregate and has no constexpr constructors other than copy or move
constructors
struct Value {
^
unit.cpp:18:24: error: constexpr function's return type 'Value<Meter>' is not a
literal type
constexpr Value<Meter> operator "" _m(long double d)
^
unit.cpp:5:8: note: 'Value<Unit<1, 0, 0> >' is not literal because it is not an
aggregate and has no constexpr constructors other than copy or move
constructors
struct Value {
^
unit.cpp:26:20: error: no matching literal operator for call to 'operator "" _m'
with argument of type 'unsigned long long' or 'const char *', and no
matching literal operator template
Speed sp1 = 100_m / 9.8_s;
^
unit.cpp:26:28: error: no matching literal operator for call to 'operator "" _s'
with argument of type 'long double' or 'const char *', and no matching
literal operator template
Speed sp1 = 100_m / 9.8_s;
^
4 errors generated.
Maybe the examples given in the slides and I'm missing some more code? Does somebody have a complete example of what Bjarne was trying to demonstrate?
There are no strong typedefs in C++11. There is support for units with <chrono> but that is a totally different thing. Nobody can agree on what behaviour strong typedefs should have, exactly, so there has never been a proposal for them that got anywhere, so not only are they in neither C++11 nor C++14, there is no realistic prospect at this time that they will get into any future Standard.
Not sure this is what you want, it is ugly, but it works :)
You can wrap the type into a template class,
template <typename T, int N> // N is used for tagging
struct strong_typedef
{
using strong_type = strong_typedef<T,N>; // typedef for the strong type
using type = T; // the wrapped type
T value; // the wrapped value
strong_typedef(T val): value(val){}; // constructor
strong_typedef(){value={};}; // default, zero-initialization
// operator overloading, basic example:
strong_type operator+(const strong_type& rhs) const
{
return value + rhs.value;
}
// display it
friend ostream& operator<<(ostream & lhs, const strong_typedef& rhs)
{
lhs << rhs.value;
return lhs;
}
};
then use it as
// these are all different types
strong_typedef<double, 0> x = 1.1;
strong_typedef<double, 1> y = 2.2;
strong_typedef<double, 2> z = 3.3;
std::cout << x + x << std::endl; // outputs 2.2, can add x and x
// cout << x + y << endl; // compile-time ERROR, different types
x, y and z are 3 different types now, because of the different N-s used in the template. You can access the type and value using the fields type and value, like x::value (will be double 1.1). Of course if you directly typedef the struct_typedef::type, you're back to square one, as you are losing the strong type. So basically your type should be strong_typedef and not strong_typedef::type.
C++ compilers generally expect the command line option -std=c++11 (or -std=c++0x for slightly older ones, respectively) to activate C++11-support.
not supporting C++11 style at all.
No, it perfectly does. GCC 4.7.2's support can be checked here. To activate some experimental features, pass -std=gnu++11.
And Clang 3.4 actually supports pretty much everything in C++11 and already much out of C++1y.
There are several ways to solve this, but since I was looking for a fix to Bjarne's code in the presentation slides, I'm accepting this answer which #robson3.14 left in comments to the question:
#include <iostream>
template<int M, int K, int S> struct Unit { // a unit in the MKS system
enum { m=M, kg=K, s=S };
};
template<typename Unit> // a magnitude with a unit
struct Value {
double val; // the magnitude
// construct a Value from a double
constexpr explicit Value(double d) : val(d) {}
};
using Meter = Unit<1,0,0>; // unit: meter
using Second = Unit<0,0,1>; // unit: sec
using Speed = Value<Unit<1,0,-1>>; // meters/second type
// a f-p literal suffixed by ‘_s’
constexpr Value<Second> operator "" _s(long double d)
{
return Value<Second> (d);
}
// a f-p literal suffixed by ‘_m’
constexpr Value<Meter> operator "" _m(long double d)
{
return Value<Meter> (d);
}
// an integral literal suffixed by ‘_m’
constexpr Value<Meter> operator "" _m(unsigned long long d)
{
return Value<Meter> (d);
}
template<int m1, int k1, int s1, int m2, int k2, int s2>
Value<Unit<m1 - m2, k1 - k2, s1 - s2>> operator / (Value<Unit<m1, k1, s1>> a, Value<Unit<m2, k2, s2>> b)
{
return Value<Unit<m1 - m2, k1 - k2, s1 - s2>>(a.val / b.val);
}
int main()
{
Speed sp1 = 100_m / 9.8_s;
std::cout << sp1.val;
}
One alternative that does not involve typedef and is not strongly enforced by the compiler but makes it very hard for the programmer to make mistakes it to encode the unit in question as a struct member.
For my arduino project I have types like
template <typename T>
struct millisecond {
T millisecond;
static constexpr const struct millisecond<T> zero = { 0 };
};
template <typename T>
struct microsecond {
T microsecond;
static constexpr const struct microsecond<T> zero = { 0 };
};
and use like
auto time_diff = millisecond<unsigned long>::zero;
time_diff.millisecond = nowMilliseconds() - s_lastPollTime.millisecond;
So with this strategy the compiler does not stop you from mixing units, but if you do then the error will always scream at you:
total_expenses.euros = expence1.euros + expence2.dollars;

Getting constexpr to work with pow in C++17 on OSX

I'm working on trying to get a Linux based project, written in C++17 to work on OSX (Mojave). Most everything compiles just fine, until I get to this file: ClassName.hpp:
class ClassName {
public:
static constexpr double DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1 = 2; // represents 0.99
static constexpr double DEFAULT_TARGET_TFINAL_DIGITS_FROM_0 = 10; // represents 1e-10
static constexpr double DEFAULT_TARGET_INITIAL_PBAD = (1-pow(10,-DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1));
static constexpr double DEFAULT_TARGET_FINAL_PBAD = pow(10,-DEFAULT_TARGET_TFINAL_DIGITS_FROM_0);
static constexpr double DEFAULT_ERROR_TOL_DIGITS = 0.9; // as a fraction of digits in the last place from the above.
static constexpr double DEFAULT_SAMPLE_TIME = 1;
// more unrelated code
};
When compiling this I get the following error:
error: constexpr variable
'DEFAULT_TARGET_INITIAL_PBAD' must be initialized by a constant expression
...double DEFAULT_TARGET_INITIAL_PBAD = (1-pow(10,-DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1));
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ClassName.hpp: note: non-constexpr function 'pow<int, double>'
cannot be used in a constant expression
static constexpr double DEFAULT_TARGET_INITIAL_PBAD = (1-pow(10,-DEFAULT_TARGET_TINITI...
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/math.h:968:1: note:
declared here
pow(_A1 __lcpp_x, _A2 __lcpp_y) _NOEXCEPT
So for some reason this works on Ubuntu and CentOS. I think it has to do with how pow is defined? But I'm not sure how to fix it, or if that is even the issue. I've also tried removing constexpr from DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1 and DEFAULT_TARGET_TFINAL_DIGITS_FROM_0 and making them const but still run into the same issue.
First, you can't initialize constexpr class members with functions that aren't constexpr and std::pow isn't constepxr in standard C++17 . The workaround is to declare them const. While they can't be used in places that require a compile time const they are immutable. A traditional approach is declaring them in header which you include as needed in source files. Then you hneed one implementation file that defines the static const members.
If your code requires compile time const's or constexpr your only option is to write your own pow.
Here's one way to initialize const statics with functions that are not constexpr prior to executing the main() using a portion of your question that shows the technique:
Create a header, constinit.h, that declares the class
// include header guards
// declare the static consts
struct ClassName {
static double DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1; // represents 0.99
static double DEFAULT_TARGET_INITIAL_PBAD; // to be initialized by pow
};
Create an implementation file that initializes the statics:
#include "constinit.h"
#include <cmath>
double ClassName::DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1{ 2 }; // represents 0.99
double ClassName::DEFAULT_TARGET_INITIAL_PBAD = (1 - std::pow(10, -DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1));
To use the statics:
#include <iostream>
#include "constinit.h"
int main()
{
std::cout << ClassName::DEFAULT_TARGET_INITIAL_PBAD << std::endl;
}
If constexpr for compile time initialization is required you need to define your own constexpr pow function. This works in C++17:
#pragma once // or header guards per your preference
constexpr double my_pow(double x, int exp)
{
int sign = 1;
if (exp < 0)
{
sign = -1;
exp = -exp;
}
if (exp == 0)
return x < 0 ? -1.0 : 1.0;
double ret = x;
while (--exp)
ret *= x;
return sign > 0 ? ret : 1.0/ret;
}
class ClassName {
public:
static constexpr double DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1 = 2; // represents 0.99
static constexpr double DEFAULT_TARGET_TFINAL_DIGITS_FROM_0 = 10; // represents 1e-10
static constexpr double DEFAULT_TARGET_INITIAL_PBAD = (1 - my_pow(10, -DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1));
static constexpr double DEFAULT_TARGET_FINAL_PBAD = my_pow(10, -DEFAULT_TARGET_TFINAL_DIGITS_FROM_0);
static constexpr double DEFAULT_ERROR_TOL_DIGITS = 0.9; // as a fraction of digits in the last place from the above.
static constexpr double DEFAULT_SAMPLE_TIME = 1;
// more unrelated code
};

How can I create a new primitive type using C++11 style strong typedefs?

I'm trying to emulate in C++ a distinct
type from the Nim
programming language. The following example won't
compile in Nim because the compiler catches the variables e and d
having different types (Error: type mismatch: got (Euros, float)) despite
both being a float at the binary level:
type
Euros = distinct float
when isMainModule:
var
e = Euros(12.34)
d = 23.3
echo (e + d)
One way to do this in C++ would be to write a wrapper class for floats. But
this doesn't work well with APIs which export the type because the size won't
be the same as float. Or even if a class' size matches the storage length of a
float, it will never match the size of a char type. That will work if you also implement all possible operators for operations like addition, substraction, etc, but requires much typing and duplication of code.
Older questions like Creating a new primitive
type
have as accepted answer to use boost's strong typedef. However the typedef
seems to work only for function type signatures, the typedef won't prevent two
float-inherited types to be added together and their type completely changed
(well, because there is just the illusion of a new type):
#include <boost/serialization/strong_typedef.hpp>
#include <stdio.h>
BOOST_STRONG_TYPEDEF(float, money);
void test(money a, float b)
{
int t = a + b;
printf("value is %d", t);
}
int main()
{
money a(5.5);
int euros(5);
// This is not caught!
int dollars = a + euros;
printf("dollars %d\n", dollars);
// But the compiler catches this misuse.
test(euros, a);
}
But that's nearly it, the test() call won't work because the signature
doesn't match, but the language still allows other operations to mangle types
at will.
That same answer mentions C++0x to bring strong typedefs, so I looked for this
new support and found that Bjarne Stroustrup himself gave a C++11 style
keynote in
2012.
Around minute 21 he starts talking about these new strong typedefs. If you
download just the slides, page 19 starts talking about SI Units and later
page 22 and 23 mention how this would be done. However, I have been unable to
make the examples work. Here's the patchwork I managed to concoct:
template<int M, int K, int S> struct Unit { // a unit in the MKS system
enum { m=M, kg=K, s=S };
};
template<typename Unit> // a magnitude with a unit
struct Value {
double val; // the magnitude
explicit Value(double d) : val(d) {} // construct a Value from a double
};
using Meter = Unit<1,0,0>; // unit: meter
using Second = Unit<0,0,1>; // unit: sec
using Speed = Value< Unit<1,0,-1> >; // meters/second type
constexpr Value<Second> operator "" _s(long double d)
// a f-p literal suffixed by ‘_s’
{
return Value<Second> (d);
}
constexpr Value<Meter> operator "" _m(long double d)
// a f-p literal suffixed by ‘_m’
{
return Value<Meter> (d);
}
int main(void)
{
Speed sp1 = 100_m / 9.8_s;
return 42;
}
I'm trying to compile this under MacOSX with the latest Xcode 5.1.1 with the command line:
$ g++ unit.cpp -std=c++11
unit.cpp:13:25: error: constexpr function's return type 'Value<Second>' is not a
literal type
constexpr Value<Second> operator "" _s(long double d)
^
unit.cpp:5:8: note: 'Value<Unit<0, 0, 1> >' is not literal because it is not an
aggregate and has no constexpr constructors other than copy or move
constructors
struct Value {
^
unit.cpp:18:24: error: constexpr function's return type 'Value<Meter>' is not a
literal type
constexpr Value<Meter> operator "" _m(long double d)
^
unit.cpp:5:8: note: 'Value<Unit<1, 0, 0> >' is not literal because it is not an
aggregate and has no constexpr constructors other than copy or move
constructors
struct Value {
^
unit.cpp:26:20: error: no matching literal operator for call to 'operator "" _m'
with argument of type 'unsigned long long' or 'const char *', and no
matching literal operator template
Speed sp1 = 100_m / 9.8_s;
^
unit.cpp:26:28: error: no matching literal operator for call to 'operator "" _s'
with argument of type 'long double' or 'const char *', and no matching
literal operator template
Speed sp1 = 100_m / 9.8_s;
^
4 errors generated.
Maybe the examples given in the slides and I'm missing some more code? Does somebody have a complete example of what Bjarne was trying to demonstrate?
There are no strong typedefs in C++11. There is support for units with <chrono> but that is a totally different thing. Nobody can agree on what behaviour strong typedefs should have, exactly, so there has never been a proposal for them that got anywhere, so not only are they in neither C++11 nor C++14, there is no realistic prospect at this time that they will get into any future Standard.
Not sure this is what you want, it is ugly, but it works :)
You can wrap the type into a template class,
template <typename T, int N> // N is used for tagging
struct strong_typedef
{
using strong_type = strong_typedef<T,N>; // typedef for the strong type
using type = T; // the wrapped type
T value; // the wrapped value
strong_typedef(T val): value(val){}; // constructor
strong_typedef(){value={};}; // default, zero-initialization
// operator overloading, basic example:
strong_type operator+(const strong_type& rhs) const
{
return value + rhs.value;
}
// display it
friend ostream& operator<<(ostream & lhs, const strong_typedef& rhs)
{
lhs << rhs.value;
return lhs;
}
};
then use it as
// these are all different types
strong_typedef<double, 0> x = 1.1;
strong_typedef<double, 1> y = 2.2;
strong_typedef<double, 2> z = 3.3;
std::cout << x + x << std::endl; // outputs 2.2, can add x and x
// cout << x + y << endl; // compile-time ERROR, different types
x, y and z are 3 different types now, because of the different N-s used in the template. You can access the type and value using the fields type and value, like x::value (will be double 1.1). Of course if you directly typedef the struct_typedef::type, you're back to square one, as you are losing the strong type. So basically your type should be strong_typedef and not strong_typedef::type.
C++ compilers generally expect the command line option -std=c++11 (or -std=c++0x for slightly older ones, respectively) to activate C++11-support.
not supporting C++11 style at all.
No, it perfectly does. GCC 4.7.2's support can be checked here. To activate some experimental features, pass -std=gnu++11.
And Clang 3.4 actually supports pretty much everything in C++11 and already much out of C++1y.
There are several ways to solve this, but since I was looking for a fix to Bjarne's code in the presentation slides, I'm accepting this answer which #robson3.14 left in comments to the question:
#include <iostream>
template<int M, int K, int S> struct Unit { // a unit in the MKS system
enum { m=M, kg=K, s=S };
};
template<typename Unit> // a magnitude with a unit
struct Value {
double val; // the magnitude
// construct a Value from a double
constexpr explicit Value(double d) : val(d) {}
};
using Meter = Unit<1,0,0>; // unit: meter
using Second = Unit<0,0,1>; // unit: sec
using Speed = Value<Unit<1,0,-1>>; // meters/second type
// a f-p literal suffixed by ‘_s’
constexpr Value<Second> operator "" _s(long double d)
{
return Value<Second> (d);
}
// a f-p literal suffixed by ‘_m’
constexpr Value<Meter> operator "" _m(long double d)
{
return Value<Meter> (d);
}
// an integral literal suffixed by ‘_m’
constexpr Value<Meter> operator "" _m(unsigned long long d)
{
return Value<Meter> (d);
}
template<int m1, int k1, int s1, int m2, int k2, int s2>
Value<Unit<m1 - m2, k1 - k2, s1 - s2>> operator / (Value<Unit<m1, k1, s1>> a, Value<Unit<m2, k2, s2>> b)
{
return Value<Unit<m1 - m2, k1 - k2, s1 - s2>>(a.val / b.val);
}
int main()
{
Speed sp1 = 100_m / 9.8_s;
std::cout << sp1.val;
}
One alternative that does not involve typedef and is not strongly enforced by the compiler but makes it very hard for the programmer to make mistakes it to encode the unit in question as a struct member.
For my arduino project I have types like
template <typename T>
struct millisecond {
T millisecond;
static constexpr const struct millisecond<T> zero = { 0 };
};
template <typename T>
struct microsecond {
T microsecond;
static constexpr const struct microsecond<T> zero = { 0 };
};
and use like
auto time_diff = millisecond<unsigned long>::zero;
time_diff.millisecond = nowMilliseconds() - s_lastPollTime.millisecond;
So with this strategy the compiler does not stop you from mixing units, but if you do then the error will always scream at you:
total_expenses.euros = expence1.euros + expence2.dollars;

C++: Casting for user defined types

How can I get the same handeling of casting for user-defined types as built in, eg:
float a = 5.4;
std::string s = a;//error, no conversion avaible
int x = a;//warning, possible data loss
int y = (int)a;//fine
int z = static_cast<int>a;//fine
float b = c;//warning, possible data loss
Now say I have my own Int and Float class, how do I go about getting the same errors and warnings?
class Int
{
public:
int value;
Int(int v);
...
};
class Float
{
public:
float value;
Float(float v);
...
};
Int c = 10;
Float a = 5.5;
std::string s = a;
Int x = a;
Int y = (Int)a;
Int z = static_cast<Int>a;
Float b = c;
I'm aware of creating overloaded cast operators, and using constructors, however I don't know how to make this work correctly for implicit and explicit casts, eg consider. If I dont add explicit casts within those methods, then I get a warning when there compiled but not when their call, and if I do, then I don't get an error within the classes code, but I still don't get a warning when there used either.
I'm guessing there is some way as to mark the cast operator as explicit, so that a warning is generated if it tries to cast implicitly, but not with explicit (either C-Style or static_cast) casts)
EDIT:
Ok I think I get it for cases like this where all types in question are fully, known, but what about times when one or both are templates, and that neither types map onto a built-in type?
template<typename T> class Vector2
{
public:
T x, y;
Vector2():x(0),y(0){}
Vector2(T x, T y):x(x),y(y){}
//Works as expected, warning if adding T and T2 is unsafe, or error if
//incompatible*/
template<typename T2>Vector2<T>& operator += (const Vector2<T2> &v);
//Also works as desired
Vector2<T>& operator *= (T s);
//allows the conversion, but always generates warnings if
//T and T2 would, even if this constructor is used by an explicit
//case. How can I suppress the warnings for the explicit cast, but
//not for implicit casts?
template<typename T2>Vector2(const Vector2<T2> &v);//uses implicit conversion form T2 to T
};
An implicit cast from say Vector2 to Vector2 works as expected, but the cast from say Vector2 to Vector2 always causes (2, one for x and one for y) warnings, even if a explicit C-Style or static_cast was used. I want to keep the warnings for the implicit cast, but not the explicit casts.
I know I could hack around this be creating a special T vector_cast(T2) type method that uses explicit casts for each element internally, but Id rather be able to use the C-Style and static_casts
I don't think there's a way, either. The best I could achieve is so that the line that you want to generate a warning doesn't compile at all.
class Int
{
public:
int value;
Int(int v);
};
class Float
{
public:
float value;
Float(float v);
operator int() { return static_cast<int>(value); }
};
int main()
{
Float a = 5.5;
//Int x = a; //no warning, simply doesn't compile
Int y = (int)a;
Int z = static_cast<int>(a);
}
Edit: regarding your question about Vector2
One thing to do might be to disable all implicit conversions between different Vector2 types. As a short-cut you might provide a vector_cast to allow explicit conversions:
template <class T, class S>
Vector2<T> vector_cast(const Vector2<S>& s)
{
return Vector2<T>(static_cast<T>(s.x), static_cast<T>(s.y));
}
Another thing might be to bring in some template metaprogramming, to enable conversion constructor for safe conversions.
It seems to me that boost doesn't contain such a type_trait, hence I rolled my own.
It is somewhat simplified: Target must be at least as large as Source, and Target must not be integral if Source is floating point. However, it disregards issues of signedness, and the question whether a floating-point type can represent the full range of an integer type (e.g float cannot store all 32-bit ints precisely, but double can).
#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>
template <class S, class T>
struct is_safe_conversion:
boost::integral_constant<
bool,
(sizeof(S) <= sizeof(T)) && !(boost::is_floating_point<S>::value && boost::is_integral<T>::value)
>
{
};
template<typename T> class Vector2
{
public:
T x, y;
Vector2():x(0),y(0){}
Vector2(T x, T y):x(x),y(y){}
template <class U>
Vector2(const Vector2<U>& other, typename boost::enable_if<is_safe_conversion<U, T> >::type* = 0):
x(other.x), y(other.y) {}
};
template <class T, class S>
Vector2<T> vector_cast(const Vector2<S>& s)
{
return Vector2<T>(static_cast<T>(s.x), static_cast<T>(s.y));
}
int main()
{
Vector2<double> vd, vd2;
Vector2<int> vi, vi2;
Vector2<float> vf, vf2;
vd = vd2;
vd = vi;
vd = vf;
//vi = vd; //error
vi = vector_cast<int>(vd);
vi = vi2;
//vi = vf; //error
vi = vector_cast<int>(vf); //explicit
//vf = vd; //error
vf = vector_cast<float>(vd);
//following compiles, but produces a warning (float cannot represent all integers)
//TODO: enhance is_safe_conversion!
vf = vi;
vf = vf2;
}
Some of what you want won't be possible, because it relies on special knowledge the compiler has about the types involved, and you can't teach the compiler these things.
The items you showed should do the following:
Float a = 5.5;
Should work without complaint.
std::string s = a;
Should give some compiler error, not necessarily the same as using a POD float, but still it will refuse, since your Float doesn't have a const char* operator. (And if it does, remove it to cause this error.)
Int x = a;
You should still get a warning about possible data loss here, unless Float has an "operator int()". If so, remove it, so the compiler is forced to use "operator float()", causing the warning.
Int y = (int)a;
Should work without complaint.
Int z = static_cast<int>a;
This should have the same apparent effect as the previous one. (There are technical differences between them, but in this case, they shouldn't matter.)
Float b = c;
You don't show what 'c' is, so I can't say what this will do.
I don't think there's any way to create your own compiler warning for your cast.