5

Can I use assert to enforce type definitions. Suppose there is a variable, double d, how can you use assert to assert that d is a double? If assert is not applicable (which I am betting isn't), is there another option? I am specifically looking to test for implicit type casting during debugging, while benefiting from the functionality of assert and #define NDEBUG.

P.S Obviously I would want to use this for any type definition, just using double as an example here. The solution should be cross platform compatible and be compatible with C++03.

I like to add error checking to my class setters. For example, suppose there is a class, MyClass, with a private member variable, x:

void MyClass::setX(double input)
{
   // assert x is double
   x = input;
}
Elpezmuerto
  • 5,391
  • 20
  • 65
  • 79
  • 1
    assert() is for run-time checks. For datatypes, you have a much stronger tool at your disposal - compile-time checks. Describe a specific scenario, maybe the SO collective will come up with a way to turn it into a compile error situation. – Seva Alekseyev Sep 26 '11 at 19:03
  • 1
    Wherever `x` was defined, make the type `double`. – GManNickG Sep 26 '11 at 19:24
  • @Gman, I like to add error checking to the setters for robustness. – Elpezmuerto Sep 26 '11 at 19:31
  • I'm new to C++, can you tell me in what case a **double** declared parameter could be something else ? Thanks – Niklas R Sep 26 '11 at 19:34
  • @Niklas R: He wants to know that `x` is a double, not the parameter. – K-ballo Sep 26 '11 at 19:37
  • @K ah, yeah. But that should actually be declared as well ? – Niklas R Sep 26 '11 at 19:39
  • @Elpezmuerto: I'm not sure I follow. The value you're getting for the setter should also have the type `double`. This is all compile-time, there is no generic run-time type. – GManNickG Sep 26 '11 at 19:40
  • @Gman, Seva has already pointed out that this is compile-time – Elpezmuerto Sep 26 '11 at 19:43
  • 1
    @NiklasR: He seems to be wanting an assert for if the _member_ is something else. Can't imagine how that would be though. – Mooing Duck Sep 26 '11 at 19:47
  • @Elpezmuerto: Right, so then what's the question? How could it possibly be useful to assert the member variable `x` is a `double`? Just make it a `double` at its declaration. – GManNickG Sep 26 '11 at 20:04
  • @Gman For simple projects, making sure it is a double is relatively easy. But for large projects and for the development of custom unit tests, attempting making sure it is a `double` at declaration isn't feasible – Elpezmuerto Sep 26 '11 at 20:22
  • @Elpezmuerto What else could it be? It's not like the type is unknown. Can you post some actual code where this is an issue? – GManNickG Sep 26 '11 at 20:30

5 Answers5

11

It's really a compile time check, so you should use static asserts for this.

Here is an example using boost's static asserts and type traits.

#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>

template<typename T>
  void some_func() {
    BOOST_STATIC_ASSERT( (boost::is_same<double, T>::value) );
  }

TEST(type_check) {
  some_func<double>();
}

I assume you mean in terms of a template anyway.

Tom Kerr
  • 10,444
  • 2
  • 30
  • 46
5

You can use the == operator defined in the type_info class to test for a specific type definition.

#include <assert.h>
#include <iostream>
#include <typeinfo>

int main ()
{
    double a = 0;

    std::cout << typeid(a).name() << std::endl;

    assert(typeid(a)==typeid(double));
    assert(typeid(a)==typeid(int)); // FAIL
}

Or borrowing from another SO answer using templates and try/catch:

#include <assert.h>
#include <iostream>
#include <typeinfo>

template <typename X, typename A>
inline void Assert(A assertion)
{
    if( !assertion ) throw X();
}

#ifdef NDEBUG
    const bool CHECK_ASSERT = false;
#else
    const bool CHECK_ASSERT = true;
#endif

struct Wrong { };

int main ()
{
    double a = 0;

    std::cout << typeid(a).name() << std::endl;

    assert(typeid(a)==typeid(double));
    Assert<Wrong>(!CHECK_ASSERT || typeid(a)==typeid(double));
    try
    {
    //assert(typeid(a)==typeid(int)); // FAIL and Abort()
        Assert<Wrong>(!CHECK_ASSERT || typeid(a)==typeid(int)); // FALL
    }
    catch (Wrong)
    {
        std::cerr <<"Exception, not an int" <<std::endl;
    }
}
Community
  • 1
  • 1
Elpezmuerto
  • 5,391
  • 20
  • 65
  • 79
3

You should be able to compare using std::is_same and using decltype. You can even use std::static_assert to move the check to compile time. I've seen it happen in libc++ :)

Note these are C++11 features, so you'll need to have a compiler that supports decltype

rubenvb
  • 74,642
  • 33
  • 187
  • 332
  • Should be `std::is_same`, note that a solution based on C++03 is still possible but there is not enough context information in the question to propose so. Also static_assert suits better once in C++11 world. – K-ballo Sep 26 '11 at 19:04
  • @K-ballo what other context is needed for C++03? – Elpezmuerto Sep 26 '11 at 19:32
1

Given the current definition of the code, a way to check at compile time whether both are of the same type is:

template< typename T, typename U >
void assert_same_type( T const&, U const& )
{
     int error[ sizeof( T ) ? -1 : -2 ]; // error array of negative size, dependent on T otherwise some implementations may cause an early error message even when they shouldn't
}

template< typename T >
void assert_same_type( T&, T& ){}

void MyClass::setX(double input)
{
   assert_same_type( x, input ); // If the fallback case is instantiated then a compile time error will arise of trying to declare an array of negative size.

   x = input;
}
K-ballo
  • 80,396
  • 20
  • 159
  • 169
  • @rubenvb: I'm not sure if that is actually the case, but if it is simply add an error within the fallback case. I will update my answer. – K-ballo Sep 26 '11 at 19:44
0

You can create a template function, then overload the argument type for double like this:

#include <cassert>

template<class T>
bool is_double(T) { return false; }
bool is_double(double) { return true; }

int main() {
    int i = 1;
    double d = 3.14;
    assert( is_double(d) );
    assert( is_double(i) ); // fails
}

That would give a run-time error. You can generate a compile time error by simply defining a function that takes a double reference:

void is_double(double&) { }

void MyClass::setX(double input)
{
  is_double(x); // assert x is double
  x = input;
}
JohnPS
  • 2,518
  • 19
  • 17