
Reece Dunn wrote:
The primary case is debugging. I want to either output small vectors to some log/dump file (in which case the output will be small one-line), or output some huge structures (vector<Function>, where Function has a lot of data). In the latter case, the output should be multiline, with nice indentation, or I won't understand anything.
You can explicitally state where/how new lines and indentation are going to be within my library by adding them to the open/close/separator formatting (see output-3D.cpp for an example).
Yes, but I'd rather write os << v and have the right formatting, even if 'v' is a vector of something, which has vector members, and the vector element type has another vector member, and so on...
As for more intelligent indentation, it is hard to know in advance if an object you are writing to the stream should be wrapped or not. This is where feeding it through Jonathan's library (via an indentation filter) would be useful.
The wrapping is indeed a separate issue. What I was asking is support of indenting in your library. For example, the output such as - foo: 1 - bar: 2 - biz: - [1, 2, 3 ] - giz: p1: 1 p2: - [1, 2, 3]
A particularly interesting question is how the proposed library overlaps with serialization. When outputting vector<Function> I'd prefer the content of 'Function' to be outputted too, preferably by describing the members with the 'serialize' method.
The default behaviour for an unrecognised type is to output it directly to the stream so it will use the << operator associated with that type. If you want to use a particular method, such as 'serialize', then you will need to write your own format object that calls that function.
The problem is that I need support from your library to get right indentation.
It's desirable that the library support some multiline output style out of the box, so that I could write:
os << io::multiline(v) << ...
Do you mean something like:
std::cout << io::object( v ).decorate( "[\n ", "\n]", ",\n " );
resulting in:
[ 1, 2, 3 ]
Yes, except that I'd want "multiline" to act recursively, so if I print vector of maps, the result will look like: [ { a: 1 b: 2 } { c: 3 } ] or using a different syntax.
I don't think defining methods in the class is a good idea -- this makes the class interface less obvious.
I agree. I prefer to keep interface and implementation seperate, but MS VC (prior to 7.1) will choke on nested templates declared out of line, e.g.:
template< typename T > struct foo { template< typename U > void bar(); template< typename U > void ok(){} };
template< typename T > template< typename U > // VC<=7.0 ==> error void foo< T >::bar(){}
Yes, that's a bad thing.
I also try to be consistent where possible, so don't tend to mix the two.
I'd suggest that you at least try to make top-level functions non-inline. That would reduce the code size for application which use your library -- I've had this experience both with program_options and function. For a single library, it's probably not a huge difference, but if all libraries use inline less liberaly, the code size of C++ apps would be lower.
Why is the formatob_t necessary? It seems to work by delegating everything to the underlying formatter. Can't the 'formatob' just return the appropriate formatter?
It's necessary because the format objects don't define << and it is necessary to keep a refererence to the object passed to it for formatting so that it can be accessed when inside the implementation of the << operator.
Understood.
I might be missing something, but the mechanism for getting the type of formatter from a type to be output seems too complex. First, the type_deducer.hpp file is used, and 'select' computes a 'category'. Then format_deducer.hpp takes the category, and again uses 'select' to obtain the real type of formatter. Why the type_deducer.hpp is needed?
...
template<> struct deduce_type< io::seq_container_type > { template< typename CharT, typename T > struct type_from { typedef typename T::value_type value_type; typedef typename get_deducer< CharT, value_type >::type value_deducer;
typedef container_t < CharT, typename value_deducer::format_object > format_object;
static format_object deduce( const T & ) { return( format_object( value_deducer::deduce( value_type())));
Does this expects the T::value_type is DefaultConstructible? IIRC, containers only require that objects be CopyConstructible and Assignable.
}
Still, I don't understand the need for integer type category. Can't you have 'deduce_sequence_type' 'deduce_pair_type' and so on, and use 'select' to map a type directly into those classes, not into integer?
"providing an extensible framework that sits"
How it's extensible? I see only one section about extending and it's just one paragraph long
I really need to add more documentation regarding this. The idea is that you can:
* add your own decorators (delimiters in the review docs) to support more complex types (e.g. trees and graphs).
That's interesting. Can you explain?
boost::io::formatobex< DelimeterType >( const T & ob );
This will format ob according to it's underlying type.
How is this different from using the 'formatob' function. What's "underlying type".
Underlying type is: typeid(ob).name().
Sorry, that's the name of type ;-) Maybe you just mean "according to its type?" (also note "its", not "it's"). The "underlying" implies there's some another type, besides 'T'.
This is more of a language problem. Range, by definition, is a pair (but not std::pair) of iterators. It's never a container.
What I meant is that:
std::cout << io::object( vec ); // [1] ok - know how to process containers std::cout << io::object( a, a + 5 ); // [2] oops! don't know how to handle ranges std::cout << io::object( io::range( a, a + 5 )); // [3] ok - range is now explicit
The reason I don't allow variant 2 is because that would mess up argument resolution.
Maybe, you can use something like: "The 'object' function can only output a container, so to output a range of iterators you need to make a container from it using io::range".
boost::io::openclose_formatter is a class that allows the user to change and access the format used for open and close delimeters
Here we're definitely in the reference docs already, while I did not get an overall picture yet. Then, what's "change and access the format". If the format can be changed, it is stored somewhere. Where?
It is stored inside the [openclose_]formatter object (wrapper_decorators and sequence_decorators in the new version). You can set the delimiters (decorators in the new version) using the various 'format' functions ('decorate') and get their values via open(), close() and separator() (used when implementing your own format objects).
The original sentence sounds like the format is something external to openclose_formatter, and the class is just a proxy which allows to set it.
The code block before this comment defines two classes openclose_formatter_t and openclose_formatter. Is this a typo, or you really have two classes?
This is not a typo. The _t variants take a template parameter that contains the return value of the 'format' ('decorate') functions. This is so that: std::cout << io::object( vec, fmt::container().decorate( " | " )); works properly.
Why don't 'decorate' return the same type as 'fmt::container()'? - Volodya