Boost logo

Boost :

From: Jeff Garland (jeff_at_[hidden])
Date: 2006-07-08 22:06:55


Daryle Walker wrote:
> On 7/3/06 9:08 AM, "Jeff Garland" <jeff_at_[hidden]> wrote:
>
>> double dbl = 1.12345;
>> int i = 100;
>>
>> s.append(dbl, " a string ", i);
>> //"1.12345 a string 100"
>
> I don't think this is a good idea. Could this be implemented any more
> efficiently than:
>
> s.append( dbl ).append( " a string " ).append( i );
>
> You would have to do the memory allocations in piecemeal in either case.

Not really:

template<class char_type>
template<typename T1, typename T2, typename T3>
inline
basic_super_string<char_type>&
basic_super_string<char_type>::append(const T1& val1,
                                       const T2& val2,
                                       const T3& val3)
{
   string_stream_type ss;
   ss << val1 << val2 << val3;
   *this += ss.str();
   return *this;
}

In most cases I doubt the string stream will need to reallocate.

> And no matter how many parameters are given, users would have to resort to
> chaining whenever they go above your arbitrary limit. There would be little
> gain over the cost of having to drag in tuples and advanced programming.

Are you saying I should force users to create a tuple to do this? That seems
like can unnecessary complication and I think I'd still have to have many
overloads to make this work seamlessly.

> The "append" method could have an alternative version with a second
> parameter, that of a locale so the user can control the string conversion.
> The single-parameter version would just use the default locale.

It could, but I'm going to just give them direct access to the stream. Here's
the 3 parameter variant:

   template<typename T1, typename T2, typename T3>
   basic_super_string<char_type>& append (const T1& val1,
                                             const T2& val2,
                                             const T3& val3,
                                             string_stream_type& ss);

Used like this:

     std::ostringstream ss;
     super_string s;
     ss << std::setprecision(3);
     double dbl = 1.987654321;
     int i = 1000;
     s.append(dbl, i, " stuff", ss);
     //s == "1.99 1000 stuff"

So if they want to change the locale for this conversion stream they can. This
is also much more efficient than reallocating the string stream for each set
of conversions. In my performance tests allocating the stringstream is a
significant impact, so doing more conversions at once or having the user
supply one that doesn't get reallocated is a definite benefit.

>> I'll probably do something like boost.tuple and support up to 10 arbitrary
>> parameters. And at the suggestion of someone else, I'm also adding
>> boost.format into the mix:
>>
>> s.append_formatted(dbl, "-some string-", i, "%-7.2f %s %=5d");
>> //"1.12 -some string- 100 "
>
> Unless you are limiting the method to be exactly four parameters, how would
> it know that the last parameter is a format string and not a direct argument
> (that looks like a format by coincidence)?

Simple -- append_formatted always requires the last argument to be a string
and it is always interpreted as a format -- it's different from 'append'. If
the format doesn't match with the rest you get a boost.format exception.

>> So I'm afraid I'm making it more mutable, not less ;-)
>
> You still have to evaluate each potential method, and not make your class a
> dumping ground of mixed quality.

True enough. I'm still exploring what should be in the interface -- that's
part of the reason I posted -- I knew I would get lots of advice. That said,
the type conversion capabilities are essential to my personal goals, even
though that cranks up the number of overloads. Also, after playing with the
append_formatted I really like it for simple creation of table-like output:

     double dbl = 1.123456789;
     int i = 1000;
     s.append_formatted(dbl, i, dbl, i , "|%-7.2f|%-7d|%-7.2f|%-7d|\n");
     s.append_formatted(i, dbl, i, dbl , "|%=7d|%=7.4f|%=7d|%=7.4f|\n");
      // s == "|1.12 |1000 |1.12 |1000 |\n"
      // "| 1000 | 1.1235| 1000 | 1.1235|\n"

On the other side of the ledger, for v2 I've removed all the *_copy methods in
the mutable string class. Discussion convinced me that they were over the
line -- and after all, if you want to operate on a copy, it's easy enough to
make one without building it into every method (eg: to_upper_copy). Also, I'll
be introducing an const_super_string based on boost::const_string that is
immutable -- for those that want to see how that interface works out.
Personally, I prefer the mutable interface, but this works too:

     const_super_string s;
     double dbl = 1.543;
     s = s.append("double: ", dbl, " blah, blah");
     // s == "double: 1.543 blah, blah"

and the performance hit isn't too bad. More to come...

Jeff


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk