Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 15.  Values


15.2. Down to Zero

We saw in the previous item how to protect our client code from changes (from pointer to integer) in library code. What about the opposite situation? Suppose we had client code using the newer lookup(int).



// library.h


struct Token *lookup(int tokenId);





// client.cpp


struct Token *token = lookup(0);



Let's now imagine the requirements have changed again, and the API is to migrate back to using char const*. Now our client code will be changed with no warning, potentially for the worse. What we need is an equivalent to NULL_v for 0 literals.

Given the lessons learned with NULL, we can skip straight to the solution, shown in Listing 15.3.

Listing 15.3.


struct ZERO_v


{


// Conversion


public:


  operator sint8_t () const


  {


    return 0;


  }


  operator uint8_t () const;


  operator sint16_t () const;


  operator uint16_t () const;


  operator sint32_t () const;


  operator uint32_t () const;


#ifdef NATIVE_64BIT_INTEGER_SUPPORT


  operator sint64_t () const;


  operator uint64_t () const;


#endif /* NATIVE_64BIT_INTEGER_SUPPORT */


#ifndef INT_USED_IN_STDINT_TYPES


  operator signed int () const;


  operator unsigned int () const;


#endif /* ! INT_USED_IN_STDINT_TYPES */


  operator float () const;


  operator double () const;


  operator long double () const;


// Not to be implemented


private:


  void operator &() const;


  ZERO_v(ZERO_v const &);


  ZERO_v const &operator =(ZERO_v const &);


};





/// operator == for ZERO_v and integral types


bool operator ==(ZERO_v const &, sint8_t i)   { return i == 0; }


bool operator ==(ZERO_v const &, uint8_t i)   { return i == 0; }


. . .


bool operator ==( ZERO_v const &


                , long double const &i)       { return i == 0; }





/// operator == for an arbitrary type and ZERO_v


bool operator ==(sint8_t i, ZERO_v const &)   { return i == 0; }


. . .





/// operator != for ZERO_v and an arbitrary type


bool operator !=(ZERO_v const &, sint8_t i)   { return i != 0; }


. . .





/// operator != for an arbitrary type and ZERO_v


bool operator !=(sint8_t i, ZERO_v const &)   { return i != 0; }


. . .



Note that there is no operator T() const, since that would also convert to pointers as well as integers. This means that we must supply individual conversion operators for all numeric fundamental types. Furthermore, there is a deliberate omission of conversion operators for char, wchar_t, and bool types. In the same way that NULL is defined to be NULL_v() in stlsoft_nulldef.h, so ZERO is defined to be ZERO_v() in stlsoft_zerodef.h.

For me, however, this is a step too far. Though I've implemented this in the STLSoft libraries, it's not become a part of my day-to-day tool kit, unlike NULL_v, for a couple of reasons:

1. I'm uncomfortable with the potential for clashes with macros defined in third-party and client code. As I said, the assumption that there will be no perverse redefinitions of NULL (other than ours, of course) is reasonable, and I've certainly not encountered one. However, the same cannot be said of the symbol ZERO. I've seen several of these in the past, so the likelihood of a clash is considerably greater.

2. It's plain ugly. Naturally this is merely an artifact of experience—we are used to seeing NULL, and not used to seeing ZERO—but programmers are notoriously churlish about ugliness being foisted on them. In this case I churl with the best of them.

Nonetheless, it's worth knowing about. If you have a "clean" application development where you can be pretty sure that ZERO is not already used, and you want every last drop of type safety that C++ can provide, you are most welcome to use it.

Furthermore, using ZERO_v() directly is useful as a straightforward way to constrain a template to be applied to numeric types only, as in:



template <typename T>


bool is_zero(T const &t)


{


  return ZERO_v() == t; // Won't compile if T is not numeric!


}




      Previous section   Next section