Subject: Re: [boost] Formal Review Request: Boost.String.Convert
From: Andrey Semashev (andrey.semashev_at_[hidden])
Date: 2009-02-18 17:05:48


Emil Dotchevski wrote:

> You can still do this with what I am proposing. When someone does
>
> foo x;
> std::string s=to_string(x);
>
> this would bind to the foo to_string overload if available, if not it
> would bind to the foo/ostream overload of << if available, etc. The
> idea is to automatically pick the best conversion available to produce
> the string the caller needs.
>
> This makes the to_string library implementation more complex, but the
> benefit is that it is possible to define simple, non-template
> to_string overloads for various types. The reason I like that is
> because I don't have to put to_string implementations in headers.
>
> Also, it makes it very easy to integrate 3rd party types into the
> framework. Suppose you have something like:
>
> class bar
> {
> public:
> std::string getstr() const;
> private:
> ...
> };
>
> and you want to integrate it into the to_string framework. All you
> have to do is (presumably you can't alter bar itself):
>
> std::string to_string( bar const & x ) { return x.getstr(); }
>
> (note, no coupling between the above to_string overload and the
> to_string library) and now you can do:
>
> bar x;
> std::wstring s=to_wstring(x); //the library handles string->wstring
> conversion automatically.

And what if I write my own class outer_bar that owns bar and also want
to be ostreamable? I would have to duplicate my operator<< in order to
call either to_string or to_wstring, wouldn't I?

   struct outer_bar
   {
     bar b_;

     friend std::ostream& operator<< (
       std::ostream& s, outer_bar const& ob)
     {
        return s << to_string(ob.b_);
     }

     friend std::wostream& operator<< (
       std::wostream& s, outer_bar const& ob)
     {
        return s << to_wstring(ob.b_);
     }
   };

Things will get even worse with char16_t and char32_t.

Why not make it a template? It would still cover your case just fine:

   struct outer_bar
   {
     bar b_;

     template< typename CharT, typename TraitsT >
     friend std::basic_ostream< CharT, TraitsT >&
       operator<< (
         std::basic_ostream< CharT, TraitsT >& strm,
         outer_bar const& ob)
     {
       typedef std::basic_string< CharT, TraitsT > string_t;
       return s << convert< string_t >(ob.b_);
     }
   };

And supporting bar for the templated "convert" is no less difficult than
what you suggested:

   template< typename StringT >
   StringT convert(bar const & x);

   template< >
   std::string convert(bar const & x) { return x.getstr(); }

   template< >
   std::wstring convert(bar const & x)
   {
     return convert< std::wstring >(convert< std::string >(x));
   }

The equivalent of the latter specialization will be needed in your
approach, too. If the library is clever enough, it may be done in
generic manner in the library itself.