
From: "Stewart, Robert" <Robert.Stewart@sig.com> I was thinking a bit about syntax for the default value for the notional boost::convert() and kin. How about the following:
int const i(3); string const s("something"); int const j(convert<int>(s) | 5);
Yes, this idea is similar to int j = convert<int>(s).or_default(5); but does not introduce new vocabulary (my main hesitation). With the suggested op|, as I am reading it, it makes perfect reading just like I'd read English. And that is usually my criterion for an interface. So, I am voting 'yes'... in principle. See further below.
convert<DestType,SourceType>(SourceType) can return a type which throws on failure in operator DestType(), but has an operator |(SourceType) which returns a second type constructed with the SourceType value. The second type provides its own operator |(SourceType) which returns that value in its operator DestType() on failure.
When the user does not want comprehensive result, it works. The problem as I see it is when the user wants the comprehensive report -- we return two different types, convert1<string, int> j1 = convert<string, int>(s); convert2<string, int> j2 = convert<string, int>(s) | 5; Sometimes I feel we need that as I might like to explicitly check if the call failed without relying on the default convert1<string, int> j1 = convert<string, int>(s) & boost::nothrow; convert2<string, int> j2 = convert<string, int>(s) | 5; if (j1 == 0 && !j1.good()) j1 = 0 but conversion failed if (j2 == 5 && !j1.good()) j2 = 5 but conversion failed Unless we convert both convert1 and convert2 to convert<string, int>::result as convert<string, int>::result j1 = convert<string, int>(s) & boost::nothrow; convert<string, int>::result j2 = convert<string, int>(s) | 5; if (j1 == 0 && !j1.good()) j1 = 0 but conversion failed if (j2 == 5 && !j1.good()) j2 = 5 but conversion failed Or we might simplify that as convert::result<int> j1 = convert::to<int>(s) & boost::nothrow; convert::result<int> j2 = convert::to<int>(s) | 5; if (j1 == 0 && !j1.good()) j1 = 0 but conversion failed if (j2 == 5 && !j1.good()) j2 = 5 but conversion failed The reason I am sticking 'to' in again is that I need the 'convert' as a namespace... or rather as a namespace-like structure: struct convert : mimic_namespace /*I have such a class*/ { template<class TypeOut> struct result { TypeOut value_; bool good_; }; struct internal_with_default { ... }; struct internal_with_no_default { ... }; template<class TypeOut, class TypeIn> internal_with_default<TypeOut, TypeIn> to(TypeIn const&); }; With namespace-like convert structure people will not be able to shortcut 'convert'. I.e. they'll always have to say convert::to which is darn close to convert_to with the advantage of having a dedicated namespace-like place instead of messing inside the boost namespace. Now it seems like I am back where I started. :-)
Returning a default value in lieu of an exception is a special case; it ought not be treated like formatting.
I have an impression that throwing/not-throwing and providing/not-providing the default have to be configured independently. Like I *might* need to throw even if I provide the default: direction dir = convert<string, direction>(str, direction::up) & boost::dothrow; Above I provide the default because I have to (direction does not have the def. ctor). However, I still *might* like to throw on failure.
Thus, while Boost. Parameter might be appropriate to package the extra information desired for formatting, I don't like the idea of writing "default_ = 5" in the call, and I don't think ", 5" in the argument list is as clear as it could be.
I tend to agree with you on both.
The syntax above is obvious and concise while being distinct from whatever may be done for formatting.
You meant to say "laconic", right? Oh, just kidding. Thanks for pointing it out BTW. BTW, what do you think of convert<string, int>(s) & boost::dothrow; instead of convert<string, int>(s) >> boost::throw_t(); People seem to get put off by op>>. Best, V.