Denis
Denis

Reputation: 3707

Why std::is_same does not work?

My code snippet is:

namespace serialization
{
    struct output_stream
    {
        /////
        void write(const void* data, size_t size)
        {
            const byte_t* p = static_cast<const byte_t*>(data);

            size_t old_size = buffer_.size();
            buffer_.resize(old_size + size);
            memcpy(buffer_.data() + old_size, p, size);
        }
    }

    struct input_stream
    {
        ///////

        void read(void* to, size_t size)
        {
            assert(cur_ + size <= end_);
            memcpy(to, cur_, size);
            cur_ += size;
        }
    }
}

template <class stream_t>
void serialize(not_pod_struct& r, stream_t stream)
{
    if (std::is_same<stream_t, serialization::output_stream>::value)
    {
        stream.write(&r, sizeof(r));
    }
    else if (std::is_same<stream_t, serialization::input_stream>::value)
    {
        not_pod_struct* buf = new not_pod_struct[sizeof(not_pod_struct)];
        stream.read(buf, sizeof(not_pod_struct));
        r = *buf;
    }
    else 
    {
        throw std::invalid_argument("stream_t is not a stream.");
    }
}

template<class T>
typename enable_if<!is_pod<T>::value, void>::type
read(input_stream & input_stream, T & non_pod_struct)
{
    serialize(non_pod_struct, input_stream);
}

template<class T>
typename enable_if<!is_pod<T>::value, void>::type
write(output_stream & output_stream, T & non_pod_struct)
{
    serialize(non_pod_struct, output_stream);
}

I have errors:

Error   1   error C2039: 'read' : is not a member of 'serialization::output_stream'
Error   2   error C2039: 'write' : is not a member of 'serialization::input_stream'

This is strange. I do not understand why these errors happen.

Upvotes: 1

Views: 1458

Answers (3)

StefanW
StefanW

Reputation: 347

You can also use full template specification and throw an exception when serialize is called with another argument than input_stream or output_stream. This is close to your original code.

template<typename X>
void serialize(not_pod_struct & r, X & x)
{
  throw std::invalid_argument("stream_t is not a stream.");
}

template<>
void serialize(not_pod_struct& r, serialization::input_stream & stream)
{
    not_pod_struct* buf = new not_pod_struct[sizeof(not_pod_struct)];
    stream.read(buf, sizeof(not_pod_struct));
    r = *buf;
}

template<>
void serialize(not_pod_struct& r, serialization::output_stream & stream)
{
    stream.write(&r, sizeof(r));
}

Upvotes: 1

T.C.
T.C.

Reputation: 137330

if is not static if in certain other languages. A branch that is not reached still must compile.

Tag dispatch (this is more useful if you are doing something more complicated than is_same (e.g., accept various streams based on an is_input_stream trait); there's not much point in using a template that only accepts two types and do completely different things with each):

template <class stream_t>
void serialize(not_pod_struct& r, stream_t & stream, 
               std::true_type /* is_input */, std::false_type /* is_output */)
{
    not_pod_struct* buf = new not_pod_struct[sizeof(not_pod_struct)];
    stream.read(buf, sizeof(not_pod_struct));
    r = *buf;
}

template <class stream_t>
void serialize(not_pod_struct& r, stream_t & stream,
               std::false_type /* is_input */, std::true_type /* is_output */)
{
    stream.write(&r, sizeof(r));
}

template <class stream_t>
void serialize(not_pod_struct& r, stream_t & stream)
{
    serialize(r, stream, std::is_same<stream_t, serialization::input_stream>(),
                         std::is_same<stream_t, serialization::output_stream>());
}

Or just overload serialize for output_stream and input_stream:

void serialize(not_pod_struct& r, serialization::input_stream & stream)
{
    not_pod_struct* buf = new not_pod_struct[sizeof(not_pod_struct)];
    stream.read(buf, sizeof(not_pod_struct));
    r = *buf;
}

void serialize(not_pod_struct& r, serialization::output_stream & stream)
{
    stream.write(&r, sizeof(r));
}

I took the liberty of having serialize accept the stream by reference. Also, I don't think your "read" logic is correct. Not only do you leak the memory allocated with new, I'm extremely doubtful that you meant to allocate an array of sizeof(not_pod_struct) not_pod_structs.

Upvotes: 5

Anton Savin
Anton Savin

Reputation: 41301

This code

if (std::is_same<stream_t, serialization::output_stream>::value)
{
    stream.write(&r, sizeof(r));
}
else if (std::is_same<stream_t, serialization::input_stream>::value)
{
    not_pod_struct* buf = new not_pod_struct[sizeof(not_pod_struct)];
    stream.read(buf, sizeof(not_pod_struct));
    r = *buf;
}

is not doing what you think. When the function template serialize is instantiated, its code must be well-formed, but it is not. Consider what happens inside e.g serialize<output_stream>:

if (true)
{
    stream.write(&r, sizeof(r));
}
else if (false)
{
    // This branch never executes, but this doesn't mean it's not compiled!
    not_pod_struct* buf = new not_pod_struct[sizeof(not_pod_struct)];
    // And output_stream doesn't have read() method.
    stream.read(buf, sizeof(not_pod_struct));
    r = *buf;
}

Upvotes: 5

Related Questions