|
Boost : |
From: Maciej Sobczak (maciej_at_[hidden])
Date: 2002-10-24 02:21:26
Hi Boosters,
My previous attempt to the xalloc & pword wrapper was a complete
failure. I was misled by the VC++6SP5 compiler, which proved to be bad
authority... I've reviewed te code with the Standard and some better
compiler.
Now I would like to present to you two approaches to the problem.
1. State associated with the type (class streamstate).
The slot identifier is a static property of the particular
instantiation. It allows to use the stream state without the need to
keep around any object wrapping the knowledge about the slot. Can be
handy for formatting uses, like in the example code.
2. State associated with object (class streamstate_value, for the lack
of better name - any ideas?).
The slot identifier is a property of some real object with value
semantics, that can be a standalone object, member, mix-in or whatever.
It encapsulates the knowledge about the slot dentifier and allows to use
many objects of the same type in the stream state.
I would like to know your opinion about these two approaches.
The code compiles on g++-3.2.
VC++ 6 and 7 gave up.
Cheers,
--- Maciej Sobczak http://www.maciejsobczak.com/
// a test program for the streamstate utility
#include "streamstate.h"
#include <iostream>
struct brackets
{
// default state
brackets()
: left_('('), right_(')') {}
// arbitrary state
brackets(char l, char r)
: left_(l), right_(r) {}
char left_;
char right_;
};
class mypair
{
public:
mypair(int a, int b) : a_(a), b_(b) {}
std::ostream & print(std::ostream &os) const
{
// get the stream state (maybe default) and use it
brackets &b = streamstate<brackets>::get(os);
return os << b.left_ << a_ << ", " << b_ << b.right_;
}
private:
int a_;
int b_;
};
std::ostream & operator<<(std::ostream &os, const mypair &p)
{
return p.print(os);
}
// this example shows how to store and retrieve state
// associated with some type
// any time we refer to some type, we get the same state slot
void example1()
{
std::cout << "example 1 - state associated with type" << std::endl;
mypair p(1, 2);
// print with default brackets
std::cout << p << std::endl;
// store in the stream new brackets
std::cout << streamstate<brackets>(brackets('[', ']'));
// print with new brackets
std::cout << p << std::endl;
// again, change the brackets
std::cout << streamstate<brackets>(brackets('{', '}'));
// and use them
std::cout << p << std::endl;
// we can also store long values,
// but only one per stream
// print the default long
std::cout << streamstate<long>::get(std::cout) << std::endl;
// set new long value
std::cout << streamstate<long>(7);
// the same, but different syntax
streamstate<long>::get(std::cout) = 7;
// print new long value
std::cout << streamstate<long>::get(std::cout) << std::endl;
}
// this example shows how to store and retrieve state
// associated with some "third-party" object
// in fact, the state object encapsulates the slot identifier
// this way we can store many different state values of the same type
void example2()
{
std::cout << "example 2 - state associated with object" << std::endl;
streamstate_value<brackets> state1;
streamstate_value<brackets> state2;
// we can store many objects of the same type
state1.get(std::cout) = brackets('[', ']');
state2.get(std::cout) = brackets('{', '}');
// and of course retrieve them
std::cout << state1.get(std::cout).left_ << state1.get(std::cout).right_;
std::cout << std::endl;
std::cout << state2.get(std::cout).left_ << state2.get(std::cout).right_;
std::cout << std::endl;
// we can also store many longs in a stream
streamstate_value<long> state3;
streamstate_value<long> state4;
state3.get(std::cout) = 8;
state4.get(std::cout) = 9;
std::cout << state3.get(std::cout) << std::endl;
std::cout << state4.get(std::cout) << std::endl;
}
int main()
{
example1();
example2();
return 0;
}
// Copyright Maciej Sobczak, 2002
//
// Permission to use, copy, modify, and distribute this software for any
// purpose is hereby granted without fee, provided that this copyright and
// permissions notice appear in all copies and derivatives.
//
// This software is provided "as is" without express or implied warranty.
#ifndef STREAMSTATE_H_INCLUDED
#define STREAMSTATE_H_INCLUDED
#include <iostream>
#include <new>
// State should be:
// default-constructible
// copy-constructible
// assignable
template
<
class State,
class charT = char,
class traits = std::char_traits<charT>
>
class streamstate
{
public:
// construct with the default state value
streamstate() {}
// construct with the given stream value
streamstate(const State &s) : state_(s) {}
// modifies the stream
// the return value is not really useful,
// it has to be downcasted to the expected stream type
std::basic_ios<charT, traits> &
modify(std::basic_ios<charT, traits> &ios) const
{
void *&p = state_slot(ios);
if (ios.bad())
throw std::bad_alloc();
if (p == NULL)
{
// copy existing state object if this is new slot
p = new State(state_);
ios.register_callback(state_cleanup, 0);
}
else
*static_cast<State*>(p) = state_;
return ios;
}
static State & get(std::basic_ios<charT, traits> &ios)
{
void *&p = state_slot(ios);
if (ios.bad())
throw std::bad_alloc();
if (p == NULL)
{
// create default state if this is new slot
p = new State;
ios.register_callback(state_cleanup, 0);
}
return *static_cast<State*>(p);
}
private:
static void state_cleanup(std::ios_base::event e,
std::ios_base &ios, int)
{
if (e == std::ios_base::erase_event)
{
// safe delete if state_slot fails
delete static_cast<State*>(state_slot(ios));
}
}
static void *& state_slot(std::ios_base &ios)
{
static int index = std::ios_base::xalloc();
void *&p = ios.pword(index);
// note: if pword failed,
// then p is a valid void *& initialized to 0
// (27.4.2.5/5)
return p;
}
State state_;
};
// partial specialization for iword functionality
template
<
class charT,
class traits
>
class streamstate<long, charT, traits>
{
public:
// construct with the default state value
streamstate() {}
// construct with the given stream value
streamstate(long s) : state_(s) {}
// modifies the stream
// the return value is not really useful,
// it has to be downcasted to the expected stream type
std::basic_ios<charT, traits> &
modify(std::basic_ios<charT, traits> &ios) const
{
long &s = state_slot(ios);
s = state_;
return ios;
}
static long & get(std::basic_ios<charT, traits> &ios)
{
return state_slot(ios);
}
private:
static long & state_slot(std::basic_ios<charT, traits> &ios)
{
static int index = std::ios_base::xalloc();
long &s = ios.iword(index);
if (ios.bad())
throw std::bad_alloc();
return s;
}
long state_;
};
// convenience inserter for ostream classes
template
<
class State,
class charT,
class traits
>
std::basic_ostream<charT, traits> &
operator<<(std::basic_ostream<charT, traits> &os,
const streamstate<State> &s)
{
s.modify(os);
return os;
}
// the alternative if there is a need to have
// many different state values of the same type
template
<
class State,
class charT = char,
class traits = std::char_traits<char>
>
class streamstate_value
{
public:
streamstate_value()
: index_(-1)
{
}
State & get(std::basic_ios<charT, traits> &ios)
{
void *&p = state_slot(ios, index_);
if (ios.bad())
throw std::bad_alloc();
if (p == NULL)
{
// create default state if this is new slot
p = new State;
ios.register_callback(state_cleanup, index_);
}
return *static_cast<State*>(p);
}
private:
static void state_cleanup(std::ios_base::event e,
std::ios_base &ios, int index)
{
if (e == std::ios_base::erase_event)
{
// safe delete if state_slot fails
delete static_cast<State*>(state_slot(ios, index));
}
}
static void *& state_slot(std::ios_base &ios, int & index)
{
if (index < 0)
{
// first index usage
index = std::ios_base::xalloc();
}
void *&p = ios.pword(index);
// note: if pword failed,
// then p is a valid void *& initialized to 0
// (27.4.2.5/5)
return p;
}
int index_;
};
// partial specialization for iword functionality
template
<
class charT,
class traits
>
class streamstate_value<long, charT, traits>
{
public:
// construct with the default state value
streamstate_value()
: index_(-1)
{
}
long & get(std::basic_ios<charT, traits> &ios)
{
if (index_ < 0)
{
// first index usage
index_ = std::ios_base::xalloc();
}
long &s = ios.iword(index_);
if (ios.bad())
throw std::bad_alloc();
return s;
}
private:
long index_;
};
#endif // STREAMSTATE_H_INCLUDED
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk