Copy-Assignment and Concept Checking
Eagle-eyed STL extenders may have spotted one remaining problem with the implementation: It is not copy-assignable. To all practical extents, this does not matter, since it's hard to conceive of any practical use case where one would need to take a copy of what is effectively a stateless output iterator. The non-copyability arises from the use of const and reference members (a practice I advocate strongly in Imperfect C++ as it makes the compiler a proactive aid in enforcing design).
Notwithstanding, the C++ standard requires copy-assignment of output iterators, and some implementations of the standard library incorporate active concept checks, preventing compilation of statements involving standard algorithms (such as the examples shown using std::copy()). So, we need to make iterator instances copyable, which is achieved by making all reference members be pointers, and by making all members mutable. Thankfully, all the necessary changes are within the format_output_iterator class template implementation (see Listing 8), and the class interface and the creator functions are unchanged.
template< typename S
. . .
>
class format_output_iterator
: . . .
{
. . .
format_output_iterator(sink_type& sink, format_type const& format)
: m_sink(&sink)
, m_format(::stlsoft::c_str_data(format), ::stlsoft::c_str_len(format))
, m_n(0)
, m_arg1(&no_arg_ref_())
, m_arg2(&no_arg_ref_())
, m_arg3(&no_arg_ref_())
, m_arg4(&no_arg_ref_())
, m_arg5(&no_arg_ref_())
, m_arg6(&no_arg_ref_())
, m_arg7(&no_arg_ref_())
, m_arg8(&no_arg_ref_())
{}
format_output_iterator(
sink_type& sink
, format_type const& format
, unsigned n
, A1 const& arg1
, A2 const& arg2 = no_arg_ref_()
, A3 const& arg3 = no_arg_ref_()
, A4 const& arg4 = no_arg_ref_()
, A5 const& arg5 = no_arg_ref_()
, A6 const& arg6 = no_arg_ref_()
, A7 const& arg7 = no_arg_ref_()
, A8 const& arg8 = no_arg_ref_()
)
: m_sink(&sink)
, m_format(::stlsoft::c_str_data(format), ::stlsoft::c_str_len(format))
, m_n(n)
, m_arg1(&arg1)
, m_arg2(&arg2)
, m_arg3(&arg3)
, m_arg4(&arg4)
, m_arg5(&arg5)
, m_arg6(&arg6)
, m_arg7(&arg7)
, m_arg8(&arg8)
{}
private:
class_type& operator =(class_type const&);
. . .
template <typename A>
void invoke_(A const& value)
{
switch(m_n)
{
case 0:
fmt(*m_sink, m_format, value);
break;
case 1:
fmt(*m_sink, m_format, value
, impl::ref_helper<A1>::get_ref(*m_arg1));
break;
case 2:
fmt(*m_sink, m_format, value
, impl::ref_helper<A1>::get_ref(*m_arg1)
, impl::ref_helper<A2>::get_ref(*m_arg2));
break;
case 3:
fmt(*m_sink, m_format, value
, impl::ref_helper<A1>::get_ref(*m_arg1)
, impl::ref_helper<A2>::get_ref(*m_arg2)
, impl::ref_helper<A3>::get_ref(*m_arg3));
break;
. . . 4 - 7:
}
}
. . .
private: // Fields
sink_type* m_sink;
string_type_ m_format;
unsigned m_n;
A1 const* m_arg1;
A2 const* m_arg2;
A3 const* m_arg3;
A4 const* m_arg4;
A5 const* m_arg5;
A6 const* m_arg6;
A7 const* m_arg7;
A8 const* m_arg8;
};
Robustness
One final note: Although use of format_iterator is, like the FastFormat library as a whole, 100% type-safe, being implemented in terms of a replacement-based API means that its use is always subject to format specification defects, to which FastFormat responds by throwing an appropriate exception, as illustrated by the following code.
std::copy(
headers.begin(), headers.end()
, fastformat::format_iterator(
stm
, "{1}{0}{2}{5}"
, prefix
, suffix)); // throws fastformat::missing_argument_exception
In this respect, then, it is less robust than either std::ostream_iterator and stlsoft::ostream_iterator. You should balance that against its substantially increased expressiveness and flexibility when choosing the tools for your particular requirements.
Note: if you have a good reason to ignore format specification defects, this can be done using the scoping classes -- ignore_missing_arguments_scope and ignore_unreferenced_arguments_scope -- supplied with the library.)
Acknowledgements
Many thanks to Chris Oldwood and Garth Lancaster for their efforts in helping to increase the readability of the article and to reduce its size. Any failures in either regard are entirely my own.


