
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<class TypeOut, class TypeIn> bool operator()(TypeIn const&, TypeOut&); //#1 when you suggest (if I understand correctly) template<class TypeOut, class TypeIn> bool operator()(TypeIn const&, boost::optional<TypeOut>&); //#2 If so, I'd take it one step further to template<class TypeOut, class TypeIn> 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<TypeIn, TypeOut> 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<TypeIn, TypeOut> 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?..