views:

401

answers:

3

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

A: 

I don't think there's any way to create your own compiler warning for your cast.

SLaks
+1  A: 

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.

Warren Young
static_cast<> is normally safe, and will give you wnat you would expect - either that, or it will fail to compile. reinterpret_cast<> is the dangerous one: it will compile, it will run, and you will get something which may make no sense at all.
David Thornley
Yes, you're right. I'll edit the post.
Warren Young
I added what c is :)
Fire Lancer
+1  A: 

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;
}
UncleBens