SFINAE to test if a type is streamable, is it possible?

V

Victor Bogado

Why does this print "1 1" and not "1 sizeof(int)"?

#include <string>
#include <iostream>

class A {};

template <class C>
struct streamable
{
~streamable()
{
(*static_cast<std::eek:stream*>(0)) <<
(*static_cast<C*>(0));
}
};

template<typename T>
struct is_streamable
{
static char Test(streamable<T>&);
static int Test(...);
static const int value = sizeof(Test((streamable<T>*)(0)));
};

int main()
{
std::cout << is_streamable<A>::value << "\n";
std::cout << is_streamable<int>::value << "\n";
}
 
J

James Kanze

Why does this print "1 1" and not "1 sizeof(int)"?
#include <string>
#include <iostream>
class A {};
template <class C>
struct streamable
{
~streamable()
{
(*static_cast<std::eek:stream*>(0)) << (*static_cast<C*>(0));

This will result in undefined behavior if the destructor is ever
executed, and the destructor will never be instantiated unless
it is actuall used (and will be executed). Whatever you're
trying to accomplish here, it won't work. (The title mentionned
SFINAE, but of course, that's completely irrelevant with regards
to whatever may be found in the implementation of a member
function.)
template<typename T>
struct is_streamable
{
static char Test(streamable<T>&);

Requires a reference...
static int Test(...);
static const int value = sizeof(Test((streamable<T>*)(0)));

And here you pass a pointer. So function overloading will never
choose the first function above.
int main()
{
std::cout << is_streamable<A>::value << "\n";
std::cout << is_streamable<int>::value << "\n";
}

Maybe because on your machine sizeof(int) is 1?

I get "4 4" on my Linux box, which is what I'd expect. As
written, there's no code which will ever match the first Test.

If you're trying to distinguish which objects have a << defined
for them, that's something else entirely. Somewhere, you need
a template function declaration whose instantiation depends on
the expression x<<y. Something like:

template <typename T, size_t> struct D{};

template <typename T>
struct is_streamable
{
template <typename U> static char Test(
U*, D<U, sizeof( std::cout << *(static_cast<U*>( 0 )) )>* );
template <typename U> static int Test(U*, ...);
static const int value = sizeof( Test<T>( 0, 0 ) );
};

int main()
{
std::cout << is_streamable<A>::value << "\n";
std::cout << is_streamable<int>::value << "\n";
return 0;
}

Anyway, with the necessary includes added, this compiles and
outputs 4 1 with g++. Not with VC++ (VS 8), however; I suspect that
this is a bug in VC++, but I'm too busy right now to dig into
the standard to be sure. Anyway, SFINAE only comes into play
when attempting to instantiate a function template, so you need
something which gets the expression into a function template
parameters.
 
V

Victor Bogado

This will result in undefined behavior if the destructor is ever
executed, and the destructor will never be instantiated unless
it is actuall used (and will be executed).  Whatever you're
trying to accomplish here, it won't work.  (The title mentionned
SFINAE, but of course, that's completely irrelevant with regards
to whatever may be found in the implementation of a member
function.)

This was one of several attempts I made, none of them worked :p.
But the idea was that this class would never be instantiated, the
compiler should only check if he can instantiate or not. My guess
is that with the code above that destructor is never called. Never
the less, it would be wise not to leave such code in a production
environment (this was just a test).
Requires a reference...


And here you pass a pointer.  So function overloading will never
choose the first function above.

You're right, but this might be an error on the post, since my test
output was "1 1" and not "4 4". Weird, I may have tested with other
version, as I said I made several tests and posted one, so I could
get help here. :p
Maybe because on your machine sizeof(int) is 1?

My machine is a linux in a 64bit environment. sizeof(int) is not 1. :)
I get "4 4" on my Linux box, which is what I'd expect.  As
written, there's no code which will ever match the first Test.

If you're trying to distinguish which objects have a << defined
for them, that's something else entirely.  Somewhere, you need
a template function declaration whose instantiation depends on
the expression x<<y.  Something like:

        template <typename T, size_t> struct D{};

        template <typename T>
        struct is_streamable
        {
                template <typename U> static char Test(
                        U*, D<U, sizeof( std::cout << *(static_cast<U*>( 0 )) )>* );
                template <typename U> static int Test(U*,...);
                static const int value = sizeof( Test<T>( 0, 0 ) );
        };

        int main()
        {
                std::cout << is_streamable<A>::value << "\n";
                std::cout << is_streamable<int>::value <<"\n";
                return 0;
        }

Anyway, with the necessary includes added, this compiles and
outputs 4 1 with g++.  Not with VC++ (VS 8), however; I suspect that
this is a bug in VC++, but I'm too busy right now to dig into
the standard to be sure.  Anyway, SFINAE only comes into play
when attempting to instantiate a function template, so you need
something which gets the expression into a function template
parameters.

I will use it with boost::enable_if to enable or disable a method on a
class. :) Unfortunately, this don't work with g++ version 4.1.2, the
compiler only attempts to instantiate the first version. But it does
work in the g++ 4.4.0.
 
J

James Kanze

This was one of several attempts I made, none of them worked :p.
But the idea was that this class would never be instantiated, the
compiler should only check if he can instantiate or not.

The compiler will have no problem instantiating the class, since
all it contains is a destructor. The compiler will not be able
to instantiate the destructor definition. And there's no
context where failing to instantiate a function definition is
not an error.
My guess is that with the code above that destructor is never
called. Never the less, it would be wise not to leave such
code in a production environment (this was just a test).
You're right, but this might be an error on the post, since my test
output was "1 1" and not "4 4". Weird, I may have tested with other
version, as I said I made several tests and posted one, so I could
get help here. :p
My machine is a linux in a 64bit environment. sizeof(int) is not 1. :)

So you must have tested something else. I both expected and got
4 4. On a Linux machine (not sure if the environment was 32 bit
or 64, but it shouldn't matter).
I will use it with boost::enable_if to enable or disable a method on a
class. :) Unfortunately, this don't work with g++ version 4.1.2, the
compiler only attempts to instantiate the first version. But it does
work in the g++ 4.4.0.

My tests were run with g++ 4.4.2.
 

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

Similar Threads


Members online

Forum statistics

Threads
473,731
Messages
2,569,432
Members
44,832
Latest member
GlennSmall

Latest Threads

Top