What is it?
ostream_format_iterator is a replacement big brother to std::ostream_iterator, the little engine that tried to give us flexible outputing of our containers through for_each, but was really rather... incompetent. It requires the Boost Library, and is specifically meant to work with the Boost Format Library.The Example
#include <boost/format.hpp>
#include <algorithm>
#include <iostream>
#include <list>
#include <map>
#include <string>
#include "ostream_format_iterator.hh"
#include "ostream_multi_format_iterator.hh"
using namespace boost;
using namespace boost::tuples;
using namespace evo;
using namespace std;
int main ( int argc , char ** argv )
{
try
{
//Fancy formatting through normal elements
list< string > numbers;
numbers.push_back("one");
numbers.push_back("two");
numbers.push_back("three");
cout << "Lets count:" << endl;
for_each( numbers.begin() , numbers.end() , ostream_format_iterator( cout , format( "~~~ %1% ~~~\n" ) ) );
cout << endl;
//Fancy formatting through normal, paired up elements
list< string > likes;
likes.push_back("Mike");
likes.push_back("Barqs Rootbeer");
likes.push_back("Noel");
likes.push_back("Orange Gatoraid");
likes.push_back("Doug");
likes.push_back("Dr. Pib");
cout << "Favorite drinks of people I know:" << endl;
for_each( likes.begin() , likes.end() , ostream_format_iterator( cout , format( "%1% likes %2%\n" ) , 2 ) );
cout << endl;
//Fancy formatting of containers with pair<...> value types.
map< string , int > ages;
ages["Mike"] = 19;
ages["Noel"] = 17;
ages["Doug"] = 18;
cout << "Ages of people I know:" << endl;
for_each( ages.begin() , ages.end() , ostream_multi_format_iterator( cout , format( "%1% is %2% year(s) old\n" ) ) );
cout << endl;
//Fancy formatting of containers of boost tuples.
list< tuple< string , string , string , string > > foods;
foods.push_back( make_tuple( "Food A" , "Rootbeer" , "drink" , "chug too much of" ) );
foods.push_back( make_tuple( "Food B" , "Pizza" , "food" , "love" ) );
foods.push_back( make_tuple( "Food C" , "An \"Energy Bar\"" , "\"food\"" , "find not very filling" ) );
cout << "Foods and what I think of them:" << endl;
for_each( foods.begin() , foods.end() , ostream_multi_format_iterator( cout , format( "%1% : %2% is a %3% that I %4%.\n" ) ) );
cout << endl;
}
catch ( const std::exception & e )
{
//This will catch argument under/overruns.
cerr << endl << "Exception:" << endl;
cerr << " " << e.what() << endl;
}
//This will let us see the window :-).
string null;
getline( cin , null );
}
Output:
Lets count:
~~~ one ~~~
~~~ two ~~~
~~~ three ~~~
Favorite drinks of people I know:
Mike likes Barqs Rootbeer
Noel likes Orange Gatoraid
Doug likes Dr. Pib
Ages of people I know:
Doug is 18 year(s) old
Mike is 19 year(s) old
Noel is 17 year(s) old
Foods and what I think of them:
Food A : Rootbeer is a drink that I chug too much of.
Food B : Pizza is a food that I love.
Food C : An "Energy Bar" is a "food" that I find not very filling. News
Edit 4:
Frogot to mention in Edit 3: everything is now in the "evo" namespace since I'm too lazy to edit the code every time I post it (I originally developed it for my compiler debug output...). Include guards are in, and support for string construction is now available although not shown in the example - these are the same:ostream_format_iterator( cout , "I like %1%" ) ostream_format_iterator( cout , format( "I like %1%" ) ) Further, ostream_format_iterator can now be used to read multiple entries from a single container for a formatting object: ostream_format_iterator( cout , "%1% == %2%" , 2 ) I would have automated this, but boost::format has no function for returning the number of arguments used (format("~~~ %1% ~~~").size() == 9, not 1 as one might expect). Edit 3:
Removed tuple support from ostream_format_iterator and seperated it into ostream_multi_format_iterator. This allowed removal of triangle code into a single cons list acceptor. Also, considering making "ostream_container_format_iterator" which would be used upon containers... ala:list< vector< string > > records;
vector< string > record(3);
record[0] = "Mike";
record[1] = "19";
record[2] = "123-45-6789";
for_each( records.begin() , records.end() , ostream_container_format_iterator( format("Name: %1% Age: %2% Social Security #: %3%") ) ); Edit 2:
I'm an idiot, edited post to include the tuple code I talk about and use in the example.Edit 1:
Changed the title, and made this post nice - after my move I will be bringing my server up to a more stable level, at which point I'll move all this over there ~_~. added working tuple support. The current version is a bit icky and will need to be modified to support tupples greater than size 10 due to the fact that the tuples match to the single-value operator() instead of a cons-template operator(). If there's a workaround the current code should be fairly easy to modify to fix (I'm guessing something along the lines of type_traits, only for tuple detection).The Source
ostream_format_iterator.hh
#ifndef IC__EVO_OSTREAM_FORMAT_ITERATOR
#define IC__EVO_OSTREAM_FORMAT_ITERATOR
#include <boost/format.hpp>
#include <ostream>
#include <utility>
namespace evo
{
class ostream_format_iterator
{
boost::format formatter;
std::ostream & os;
std::size_t arg_count;
std::size_t args;
public:
ostream_format_iterator( std::ostream & os , const boost::format & formatter , std::size_t args = 1 )
: formatter(formatter)
, os(os)
, args(args)
, arg_count(0)
{
}
ostream_format_iterator( std::ostream & os , const std::string & format , std::size_t args = 1 )
: formatter(format)
, os(os)
, args(args)
, arg_count(0)
{
}
ostream_format_iterator( const ostream_format_iterator & os_fmt_i )
: formatter(os_fmt_i.formatter)
, os(os_fmt_i.os)
, args(os_fmt_i.args)
, arg_count(0)
{
}
template < typename ArgT >
inline void operator()( const ArgT & arg )
{
formatter % arg;
++arg_count;
//assert(arg_count <= args);
if (arg_count >= args) //this should keep output comming (possibly in error) if things get FUBARED - signaling a bug hopefuly
{
os << formatter;
arg_count -= args;
}
}
};
}
#endif //ndef IC__EVO_OSTREAM_FORMAT_ITERATOR
ostream_multi_format_iterator.hh
#ifndef IC__EVO_OSTREAM_MULTI_FORMAT_ITERATOR
#define IC__EVO_OSTREAM_MULTI_FORMAT_ITERATOR
#include <boost/format.hpp>
#include <boost/tuple/tuple.hpp>
#include <ostream>
#include <utility>
namespace evo
{
class ostream_multi_format_iterator
{
boost::format formatter;
std::ostream & os;
public:
ostream_multi_format_iterator( std::ostream & os , const boost::format & formatter )
: formatter(formatter)
, os(os)
{
}
ostream_multi_format_iterator( std::ostream & os , const std::string & format )
: formatter(format)
, os(os)
{
}
ostream_multi_format_iterator( const ostream_multi_format_iterator & os_fmt_i )
: formatter(os_fmt_i.formatter)
, os(os_fmt_i.os)
{
}
template < typename Arg1T , typename Arg2T >
inline void operator()( const std::pair< Arg1T , Arg2T > & args )
{
os << formatter % args.first % args.second;
}
inline void operator ()( const boost::tuples::null_type & )
{
os << formatter;
}
template < typename H , typename T >
inline void operator()( const boost::tuples::cons< H , T > & cons )
{
formatter % cons.get_head();
operator()(cons.get_tail());
}
};
}
#endif //ndef IC__EVO_OSTREAM_MULTI_FORMAT_ITERATOR
Rationale
1) Keeping a copy of the format object in the iterator. pro: I keep a copy of the format object in the iterator so that temporaries may be used - this would allow code such as:ostream_format_iterator( format("%1%") ) my_iterator; To not get buggy when it's used later and the temporary format object no longer exists. con: more memory overhead/increased bandwidth use on copy, probably trivial compared to video memory access. [Edited by - MaulingMonkey on January 8, 2005 12:49:55 AM]