Re: [boost] [Output Formatter]: Cascading Style Sheets (CSS) proposal

Vladimir Prus wrote:
Hi Reece,
io::sequence_decorators< char > seq( "; " ); std::cout << '"' << seq.open( std::cout ) << "\"\n" // "[ " << '"' << seq.close( std::cout ) << "\"\n" // " ]" << '"' << seq.separator( std::cout ) << "\"\n"; // "; "
where separator( std::cout ) uses the separator value specified by seq and the others use the values set on the std::cout stream.
I don't understand this phrase, could you clarify. What does it mean to "use the separator".
"use the separator value": the io::sequence_decorators< char > (io::formatter in the review implementation of the library) type holds 3 io::decoration< char > properties -- open; close; and separator. These are used when performing input/output using my library to render a given type. In the example above, the seq object provides a custom value for the separator decoration. The io::decoration< char >::operator()( StreamType & ) method will look up the default value of the decoration using the stream passed to it unless a custom value is provided. Thus, when accessing the separator value using this method, the custom one is chosen instead of the default one.
To change these defaults, you can use:
std::cout << io::cdecorate( "[{ ", " }]" ); // [{ a, b, c }]
The problem with this is that it changes the defaults for all the formatting used, thus you cannot have different defaults for sequences and n-ary types.
I don't undestand this, either. Probably I've missed something in earlier messages. What's "n-ary" type?
An n-ary type is a type that has a fixed number of elements. std::pair is a 2-ary type, boost::math::quaternion is a 4-ary type, and so on.
Do you provide the ability to use different separators for sequences, sets and maps?
In the review version of the library, there are two sets of default values: one for n-ary types formatting as "( a, b )" and one for the other sequence types, formatting as "[ a, b, c ]". In the version I am rewriting, I only have one set of default formatting: "[ a, b, c ]".
Then I don't understand why you can set different default style for those kinds of object, and cannot selectively override the style
Because the way I am storing it on the stream is based on decoration type (open; close; separator) and not on the format object type.
== Dynamic Styles
You can also set CSS properties within script code, e.g. tag.style.fontFamily = "Times New Roman";
This style behaviour is available in the new decorators, for example: io::wrapper_decorators< char > wrap; wrap.open = "<< "; wrap.close = " >>";
What does this decorator "wrap"?
A wrapper_decorator is one that only has open and close decoration values. Thus, it wraps the object associated with it (via a format object): template< class OutStream, typename T > OutStream & write( OutStream & os, const T & elem ) { return( os << open << elem << close ); } The sequence_decorators< char > type extends the wrapper_decorator< char > type, adding the separator property.
== Cascading Style Sheets
HTML provides a <style> tag to specify styling across the document without linking to an external file, e.g.: <style>h1{ color: orange; font-size: 2pc; }</style>
The equivalent within my library would be to define the styling on a stream, e.g.:
std::cout << css::element( "pair" ) [ ( css::attribute( "open" ) = "<< " ) + ( css::attribute( "close" ) = " >>" ) ] << css::element( "container" ) [ css::attribute( "separator" ) = " | " ] << io::object( ob ) // output: [ << a, 5 >> | << b, -1 >> ] << css::element( "pair" ) [ css::attribute( "separator" ) = " = " ] << io::object( ob ); // output: [ << a = 5 >> | << b = -1 >> ]
I'm sorry, but I'm worried of the direction you took. For me, the primary utility of this library is debugging output. For that, I'd be happy with a single style, as long as it's something usable, like YAML (http://www.yaml.org/).
I haven't implemented CSS functionality yet. It is my thoughts based on the comments by Jonathan.
I agree that you might want to store and then restore STL container from a file, and the library addresses this need to. But I don't understand why you would want to customize the formatting style. Basically, you either should the data to user, or not. In first case, you need a single usable style. For the second case, you might need another "compact" style. I don't see the point of customization.
It is currently possible to state what style formatting an object takes using the decorate function (format in the review implementation). Thus: // review implementation: std::cout << io::formatob( vec ).format( "( ", " )" ); // ( a, b, c, d ) Here, the separator is not set and thus takes the default value (the one provided by one of the mechanisms outlined above). You might want to customize formatting for various reasons. One example is if you have: std::list< std::string > names; and want to write that out as a HTML ordered list. You can do the following: std::cout << io::formatob( names ).format( "<ol><li>", "</li></ol>", "</li><li>" ); The examples provided in the library show some advanced formatting that can be achieved.
Finally, is this library going to be used for producing generic text files? E.g. code generation. I think no, to generate text you need some text template system, and that's way beyond the scope of the library.
It is possible to do this to a limited degree, for example, outputting 4, 7, 10, -5 27, 11, 5, 17 ... so the data can be read in by a spreadsheet, for example. It is also possible to provide a basic form of message localization, reading a std::map< std::string, std::string > in from a file. E.g.: english.loc "goodnight" = "Good night. Sweet dreams." "..." = "..." german.loc "goodnight" = "Gute nacht. Sussen Schlafen." "..." = "..." spanish.loc "goodnight" = "Buenos noches." "..." = "..." portuguese.loc "goodnight" = "Boa noite." "..." = "..." int main( ... ) { std::ifstream loc( argv[ 1 ]); std::map< std::string, std::string > messages; loc >> io::formatob( messages, io::containerfmt( io::pairfmt ( io::containerfmt().format( "\"", "\"", "" ), // currently unsupported io::containerfmt().format( "\"", "\"", "" ) // currently unsupported ).format( " = " )) ).format( "", "\n[end]", "\n" ); std::cout << "goodnight = " << messages[ "goodnight" ] << '\n'; }
So, I'm not convinced custom style gives you anything. On the other hand, it will cost a lot. At the very least, even documenting the above will be considerable effort. And given that the library is header-only, this features will cost everyone extra compile time. Even is 'css' stuff is in separate header, you'd need the hooks for customization.
Custom styling is currently implemented and is relatively cheap. The CSS design proposal is - I agree - complex in nature and would give a performance penalty. I was discussing a possible implementation to Jonathan's proposal. Regards, Reece _________________________________________________________________ Want to block unwanted pop-ups? Download the free MSN Toolbar now! http://toolbar.msn.co.uk/

Hi Reece,
To change these defaults, you can use:
std::cout << io::cdecorate( "[{ ", " }]" ); // [{ a, b, c }]
The problem with this is that it changes the defaults for all the formatting used, thus you cannot have different defaults for sequences and n-ary types.
I don't undestand this, either. Probably I've missed something in earlier messages. What's "n-ary" type?
An n-ary type is a type that has a fixed number of elements. std::pair is a 2-ary type, boost::math::quaternion is a 4-ary type, and so on.
Ok.
Do you provide the ability to use different separators for sequences, sets and maps?
In the review version of the library, there are two sets of default values: one for n-ary types formatting as "( a, b )" and one for the other sequence types, formatting as "[ a, b, c ]". In the version I am rewriting, I only have one set of default formatting: "[ a, b, c ]".
That's bad, I'd rather prefer different formatting styles for sets and vectors.
Then I don't understand why you can set different default style for those kinds of object, and cannot selectively override the style
Because the way I am storing it on the stream is based on decoration type (open; close; separator) and not on the format object type.
Why? Is there any specific difficulty with using format object type (or "kind") as key?
== Dynamic Styles
You can also set CSS properties within script code, e.g. tag.style.fontFamily = "Times New Roman";
This style behaviour is available in the new decorators, for example: io::wrapper_decorators< char > wrap; wrap.open = "<< "; wrap.close = " >>";
What does this decorator "wrap"?
A wrapper_decorator is one that only has open and close decoration values. Thus, it wraps the object associated with it (via a format object):
template< class OutStream, typename T > OutStream & write( OutStream & os, const T & elem ) { return( os << open << elem << close ); }
Is this a method of 'wrapper_decorators'?
I agree that you might want to store and then restore STL container from a file, and the library addresses this need to. But I don't understand why you would want to customize the formatting style. Basically, you either should the data to user, or not. In first case, you need a single usable style. For the second case, you might need another "compact" style. I don't see the point of customization.
It is currently possible to state what style formatting an object takes using the decorate function (format in the review implementation). Thus:
// review implementation: std::cout << io::formatob( vec ).format( "( ", " )" ); // ( a, b, c, d )
Here, the separator is not set and thus takes the default value (the one provided by one of the mechanisms outlined above).
Ok, aside from naming, the above is fine.
You might want to customize formatting for various reasons. One example is if you have: std::list< std::string > names; and want to write that out as a HTML ordered list. You can do the following:
std::cout << io::formatob( names ).format( "<ol><li>", "</li></ol>", "</li><li>" );
Is this a practical use case? Well, you can print 'names' this way, but if you want to print 'Person', you'll need much more powerfull tools. Anyway, if the overhead is small, no problem.
Finally, is this library going to be used for producing generic text files? E.g. code generation. I think no, to generate text you need some text template system, and that's way beyond the scope of the library.
It is possible to do this to a limited degree, for example, outputting 4, 7, 10, -5 27, 11, 5, 17 ...
so the data can be read in by a spreadsheet, for example.
Agreed, that's reasonable.
It is also possible to provide a basic form of message localization, reading a std::map< std::string, std::string > in from a file. E.g.:
english.loc "goodnight" = "Good night. Sweet dreams." "..." = "..." german.loc "goodnight" = "Gute nacht. Sussen Schlafen." "..." = "..." spanish.loc "goodnight" = "Buenos noches." "..." = "..." portuguese.loc "goodnight" = "Boa noite." "..." = "..."
int main( ... ) { std::ifstream loc( argv[ 1 ]); std::map< std::string, std::string > messages;
loc >> io::formatob( messages, io::containerfmt( io::pairfmt ( io::containerfmt().format( "\"", "\"", "" ), // currently unsupported io::containerfmt().format( "\"", "\"", "" ) // currently unsupported ).format( " = " )) ).format( "", "\n[end]", "\n" );
std::cout << "goodnight = " << messages[ "goodnight" ] << '\n'; }
That's more like an artificial example, I think. For practical uses, the message catalog would have to contain comments, at least, and that again requires much more complex parser.
So, I'm not convinced custom style gives you anything. On the other hand, it will cost a lot. At the very least, even documenting the above will be considerable effort. And given that the library is header-only, this features will cost everyone extra compile time. Even is 'css' stuff is in separate header, you'd need the hooks for customization.
Custom styling is currently implemented and is relatively cheap. The CSS design proposal is - I agree - complex in nature and would give a performance penalty. I was discussing a possible implementation to Jonathan's proposal.
I'll try to write a complete review soon, so stay tuned ;-) - Volodya
participants (2)
-
Reece Dunn
-
Vladimir Prus