Boost logo

Boost :

From: Vladimir Prus (ghost_at_[hidden])
Date: 2004-11-02 10:40:04


On Thursday 28 October 2004 12:26, Reece Dunn wrote:
> Hi All,
>
> I have been working on the library and have got a local reimplementation of
> my Output Formatters library based on the review comments. There are still
> a few issues that need to be resolved and I would like to expand stream
> state support.
>
> I have created a preliminary evolution document available at:
> http://uk.geocities.com/msclrhd/iocoll/evolution.html
> that outlines what the new functionality looks like. This redesign
> addresses several of the issues brought up during the review.

Hi Reece, here are my comments on the design.

> Use () if you want to get the value of a decoration:
>
> sep() == "|"; // instrinsinc value

Why would I want to do this?

> sep( std::cout ) == "|"; // stream default if sep() == std::string()

So, this means: 'value in sep if any, otherwise value in stream'?

> If you want to see if a decorator is on an input stream, you can use match:
>
> io::match( std::cin, sep );

What are the use cases for this?

> boost::io::wrapper_decorators< CharT, RetType = void >
>

While I see why it's called "wrapper", the name still sounds a bit strange for
me. Maybe, "open_close_decorators"?

> Holds an (open, close) decorator set.
>
> boost::io::wrapper_decorators< char > wrap( "< ", " >" );
> boost::io::wrapper_decorators< char > wrap2( wrap );
>
> wrap.decorate( "[[ ", " ]]" );
> wrap2.decorate( wrap );

Why do I need the 'decorate' method as opposed to
 
    wrap = wrapper_decorators<char>("<", ">")

or

    wrap.assign("[[", "]]")

The idea that I can decorate a decorator does not seems good for me. Why do I
need to change decorators in this way? To change them, I need to access
decorators stored on the stream. How do I do that?

> RATIONALE: RetType -- a class may derive from this class and you may want to
> chain function calls. For example:
>
> derived_class dc;
> dc.decorate( "/ ", " /" ).derived_fn();
>
> RetType is needed to allow this type of behaviour.

Do you have the need for it at the moment? If not, I suggest you drop this
template argument. I had something similar in program_options and was messy,
did not work everywhere and added no value.

> [3]: sequence decorators
>
> boost::io::sequence_decorators< CharT, RetType = void >
>
> Holds an (open, close, separator) decorator set. Derives from
> wrapper_decorations.
>
> io::sequence_decorators< char > seq1;
> io::sequence_decorators< char > seq2( " : " );
> io::sequence_decorators< char > seq3( "( ", " )" );
> io::sequence_decorators< char > seq4( "[ ", " ]", " | " );
> io::sequence_decorators< char > seq5( wrap );

Do I ever need to use this constructor?

> ===== TYPE TRAITS AND INTROSPECTION
>
> [4] type traits
>
> The type traits unit consists of a series of identifiers (basic_type,
> array_type, etc.) that mark a specific class/template as belonging to
> that generic class. This allows you to manipulate a generic template
> type based on it's generic classification.
>
> RATIONALE: This allows the input/output algorithms to be written without
> knowing the underlying template type, so the algorithms can support
> additional class/template types without modifying the code.

Fully agree with this.

> In order to tell the framework about a type, you need to register it
> using:
>
> BOOST_IO_CLASSIFY_TYPE( nargs, tempate_type, type_class )
>
> where
> nargs is the number of template arguments the class has
> template_type is the name of the class (e.g. std::list)
> template_class is the type to which the class belongs
> (e.g. boost::io::seq_container_type)
>
> BOOST_IO_CLASSIFY_TYPE( 1, std::complex, io::nary2value_type );

Cool.

> [5] boost::io::is_XXX< T >
>
> These MPL objects assert whether T is of type XXX, e.g.:

Cool.

> [6] n-ary objects
>
> An n-ary object (currently, 2-ary, 4-ary and 8-ary objects are supported)
> is a statically-sized sequence of n elements. There is support here for
> describing how one of these types is constructed and for manipulating its
> elements.
>
> In order to get the nth element of an n-ary object ob, you can use getval:
>
> std::cout << io::getval< n >( ob ) << '\n';

Isn't 'getval' an implementation detail of the library?

> BOOST_IO_NARY_PARAM2 is used to register a 2-ary type as containing two
> different types for elements 1 and 2, e.g. std::pair. BOOST_IO_NARY_PARAM1
> is used in all other cases.
>
> BOOST_IO_NARY_PARAM2( std::pair, seperable_pair )
> BOOST_IO_NARY_PARAM1( std::complex, inseperable_pair< T > )

And what about 4-ary objects? Still BOOST_IO_NARY_PARAM1? What if there are 4
different template parameters?

> This is implemented like this:
>
> template< int n, typename T1, typename T2 >
> typename io::detail::selector< n, T1, T2 >::ref_type
> io::refval( std::pair< T1, T2 > & p )
> {
> return( detail::selector< n, T1, T2 >::ref( p.first, p.second ));
> }
>
> where the ref function of detail::selector takes 2, 4 or 8 arguments and
> returns a reference to the nth argument.
>
> An inseperable pair (io::inseperable_pair< T1, T2 = T1 >), 4-ary
> (io::nary4_type< T >) or 8-ary (io::nary8_type< T >) object cannot obtain
> a reference to individual elements. They need to be set at the same time
> using io::assignval:
>
> io::assignval( ob2ary, a, b );
> io::assignval( ob4ary, a, b, c, d );
> io::assignval( ob8ary, a, b, c, d, e, f, g, h );

I think you're again discussing implementation, not interface. I don't think
this function should be initially present in the library public interface.

> [7] automatic type deduction
>
> The fmt::deduce function:
>
> template< typename CharT, typename T >
> [undefined] fmt::deduce( const T & ob );
>
> will return an instance to the nested format object construct that maps to
> the class T.

Ok, we've got to interesting part. I don't understand what's "nested format
object", what's "construct", and what does it mean for "construct" to map to
some class. And you haven't defined "format object" yet.

> The deduction mechanism involves registering the format object
> with a type class.

Maybe, you should call that "type category" to avoid confusion.

> In order to save writing code when several types are
> implemented by a format object, you can map these types into a formatter
> class type:
>
> SEQ_TYPE_TO_FORMATTER( nary2value_type, pair_type );
> SEQ_TYPE_TO_FORMATTER( nary2base_type, pair_type );
> SEQ_TYPE_TO_FORMATTER( nary2int_type, pair_type );

Hmm... what's nary2int_type, nary2base_type and nary2value_type? I though
there's only one type category for n-ary objects, now I see two more.

> This means that any nary2xxx type is formatted as a pair_type. The class
that
> extracts the format object information for a type is looked up internally
> using:
>
> fmt::detail::get_deducer< CharT, T >::type
>
> As a user, you don't need this, but if you are adding a format object into
> the type deduction framework, you will need this to extract the nested
> format object.

Could you rephrase the above? I did not understand anything. Maybe, part of
confusion is the use of "decuder" which you did not define. The word
"deducer" automatically brings "template argument deduction" phrase for me
and don't know how it's related to 'deducer' that you use.

> A format object is deduced by defining fmt::detail::deduce_type for the
> formatter class type

What's "formatter class type"? Is that "type category"?

> that the format object handles. It has the form:

Say I've used SEQ_TYPE_TO_FORMATTER( nary2value_type, pair_type ) already. Do
I need to define this?

> template<>
> struct fmt::detail::deduce_type< type >
> {
> template< typename CharT, typename T >

What's 'T' here? The type being output?

> [8] format object generators
>
> The format objects provided by the library supply generator functions to
> simplify the creation of format objects.
>
> A generator gen will have the form gen< CharT >() to specify the character
> type used by the format object to store the decoration values. It will have
> the form gen() that is equivalent to gen< char >().

Such overloads (non-template + template) used to cause problems for me. IIRC,
on borland.

> In addition, the
> format object may provide additional forms to allow format object nesting.

I did not understood what's generator and when it should be used.

> [9] format objects
>
> A format object is a class that tells the I/O collection framework how to
> render an object to/from a stream.
>
> In order to read an object in from a stream, it implements the read function
> that takes the stream (is) being used as the input source and the object
(ob)
> to read into. It has the form:
>
> std::basic_istream< CharT, TraitsT > &
> read( std::basic_istream< CharT, TraitsT > & is, T & ob ) const;
>
> In order to write an object out to a stream, it ipmlements the write
function
> that takes the stream (out) being used as the output source and the object
> (ob) that is being written to the stream. It has the form:
>
> std::basic_ostream< CharT, TraitsT > &
> write( std::basic_ostream< CharT, TraitsT > & os, const T & ob )
const;

Ok, this is reasonable.

> <1> basic format object
>
> fmt::basic_t< CharT > is the default format object that renders the object
> using the << and >> operators. The generator function is fmt::basic().
>
> <2> pair format object
>
> fmt::pair_t< CharT, First, Second > will render 2-ary object types using
> First to render the first element and Second to render the second. The
> generator function is fmt::pair() and has the additional form:
>
> [unspecified] fmt::pair( const First & f, const Second & s = basic());

"unspecified"? Shouldn't that be fmt::pair_t<charT, First, Second>? And BTW,
I'm strongly against the "_t" suffix. Does it add any information or you just
_t because 'pair' is already used? What about calling the type
"pair_formatter"?

And, why do I ever need to create the formatter explicitly? Can I call any
methods of it?

> <7> state format object
>
> fmt::state_t< CharT, StateObject, bool pre, FO > will render the state
object
> defined by StateObject before the object being read or written if pre is
true.

I think the name "state format" is a bit unclear. I'd suggest some more
explicit pre_format_hook/post_format_hook, or something like that. BTW, some
thinks (and I'd agree) that using 'bool' as parameter to function/template is
not good. If I look at

   state<Foobar, true>()

I have no idea what 'true' means.

> <8> object manipulator
>
> io::object_t< CharT, T, FO > is a special type of format object in that it
> provides I/O manipulator functionality

What's "I/O manipulator functionality"? Do you mean stream
insertion/extraction operators? It's quite different from manipulators, which
only change stream properties, don't output anything.

Finally: you mentioned you have revised version of the code. Looks like it's
not in sandbox. Is it possible to take a look?

- Volodya


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk