Query of interest for a container output library

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
11 messages Options
Reply | Threaded
Open this post in threaded view
|

Query of interest for a container output library

Tal Zion
Hello,
lately I've been working on a library that can output different kinds of containers to standard output streams.
The library is currently completely generic and can output any object that models the input range concept or Boost.Fusion's sequence concept.
It doesn't restrict itself to std containers. If an object looks like a sequence, then it is a sequence in the eyes of the library.

The library uses different open and close characters for different kinds of containers.
For example the output of the following:
std::cout << std::vector<int> {1,2,3} << '\n';
std::cout << std::set<int> {1,2,3} << '\n';
std::cout << std::map<int,int> { {1,2}, {2,3}, {3,1} } << '\n';
is:
[1,2,3]
{1,2,3}
{(1,2), (2,3), (3,1)}

In the example std::vector is interpreted as an ordered_dynamic container and std::set and std::map are interpreted as associative_dynamic containers by the library.
The library recursively outputs the contents of each container, so because std::map::value_type is a std::pair (which models Boost.Fusion's sequence concept) it can also output that. std::pair is interpreted as an ordered_fixed container by the library.
The library picks the appropriate category for each type being outputted by using Boost.MPL to introspect the type of the object being outputted.

In the future I would like to extend the library to offer facilities to help users implement a operator<< (std::ostream&, T) function for arbitrary types but I haven't really given that a lot of thought yet.

The library's name is currently Boost.Stringize but I don't really like that name and I'm really open for suggestions.

Overall I think this library would make a good addition to Boost as it simplifies things for the end user.
If a user wants to output a vector, he/she shouldn't have to iterate over it. It should be as simple as outputting an int.
In Python a user can just "print [1,2,3]". Why can't we have something similar in C++?

Is there any interest for this library in Boost?

Thanks,
Tal Zion
Reply | Threaded
Open this post in threaded view
|

Re: Query of interest for a container output library

Klaim - Joël Lamotte
On Wed, Jan 30, 2013 at 3:45 PM, meta221 <[hidden email]> wrote:

> Is there any interest for this library in Boost?


Yes! I have a lot of logging code that would be much simpler with such kind
of tool.

Joel Lamotte

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Query of interest for a container output library

Andrey Semashev-2
In reply to this post by Tal Zion
On Wed, Jan 30, 2013 at 6:45 PM, meta221 <[hidden email]> wrote:

> Hello,
> lately I've been working on a library that can output different kinds of
> containers to standard output streams.
> The library is currently completely generic and can output any object that
> models the input range concept or Boost.Fusion's sequence concept.
> It doesn't restrict itself to std containers. If an object looks like a
> sequence, then it is a sequence in the eyes of the library.
>
> The library uses different open and close characters for different kinds of
> containers.
> For example the output of the following:
> std::cout << std::vector<int> {1,2,3} << '\n';
> std::cout << std::set<int> {1,2,3} << '\n';
> std::cout << std::map<int,int> { {1,2}, {2,3}, {3,1} } << '\n';
> is:
> [1,2,3]
> {1,2,3}
> {(1,2), (2,3), (3,1)}
>
> In the example std::vector is interpreted as an ordered_dynamic container
> and std::set and std::map are interpreted as associative_dynamic containers
> by the library.
> The library recursively outputs the contents of each container, so because
> std::map::value_type is a std::pair (which models Boost.Fusion's sequence
> concept) it can also output that. std::pair is interpreted as an
> ordered_fixed container by the library.
> The library picks the appropriate category for each type being outputted by
> using Boost.MPL to introspect the type of the object being outputted.
>
> In the future I would like to extend the library to offer facilities to help
> users implement a operator<< (std::ostream&, T) function for arbitrary types
> but I haven't really given that a lot of thought yet.
>
> The library's name is currently Boost.Stringize but I don't really like that
> name and I'm really open for suggestions.
>
> Overall I think this library would make a good addition to Boost as it
> simplifies things for the end user.
> If a user wants to output a vector, he/she shouldn't have to iterate over
> it. It should be as simple as outputting an int.
> In Python a user can just "print [1,2,3]". Why can't we have something
> similar in C++?
>
> Is there any interest for this library in Boost?

Does the library define operator<< for the containers themselves? Does
it support arbitrary ranges (e.g. iterator_range<T>)?

It really worries me that in the examples you presented you create
containers rather than some manipulator that refers to the (external)
container or range. This implies that you don't have to copy the
container or elements in order to output them. I intend to provide
different manipulators, including the one for logging ranges, in
Boost.Log eventually but I haven't got to it yet.

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Query of interest for a container output library

Tal Zion
The library defines a templated operator<< that is enabled (by Boost.EnableIf) for all types that have a const_iterator typedef, or evaluate to true in boost::fusion::is_sequence<T> and are not already ostreamable.

boost::iterator_range already defines operator<< and so the library will "back down" and let iterator_range do its job. The library tries not to interfere with other objects. If another object says it can be outputted to an ostream, the library "believes" it.
By using this philosophy, the library prevents any ambiguity in operator<<.

But to answer your question, the library is completely generic and it does support arbitrary ranges.  

Here are some code snippets that show how the library captures a range:

template <typename T>
struct is_ostreamable : has_left_shift<std::ostream&, T const&> {};

struct sequence {
    BOOST_MPL_HAS_XXX_TRAIT_NAMED_DEF(has_const_iterator, const_iterator, false)

    /**
     * Determines if a type is a sequence.
     */
    template <typename I, typename T=typename remove_reference<I>::type >
    struct is : mpl::or_<
        has_const_iterator<T>,
        fusion::traits::is_sequence<T>
    > {};
};

/**** IN GLOBAL NAMESPACE ****/
template <typename T>
typename boost::enable_if<
    boost::mpl::and_<
        boost::stringize::detail::sequence::is<T>,
        boost::mpl::not_< // ignore everything that is ostreamable already
            boost::stringize::detail::is_ostreamable<T>
        >
    >,
    std::ostream&
>::type
operator<< (std::ostream& out, T const& t) {
    return boost::stringize::detail::print(out, t);
}

This code may change in the future to check if T::value_type is also ostreamable.
Reply | Threaded
Open this post in threaded view
|

Re: Query of interest for a container output library

Andrey Semashev-2
On Wed, Jan 30, 2013 at 7:27 PM, Tal Zion <[hidden email]> wrote:

> The library defines a templated operator<< that is enabled (by
> Boost.EnableIf) for all types that have a const_iterator typedef, or
> evaluate to true in boost::fusion::is_sequence<T> and are not already
> ostreamable.
>
> boost::iterator_range already defines operator<< and so the library will
> "back down" and let iterator_range do its job. The library tries not to
> interfere with other objects. If another object says it can be outputted to
> an ostream, the library "believes" it.
> By using this philosophy, the library prevents any ambiguity in operator<<.
>
> But to answer your question, the library is completely generic and it does
> support arbitrary ranges.
>
> Here are some code snippets that show how the library captures a range:

[snip]

I'm not familiar with has_left_shift behavior but from the look of it
it seems that it checks for availability of operator<< with a
particular signature. If so, the is_ostreamable check is unreliable
because there may be a suitable operator<< to be found by ADL or
otherwise which does not match the signature.

Anyway, I feel very uncomfortable about defining such blanket
operators because it can interfere with user's code and intentions. I
would prefer the manipulator-based approach.

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Query of interest for a container output library

Klaim - Joël Lamotte
I think a solution similar to to_string() overloads would be better and
would let the user define it's << operator easily if he wants.
For example

std::cout << to_string(std::vector<int> {1,2,3}) << '\n';
std::cout << to_string(std::set<int> {1,2,3}) << '\n';
std::cout << to_string(std::map<int,int> { {1,2}, {2,3}, {3,1} }) << '\n';
is:
[1,2,3]
{1,2,3}
{(1,2), (2,3), (3,1)}

It could be another function name, but I agree that adding << overloads
might not be the best way to do it.
It makes things more explicit.

Joel Lamotte

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Query of interest for a container output library

Jeff Flinn-2
In reply to this post by Tal Zion
On 1/30/2013 9:45 AM, meta221 wrote:
> Hello,
> lately I've been working on a library that can output different kinds of
> containers to standard output streams.

...


Have you investigated the Container Printing Lib from boostcon 2007?

IIRC, this was modified over a few years and was submitted for review,
but was not accepted. Some comments being: If it can output what about
input? and Isn't this best done with boost::spirit karma for printing
and qi for input?

Jeff



_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Query of interest for a container output library

Robert Ramey
Jeff Flinn wrote:

> On 1/30/2013 9:45 AM, meta221 wrote:
>> Hello,
>> lately I've been working on a library that can output different
>> kinds of containers to standard output streams.
>
> ...
>
>
> Have you investigated the Container Printing Lib from boostcon 2007?
>
> IIRC, this was modified over a few years and was submitted for review,
> but was not accepted. Some comments being: If it can output what about
> input? and Isn't this best done with boost::spirit karma for printing
> and qi for input?
>
> Jeff

It's not clear to me how this differs from the log example in the
serialization library

Robert Ramey




_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|

Re: Query of interest for a container output library

Tal Zion
In reply to this post by Jeff Flinn-2
> Have you investigated the Container Printing Lib from boostcon 2007?

I looked into Boost.Explore after I started working on this project.
My thoughts were that I could improve it by writing a more generic library which works with all types of containers.
The fact that it worked with only specific containers was odd to me.
It seemed to me that Boost.Explore was not under active development, so I wrote my own solution, mainly because I didn't know who to contact.
I really liked the name Boost.Explore.

> If it can output what about input? and Isn't this best done with boost::spirit karma for printing
and qi for input?

This library so far has been about simplicity for end users.
I think that in this case simplicity is a big factor because streaming containers should be easy. What makes a library great is the ability to be used simply in simple situations and still offer powerful tools for more elaborate problems.
Boost.Spirit is a very powerful and complex tool which might not always fit the need for simple container output.
When I first started learning Boost.Spirit it took me a few good days to understand what was going on. DSELs (domain specific embedded languages) were very new and confusing to me.
Also Boost.Spirit's complexity imposes dramatically increased compilation times.

In my opinion for simple problems there should be simple solutions.
Boost.Spirit is a great tool, but seems to me like an overkill for simple output of containers.
It might be a good idea to integrate Boost.Spirit.Karma generators to the library for more complex needs.

Input doesn't seem to me in the scope of this library, but my mind isn't completely made up about the subject.

Tal Zion
Reply | Threaded
Open this post in threaded view
|

Re: Query of interest for a container output library

Tal Zion
In reply to this post by Klaim - Joël Lamotte
You might be right.
I too thought overloading operator<< might be problematic but I haven't encountered any problems yet.
I think that
std::cout << std::vector<int> {1,2,3} << '\n';
is simpler than the alternative, but I see why people would be alarmed.
I think simplicity is worth the extra work and I will look into the subject.
For now I added a str method under boost::stringize and I put the global operator<< under #ifdef BOOST_STRINGIZE_GLOBAL_LEFT_SHIFT_OPERATOR.

Ideas regarding how to define a safe global operator<< would be much appreciated.
As has been said, currently I use Boost.EnableIf to enable/disable the overloads.
I use Boost.TypeTraits and Boost.MPL to introspect the type of the object and disable if has_left_shift is already defined and enable only for types that have a const_iterator typedef or are fusion sequences.
Currently the overloads are in the global namespace.

P.S.
Regarding the str method, for efficiency's sake it returns a detail::feeder object which simply holds a reference to the input and defines operator<< and operator std::string() as well as some other operators.
Returning a string from boost::stringize::str would be inefficient if the user wants to just stream the object.

Tal Zion
Reply | Threaded
Open this post in threaded view
|

Re: Query of interest for a container output library

pabristow
> -----Original Message-----
> From: Boost [mailto:[hidden email]] On Behalf Of Tal Zion
> Sent: Thursday, January 31, 2013 4:26 AM
> To: [hidden email]
> Subject: Re: [boost] Query of interest for a container output library
> --
> View this message in context:
http://boost.2283326.n4.nabble.com/Query-of-interest-for-a-container-
> output-library-tp4641997p4642051.html

I don't want to muddy this conversation, but I wonder if you are ignoring Steven Watanabe's
type_erasure library  (perhaps because it's highly un- or anti-descriptive name).

http://www.boost.org/doc/libs/1_52_0/doc/html/container/containers_of_incomplete_types.html

It provides what many users want to list containers without much tiresome typing or any troublesome
'typing'.

But (at the price of some compile time) it *also provides very easy (and quite fancy) formatting*.

Steven's simple example is:
int test[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

separator_printer p1(",  "); // Construct a sequence printer with comma separator.
p1.print(std::cout, test); // Outputs: 1,2,3,4,5,6,7,8,9,10

where test could be any ostreamable container.

but with a little more work one can produce other 'printers' which give a lot of layout control
using parameters, for example:

decor_printer p1decor("int[10] = {", ",  ", "};\n"); // Construct a sequence printer with prefix {,
comma separator and } newline terminator.

A few typical (and more fancy) examples of output are:

1,  2,  3,  4,  5,  6,  7,  8,  9,  10
  1,  2,  3,  4,  5,  6,  7,  8,  9,  10|
  int[10] = {1,  2,  3,  4,  5,  6,  7,  8,  9,  10};
  1___2___3___4___5___6___7___8___9___10
  1,  2,  3,  4,  5,  6,  7,  8,  9,  10
 
  +1.0000,  +2.0000,  +3.0000,  +4.0000,  +5.0000,  +6.0000,  +7.0000,  +8.0000,  +9.0000,  +10.000
 
  2345.6 m,  123.40 m,  0.012300 m
  45210.(+/-1234.0) m,  789.00(+/-2.5000) m,  0.00056700(+/-2.3400e-005) m
  one,  two,  three,  four,  five,  six,  seven,  eight,  nine,  ten
  ONE,  TWO,  THREE,  FOUR,  FIVE,  SIX,  SEVEN,  EIGHT,  NINE,  TEN
  1,2,3,4,
  5,6,7,8,
  9,10
                 1               4               7              10
                 2               5               8
                 3               6               9
 
  1.0000,  2.0000,  3.0000,  4.0000,  5.0000,  6.0000,  7.0000,  8.0000,  9.0000,  10.000
  1.0000,2.0000,3.0000,4.0000,
  5.0000,6.0000,7.0000,8.0000,
  9.0000,10.000
  3.0000,4.0000,5.0000,6.0000,
  7.0000
  0, -2.0000
  1, 9.9000
  1, 9.9000
  4, 6.6000
  1, 9.9000
  2, 8.8000
  3, 7.7000
  4, 6.6000
 
  1, 9.9000,2, 8.8000,3, 7.7000,4, 6.6000
 
  1.23 +/-0.056 (42)
  1.23 +/-0.056 (42),4.56 +/-0.021 (99)
  1.23 +/-0.056 (42) First #1, 2012-Aug-10 10:03:35
  4.56 +/-0.021 (99) Second #1, 2012-Aug-08 10:01:56
  1.23 +/-0.056 (42) First #1, 2012-Aug-10 10:03:35,4.56 +/-0.021 (99) Second #1, 2012-Aug-08
10:01:56

Some are probably messed up by the browser but you will get the flavour.

Paul





_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost