существующий конвертер на определяемый пользователем тип может
быть предпочтительным вариантом. Очевидным примером такого дизайна может быть std::iostream
библиотека и ее механизм интеграции типа на основе
std::istream& operator>>(std::istream&, Type&);
std::ostream& operator<<(std::ostream&, Type const&);
В рамках структуры Boost.Convert интеграция и поддержка пользовательских типов является частным бизнесом каждого конвертера. Каждый преобразователь может свободно реализовывать свой собственный (или повторно использовать существующий) механизм интеграции пользовательского типа.
Неудивительно, что преобразователи на основе библиотеки std::iostream
используют механизм, введенный и поддерживаемый этой библиотекой. То есть,
- для вывода Type должен быть Output Streamable;
- для ввода Тип должен быть Входной потоковый.
что на практике означает, что тип должен иметь следующие операторы:
std::istream& operator>>(std::istream&, Type&);
std::ostream& operator<<(std::ostream&, Type const&);
Например,
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");
}
Это позволяет обрабатывать конверсии определенных пользователем типов с помощью преобразователей на основе std::iostream
:
boost::cnv::cstream cnv1;
boost::cnv::lexical_cast cnv2;
change chg = change::up;
string s1 = convert<string>(chg, cnv1, "bad");
string s2 = convert<string, change>(change::dn, cnv1, "bad");
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");
Другие преобразователи (на основе boost::cnv::cnvbase
) реализуют поддержку типов пользователей аналогично, но без std::iostream
-связанные накладные расходы (см. Converters Compared). А именно, новые типы поддерживаются преобразователями после определения следующего:
void operator>>(TypeIn const&, boost::optional<TypeOut>&);
Например, упомянутый класс change
развернут с boost::cnv::strol
после следующих change-to-string и string-to-change преобразования определены:
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;
}
которые не отличаются от ранее показанных (но значительно более эффективны):
std::istream& operator>>(std::istream&, change&);
std::ostream& operator<<(std::ostream&, change const&);
Это позволяет обрабатывать конверсии определенных пользователем типов с 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);