Я хочу получить из std::back_insert_iterator
, чтобы создать своего рода фильтр для строкового типа, скажем, back_xml_insert_iterator
, который будет проверять символы, передаваемые через него, в поисках символов, которые не могут быть переданы «голыми» в поток XML.Например, '"'
, '&'
, '<'
, '>'
и '\''
, и вместо этого будут вставлять ссылки на сущности своих символов, например, """
для '"'
.
template<class StringType>
class back_xml_insert_iterator : public std::back_insert_iterator<StringType> {
typedef std::back_insert_iterator<StringType> base_type;
public:
typedef typename base_type::container_type container_type;
typedef typename StringType::value_type value_type;
explicit back_xml_insert_iterator( StringType &s ) : base_type( s ) { }
back_xml_insert_iterator& operator=( value_type c ) {
switch ( c ) {
case '"':
case '&':
case '\'':
case '<':
case '>':
char buf[10];
this->container->append( "&#" );
this->container->append( itoa( c, buf ) );
this->container->push_back( ';' );
break;
default:
this->container->push_back( c );
}
return *this;
}
};
Компилируется нормально.Когда я создал экземпляр, я подтвердил, что вызывается конструктор, но мой operator=()
никогда не вызывается.Я думаю, это потому, что унаследованный operator*()
возвращает back_insert_iterator&
, а не back_xml_insert_iterator&
, поэтому back_insert_iterator::operator=()
вызывается вместо моего (поскольку operator=()
не является и не может быть virtual
).
Если это так, то, кажется, невозможно извлечь из back_insert_iterator
полезным способом.
Если я вместо этого создаю свой собственный класс back_insert_iterator_base
, например:
template<class ContainerType,class DerivedType>
class back_insert_iterator_base :
public std::iterator<std::output_iterator_tag,void,void,void,void> {
public:
typedef ContainerType container_type;
DerivedType& operator*() {
return *static_cast<DerivedType*>( this );
}
DerivedType& operator++() {
return *static_cast<DerivedType*>( this );
}
DerivedType& operator++(int) {
return *static_cast<DerivedType*>( this );
}
protected:
back_insert_iterator_base( ContainerType &c ) : container( &c ) {
}
ContainerType *container;
};
и получайте из этого вместо этого:
template<class StringType>
class back_xml_insert_iterator :
public back_insert_iterator_base< StringType, back_xml_insert_iterator<StringType> > {
// ... as before ...
};
, тогда back_xml_insert_iterator
работает как нужно.Так можно ли извлечь из std::back_insert_iterator
и заставить его работать как нужно?
Обновление
Вот как я хотел бы использовать back_xml_insert_iterator
.Во-первых, будет вспомогательная функция:
template<class StringType> inline
back_xml_insert_iterator<StringType> back_xml_inserter( StringType &s ) {
return back_xml_insert_iterator<StringType>( s );
}
Тогда написание функции to_xml()
будет тривиальным:
template<class InputStringType,class OutputStringType> inline
void to_xml( InputStringType const &in, OutputStringType *out ) {
std::copy( in.begin(), in.end(), back_xml_inserter( *out ) );
}