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

Discussion in 'C++' started by Victor Bogado, Mar 2, 2011.

  1. 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";
    }
     
    Victor Bogado, Mar 2, 2011
    #1
    1. Advertising

  2. Victor Bogado

    James Kanze Guest

    On Mar 2, 4:50 pm, Victor Bogado <> wrote:
    > 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.

    --
    James Kanze
     
    James Kanze, Mar 2, 2011
    #2
    1. Advertising

  3. On Mar 2, 3:35 pm, James Kanze <> wrote:
    > On Mar 2, 4:50 pm, Victor Bogado <> wrote:
    >
    > > 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.)


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

    >
    > >       }
    > > };
    > > 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.


    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

    >
    > > };
    > > 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?


    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.

    >
    > --
    > James Kanze
     
    Victor Bogado, Mar 3, 2011
    #3
  4. Victor Bogado

    James Kanze Guest

    On Mar 3, 1:07 pm, Victor Bogado <> wrote:
    > On Mar 2, 3:35 pm, James Kanze <> wrote:
    > > On Mar 2, 4:50 pm, Victor Bogado <> wrote:


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


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


    > > > }
    > > > };
    > > > 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.


    > 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


    >
    > > > };
    > > > 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?


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


    My tests were run with g++ 4.4.2.

    --
    James Kanze
     
    James Kanze, Mar 3, 2011
    #4
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. zaemin
    Replies:
    0
    Views:
    363
    zaemin
    May 9, 2004
  2. zaemin

    Streamable File Format + Interactivity

    zaemin, May 9, 2004, in forum: C Programming
    Replies:
    2
    Views:
    297
    Stephen Sprunk
    May 9, 2004
  3. Mikael
    Replies:
    10
    Views:
    516
    n2xssvv g02gfr12930
    Dec 6, 2005
  4. siddhu
    Replies:
    1
    Views:
    307
    Victor Bazarov
    May 17, 2007
  5. Fei Liu
    Replies:
    5
    Views:
    488
    Barry
    May 29, 2008
Loading...

Share This Page