none_t I/O operators

Hey everyone, I have recently run into a scenario where it was useful to have << and
I/O operators defined for none_t. I was using Boost Program Options to create a list of program options, and I wanted one of my options to be a help message. Since this option has no value except for the side effect of printing a help message and exiting, I gave it the type none_t, i.e.:
options.add_options() ( "help" ,opts::value<none_t>()->zero_tokens()->notifier(...function that prints help message...) ,"print help message and exit\n" ) When I did this, it complained that the >> operator was not defined for none_t, so I went ahead and defined my own I/O operators, which are essentially just no-ops: inline std::istream& operator>> (std::istream& in, boost::none_t& _) { return in; } inline std::ostream& operator<< (std::ostream& out, const boost::none_t& _) { return out; } It seems to me that these are the natural I/O operators to define for this type, and so it would be useful to have them be officially part of Boost. Although it is unlikely that a user would ever call them directly, their existence could prove useful in various circumstances where a generic I/O function is being applied to a none_t, such as the case I ran into/ If people were willing to accept a patch that adds these operators to Boost, then I would be willing to write such a patch. Obviously I would need to generalize the istream/ostream types to the full generic versions which include the template parameters for character traits, etc. I would also add forward declarations of istream and ostream just before these declarations so that the implementation would not need to have the <istream> and <ostream> headers as dependencies, though this would be tricky because I'd need to figure out how to detect if <istream> and/or <ostream> have not been included yet. Thoughts? Cheers, Greg

On 5/16/11 10:45 PM, Gregory Crosswhite wrote:
Hey everyone,
I/O operators defined for none_t. I was using Boost Program Options to create a list of program options, and I wanted one of my
I have recently run into a scenario where it was useful to have << and options to be a help message. Since this option has no value except for the side effect of printing a help message and exiting, I gave it the type none_t, i.e.:
options.add_options() ( "help" ,opts::value<none_t>()->zero_tokens()->notifier(...function that prints help message...) ,"print help message and exit\n" )
When I did this, it complained that the >> operator was not defined for none_t, so I went ahead and defined my own I/O operators, which are essentially just no-ops:
inline std::istream& operator>> (std::istream& in, boost::none_t& _) { return in; } inline std::ostream& operator<< (std::ostream& out, const boost::none_t& _) { return out; }
It seems to me that these are the natural I/O operators to define for this type, and so it would be useful to have them be officially part of Boost. Although it is unlikely that a user would ever call them directly, their existence could prove useful in various circumstances where a generic I/O function is being applied to a none_t, such as the case I ran into/
If people were willing to accept a patch that adds these operators to Boost, then I would be willing to write such a patch. Obviously I would need to generalize the istream/ostream types to the full generic versions which include the template parameters for character traits, etc. I would also add forward declarations of istream and ostream just before these declarations so that the implementation would not need to have the <istream> and <ostream> headers as dependencies, though this would be tricky because I'd need to figure out how to detect if <istream> and/or <ostream> have not been included yet.
Thoughts?
Cheers, Greg
YAY! By the three-week rule, since nobody has replied in the negative that means that my idea has therefore been approved to be part of Boost!!! :-D On a more serious note, though, is there a trick one can use to write I/O operators like the ones I defined above without necessarily pulling in <iostream>? I am of course aware of the existence of forward declarations, but unless my understanding of them is wrong (and hopefully it is :-) ) they cause compiler errors when the forward declared class has already been actually declared, so using forward declarations would break in the case that the user had already pulled in <iostream> before the forward declarations appeared. Or, on the other hand, is the relative overhead of pulling in <iostream> actually relatively small in the grand scheme of things so that this is something that a library writer should not worry about in general when thinking about supplying I/O operators for a supplied type? Thanks! Greg

On 6/5/11 1:29 PM, Gregory Crosswhite wrote:
On a more serious note, though, is there a trick one can use to write I/O operators like the ones I defined above without necessarily pulling in <iostream>? I am of course aware of the existence of forward declarations, but unless my understanding of them is wrong (and hopefully it is :-) ) they cause compiler errors when the forward declared class has already been actually declared, so using forward declarations would break in the case that the user had already pulled in <iostream> before the forward declarations appeared.
Doh! It turns out that my misunderstanding came from the fact that the forward declaration class ostream; is a special case where ostream is a *typedef* rather than a class. Replacing this with ==================== namespace std { template<typename _CharT> class char_traits; template<typename _CharT, typename _Traits> class basic_ostream; } ... std::basic_ostream<char,std::char_traits<char> > ... ... ==================== seems so far to work like a charm! Cheers, Greg

On Sun, Jun 5, 2011 at 6:16 PM, Gregory Crosswhite <gcross@phys.washington.edu> wrote:
On 6/5/11 1:29 PM, Gregory Crosswhite wrote:
On a more serious note, though, is there a trick one can use to write I/O operators like the ones I defined above without necessarily pulling in <iostream>?
If I'm not mistaken you can #include <iosfwd>. [snip]
Cheers, Greg
Regards, -- Felipe Magno de Almeida

On 05/06/11 21:29, Gregory Crosswhite wrote: <snip>
On a more serious note, though, is there a trick one can use to write I/O operators like the ones I defined above without necessarily pulling in <iostream>?
#include <iosfwd> On no account should you write your own forward declarations for standard library objects. It will cause you grief if and when you (or your users) try to e.g. use libc++, which declares them in a different namespace.
I am of course aware of the existence of forward declarations, but unless my understanding of them is wrong (and hopefully it is :-) ) they cause compiler errors when the forward declared class has already been actually declared, so using forward declarations would break in the case that the user had already pulled in <iostream> before the forward declarations appeared.
There is no such issue with forward declarations in general. There is an issue when they involve default arguments, which some of these ones will. This is however a consequence of the same argument being given more than one default value, and is independent of whether the declaration appears before the definition. However, <iostream> will certainly have included the same forward declarations provided by <iosfwd> anyway, so multiple-include guards will prevent the forward declarations appearing after <iostream> has been included. Therefore, #include <iosfwd> is safe.
Or, on the other hand, is the relative overhead of pulling in <iostream> actually relatively small in the grand scheme of things so that this is something that a library writer should not worry about in general when thinking about supplying I/O operators for a supplied type?
One should always be cautious of including <iostream>. Not only is it general good practice to include minimally and a habit that is wise to develop, but <iostream> has the unusual property that it has a (small) runtime overhead in the program initialization phase for each translation unit that includes it to ensure that all the global objects are properly initialized. John Bytheway

On 6/5/11 2:35 PM, John Bytheway wrote:
On 05/06/11 21:29, Gregory Crosswhite wrote: <snip>
On a more serious note, though, is there a trick one can use to write I/O operators like the ones I defined above without necessarily pulling in<iostream>? #include<iosfwd>
On no account should you write your own forward declarations for standard library objects. It will cause you grief if and when you (or your users) try to e.g. use libc++, which declares them in a different namespace.
Great, that is very good to know! :-)
I am of course aware of the existence of forward declarations, but unless my understanding of them is wrong (and hopefully it is :-) ) they cause compiler errors when the forward declared class has already been actually declared, so using forward declarations would break in the case that the user had already pulled in <iostream> before the forward declarations appeared. There is no such issue with forward declarations in general. There is an issue when they involve default arguments, which some of these ones will. This is however a consequence of the same argument being given more than one default value, and is independent of whether the declaration appears before the definition.
However,<iostream> will certainly have included the same forward declarations provided by<iosfwd> anyway, so multiple-include guards will prevent the forward declarations appearing after<iostream> has been included. Therefore, #include<iosfwd> is safe.
Excellent! So even though my question was inspired by a silly misunderstanding about forward declarations, I seem to have nonetheless gotten very useful advice out of it. :-)
Or, on the other hand, is the relative overhead of pulling in<iostream> actually relatively small in the grand scheme of things so that this is something that a library writer should not worry about in general when thinking about supplying I/O operators for a supplied type? One should always be cautious of including<iostream>. Not only is it general good practice to include minimally and a habit that is wise to develop, but<iostream> has the unusual property that it has a (small) runtime overhead in the program initialization phase for each translation unit that includes it to ensure that all the global objects are properly initialized.
Okay, great, that was actually my first instinct so I am glad to hear I was on the right track on at least one thing. :-) Thanks a lot! Greg
participants (3)
-
Felipe Magno de Almeida
-
Gregory Crosswhite
-
John Bytheway