Boost logo

Boost :

Subject: Re: [boost] [review] string convert
From: Gordon Woodhull (gordon_at_[hidden])
Date: 2011-05-04 09:47:51


Hi Vicente,

On May 4, 2011, at 8:57 AM, Vicente BOTET wrote:
> Please, check the post around May 03, 2011; 10:09pm that includes a file with the complete post without angle brackets issues. There you will find all the cases.

I'm reposting that message with my own comments. Vicente, you need to be able to receive messages off-list (and send angle brackets ;-) if you're submitting libraries for review.

> Vladimir,
>
> you are right. My library is not designed to take care of yours needs, and never was. I could do my best trying to update my library if there is enough interest and agreement in the interface. Next follows how I see the interface that could respond to the Boost.Convert features:
>
> In order to take care of types without default constructor I see several options:
>
> * Add an overloading having a default parameter
>
> template <typename Target, typename Source>
> Target convert_to(Source const& s, Target const& t);
>
> and use it as follows
>
> Target t = convert_to<Target>(s, Target(x));
>
> * Use the existing assign_to function
>
> template <typename Target, typename Source>
> Target& assign_to(Target& t, Source const& s);
>
> as follows
>
> Target t (x);
> assign_to(t, s);
>
> * Add a meta-function that returns a default value for a type
>
> template <typename T>
> struct default_value
> {
> T apply()
> {
> return T();
> }
> };
>
> The user will need to specialize this function for types that are not default constructible.
>
> This meta-function will be used instead of declaring a defaulted variable in the string conversion function.
>
> T v(default_value<T>::apply());
>
> As a user I would prefer the second one and then the last one it is transparent except when the specialization is needed. The first option is not appealing for my taste.

The first option is the only one that allows specifying a locally appropriate fallback value in a single line.

Conceivably another spelling could be (Perl || style)

if_else(convert_cast<int>(s, dont_throw_), 17)
or
convert_cast<int>(s, dont_throw_).get_value_or(17)
or, combining with above
convert_cast<optional<int> >(s).get_value_or(17)

Also don't forget Vladimir's alternate syntax
convert_cast<int>(s, fallback_ = 17)

> In order to take care of no throwing semantics, I will choose to add a convert_to overloading with a error code parameter, not throwing without not knowing why fails seems too specific to me.
>
> template <typename Target, typename Source>
> Target convert_to(Source const& s, err_code& ec);
>
> Target t = convert_to<Target>(s, ec);
> if (ec ....

Hmm, nice.

> But I could understand there are some cases where the single reason is a bad format. We could also use the no_throw_t as additional parameter, but in this case the return type should allow to know if the operation has succeed or not. If we follow the C++ standard interface the use of pair seems to be the more appropriated.
>
> template <typename Target, typename Source>
> pair<T,bool> convert_to(Source const& s, no_thow_t const&);
>
> pair<T,bool> p = convert_to<T>(s, no_thow);
> if (p.second)
>
> Note also that there could be some conversions that could transport whether the conversion succeeds or not
>
> optional<Target> t = convert_to<optional<Target> >(s);
>
> This conversion could be overloaded and never throw.

That would nicely eliminate one of the three tags I was suggesting.

> or a Robert suggested overload the pointer to a source
>
> template <typename Target, typename Source>
> optional<Target> convert_to(Source const* s);

I find that creative but confusing; would you really pass char ** if your input was a c-string?

> I think these could be added to Boost.Conversion without not to much trouble.
> I could do my best for the Boost.Conversion part, if there is enough interest and agreement in the interface.

My concern about adding string-to-type to Boost.Conversion is that lexical_cast does type-to-string-to-type as a fallback for everything. Does that make sense for Boost.Conversion?

> Respect to the stream manipulators, what do we want? output an output streameable type and extract another type after some manipulations.
>
> Source s;
> a_iostream ios;
> Target t;
>
> ios << s;
> ios >> m1 >> m2 >> t;
>
> But you want to hide the intermediary iostream. As I stated on one of my post it will be great if we can define a specific input stream that is built from an output stremable type that allows to extract the value stating whatever format is needed. What about
>
> as_istream(s) >> m1 >> m2 >> t;

istringstream is fine for that.

But again, people want to declare a variable and assign its value from a conversion in one line of code. IMO that's an absolute requirement for any lexical_cast replacement.

So you end up with
convert_cast<int>("feed", format_ = std::hex);

Or something like that.

> Respect to the functor I think that we need to use Phoenix syntax and overload the functions with a Phoenix placeholder to create the functor. So
>
> convert_to<Target, Source>(_1)
>
> will return a functor that can be called with one Source parameter and return a Target. The same applies to each one of the other functions.
>
> In this way each feature is separated and compassable, avoiding a bloated class that do everything.
>
> The generic part of Boost.Conversion will not take care are the string conversions and the input stream view. Boost.StringConvert could take care of the string specializations.

Are you suggesting that they are still two different libraries? I am still not sure if these really are the same problem, although they might have similar interfaces.

Boost.Coerce also needs to be considered. Jeroen, have you been following this?

> I don't know if there are some features I have miss. Please let me know.

Mostly dealing with fallback and manipulators within a single expression.

HTH,
Gordon


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