Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 31.  Return Value Lifetime


31.7. Solution 5—Conversion Shims

The last solution is a departure from the previous four. Rather than returning a C-string pointer into a buffer passed to the function, or one provided by the library on a per-thread basis, this version returns an instance of a proxy object. In this way, it obviates the issues of RVL-LV and RVL-LS entirely. The proxy class—int2str_proxy—contains its own buffer as a member variable, and provides an implicit conversion to a C-string pointer. Integer to string conversion is, of course, carried out by integer_to_string() under the covers, as is the case with all the solutions. The implementations of the class and the assistor function, int2str(), are shown in Listing 31.8.

Listing 31.8.


template< typename C


        , typename I


        >


class int2str_proxy


{


public:


  typedef C   char_type;


  typedef I   int_type;


public:


  int2str_proxy(int_type i)


    : m_result(integer_to_string(m_sz, dimensionof(m_sz), i))


  {}


  int2str_proxy(int2str_proxy const &rhs)


    : m_result(m_sz)


  {


    char_type       *dest = m_sz;


    char_type const *src  = rhs.m_result;


    for(; 0 != (*dest++ = *src++);)


    {}


  }


  operator char_type const *() const


  {


    return m_result;


  }


private:


  char_type const * const m_result;


  char_type               m_sz[21];


// Not to be implemented


private:


  int2str_proxy &operator =(int2str_proxy const &rhs);


};





template< typename C


        , typename I


        >


int2str_proxy<C, I> int2str(I i)


{


  return int2str_proxy<C, I>(i);


}



To use it, one simply calls int2str(), stipulating the appropriate character type, as in int2str<wchar_t>(101). int2str(), returns an instance of int2str_proxy, and is therefore a conversion shim (see section 20.5.)

31.7.1 RVL

The advantage of this technique is that it is immune to the return value lifetime problems inherent in Solutions 2–4. Hence an expression such as the following will yield correct results:



void dump_2_ints(char const *s1, char const *s2);





int i = . . . ;


int j = . . . ;





dump_2_ints(int2str<char>(i), int2str<char>(j));



However, in common with conversion shims, this one exposes a susceptibility to RVL-PDP:



int         i = . . . ;


char const  *s = int2str<char>(i);





puts(s); // Eeek! s points into hyperspace



This code is bad. Although it may work, that will only be as an artifact of your compiler and the precise layout of your program. In principle, the behavior of the above code is undefined, and therefore it is broken. This is one of the caveats of conversion shims: the returned converted values must not be retained if they are pointers, but used immediately, or deep copies taken.

The efficiency we're buying is, in part, derived from the fact that we're trading in pointers, specifically C-string pointers. I think it's been amply demonstrated that the cost of such pointer-derived efficiencies are that the code is, if not dangerous, at least accompanied by a health warning. The way around this is to return something that is a value type, such as a string instance, as in:



std::string int_to_string_instance(int i)


{


  char  buffer[21];


  return std::string(integer_to_string(buffer, stlsoft_num_elements(buffer), i));


}



Naturally, the safety would be bought at the cost of a loss in efficiency.


      Previous section   Next section