...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
Probably the most obvious way to support a new type is to write a converter
(potentially by inheriting from an existing converter) that understands the
type and implements whatever transformation functionality is required. That
said, conversions to certain types are very common (for example, string/text-related
conversions) and, consequently, an ability to extend support
of an existing converter onto a user-defined type might
be the preferred option. The obvious example of such design might be the std::iostream
library and its type-integration mechanism based on
std::istream& operator>>(std::istream&, Type&); // For input std::ostream& operator<<(std::ostream&, Type const&); // For output
Within the Boost.Convert framework the integration and support of user-defined types is every converter's private business. Every converter is free to implement its own (or re-use an existing) user-type-integration mechanism.
Unsurprisingly, the converters based on the std::iostream
library use the mechanism introduced and supported by that library. That is,
which in practical terms means that the type needs to have the following operators defined:
std::istream& operator>>(std::istream&, Type&); // For input std::ostream& operator<<(std::ostream&, Type const&); // For output
For example,
struct change { enum value_type { no, up, dn }; change(value_type v =no) : value_(v) {} bool operator==(change v) const { return value_ == v.value_; } value_type value() const { return value_; } private: value_type value_; };
std::istream& operator>>(std::istream& stream, change& chg) { std::string str; stream >> str; /**/ if (str == "up") chg = change::up; else if (str == "dn") chg = change::dn; else if (str == "no") chg = change::no; else stream.setstate(std::ios_base::failbit); return stream; } std::ostream& operator<<(std::ostream& stream, change const& chg) { return stream << (chg == change::up ? "up" : chg == change::dn ? "dn" : "no"); }
That allows handling conversions of user-defined types with std::iostream
-based
converters:
boost::cnv::cstream cnv1; boost::cnv::lexical_cast cnv2; change chg = change::up; string s1 = convert<string>(chg, cnv1, "bad"); // Input type (change) deduced string s2 = convert<string, change>(change::dn, cnv1, "bad"); // Input type (change) enforced BOOST_TEST(convert<change>("up", cnv1, change::no) == change::up); BOOST_TEST(convert<change>("up", cnv2, change::no) == change::up); BOOST_TEST(s1 == "up"); BOOST_TEST(s2 == "dn");
Other converters (based on boost::cnv::cnvbase
) implement support for user types
similarly but without the std::iostream
-related
overhead (see Converters
Compared). Namely, new types are supported by the converters after the
following is defined:
void operator>>(TypeIn const&, boost::optional<TypeOut>&);
For example, the mentioned change
class is deployed with boost::cnv::strol
after the following change-to-string
and string-to-change conversions are defined:
inline void operator>>(change chg, boost::optional<std::string>& str) { str = chg == change::up ? "up" : chg == change::dn ? "dn" : "no"; } inline void operator>>(std::string const& str, boost::optional<change>& chg) { /**/ if (str == "up") chg = change::up; else if (str == "dn") chg = change::dn; else if (str == "no") chg = change::no; }
which are not that dissimilar to (but considerably more efficient than) previously shown:
std::istream& operator>>(std::istream&, change&); std::ostream& operator<<(std::ostream&, change const&);
That allows handling conversions of user-defined types with boost::cnv::strtol
:
boost::cnv::strtol cnv; change up_chg = change::up; change dn_chg = change::dn; BOOST_TEST(convert<std::string>(up_chg, cnv, "bad") == "up"); BOOST_TEST(convert<std::string>(dn_chg, cnv, "bad") == "dn"); BOOST_TEST(convert<std::string>( 12, cnv, "bad") == "12"); BOOST_TEST(convert<change>("up", cnv, change::no) == change::up); BOOST_TEST(convert<change>("dn", cnv, change::no) == change::dn); BOOST_TEST(convert< int>("12", cnv, -1) == 12);