Jump to content
  • Advertisement
Sign in to follow this  

boost::format + for_each = ostream_format_iterator

This topic is 4880 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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]

Share this post


Link to post
Share on other sites
Advertisement
Another constructor that accepts a string instead of a format object, and constructs a format object out of that, would be a useful addition IMO.

Share this post


Link to post
Share on other sites
Would it be OTT to do a version using a dynamically polymorphic 'tuple' rather than a statically polymorphic one? (I hate staring at such triangles of overloads... oh, I also hate the misspelled "formater" [sic] :s )

Something like:


template <typename T>
struct Tuple {
T data;
}

template <typename T>
class Tuple: public std::list<TupleItem<T> > {
void expand(boost::format& dest) {
Tuple::iterator e = this->end();
for(Tuple::iterator pos = this->begin(); pos != e; ++pos) {
dest % *pos;
}
}
// And add a bunch of stuff to make them easier to construct too :)
}

inline void ostream_format_iterator::operator()(Tuple & tuple ) {
tuple.expand(formater);
os << formater;
}

Share this post


Link to post
Share on other sites
You might want to check your performance- I ended up dumping boost::format from my game in favor of plain old streams because it took 3ms to format a screen FPS counter.

Share this post


Link to post
Share on other sites
Quote:
Original post by civguy
Another constructor that accepts a string instead of a format object, and constructs a format object out of that, would be a useful addition IMO.


I had been holding off to see if I could think of any disadvantages to that design... I can't think of any except that it forces me to keep the local format object, which means I wouldn't be able to reverse my decision about that... but ehh, why not :-).

Quote:
Original post by Zahlman
Would it be OTT to do a version using a dynamically polymorphic 'tuple' rather than a statically polymorphic one? (I hate staring at such triangles of overloads... oh, I also hate the misspelled "formater" [sic] :s )


OTT? My googlefu is not strong today... I'm guessing you don't mean "Office of Technology Transfer"...

The flaw in your tuple design is the fact that it can't support multiple argument types (aka, a tuple for an employee of std::string name, int age, ...). I too dislike the triangle, and would have avoided it if possible - and it would be possible, at the sacrifice of the singular element mode. I may create a second iterator class for boost tuples in general - which would fix the problem, albiet in a slightly ugly manner (two seperate classes to do the same job).

Also, how does one spell... that? formatter? dictionary.com dosn't like either one ;_;.

Quote:
Original post by thedustbustr
You might want to check your performance- I ended up dumping boost::format from my game in favor of plain old streams because it took 3ms to format a screen FPS counter.


How were you using them?

Due to their complex formatting models, It's probably a good idea to save a copy instead of regenerating the format object each frame - if you profiled it, was the time being spent in the ctor or the operator% ? I'm pretty certain boost interprets the formatting string at construction time, which would allow the element modifications to work much easier.

Aka, instead of:

void draw_fps_counter( void )
{
string text = str( format( "%1% FPS" ) % fps );
}


Do:

void draw_fps_counter( void )
{
static format format_object( "%1% FPS" );
string text = str( format_object % fps ); //could maybe benifit from static too
}


Note this will bork up multithreaded rendering... but I didn't know anyone could do that yet :3.

Share this post


Link to post
Share on other sites
Quote:
Original post by Fruny
OTT = over the top
formatter with 2 't' (q.v. "formatted")


Thanks, will fix.

Edit: fixed, also, new version up.

[Edited by - MaulingMonkey on January 8, 2005 12:48:48 AM]

Share this post


Link to post
Share on other sites
ostream_container_format_iterator sounds like a bad idea. Containers will typically contain N amount of data, not a fixed amount like in your example. A tuple would be a better choice in your example case, but normally people just use a custom class in C++. I think you need to come up with a valid use case before introducing it.

Share this post


Link to post
Share on other sites
Quote:
Original post by MaulingMonkey
The flaw in your tuple design is the fact that it can't support multiple argument types (aka, a tuple for an employee of std::string name, int age, ...).


Mmm yes. I perceived that as a requirement, but seem to have failed to implement it [wink] I guess it's not so easy to do manually, but it should be possible with boost::any, yes? [smile]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!