RE: [boost] OutFrmt lib design questions

Here some question I've got while reading submitted library.
1. What is an advantage if using this library for scalar types (vs. defining operator<<)
Do you mean for things like int, Person, etc. If that is the case, the default behaviour of the library is to delegate output of these types to operator<<, so it doesn't do much. However, with support for a fmt::serialize formatter that calls 'serialize' instead of <<, you can register a particular class as being serializable so my type deduction system knows to associate instances of that type with fmt::serialize, so you can then do:
// new design std::cout << io::object( my_serializable_object ) << '\n'; std::cout << io::object( vec_of_serializable_objects ) << '\n'; // [ ..., ..., ... ]
I don't really interested in another serialization library. We already have one.
The advantage of using this for things like std::complex or boost::math::quaternion that already supply << and >> operators is if you want to have control over how a type is rendered, e.g.:
// new design std::cout << io::object( complex_val ).decorate( "", "", " + i" ) << " = " << io::object( complex_val ).decorate( "( ", " )" ) << '\n';
And you believe that I will keep repeating this code every time I need to print my complex value?
2. If primary target is collection formatting wouldn't it be better to name library "collection formats something"?
This is a good idea. The name is from when the library just had output support, thus the "outfmt" directory in the sandbox. I am thinking about moving to a "formatter" directory. What do other people think?
3. If there is a way to assign format to collection/type combination permanently (through global operator <<), why would I want to do it in every instance of output operation?
Do you mean: std::vector< int > vec; std::cout << "vector = " << vec << '\n'; // [1] std::cout << "vector = " << io::formatob( vec ) << '\n'; // [2]
using [1] vs using [2]? If so, these are the same, but the second allows you to customize the decoration around the sequence, for example: std::cout << io::formatob( vec ).format( " : " ); // [ 1 : 2 : 3 : 4 ]
No my question was Is there a way to assign custom (meaning different from one you selected) decoration permanently.
4. What is an advantage of using this library to assign format to collection/type combination permanently vs. explicit implementation (using FOREACH construct for example)
Using a foreach construct you would need to hand-code the surrounding decoration, e.g.:
// output: { a + b + c } std::cout << "{ "; foreach( char ch : vec ) { std::cout << ch; if( !atend ) std::cout << " + "; } std::cout << " }";
vs:
std::cout << io::formatob( vec ).format( "{ ", " }", " + " ); // output: { a + b + c }
If you have a nested construct, such as int tictactoe[ 3 ][ 3 ], std::list< std::list< float > > or math::matrix4x4< float > then outputting/inputting it manually would be more complex, whereas my library has all the machinery to handle this simply.
Yeah. But I do it only once. And using foreach is way more flexible. I don't believe your library bring any advantage in this scenario.
5. Why do we need boost::io::range? Couldn't we use boost::range instead?
I suppose it would be possible to use boost::range::make_iterator_range instead. The only thing I have against this is its length. Consider:
// new design using boost::range::make_iterator_range; namespace range = boost::range; namespace io = boost::io;
std::cout << io::object( range::make_iterator_range( i, i + 7 )); // [1] std::cout << io::object( make_iterator_range( i, i + 7 )); // [2] std::cout << io::object( io::range( i, i + 7 )); // [3]
If only you could alias functions, e.g.: namespace io{ alias range = boost::range::make_iterator_range; }
IMO you shouldn't introduce yet another range notion.
6. In a formatter usage how would I guess what is what? boost::io::formatter< char * > fmt( "\ ", " /", " | " ); boost::io::formatob( vec, boost::io::containerfmt()).format( fmt );
I have revised the names, so that is now: namespace fmt = boost::io::format;
io::sequence_decorators< char > seq( "\\ ", " /", " | " ); io::object( vec, fmt::container()).decorate( seq );
IMO it's really bad idea to introduce interfaces that change meaning depend on number of arguments of the same type. Moreover it easy to confuse one with another. Your interface: ( str1, str2, str3 ) - open, close, separator ( str1, str2 ) - open, close ( str1 ) - separator Now If I do not use your library on everyday basis, And look on invocation of first function - how would I now what is what. There should be something that tell explicitly - here is open/close brackets, here is separator.
7. Who come format calls order dependent? boost::io::formatob( vec, boost::io::containerfmt()).format( fmt ).format( " : " );
format( fmt ) is called first, then format( " : " ). Thus, you will have "\ a : b : c /" as the formatting.
You did not answer the question. I believe that interface is incorrect if it order dependent
9. Let say I print vector<char>. Is there way to print it like this: { 'a', 'b', 0x01, ' ' }
Actually I did not want '' around hex value - intent was to print array in c++ format.
Yes. You need to provide a wrappedfmt() format object. For example:
formatob( vec, containerfmt( wrappedfmt().format( "'", "'" ))).format( "{ ", " }" ); // output: { 'a', 'b', 'c' }
So this wouldn't wark for me since I do not want '' around some of the values.
With the 0x01 character, special treatment needs to be applied. You will need to supply a custom format object to see if the character is printable, e.g.:
[your 20 lines solution skipped] IMO In this scenario your library doesn't stand a comparison with handcrafted solution.
Regards, Reece
Gennadiy.

Rozental, Gennadiy wrote:
Here some question I've got while reading submitted library.
1. What is an advantage if using this library for scalar types (vs. defining operator<<)
Do you mean for things like int, Person, etc. If that is the case, the default behaviour of the library is to delegate output of these types to operator<<, so it doesn't do much. However, with support for a fmt::serialize formatter that calls 'serialize' instead of <<, you can register a particular class as being serializable so my type deduction system knows to associate instances of that type with fmt::serialize, so you can then do:
// new design std::cout << io::object( my_serializable_object ) << '\n'; std::cout << io::object( vec_of_serializable_objects ) << '\n'; // [ ..., ..., ... ]
I don't really interested in another serialization library. We already have one.
Reece was answering a request from Vladimir Prus - who wanted the outfmt library to use the "serialize" function if it exists, or a default (operator<<) otherwise. Best, John -- John Torjo -- john@torjo.com Contributing editor, C/C++ Users Journal -- "Win32 GUI Generics" -- generics & GUI do mix, after all -- http://www.torjo.com/win32gui/ -- v1.4 - save_dlg - true binding of your data to UI controls! + easily add validation rules (win32gui/examples/smart_dlg)

Rozental, Gennadiy wrote:
Here some question I've got while reading submitted library.
1. What is an advantage if using this library for scalar types (vs. defining operator<<)
Do you mean for things like int, Person, etc. If that is the case, the default behaviour of the library is to delegate output of these types to operator<<, so it doesn't do much. However, with support for a fmt::serialize formatter that calls 'serialize' instead of <<, you can register a particular class as being serializable so my type deduction system knows to associate instances of that type with fmt::serialize, so you can then do:
// new design std::cout << io::object( my_serializable_object ) << '\n'; std::cout << io::object( vec_of_serializable_objects ) << '\n'; // [ ..., ..., ... ]
I don't really interested in another serialization library. We already have one.
The advantage of using this for things like std::complex or boost::math::quaternion that already supply << and >> operators is if you want to have control over how a type is rendered, e.g.:
// new design std::cout << io::object( complex_val ).decorate( "", "", " + i" ) << " = " << io::object( complex_val ).decorate( "( ", " )" ) << '\n';
And you believe that I will keep repeating this code every time I need to print my complex value?
2. If primary target is collection formatting wouldn't it be
better to
name library "collection formats something"?
This is a good idea. The name is from when the library just had output support, thus the "outfmt" directory in the sandbox. I am thinking about moving to a "formatter" directory. What do other people think?
3. If there is a way to assign format to collection/type combination permanently (through global operator <<), why would I want
to do it in
every instance of output operation?
Do you mean: std::vector< int > vec; std::cout << "vector = " << vec << '\n'; // [1] std::cout << "vector = " << io::formatob( vec ) << '\n'; // [2]
using [1] vs using [2]? If so, these are the same, but the second allows you to customize the decoration around the sequence, for example: std::cout << io::formatob( vec ).format( " : " ); // [ 1 : 2 : 3 : 4 ]
No my question was Is there a way to assign custom (meaning different from one you selected) decoration permanently.
4. What is an advantage of using this library to assign format to collection/type combination permanently vs. explicit implementation (using FOREACH construct for example)
Using a foreach construct you would need to hand-code the surrounding decoration, e.g.:
// output: { a + b + c } std::cout << "{ "; foreach( char ch : vec ) { std::cout << ch; if( !atend ) std::cout << " + "; } std::cout << " }";
vs:
std::cout << io::formatob( vec ).format( "{ ", " }", " + "
); // output:
{ a + b + c }
If you have a nested construct, such as int tictactoe[ 3 ][ 3 ], std::list< std::list< float > > or math::matrix4x4< float > then outputting/inputting it manually would be more complex, whereas my library has all the machinery to handle this simply.
Yeah. But I do it only once. And using foreach is way more flexible. I don't believe your library bring any advantage in this scenario.
5. Why do we need boost::io::range? Couldn't we use boost::range instead?
I suppose it would be possible to use boost::range::make_iterator_range instead. The only thing I have against this is its length. Consider:
// new design using boost::range::make_iterator_range; namespace range = boost::range; namespace io = boost::io;
std::cout << io::object( range::make_iterator_range( i, i + 7 )); // [1] std::cout << io::object( make_iterator_range( i, i + 7 )); // [2] std::cout << io::object( io::range( i, i + 7 )); // [3]
If only you could alias functions, e.g.: namespace io{ alias range = boost::range::make_iterator_range; }
IMO you shouldn't introduce yet another range notion.
6. In a formatter usage how would I guess what is what?
boost::io::formatter< char * > fmt( "\ ", " /", " | " ); boost::io::formatob( vec,
boost::io::containerfmt()).format( fmt );
I have revised the names, so that is now: namespace fmt = boost::io::format;
io::sequence_decorators< char > seq( "\\ ", " /", " | " ); io::object( vec, fmt::container()).decorate( seq );
IMO it's really bad idea to introduce interfaces that change meaning depend on number of arguments of the same type. Moreover it easy to confuse one with another.
Your interface: ( str1, str2, str3 ) - open, close, separator ( str1, str2 ) - open, close ( str1 ) - separator
Now If I do not use your library on everyday basis, And look on invocation of first function - how would I now what is what.
Yup - I definitely tend to agree with you here (see the "outputformatters - using just one decorator string"). Best, John -- John Torjo -- john@torjo.com Contributing editor, C/C++ Users Journal -- "Win32 GUI Generics" -- generics & GUI do mix, after all -- http://www.torjo.com/win32gui/ -- v1.4 - save_dlg - true binding of your data to UI controls! + easily add validation rules (win32gui/examples/smart_dlg)
participants (2)
-
John Torjo
-
Rozental, Gennadiy