|
|||
|
Hello,
I want to be able to stream custom classes to ostreams like this: MyClass foo; myStream << foo; AIUI, this requires an operator<<() with this signature: std: stream &operator<<( std: stream &str, MyClass const &printable );In order to avoid creating such a function for every class, I tried a template like this: template < class Printable > std: stream &operator<<( std: stream &stream,Printable const &printable ) { printable.print( stream ); return stream; } (All the classes in question have a public member function "void print( std: stream &stream )".)However, with this I get tons of errors like "'operator <<' is ambiguous". Is there a way to define this template function so that it is only instantiated for classes that implement this print() function? (I could make the name such that it is very unlikely that any other class has such a name.) Or is there another way to avoid having to define such a global operator<< for every class with ostream support? Thanks, Gerhard |
|
|
||||
|
||||
|
|
|
|||
|
On 6/19/2012 3:30 PM, Gerhard Fiedler wrote:
> I want to be able to stream custom classes to ostreams like this: > > MyClass foo; > myStream<< foo; > > AIUI, this requires an operator<<() with this signature: > > std: stream&operator<<( std: stream&str, MyClass const&printable );> > In order to avoid creating such a function for every class, I tried a > template like this: > > template< class Printable> > std: stream&operator<<( std: stream&stream,> Printable const&printable ) > { > printable.print( stream ); > return stream; > } > > (All the classes in question have a public member function "void print( > std: stream&stream )".)> > > However, with this I get tons of errors like "'operator<<' is > ambiguous". > > Is there a way to define this template function so that it is only > instantiated for classes that implement this print() function? (I could > make the name such that it is very unlikely that any other class has > such a name.) > > Or is there another way to avoid having to define such a global > operator<< for every class with ostream support? Have you considered using polymorphism at all, or is it out of fashion already? struct Printable { virtual void print(std: stream& os) const = 0;}; class GerhardsClass : public Printable { ... void print(std: stream& os) const; // whatever you have}; // no need for the template ostream& operator <<(ostream& os, Printable const& pr) { pr.print(os); return os; } V -- I do not respond to top-posted replies, please don't ask |
|
|||
|
Gerhard Fiedler wrote:
> Hello, > > I want to be able to stream custom classes to ostreams like this: > > MyClass foo; > myStream << foo; > > AIUI, this requires an operator<<() with this signature: > > std: stream &operator<<( std: stream &str, MyClass const &printable );> > In order to avoid creating such a function for every class, I tried a > template like this: > > template < class Printable > > std: stream &operator<<( std: stream &stream,> Printable const &printable ) > { > printable.print( stream ); > return stream; > } > > (All the classes in question have a public member function "void print( > std: stream &stream )".)Aren't you trying to avoid the need to define a operator<<() for each class with the requirement of not only defining a print() for each class but also wrap it in some template function? Rui Maciel |
|
|||
|
Rui Maciel wrote:
> Gerhard Fiedler wrote: > >> Hello, >> >> I want to be able to stream custom classes to ostreams like this: >> >> MyClass foo; >> myStream << foo; >> >> AIUI, this requires an operator<<() with this signature: >> >> std: stream &operator<<( std: stream &str, MyClass const &printable );>> >> In order to avoid creating such a function for every class, I tried a >> template like this: >> >> template < class Printable > >> std: stream &operator<<( std: stream &stream,>> Printable const &printable ) >> { >> printable.print( stream ); >> return stream; >> } >> >> (All the classes in question have a public member function "void print( >> std: stream &stream )".)> > Aren't you trying to avoid the need to define a operator<<() for each > class with the requirement of not only defining a print() for each > class but also wrap it in some template function? Sort of, yes ![]() These are the two basic approaches I see: 1) Define something like my .print() member function that does the actual streaming (that is, accessing private/protected members etc.) and additionally an operator<<() that is not a member of the class that then calls the .print() member function. (My question is about a variant of this approach, where the individual operator<<() functions are replaced by a single template.) 2) Define a free function operator<<() for each class that essentially does what my .print() function does. For this, it needs to be a friend of the class. Is there another approach? It seems to me that you're implying that approach 2 is simpler. I can see how it can be, sort of, from a given angle, but I don't like the friend declaration. This causes this implementation to affect one location more than approach 1 (no friend) and a single template operator<<(). Mostly a matter of taste, probably. Gerhard |
|
|||
|
On 19/06/2012 21:30, Gerhard Fiedler wrote:
> Is there a way to define this template function so that it is only > instantiated for classes that implement this print() function? (I could > make the name such that it is very unlikely that any other class has > such a name.) Yes, there is a way: #include <type_traits> template < class Printable, typename std::enable_if<std::is_member_function_pointer <decltype(&Printable: rint)>::value, int>::type= 0 >std: stream &operator<<( std: stream &stream,Printable const &printable ) { printable.print( stream ); return stream; } |
|
|||
|
Gerhard Fiedler wrote:z
> Sort of, yes ![]() > > These are the two basic approaches I see: > > 1) Define something like my .print() member function that does the > actual streaming (that is, accessing private/protected members etc.) and > additionally an operator<<() that is not a member of the class that then > calls the .print() member function. (My question is about a variant of > this approach, where the individual operator<<() functions are replaced > by a single template.) > > 2) Define a free function operator<<() for each class that essentially > does what my .print() function does. For this, it needs to be a friend > of the class. > > Is there another approach? > > It seems to me that you're implying that approach 2 is simpler. I can > see how it can be, sort of, from a given angle, but I don't like the > friend declaration. You only need to declare operator<<() as friend if your ouput routine requires access to non-public members. If all the data is available through public members then this requirement ceases to exist. Considering that you are willing to output that information without any restriction (i.e., output to std::cout) then it is a good indicator that you don't really want to hide it from everyone. Also, it is better to force components to access data through interfaces instead of granting access to raw data, as it gives you some leeway to refactor the entrails of your class without having to deal with a cascade of problems spreading through your project. Hence, as you are already willing to add members to your classes, nothing stops you from adding the relevant get() members that let you define an unfriendly operator<<(). Nonetheless, this means that you would still be adding at least a member function to each of your classes, and then defining a operator<<() based on them. That is, this solution would require you to add at least two member functions to each of your classes. With this in mind, it would be far simpler and straight-forward to just add the only method that is actually needed, a friendly operator<<(), and move on to other ventures. Rui Maciel |
|
|||
|
Luca Risolia wrote:
> On 19/06/2012 21:30, Gerhard Fiedler wrote: >> Is there a way to define this template function so that it is only >> instantiated for classes that implement this print() function? (I could >> make the name such that it is very unlikely that any other class has >> such a name.) > > Yes, there is a way: > > #include <type_traits> > > template < class Printable, > typename std::enable_if<std::is_member_function_pointer > <decltype(&Printable: rint)>::value, int>::type= 0 >> std: stream &operator<<( std: stream &stream,> Printable const &printable ) > { > printable.print( stream ); > return stream; > } Aha! I had the distinct impression that there was something along these lines, but this way of using templates is still not fluent for me. I guess this either works or it gets me going towards something that will work for my purposes. Thank you... the only post so far that actually answered my question! ![]() Gerhard |
|
|||
|
Gerhard Fiedler wrote:
> Luca Risolia wrote: > >> On 19/06/2012 21:30, Gerhard Fiedler wrote: >>> Is there a way to define this template function so that it is only >>> instantiated for classes that implement this print() function? (I could >>> make the name such that it is very unlikely that any other class has >>> such a name.) >> >> Yes, there is a way: >> >> #include <type_traits> >> >> template < class Printable, >> typename std::enable_if<std::is_member_function_pointer >> <decltype(&Printable: rint)>::value, int>::type= 0 >>> std: stream &operator<<( std: stream &stream,>> Printable const &printable ) >> { >> printable.print( stream ); >> return stream; >> } > > Aha! I had the distinct impression that there was something along these > lines, but this way of using templates is still not fluent for me. I > guess this either works or it gets me going towards something that will > work for my purposes. > > Thank you... the only post so far that actually answered my question! ![]() Ok... Now I had some time experimenting with this approach. Your proposal, as-is (used with the code I posted here before), gives me "error C4519: default template arguments are only allowed on a class template" (VC++ 10). Is this a limitation of VC++ or a problem with the code? I also tried a similar but different version, using the return type: template < class Printable > typename std::enable_if < std::is_member_function_pointer < decltype( &Printable: rint ) >::value, std: stream &>::type operator<<( std: stream &stream, Printable const &printable ){ printable.print( stream ); return stream; } This gave me "error C2039: 'type' : is not a member of 'std::tr1::enable_if<_Test,_Type>' 1> with 1> [ 1> _Test=false, 1> _Type=std: stream &1> ]" Can somebody please help me to massage the template definition to do what I want? Thanks, Gerhard |
|
|||
|
Gerhard Fiedler skrev 2012-06-20 14:51:
> Gerhard Fiedler wrote: > >> Luca Risolia wrote: >> >>> On 19/06/2012 21:30, Gerhard Fiedler wrote: >>>> Is there a way to define this template function so that it is only >>>> instantiated for classes that implement this print() function? (I could >>>> make the name such that it is very unlikely that any other class has >>>> such a name.) >>> >>> Yes, there is a way: >>> >>> #include <type_traits> >>> >>> template < class Printable, >>> typename std::enable_if<std::is_member_function_pointer >>> <decltype(&Printable: rint)>::value, int>::type= 0 >>>> std: stream &operator<<( std: stream &stream,>>> Printable const &printable ) >>> { >>> printable.print( stream ); >>> return stream; >>> } >> >> Aha! I had the distinct impression that there was something along these >> lines, but this way of using templates is still not fluent for me. I >> guess this either works or it gets me going towards something that will >> work for my purposes. >> >> Thank you... the only post so far that actually answered my question! ![]() > > Ok... Now I had some time experimenting with this approach. > > Your proposal, as-is (used with the code I posted here before), gives me > "error C4519: default template arguments are only allowed on a class > template" (VC++ 10). Is this a limitation of VC++ or a problem with the > code? The error message is correct for C++03. Default template arguments for functions has been added to C++11, and is supported by some compilers. VC10 is not aware of that. > > I also tried a similar but different version, using the return type: > > template < class Printable > > typename std::enable_if < > std::is_member_function_pointer < > decltype( &Printable: rint ) >::value> , std: stream &>> ::type > operator<<( std: stream &stream, Printable const &printable )> { > printable.print( stream ); > return stream; > } > > This gave me "error C2039: 'type' : is not a member of > 'std::tr1::enable_if<_Test,_Type>' > 1> with > 1> [ > 1> _Test=false, > 1> _Type=std: stream &> 1> ]" > > > Can somebody please help me to massage the template definition to do > what I want? > I believe this is similar to the above. C++11 has added more cases where SFINAE can be used. VC10 complains that the function in invalid for types without a print member. Bo Persson |
|
|||
|
Bo Persson wrote:
> Gerhard Fiedler skrev 2012-06-20 14:51: >> Gerhard Fiedler wrote: >> >>> Luca Risolia wrote: >>> >>>> On 19/06/2012 21:30, Gerhard Fiedler wrote: >>>>> Is there a way to define this template function so that it is only >>>>> instantiated for classes that implement this print() function? (I could >>>>> make the name such that it is very unlikely that any other class has >>>>> such a name.) >>>> >>>> Yes, there is a way: >>>> >>>> #include <type_traits> >>>> >>>> template < class Printable, >>>> typename std::enable_if<std::is_member_function_pointer >>>> <decltype(&Printable: rint)>::value, int>::type= 0 >>>>> std: stream &operator<<( std: stream &stream,>>>> Printable const &printable ) >>>> { >>>> printable.print( stream ); >>>> return stream; >>>> } >>> >>> Aha! I had the distinct impression that there was something along these >>> lines, but this way of using templates is still not fluent for me. I >>> guess this either works or it gets me going towards something that will >>> work for my purposes. >>> >>> Thank you... the only post so far that actually answered my question! ![]() >> >> Ok... Now I had some time experimenting with this approach. >> >> Your proposal, as-is (used with the code I posted here before), gives me >> "error C4519: default template arguments are only allowed on a class >> template" (VC++ 10). Is this a limitation of VC++ or a problem with the >> code? > > The error message is correct for C++03. Default template arguments for > functions has been added to C++11, and is supported by some compilers. > > VC10 is not aware of that. > >> >> I also tried a similar but different version, using the return type: >> >> template < class Printable > >> typename std::enable_if < >> std::is_member_function_pointer < >> decltype( &Printable: rint ) >::value>> , std: stream &>>> ::type >> operator<<( std: stream &stream, Printable const &printable )>> { >> printable.print( stream ); >> return stream; >> } >> >> This gave me "error C2039: 'type' : is not a member of >> 'std::tr1::enable_if<_Test,_Type>' >> 1> with >> 1> [ >> 1> _Test=false, >> 1> _Type=std: stream &>> 1> ]" >> >> >> Can somebody please help me to massage the template definition to do >> what I want? >> > > I believe this is similar to the above. C++11 has added more cases where > SFINAE can be used. VC10 complains that the function in invalid for > types without a print member. Thanks for the confirmation. Gerhard |
|
|||
|
Rui Maciel wrote:
> Gerhard Fiedler wrote: > >> Rui Maciel wrote: >> >>> Gerhard Fiedler wrote: >>> > [...] should be used as a basis to condemn and insult others. You stated repeatedly that I insulted you and/or others. I'm sorry if you feel that way; it was not my intention. Since you fail to provide specifics, I don't even know with what you feel I insulted you and/or others. >> In this case, I'm simply interested in the question whether I can >> restrict the template definition of a function to classes that >> implement a given member -- as stated in the OP. > > Yes, you can. You just need to implement a template strategy pattern > to wrap your print() methods and add compiler checks to check for > their existence. With C++11, you can implement them like Luca > Risolia already explained, but with previous versions you either > implement the relevant type traits or you need to rely on third-party > libraries, such as Boost's Boost.type_traits. Thanks. I'm looking at Boost's type_traits library. But so far I can't figure out how to use it to answer my first question (see the OP or the end of this post). > The people in this newsgroup are only aware of the reasons you > presented. Again, if you believe that you haven't provided enough > details for others to adequately understand your reasons then you > should try to do a better job presenting them, particularly when you > believe that others aren't seeing the full picture due to your > inability to show it. I thought that for the purposes of my question, the picture was enough. It was enough for Luca, wasn't it? The answers you're giving are for a question that I didn't ask, that's why there's information missing. >> It seems you forgot my original question. I don't want to employ >> wrapper function/s/, but one single wrapper function. This is one >> purpose of this exercise. > > Your template policy pattern is nothing more than a thin wrapper over your > classes' print() method. You yourself defined it as nothing more than: > > template < class Printable > > std: stream &operator<<( std: stream &stream, Printable const &printable )> { > printable.print( stream ); > return stream; > } > > I don't believe it is possible to write a thinner wrapper than this. It's the language and its common usage patterns with streaming that require this wrapper. I just want to make (a single) one that works, with the compilers I'm interested in (gcc 4.6, VC++ 10). This one doesn't work. >>> I pointed out the obvious alternative: define the necessary getter >>> functions, define your non-friend operator<<() so that it uses that >>> interface and you won't have to employ friendship declarations. >> >> This is the "standard" solution, and it is used often enough. I know >> it. In this particular case, this is possible but not that >> desirable. > > As you provided no justification or reasoning for this fixation and > yet it appears you don't to steer away from it, do you understand why > your decision has been described as irrational? You may or may not understand my reasons, you may or may not agree with them, but neither of this indicates necessarily irrationality on my end. This whole sub-thread with you seems largely irrational... (and it always takes two for such a conversation ![]() Anyway, this is for a trace/debug logging facility. It's used with hundreds of classes, potentially, by dozens of developers, potentially. It should be as simple as possible to stream relevant contents of a class to an ostream. Adding a single public member to a class without further modifying the class seems to be the minimum necessary, and if it's not a whole lot of effort, to me it's worth getting there. Now, the effort I'm employing here is more, for mainly two reasons. One is that I thought this was a good opportunity to get myself educated about enabling/disabling templates for certain conditions. I've seen code like this every now and then, and I can mostly figure out what it does, but I've never written conditional templates and there are some gaps I have to fill. The other is that I'm taking this conversation with you in areas that are way off my original objective, mostly for respect for you and your attempt to help me. >> This is why I'm trying to see whether I can solve this in the way I >> asked about. > > It looks like a case of "give me what I want, not what I asked for". Not quite. Let me cite the two question from my OP: "Is there a way to define this template function [the thin wrapper you cited above] so that it is only instantiated for classes that implement this print() function?" Apparently this was clear enough for Luca to "get it". To me, it seems a very clear question that doesn't need more context than what I gave. And: "Or is there another way to avoid having to define such a global operator<< for every class with ostream support?" Same here. None of your answers has even attempted to address either of these two questions. (In your last post, you answered "yes" to the first question -- after someone else already posted how to do it.) While I appreciate the effort of getting at the bigger picture, I think my questions were clear enough, and I provided enough information for whoever was willing to answer them (that is, them and not some other question) to do so. Gerhard |
|
|||
|
Luca Risolia wrote:
> On 19/06/2012 21:30, Gerhard Fiedler wrote: >> Is there a way to define this template function so that it is only >> instantiated for classes that implement this print() function? (I could >> make the name such that it is very unlikely that any other class has >> such a name.) > > Yes, there is a way: > > #include <type_traits> > > template < class Printable, > typename std::enable_if<std::is_member_function_pointer > <decltype(&Printable: rint)>::value, int>::type= 0 >> std: stream &operator<<( std: stream &stream,> Printable const &printable ) > { > printable.print( stream ); > return stream; > } Thanks again to Luca for posting this. Apparently, this is valid C++11, but not supported by VC++ 10. I'm looking for a solution that works with VC++ 10 and gcc 4.6. At this point, I'm trying to better understand how this works. This is what I currently think I understand: This template < class Printable, typename std::enable_if < std::is_member_function_pointer< decltype(&Printable: rint)>::value, int >::type = 0 > evaluates to the following for 'Printable' classes: template < class Printable, int > and to this for classes that aren't: template < class Printable, 0 > What I don't understand is why this prevents this template from matching non-Printable classes. The second question I have is how to use the Boost type_traits to implement this, without using a default template argument. Reading some of the library documentation, it seems that it is mostly based on template functions that evaluate to either true_type or false_type. In the examples they use these two types to defer to different implementations for different conditions, using simple signature matching (true_type and false_type are two distinct types). An example of this approach is <http://www.boost.org/doc/libs/1_46_1/libs/type_traits/doc/html/boost_typetraits/examples/iter.html>. However, I yet fail to see how this mechanism can be used to avoid that a template matches a given case; it only seems to work for switching between different implementations, not for avoiding a match. (Once the template is matched, simply not providing an implementation for one of the cases doesn't seem to work.) Part of my limitation here is also that I don't yet understand what prevents the match in Luca's solution. Can someone please show me how I can use the Boost type_traits to avoid a match on the original template (similar to what Luca's code does), or point me to an example or tutorial where this is show? Thanks, Gerhard |
|
|||
|
Victor Bazarov wrote:
> Well, you defined a very broad operator<<. It will match pretty much > any type that allows binding to a const ref. Considering that VC++ > doesn't let you have default template arguments for function > templates (based on your attempt to get Luca's solution to build), I > am not sure how you'd be able to narrow down the set of types to > which your templated op<< applies without using inheritance. Here is > a suggestion (it does require editing of your types): [...] Thanks. I keep this in mind, but it doesn't look much better than explicitly declaring a friendly global operator<<(). I'm still working on getting my original approach to work (please see my other post in this thread if you're interested). I'm not yet understanding well enough how I can limit the matching. Gerhard |
|
|||
|
On Thursday, June 21, 2012 7:59:23 AM UTC-5, Gerhard Fiedler wrote:
> I'm still working on getting my original approach to work (please see my > other post in this thread if you're interested). I'm not yet > understanding well enough how I can limit the matching. See http://stackoverflow.com/questions/2...ions-existence for a very good discussion of how to detect a member function. Applying the principles discussed there to your program: #include <iostream> #include <type_traits> template <typename T> class is_printable { typedef char yes[1]; typedef char no[2]; template <typename C> static yes& test( decltype(&C: rint) );template <typename C> static no& test(...); public: static const bool value = (sizeof(test<T>(0)) == sizeof(yes)); }; template < class Printable > typename std::enable_if<is_printable<Printable>::value,std: stream&>::typeoperator<<( std: stream &stream, Printable const &printable ){ printable.print( stream ); return stream; } class MyClass { public: MyClass( char const *val ) : str( val ) {} void print( std: stream &stream ) const { stream << str; }private: char const *str; }; int main(int argc, char* argv[]) { std::cout << MyClass( "bar" ) << std::endl; } If you have types in your codebase that you want to exclude - say they have a print member that serves some other purpose - you can do so with a specialization: template <> class is_printable<Foo> { public: static const bool value = false; }; |
|
|||
|
cartec69@gmail.com wrote:
> On Thursday, June 21, 2012 7:59:23 AM UTC-5, Gerhard Fiedler wrote: >> I'm still working on getting my original approach to work (please see >> my other post in this thread if you're interested). I'm not yet >> understanding well enough how I can limit the matching. > > See http://stackoverflow.com/questions/2...ions-existence > for a very good discussion of how to detect a member function. > Applying the principles discussed there to your program: > > #include <iostream> > #include <type_traits> > > template <typename T> > class is_printable > { > typedef char yes[1]; > typedef char no[2]; > > template <typename C> static yes& test( decltype(&C: rint) );> template <typename C> static no& test(...); > > public: > static const bool value = (sizeof(test<T>(0)) == sizeof(yes)); > }; > > template < class Printable > > typename std::enable_if<is_printable<Printable>::value,std: stream&>::type> operator<<( std: stream &stream, Printable const &printable )> { > printable.print( stream ); > return stream; > } > > class MyClass > { > public: > MyClass( char const *val ) : str( val ) {} > void print( std: stream &stream ) const { stream << str; }> private: > char const *str; > }; > > int main(int argc, char* argv[]) > { > std::cout << MyClass( "bar" ) << std::endl; > } > > If you have types in your codebase that you want to exclude - say they > have a print member that serves some other purpose - you can do so > with a specialization: > > template <> > class is_printable<Foo> > { public: static const bool value = false; }; Thanks! At first I thought this works as I want it, but then I ran into this snag. Google Test defines a function like this: template <typename T> void GTestStreamToHelper(std: stream* os, const T& val) {*os << val; } I invoke it like this (for testing resolution): int main(int argc, char* argv[]) { MyClass foo( "bar" ); std::cout << "test " << foo << std::endl; std::string const gtest( "gtest" ); GTestStreamToHelper( &std::cout, gtest ); std::cout << std::endl; } For some reason, my operator<<() gets chosen for the '<<' inside GTestStreamToHelper, then it complains that std::string doesn't have a print() method. Shouldn't it use a different operator<<() definition? Thanks, Gerhard |
|
|
![]() |
| Thread Tools | |
| Display Modes | |
|
|