简体   繁体   中英

Boost iostreams usage with custom buffer

I'm writing an interface for Windows RTF streaming. I can do this without boost::iostreams but this gives me a chance to learn the library. The code below does compile as long as I leave the commented lines.

I am looking to do this also in Unicode as well as narrow as some of my RTFs are stored in an access database and the memo field is handed to me as Unicode.

I'm beginning to think I'm making this way more complicated than I need to. Right off, I'm not sure how to connect the passed buffer to my device so I can stream it out to Windows. OTOH I could just use it in my basic_stream , but that doesn't seem right.

And when I try to use my typedef file_wstream I get:

1>c:\cpp\reserveanalyst\stock_database\rtf.cpp(74): error C2614: 'rtf::basic_stream<_Elem,Dev>' : illegal member initialization: 'stream<boost::iostreams::basic_file_source<char>,rtf::wbuffer<wchar_t>,std::allocator<wchar_t> >' is not a base or member
1>          with
1>          [
1>              _Elem=rtf::wbuffer<wchar_t>,
1>              Dev=boost::iostreams::file_source
1>          ]

It reaches for basic_file_source< char > even though I'm using io::wfile_source . Using a wfile should not come up with RTFs but I'd like to know why.

namespace io = boost::iostreams;
namespace rtf
{
    enum io_mode
    {
        sbad,
        read,
        write,
    };
// ........     wbuffer      ...........
    template< typename E >
    class wbuffer : public io::char_traits< E >
    {
    public:
        typedef E                       char_type;
        typedef std::streamsize         size_type;
        typedef size_t                  int_type;

        wbuffer( E* buf )
            :pointer( buf )
        { }
        size_type size( ) const { return 0; }
        E* begin( ) { return NULL; }
        static int_type eof( ) { return 0; }
    private:
        E* pointer;
    };

// ........       my_dev     ........
    class my_dev
    {
    public:
        typedef wchar_t        char_type;
        typedef io::bidirectional_device_tag  category;

        //my_dev( Container& container)
        //  :container_(container)
        //{ }
        std::streamsize read( char_type* s, std::streamsize n)
        {
            return 0;
        }
       std::streamsize write( const char_type* s, std::streamsize n)
       {
           return 0;
       }
    };

// ........     basic_stream     ..........
    template< typename _Elem, typename Dev= my_dev >
    class basic_stream : public boost::iostreams::stream< Dev, _Elem >
    {
    public:
        typedef typename boost::iostreams::char_type_of< _Elem >::type  char_type;
        typedef typename io::char_traits< _Elem >                traits_type;
        basic_stream( char_type* buf, io_mode mode= sbad )
            :mode( mode )
        { }
        //
        basic_stream( io::wfile_source& stream, io_mode mode= sbad )
            :mode( mode )
            ,boost::iostreams::stream< Dev, _Elem, std::allocator< _Elem::char_type > >( stream )
        { }

        std::streamsize write( char_type* s, std::streamsize n )
        {
            //io::write( s, io::stream< Dev >, &io::char_type_of< _Elem >::type, n ); 
            return 0;
        }
        std::streamsize read( char_type* s, std::streamsize n )
        {
//          std::basic_iostream< _Elem, _Traits >::read( s, n ); 
            return 0;
        }

        io_mode get_mode( ) const { return mode; }
        std::streamsize count( ) const { return 0; }

    private:
        io_mode mode;
    };

// ........ 
    typedef rtf::basic_stream< wbuffer< wchar_t > > read_wstream;
    typedef rtf::basic_stream< wbuffer< wchar_t >, io::file_source > file_wstream;

};//namespace rtf

/*
DWORD CALLBACK EditStreamCallback(
  _In_ DWORD_PTR dwCookie,  whatever we please as an object pointer
  _In_ LPBYTE    pbBuff,    the RTF buffer
  _In_ LONG      cb,        number of bytes to transfer
  _In_ LONG      *pcb       number of bytes that were transfered
);
*/

template< typename _Char >
DWORD CALLBACK rtf_stream_callback(
        DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb )
{
    rtf::basic_stream< rtf::wbuffer< _Char > >& cookie= *(rtf::basic_stream< rtf::wbuffer< _Char > >*)dwCookie;
    try
    {
        if( cookie.get_mode( ) == rtf::read )
        {
            *pcb= (LONG)cookie.read( (_Char*)pbBuff, cb );
        }
        else if( cookie.get_mode( ) == rtf::write )
        {
            //*pcb= (LONG)cookie.write( (_Char*)pbBuff, cb );
            cookie.write( (_Char*)pbBuff, cb );
        }
        else
            ;//error!

    }
    catch( std::exception e )
    {
    }
    return 0;
    }

//The actual public calls
    BOOL SetRichText( CRichEditCtrl& re, LPCTSTR buf, int format )//| SFF_SELECTION
    {
        rtf::read_wstream ios( const_cast< wchar_t* >( buf ), rtf::write );
        EDITSTREAM es= { (DWORD_PTR)&ios, 0, rtf_stream_callback< wchar_t > };
        long r= (long)::SendMessage( re, EM_STREAMIN, format, (LPARAM)&es );
        return true;
    }

    bool SetRichText( CRichEditCtrl& r, std::ifstream& istr, int format )
    {
        std::wstringstream str;
        io::wfile_source f( "test.rtf" );
    //  rtf::file_wstream ws( f, rtf::write );
        return false;
    }

This is what I ended up with. It may be way the wrong way to do this but I learned a lot.

namespace io = boost::iostreams;

namespace rtf
{
    enum io_mode
    {
        e_bad,
        e_read,
        e_write,
    };
    // ........       rtf_buffer     ........
    template< typename Char >
    class rtf_buffer
    {
    public:
        typedef Char                            char_type;
        typedef io::bidirectional_device_tag    category;
        typedef io::char_traits< Char >         char_traits;
        typedef std::streamsize                 buf_count;

        rtf_buffer( char_type** buf, io_mode mode )
            :buf_( *buf )
            ,cur_( *buf )
            ,user_( buf )
            ,mode( mode )
        {
            if( mode == e_write )
                if( std::is_same< Char, wchar_t >::value )
                    end_= (Char*)( *buf + _tcslen( (wchar_t*)*buf ) );
                else
                    end_= (Char*)( *buf + strlen( (char*)*buf ) );
            else
                end_= *buf;
        }

        buf_count read( char_type* s, buf_count n )
        {
            std::streamsize left= end_ - cur_;
            if( ! left )
                return -1;

            if( left < n )
            {
                if( std::is_same< Char, wchar_t >::value )
                {
                    size_t c;
                    char* buffer= new char[ (size_t)n ]; //TODO not big enough for asian chars
                    errno_t err= wcstombs_s( &c, buffer, (int)left, (wchar_t*)cur_, _TRUNCATE ); //TODO errno
                    memcpy( s, buffer, c );
                    delete [ ] buffer;
                }
                else
                    memcpy( s, cur_, (size_t)left );

                cur_= end_;
                return left;
            }
            //else
            {
                if( std::is_same< Char, wchar_t >::value )
                {
                    size_t c;
                    char* buffer= new char[ (size_t)n ]; //TODO not big enough for asian chars ?
                    errno_t err= wcstombs_s( &c, buffer, (int)n, (wchar_t*)cur_, _TRUNCATE ); //TODO errno
                    memcpy( s, buffer, c );
                    delete [ ] buffer;
                }
                else
                    memcpy( s, cur_, (size_t)n );
                cur_+= n;
                return n;
            }
        }
        buf_count write( char_type* s, buf_count n )
        {
            buf_count old_size= buffer.size( );
            if( std::is_same< Char, wchar_t >::value )
            {
                mbstate_t state;
                memset(&state, 0, sizeof state);
                size_t d_size;
                // mbsrtowcs_s(_Out_opt_ size_t* _Retval, _Out_opt_z_cap_(_Size) wchar_t * _Dst, _In_ size_t _Size, _Inout_ _Deref_prepost_opt_valid_ const char ** _PSrc, _In_ size_t _N, _Out_opt_ mbstate_t * _State);
                //'(size_t *, wchar_t [1], wchar_t **, int, mbstate_t *)'
                errno_t err= mbsrtowcs_s( &d_size, NULL, 0, (const char**)&s, (size_t)n, &state );
                buf_count size= d_size + old_size;
                buffer.resize( (size_t)size + 1, 0 );
                err= mbsrtowcs_s( &d_size, (wchar_t*)&buffer[ (size_t)old_size ], (size_t)size, (const char**)&s, (size_t)n, &state );
            }
            else
            {
                buffer.resize( (size_t)( n + buffer.size( ) ) );
                memcpy( s, cur_, (size_t)n );
                return n;
            }
            *user_= &buffer.front( );
            cur_+= n;
            return n;
        }
        char_type* buf_;
        char_type* end_;
        char_type* cur_;
        char_type** user_;
        io_mode mode;
        std::vector< Char > buffer;
    };

    // ........     basic_stream     ..........
    template< typename Char, typename Dev >
    class stream
        :public Dev
    {
    public:
        typedef Char                                            char_type;
        typedef typename io::char_traits< Char >                traits_type;
        typedef std::is_same< std::ifstream, Dev >              is_ifstream;
        typedef std::streamsize                                 buf_count;

// rtf_buffer constructor
        stream( char_type** buf, io_mode mode= e_bad )
            :mode( mode )
            ,Dev( buf, mode )
        {
        }
// std::ifstream construtor
        stream( std::ifstream& is, io_mode mode= e_bad )
            :mode( mode )
            ,Dev( )
        {
            set_rdbuf( is.rdbuf( ) );
        }
// std::ofstream construtor
        stream( std::ofstream& os, io_mode mode= e_bad )
            :mode( mode )
            ,Dev( )
        {
            set_rdbuf( os.rdbuf( ) );
        }
//write buf
    template <class U >
        typename std::enable_if<
            std::is_base_of< rtf_buffer< Char >, U >::value, buf_count >
        ::type
        write_rtf( char_type* s, buf_count n )
        {
            return Dev::read( s, n );
        }
//write istream
    template <class U >
        typename std::enable_if<
            std::is_base_of< std::ifstream, U >::value, buf_count >
        ::type
        write_rtf( char_type* s, buf_count n )
        {
            std::ifstream::read( (char*)s, n );
            return std::ifstream::gcount( );
            return 0;
        }
//write ostream
    template <class U >
        typename std::enable_if<
            std::is_base_of< std::ofstream, U >::value, buf_count >
        ::type
        write_rtf( char_type* s, buf_count n )
        {
            return 0;
        }
//read buf
    template <class U >
        typename std::enable_if<
            std::is_base_of< rtf_buffer< Char >, U >::value, buf_count >
        ::type
//      buf_count
        read_rtf( char_type* s, buf_count n )
        {
//          return 0;
            return Dev::write( s, n );
        }
//read istream
    template <class U >
        typename std::enable_if<
            std::is_base_of< std::ifstream, U >::value, buf_count >
        ::type
        read_rtf( char_type* s, buf_count n )
        {
            //return Dev::write( s, n );
            return 0;
        }

//read ostream
    template <class U >
        typename std::enable_if<
            std::is_base_of< std::ofstream, U >::value, buf_count >
        ::type
        read_rtf( char_type* s, buf_count n )
        {
            std::ofstream::write( s, n );
            return 0;
        }

        io_mode get_mode( ) const { return mode; }
    private:
        io_mode mode;
    };
};//namespace rtf

/*
DWORD CALLBACK EditStreamCallback(
  _In_ DWORD_PTR dwCookie,  whatever we please as an object pointer
  _In_ LPBYTE    pbBuff,    accual io buffer
  _In_ LONG      cb,        number of bytes to transfer
  _In_ LONG      *pcb       number of bytes that were transfered
);
*/

template< typename IO >
DWORD CALLBACK rtf_stream_callback(
        DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb )
{
    auto& cookie= *(IO*)dwCookie;
    try
    {
        if( cookie.get_mode( ) == rtf::e_read )
            *pcb= (LONG)cookie.read_rtf< IO >( (IO::char_type*)pbBuff, cb );

        else if( cookie.get_mode( ) == rtf::e_write )
            *pcb= (LONG)cookie.write_rtf< IO >( (IO::char_type*)pbBuff, cb );

        else
            ;//error!
    }
    catch( std::exception e )
    {
    }
    return 0;
}

//......
class rtf_fstream
{
};

typedef rtf::stream< wchar_t, rtf::rtf_buffer< wchar_t > > rtf_wbuf;
typedef rtf::stream< char, std::ifstream > rtf_istream;
typedef rtf::stream< char, std::ofstream > rtf_ostream;

//format |= SFF_SELECTION
BOOL SetRichText( CRichEditCtrl& re, LPCTSTR buf, int format )
{
    rtf_wbuf  ios( const_cast< wchar_t** >( &buf ), rtf::e_write );
    EDITSTREAM es= { (DWORD_PTR)&ios, 0, rtf_stream_callback< rtf_wbuf > };
    long r= (long)::SendMessage( re, EM_STREAMIN, format, (LPARAM)&es );
    return FALSE;
}

bool SetRichText( CRichEditCtrl& re, bfs::path& path, int format )
{
    std::ifstream is( path.string( ) );
    rtf_istream  ios( is, rtf::e_write );
    EDITSTREAM es= { (DWORD_PTR)&ios, 0, rtf_stream_callback< rtf_istream > };
    long r= (long)::SendMessage( re, EM_STREAMIN, format, (LPARAM)&es );
    return true;
}

LPTSTR GetRichText( CRichEditCtrl& re, int format ) //| SFF_SELECTION
{
    wchar_t* p_buf;
    rtf_wbuf os( &p_buf, rtf::e_read );
    EDITSTREAM es= { (DWORD_PTR)&os, 0, rtf_stream_callback< rtf_wbuf > };
    long r= (long)::SendMessage( re, EM_STREAMOUT, format, (LPARAM)&es );

    return NULL;
}

bool GetRichText( CRichEditCtrl& re, CString& str, int format ) //| SFF_SELECTION
{
    wchar_t* p_buf;
    rtf_wbuf os( &p_buf, rtf::e_read );
    EDITSTREAM es= { (DWORD_PTR)&os, 0, rtf_stream_callback< rtf_wbuf > };
    long r= (long)::SendMessage( re, EM_STREAMOUT, format, (LPARAM)&es );
    wchar_t* sb= str.GetBufferSetLength( r );
    memcpy( sb, p_buf, r * sizeof( wchar_t ) );
    str.ReleaseBuffer( );
//  str= *p_buf;
    return true;
}

bool GetRichText( CRichEditCtrl& re, bfs::path& path, int format )
{
    std::ofstream os( path.string( ) );
    rtf_ostream ios( os, rtf::e_read );
    EDITSTREAM es= { (DWORD_PTR)&ios, 0, rtf_stream_callback< rtf_ostream > };
    //long r= (long)::SendMessage( re, EM_STREAMIN, format, (LPARAM)&es );
    return false;
}

The only thing I have not done is to test the ofstream write works properly.

You try to initialize the base class after a member (mode).

Also, you specify the allocator (perhaps incorrectly too) when initializing the base class, but you didn't when declaring the base class in the first place.

Initial fix:

basic_stream(io::wfile_source &stream, io_mode mode = sbad)
    : boost::iostreams::stream<Dev, _Elem>(stream), mode(mode)  {}

Now it looks like you're stuffing a io::wfile_source into the wbuffer<> constructor argument of that base class. That looks wrong, but without a SSCCE it's hard for me to tell what you wanted to do there.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM