On 05/22/2014 08:11 PM, alex wrote:
Although the code above forces
every converter to do that by themselves, right? Although passing
boost::optional into a container might have its benefits... I had it at
one point... will have to re-visit again.
The benefit is that it defers initialization and thus passes that
responsibility from the generic function to the specific converters.
That's something I'll have to think about. What jumps at me though is
what I'd call "blur of responsibilities" -- now converter not only
cares for conversion but for allocation as well. Secondly, in the
current "convert" design converters know nothing about boost::convert
"framework". The implementation above seems to require the converter to
know/deploy boost::converter_default_maker which is either part of the
"convert" infrastructure or converter's own code, i.e. the converter has
to have either additional "knowledge"/coupling or additional code. In
both cases I do not like the "additional" bit. :-)
Some converters require this additional code/knowledge and others not. For
instance, your encryption converter would probably not need it.
Alex,
I snipped other comments as I'd like to focus on this one without
distractions. We might have to rewind a little bit and you might have to
repeat something... apologies.
So, it boils down to the current converter signature
template
bool operator()(TypeIn const&, TypeOut&); //#1
when you suggest (if I understand correctly)
template
bool operator()(TypeIn const&, boost::optional<TypeOut>&); //#2
If so, I'd take it one step further to
template
boost::optional<TypeOut> operator()(TypeIn const&); //#3
#3 seems to offer all #2 does but is cleaner and idiomatic to C++. Agreed?
#3 surely seems attractive and with that interface converters can be
easily used on their own (if needed/preferred) bypassing "convert" api
altogether.
Let's take a simple case of, say, the "direction" class from the docs
(which has no default constructor) and try converting it to std::string.
Say, we want that conversion possible using a few converters with
std::sstream-based being one of them. In that setting I say #1 has the
following advantages (no matter how ugly I personally find it):
1) Converters are not concerned with how to create storage for the
result and only concerned with conversion. "Converters are only
concerned with conversion" -- I like that.
2) Converters are not aware of the "convert" infrastructure.
Your example (adjusted for #3) for 2 generic converters:
template
boost::optional<TypeOut>
basic_stringstream_converter::operator()(const TypeIn& in)
{
boost::optional<TypeOut> out =
boost::make_optional(boost::convert_default_maker<TypeOut>::make());
stream_.str(std::basic_string< char_type>());
stream_ << in; stream_ >> *out;
return out = stream_.fail() ? boost::optional<TypeOut> : out;
}
template
boost::optional<TypeOut>
lexical_cast_converter::operator()(const TypeIn& in)
{
try
{
boost::optional<TypeOut> out_value =
boost::make_optional(boost::convert_default_maker<TypeOut>::make());
*out_value = boost::lexical_cast<TypeOut>(in);
return out;
}
catch (...)
{
return out = boost::optional<TypeOut>;
}
}
I like the signature. I do not like that #2 and #3 seem to impose all
the same requirements on TypeOut, i.e. nothing gained. I do not like
that every converter has to know about and call
boost::convert_default_maker<TypeOut>::make()
Somebody has to provide that... Every container doing that by itself
sounds unreasonable. That's probably my only gripe... but it results in
losing both advantages I listed for #1. Let's try staying with
mainstream usage examples meaningful for the majority of users. I'll
unlikely be persuaded by an esoteric academic (even a real but
corner-case) example that will have negative impact on the real users.
Thoughts?..