![]() | |
![]() ![]() ![]() |
![]() | Imperfect C++ Practical Solutions for Real-Life Programming By Matthew Wilson |
Table of Contents | |
Chapter 15. Values |
15.2. Down to ZeroWe 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:
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!
}
|
![]() | |
![]() ![]() ![]() |