Previous section   Next section

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


19.4. Casts on Steroids

Using C++'s support for conversion operator member functions [Stro1997], it is possible to write classes that can act as casts. Furthermore, the casts involved may be between semantically distinct types. As is widely commented [Meye1996, Meye1998, Dewh2003], the use of such operators can cause a lot of problems, and they are much abused. But they are in the language because they can be very useful, and their use in casting classes is both appropriate and necessary. Consider the following class, which converts a (C-style) string to an integer.

Listing 19.3.


class str2int


{


// Construction


public:


  explicit str2int(char const *s)


    : m_value(atoi(s))


  {}


// Operators


public:


  operator int() const


  {


    return m_value;


  }


// Members


private:


  int m_value;


};



Given a string, we can now "cast" it to an integer by using the following form:



int val = str2int("34");



or



int val = (str2int)"34";



or even



int val = static_cast<str2int>("34");



That seems kind of cool, although in this case we might as well call atoi(). With the advent of C++'s template support, this technique can be generalized to a considerable degree, with some pleasing and surprising results.

Consider now that we'd like to expand our number parsing a bit, and generalize it to all integral types (including bool). Clearly we need some templates. Perhaps surprisingly, the solution is very simple indeed. (Note that this is written for compatibility with Metrowerks CodeWarrior only, and so uses the long long type. Nor have I bothered to cater for unsigned types or character encodings other than char. A real-world implementation would, of course, use correct abstractions, that is, int64_t, and provide a more portable solution.)

Listing 19.4.


template <typename I>


class str2int


{


// Construction


public:


  explicit str2int(char const *s);


// Operators


public:


  operator I() const


  {


    return m_value;


  }


// Members


private:


  I m_value;


};





template <typename I>


inline str2int<I>::str2int(char const *s)


  : m_value(static_cast<I>(atoi(s)))


{}


template <>


inline str2int<long long>::str2int(char const *s)


  : m_value(strtoll(s, NULL, 10))


{}


template <>


inline str2int<bool>::str2int(char const *s)


  : m_value(0 == (strcmp(s, "true"))


{}



A pleasing consequence of the implementation is that in using the cast class we emulate the syntax of the built-in casts.



short     s  = str2int<short>("34");


int       i  = str2int<int>("65536");


bool      b  = str2int<bool>("true");


long long ll = str2int<long long>("-9223372036854775808");



So we've seen how to masquerade in the clothes of C++'s built-in cast operators. That's nice. But is there a serious point? Well, I'm sure that the fact that we've also catered for bool and its non-numeric string representation (which could easily be expanded to handle other strings, such as "1" and "TRUE") has got you thinking, but you probably want to see something that answers a genuine problem. After all, str2int could just be implemented as a suite of related functions, str2short, str2bool, and so on.


      Previous section   Next section