I want to derive from std::back_insert_iterator
to create a kind of filter for a string type, say back_xml_insert_iterator
, that will examine the characters passed through it looking for characters that can not be emitted "naked" into an XML stream, e.g., '"'
, '&'
, '<'
, '>'
, and '\''
, and will on-the-fly insert their character entity references instead, e.g., """
for '"'
.
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;
}
};
This compiles fine. When I create an instance, I confirmed that the constructor is called, but my operator=()
is never called. I think it's because the inherited operator*()
returns a back_insert_iterator&
and not a back_xml_insert_iterator&
so back_insert_iterator::operator=()
is called rather than mine (since operator=()
is not, nor can not be, virtual
).
If that's the case, then it seems impossible to derive from back_insert_iterator
in a useful way.
If I instead create my own back_insert_iterator_base
class like:
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;
};
and derive from that instead like:
template<class StringType>
class back_xml_insert_iterator :
public back_insert_iterator_base< StringType, back_xml_insert_iterator<StringType> > {
// ... as before ...
};
then back_xml_insert_iterator
works as desired. So is it possible to derive from std::back_insert_iterator
and have it work as desired?
Update
Here's how I'd like to use the back_xml_insert_iterator
. First, there would be the helper function:
template<class StringType> inline
back_xml_insert_iterator<StringType> back_xml_inserter( StringType &s ) {
return back_xml_insert_iterator<StringType>( s );
}
Then writing a to_xml()
function would be trivial:
template<class InputStringType,class OutputStringType> inline
void to_xml( InputStringType const &in, OutputStringType *out ) {
std::copy( in.begin(), in.end(), back_xml_inserter( *out ) );
}