문제

I'm working on writing some test cases for a class that will presumably read from an std::istream and write to an std::ostream. As part of the testing process, I would like to manually create a block of test file data, wrap it in a std::stringstream, and then pass it to my class for processing.

I feel like my current solution lacks is lacking despite the fact that it does work. I really don't like using those raw write calls with reinterpret_cast.

std::stringstream file;

uint32_t version = 1;
uint32_t dataSize = 10;
uint32_t recordCount = 3;

file.write(reinterpret_cast<char*>(&version), sizeof(version));
file.write(reinterpret_cast<char*>(&dataSize), sizeof(dataSize));
file.write(reinterpret_cast<char*>(&recordCount), sizeof(recordCount));

myclass.read(file)

Is there a way I can use the stream operators to write this data in binary form? I'm hoping for something more akin to the following.

std::stringstream file;

uint32_t version = 1;
uint32_t dataSize = 0;
uint32_t recordCount = 3;

file << version << dataSize << recordCount;

myclass.read(file);

If I go this route, extracting a number results in 103 which is expected in an ascii context, but I'm obviously trying to avoid serializing my data in that manner.

도움이 되었습니까?

해결책

There is a problem with your code: when you use the reinterpret_cast, you don't actually know what you are writing to the stream, so you don't know what you are testing. If you want to test how your code reacts to a stream of bytes in a binary format, you can easily initialize an std::istringstream with an arbitrary stream of bytes:

char bytes[] = { /*...*/ };
std::istringstream( std::string( std::begin( bytes ), std::end( bytes ) ) );

(If you don't have C++11, you can easily write your own begin and end.)

In this way, you'll know exactly what the bytes are, rather than depending on the aleas of how your implementation represents any specific type.

Alternatively: if you're reading and writing binary data, you may want to define classes which do it, using >> and <<. Such classes would be unrelated to std::istream and std::ostream, but could logically use std::ios_base to provide support for the conventional error reporting and the interface to std::streambuf. The class would then have members something like the following:

namespace {

class ByteGetter
{
public:
    explicit            ByteGetter( ixdrstream& stream )
        :   mySentry( stream )
        ,   myStream( stream )
        ,   mySB( stream->rdbuf() )
        ,   myIsFirst( true )
    {
        if ( ! mySentry ) {
            mySB = NULL ;
        }
    }
    std::uint8_t        get()
    {
        int                 result = 0 ;
        if ( mySB != NULL ) {
            result = mySB->sgetc() ;
            if ( result == EOF ) {
                result = 0 ;
                myStream.setstate( myIsFirst
                    ?   std::ios::failbit | std::ios::eofbit
                    :   std::ios::failbit | std::ios::eofbit | std::ios::badbit ) ;
            }
        }
        myIsFirst = false ;
        return result ;
    }

private:
    ixdrstream::sentry  mySentry ;
    ixdrstream&         myStream ;
    std::streambuf*     mySB ;
    bool                myIsFirst ;
} ;
}

ixdrstream&
ixdrstream::operator>>( std::uint32_t&      dest )
{
    ByteGetter          source( *this ) ;
    std::uint32_t       tmp = source.get() << 24 ;
    tmp |= source.get() << 16 ;
    tmp |= source.get() <<  8 ;
    tmp |= source.get()       ;
    if ( *this ) {
        dest = tmp ;
    }
    return *this ;
}

(For a maximum of portability, you might wont to avoid the uint8_t and uint32_t. At this level, writing code without knowing the exact size of the type is a little more difficult, so if you are certain you'll never have to port to an exotic system where they may not be defined, it's probably worth saving yourself the extra work.)

다른 팁

You could declare an operator << for ostreams (so not just string streams, but also file streams). Something like the following might work (untested), but you might run into troubles with the type (uint32_t):

std::ostream& operator<<(std::ostream& stream, uint32_t value)
{
    stream.write(reinterpret_cast<char*>(&value), sizeof(value));
    return stream;
}

std::stringstream file;
file << version << dataSize << recordCount;

EDIT:

Because of the type value has, the << operator is already defined. One alternative would be to declare a new operator <=:

std::ostream& operator<=(std::ostream& stream, uint32_t value);
file <= version <= dataSize <= recordCount;

Both operators operate in a left-to-right fashion, so this might work, maybe not the nicest solution though.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top