Generic ostream operator<<?

G

Gerhard Fiedler

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::eek:stream &operator<<( std::eek: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::eek:stream &operator<<( std::eek:stream &stream,
Printable const &printable )
{
printable.print( stream );
return stream;
}

(All the classes in question have a public member function "void print(
std::eek: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
 
V

Victor Bazarov

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::eek:stream&operator<<( std::eek: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::eek:stream&operator<<( std::eek:stream&stream,
Printable const&printable )
{
printable.print( stream );
return stream;
}

(All the classes in question have a public member function "void print(
std::eek: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::eek:stream& os) const = 0;
};

class GerhardsClass : public Printable
{
...
void print(std::eek:stream& os) const; // whatever you have
};

// no need for the template
ostream& operator <<(ostream& os, Printable const& pr)
{
pr.print(os);
return os;
}

V
 
G

Gerhard Fiedler

Victor said:
Have you considered using polymorphism at all, or is it out of fashion
already?

Thank you for your response. In fact I didn't consider it at first,
reconsidered it after reading your post, and threw it out again :)

This is meant to work for an indeterminate number of classes of a bigger
system, some of which are not polymorphic at all for various reasons. I
don't want to put this constraint on all the classes that potentially
may want to use this interface.

If there was a way without polymorphism, I would prefer this.

Gerhard
 
V

Victor Bazarov

Thank you for your response. In fact I didn't consider it at first,
reconsidered it after reading your post, and threw it out again :)

This is meant to work for an indeterminate number of classes of a bigger
system, some of which are not polymorphic at all for various reasons. I
don't want to put this constraint on all the classes that potentially
may want to use this interface.

If there was a way without polymorphism, I would prefer this.

OK, that's fair. If you don't want to introduce polymorphism, then you
will have to give the exact example where your solution does not work,
which we then should be able to fix. Otherwise, it's going to be an
exercise in futility on our part: we give you a solution, and you tell
us it's not working due to "tons of errors". See FAQ 5.8.

V
 
R

Rui Maciel

Gerhard said:
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::eek:stream &operator<<( std::eek: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::eek:stream &operator<<( std::eek:stream &stream,
Printable const &printable )
{
printable.print( stream );
return stream;
}

(All the classes in question have a public member function "void print(
std::eek: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
 
G

Gerhard Fiedler

Rui said:
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
 
G

Gerhard Fiedler

Victor said:
OK, that's fair. If you don't want to introduce polymorphism, then
you will have to give the exact example where your solution does not
work, which we then should be able to fix. Otherwise, it's going to
be an exercise in futility on our part: we give you a solution, and
you tell us it's not working due to "tons of errors". See FAQ 5.8.

Got it. I thought this was a rather common issue with a rather generic
answer...

It seems that because there is already a templated operator for some
types, there is now a second, ambiguous definition. Hence my question
whether I can use some template tricks to restrict my template to
classes that implement a given function; I don't want my template
function to be considered for the type 'const char *'.

Thanks,
Gerhard


#include <iostream>

template < class Printable >
std::eek:stream &operator<<( std::eek:stream &stream,
Printable const &printable )
{
printable.print( stream );
return stream;
}

class MyClass
{
public:
MyClass( char const *val ) : str( val ) {}
// The next line is line 122:
void print( std::eek:stream &stream ) const { stream << str; }
private:
char const *str;
};

int main(int argc, char* argv[])
{
MyClass foo( "bar" );
cout << foo << endl;
}


Error (with VC++ 10):

1>TestApp.cpp(122): error C2593: 'operator <<' is ambiguous
1> TestApp.cpp(112): could be 'std::eek:stream &operator <<<const
char*>(std::eek:stream &,const Printable &)'
1> with
1> [
1> Printable=const char *
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio
10.0\VC\include\ostream(851): or
'std::basic_ostream<_Elem,_Traits> &std::eek:perator
<<<char,std::char_traits<char>>(std::basic_ostream<_Elem,_Traits>
&,const _Elem *)' [found using argument-dependent lookup]
1> with
1> [
1> _Elem=char,
1> _Traits=std::char_traits<char>
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio
10.0\VC\include\ostream(764): or
'std::basic_ostream<_Elem,_Traits> &std::eek:perator
<<<std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,const
char *)' [found using argument-dependent lookup]
1> with
1> [
1> _Elem=char,
1> _Traits=std::char_traits<char>
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio
10.0\VC\include\ostream(679): or
'std::basic_ostream<_Elem,_Traits> &std::eek:perator
<<<char,std::char_traits<char>>(std::basic_ostream<_Elem,_Traits>
&,const char *)' [found using argument-dependent lookup]
1> with
1> [
1> _Elem=char,
1> _Traits=std::char_traits<char>
1> ]
1> while trying to match the argument list '(std::eek:stream,
const char *const )'
 
L

Luca Risolia

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::print)>::value, int>::type= 0 >
std::eek:stream &operator<<( std::eek:stream &stream,
Printable const &printable )
{
printable.print( stream );
return stream;
}
 
R

Rui Maciel

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
 
G

Gerhard Fiedler

Rui said:
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.
Agreed.

Considering that you are willing to output that information without
any restriction (i.e., output to std::cout) [...]

I don't know where you get this from. If you mean the code that I posted
in response to Victor, you're way in fantasy land. Victor asked me for
an example that demonstrates the errors I'm seeing, and that's what I
sent. Any other conclusions are mere imagination. I thought that this
was common practice here... providing example code that shows what one
wants to show -- only, at least as far as possible. (To show the whole
context, I'd have to bury you guys with hundreds of lines of code and
several pages of system design documentation, but nothing of this has to
do with my question.)

The remainder of your post is pretty far off from my situation as
described in my previous posts, so any discussion of it from my part
seems a waste of effort.

Thanks anyway, though.

Gerhard
 
G

Gerhard Fiedler

Luca said:
Yes, there is a way:

#include <type_traits>

template < class Printable,
typename std::enable_if<std::is_member_function_pointer
<decltype(&Printable::print)>::value, int>::type= 0 >
std::eek:stream &operator<<( std::eek: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
 
G

Gerhard Fiedler

Gerhard said:
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::print ) >::value
, std::eek:stream &
operator<<( std::eek: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::eek:stream &
1> ]"


Can somebody please help me to massage the template definition to do
what I want?

Thanks,
Gerhard
 
V

Victor Bazarov

Victor said:
OK, that's fair. If you don't want to introduce polymorphism, then
you will have to give the exact example where your solution does not
work, which we then should be able to fix. Otherwise, it's going to
be an exercise in futility on our part: we give you a solution, and
you tell us it's not working due to "tons of errors". See FAQ 5.8.

Got it. I thought this was a rather common issue with a rather generic
answer...

It seems that because there is already a templated operator for some
types, there is now a second, ambiguous definition. Hence my question
whether I can use some template tricks to restrict my template to
classes that implement a given function; I don't want my template
function to be considered for the type 'const char *'.

Thanks,
Gerhard


#include<iostream>

template< class Printable>
std::eek:stream&operator<<( std::eek:stream&stream,
Printable const&printable )
{
printable.print( stream );
return stream;
}

class MyClass
{
public:
MyClass( char const *val ) : str( val ) {}
// The next line is line 122:
void print( std::eek:stream&stream ) const { stream<< str; }
private:
char const *str;
};

int main(int argc, char* argv[])
{
MyClass foo( "bar" );
cout<< foo<< endl;

Nit pick:
std::cout << foo<< std::endl;
}


Error (with VC++ 10):

1>TestApp.cpp(122): error C2593: 'operator<<' is ambiguous
1> TestApp.cpp(112): could be 'std::eek:stream&operator<<<const
char*>(std::eek:stream&,const Printable&)'
1> with
1> [
1> Printable=const char *
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio
10.0\VC\include\ostream(851): or
'std::basic_ostream<_Elem,_Traits> &std::eek:perator
<<<char,std::char_traits<char>>(std::basic_ostream<_Elem,_Traits>
&,const _Elem *)' [found using argument-dependent lookup]
1> with
1> [
1> _Elem=char,
1> _Traits=std::char_traits<char>
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio
10.0\VC\include\ostream(764): or
'std::basic_ostream<_Elem,_Traits> &std::eek:perator
<<<std::char_traits<char>>(std::basic_ostream<_Elem,_Traits> &,const
char *)' [found using argument-dependent lookup]
1> with
1> [
1> _Elem=char,
1> _Traits=std::char_traits<char>
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio
10.0\VC\include\ostream(679): or
'std::basic_ostream<_Elem,_Traits> &std::eek:perator
<<<char,std::char_traits<char>>(std::basic_ostream<_Elem,_Traits>
&,const char *)' [found using argument-dependent lookup]
1> with
1> [
1> _Elem=char,
1> _Traits=std::char_traits<char>
1> ]
1> while trying to match the argument list '(std::eek:stream,
const char *const )'

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):

#include <iostream>

template<class T> struct Printable
{
friend std::eek:stream& operator <<(std::eek:stream& os,
Printable const& rp)
{
return static_cast<T const&>(rp).print(os);
}
};

class MyClass : public Printable<MyClass>
{
public:
explicit MyClass( char const *val ) : str( val ) {}
// The next line is line 122:
std::eek:stream& print( std::eek:stream &stream ) const {
return stream << str; }
private:
char const *str;
};


int main()
{
MyClass foo( "bar" );
std::cout << foo << std::endl;
}


V
 
R

Rui Maciel

Gerhard said:
Considering that you are willing to output that information without
any restriction (i.e., output to std::cout) [...]

I don't know where you get this from. If you mean the code that I posted
in response to Victor,
<snip/>

It appears you failed to understand what I said, in addition to being
needlessly rude. I was referring to encapsulation, and how you are trying
to needlessly jump through hoops to try to defeat the restrictions that you
chose to put up, particularly when there are solutions which are obvious and
straight-forward.

As you manifested an irrational desire to avoid the straight-forward
solution, defining operator<<() as friend, and you also exhibited a fixation
to employ redundant and useless wrapper functions for what essentially is
the definition of a operator<<(), 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.

But hey, if that's your thing then, as Ernie Anastos said; "keep fucking
that chicken".


Rui Maciel
 
G

Gerhard Fiedler

Rui said:
Gerhard said:
Considering that you are willing to output that information without
any restriction (i.e., output to std::cout) [...]

I don't know where you get this from. If you mean the code that I posted
in response to Victor,
<snip/>

It appears you failed to understand what I said,

Given what you again wrote here, it appears that I did understand what
you meant. It however appears that you fail to consider what I wrote
about my situation.
I was referring to encapsulation, and how you are trying to needlessly
jump through hoops to try to defeat the restrictions that you chose
to put up, particularly when there are solutions which are obvious
and straight-forward.

I have my reasons for those restrictions, and I'm not trying to defeat
them. You may or may not agree with my reasons, but you don't know them
and so far you haven't been interested in them. You are trying to look
at the greater picture, which is a good thing, usually, but not without
knowing the context.

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.
As you manifested an irrational desire to avoid the straight-forward
solution, defining operator<<() as friend,

Not sure what you call irrational. Given that you don't know my reasons
(and have so far shown no desire to know them), how can you judge
whether this exercise is rational or not?
and you also exhibited a fixation to employ redundant and useless
wrapper functions for what essentially is the definition of a
operator<<(),

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.
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. This
is why I'm trying to see whether I can solve this in the way I asked
about.

If you find this exercise irrational, what is then your participation in
it? (Not that it's not appreciated, but I have my reasons to do it, and
for me this is not only rational but also fruitful so far. For you?)

Gerhard
 
R

Rui Maciel

Gerhard said:
Rui said:
Gerhard said:
Considering that you are willing to output that information without
any restriction (i.e., output to std::cout) [...]

I don't know where you get this from. If you mean the code that I posted
in response to Victor,
<snip/>

It appears you failed to understand what I said,

Given what you again wrote here, it appears that I did understand what
you meant. It however appears that you fail to consider what I wrote
about my situation.

You already demonstrated that you were clueless about what I wrote, to the
point that I really doubt you even took the time to actually read it.

I have my reasons for those restrictions, and I'm not trying to defeat
them. You may or may not agree with my reasons, but you don't know them
and so far you haven't been interested in them. You are trying to look
at the greater picture, which is a good thing, usually, but not without
knowing the context.


You presented a set of details which you personally picked after deciding
they were the only relevant aspects which influenced your problem. If you
believe the details you've presented are insufficient to properly analyze
your problem and that you didn't provided the adequate context then I don't
see how your decision to leave out information you deemed relevant should be
used as a basis to condemn and insult others.

In spite of this, if you read the post you wrote then you will notice that
you explicitly stated the following:
1) you wished to define a set of operator<<() to output the content of some
classes to std::eek:stream
2) you required a definition similar to
std::eek:stream &operator<<( std::eek:stream &str, MyClass const &printable );
3) you've came up with a implementation of a template policy pattern by
adding individual print() methods to each class, which you failed to make it
work, and asked for help
4) then, in addition, you asked if there was "another way to avoid having to
define such a global operator<< for every class with ostream support"

In my reply, I explicitly addressed 1), 2) and 4), leaving out 3) as it
represented a needlessly convoluted and, ultimately, unjustifiably laborious
solution to a simple problem with a straight-forward solution.

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.

Not sure what you call irrational. Given that you don't know my reasons
(and have so far shown no desire to know them), how can you judge
whether this exercise is rational or not?

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.

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::eek:stream &operator<<( std::eek:stream &stream, Printable const &printable )
{
printable.print( stream );
return stream;
}

I don't believe it is possible to write a thinner wrapper than this.

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?

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".

If you find this exercise irrational, what is then your participation in
it? (Not that it's not appreciated, but I have my reasons to do it, and
for me this is not only rational but also fruitful so far. For you?)

You asked for help and I tried to help you. That's one of the main reasons
people still subscribe to this newsgroup. But, after you posted your
replies, I've realized that that was a mistake.


Rui Maciel
 
B

Bo Persson

Gerhard Fiedler skrev 2012-06-20 14:51:
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::print ) >::value
, std::eek:stream &
operator<<( std::eek: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::eek: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
 
G

Gerhard Fiedler

Bo said:
Gerhard Fiedler skrev 2012-06-20 14:51:
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::print ) >::value
, std::eek:stream &
operator<<( std::eek: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::eek: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
 
G

Gerhard Fiedler

[...] 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.
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.
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::eek:stream &operator<<( std::eek: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.
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.
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
 
G

Gerhard Fiedler

Luca said:
Yes, there is a way:

#include <type_traits>

template < class Printable,
typename std::enable_if<std::is_member_function_pointer
<decltype(&Printable::print)>::value, int>::type= 0 >
std::eek:stream &operator<<( std::eek: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::print)
::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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,961
Messages
2,570,131
Members
46,689
Latest member
liammiller

Latest Threads

Top