From: John Kerich on
I am trying to port exist code to a Linux box. The problem is the
code is using the old C++ library classes and some of the calls are
now illegal. The class that I am having problems with is declared as
"class FoIpSocketStreambuf : public streambuf {" in its include file
and it wants to modify the streambuf address. Here is the problem
area:

// resetbuf(): reset the buffers
// The streambuf's buffer consists of 4 bytes of putback area, then
// the get buffer, then the put buffer.
// This needs to be kept in synch with the buffer setting in
underflow.
//
void FoIpSocketStreambuf::resetbuf()
{
// The precondition implies that we don't allow unbuffered I/O (yet)
RWPRECONDITION( ebuf()!=0 && base()!=0 && ebuf()-base()>6 ); <==
rogue wave call for under/over flow

// The put area starts a bit more than half way along the buffer
char *putStart = base() + (ebuf()-base()-4)/2 + 4;

// Set the get pointer to the beginning of the buffer.
setg(base(),base()+4,base()+4);

// Set the put pointer from the start of put area to the end of
buffer
setp(putStart,ebuf());
}

FoIpSocketStreambuf::FoIpSocketStreambuf()
{
char *p = new char[RWNET_BUFSIZE];
if (p) { // hmmmm this seems broken with advent of exceptions
// Set the buffer area, from p to p + buffer size.
setb(p, p+RWNET_BUFSIZE, 1); // dtor will delete buffer
resetbuf();
}
}

As you can see it is using setb be to create a buffer area. Then in
reset() it gets the start and end address of the stream and after some
math is calling setp and setg with new start and end addresses.

In the new C++ library setb(), ebuf(), and base() can no longer be
directly called anymore. I can see in the new library that setb was
replaced with setbuff or pubsetbuff, but I can't find anything that
tells me what the member is doing when called. For example is it
calling/setting setp or setg. The next problem is what to replace the
base() and ebuf() calls with. I can't find anything that does the
same thing in the new library.

From the documents I have read, I think setbuf() is calling setp() and
setg() but that's just a guess. Does anyone know how to port this
code?
From: James Kanze on
On Jun 24, 5:50 pm, John Kerich <jker...(a)sgt-inc.com> wrote:
> I am trying to port exist code to a Linux box. The problem is
> the code is using the old C++ library classes and some of the
> calls are now illegal.

Or maybe never were legal, or maybe were only legal with one
specific implementation:-).

> The class that I am having problems with is declared as "class
> FoIpSocketStreambuf : public streambuf {" in its include file
> and it wants to modify the streambuf address. Here is the
> problem area:

> // resetbuf(): reset the buffers
> // The streambuf's buffer consists of 4 bytes of putback area, then
> // the get buffer, then the put buffer.
> // This needs to be kept in synch with the buffer setting in underflow.
> //
> void FoIpSocketStreambuf::resetbuf()
> {
> // The precondition implies that we don't allow unbuffered I/O (yet)
> RWPRECONDITION( ebuf()!=0 && base()!=0 && ebuf()-base()>6 ); <==
> rogue wave call for under/over flow

> // The put area starts a bit more than half way along the buffer
> char *putStart = base() + (ebuf()-base()-4)/2 + 4;

> // Set the get pointer to the beginning of the buffer.
> setg(base(),base()+4,base()+4);

> // Set the put pointer from the start of put area to the end of
> buffer
> setp(putStart,ebuf());
> }

> FoIpSocketStreambuf::FoIpSocketStreambuf()
> {
> char *p = new char[RWNET_BUFSIZE];
> if (p) { // hmmmm this seems broken with advent of exceptions
> // Set the buffer area, from p to p + buffer size.

More to the point, why isn't char[ RWNET_BUFSIZE ] declared
directly as a member, skipping the dynamic allocation
completely? Otherwise, if RWNET_BUFSIZE isn't a compile time
constant, I'd use an std::vector<char> member to manage it.

> setb(p, p+RWNET_BUFSIZE, 1); // dtor will delete buffer
> resetbuf();
> }
> }

> As you can see it is using setb be to create a buffer area.
> Then in reset() it gets the start and end address of the
> stream and after some math is calling setp and setg with new
> start and end addresses.

I'm not too sure (it's been a long time since I used the
classical iostreams), but some of these functions look like
extensions even in classical iostream. In general, the
streambuf manages two sets of buffer pointers, which may or may
not point to the same buffers. (In the case of a socket
streambuf, they should probably NOT point to the same areas.)

The functions you refer to seem to be concerned with managing a
global buffer, from which the get and put area buffers are
defined. The standard streambuf does not have any support for
this; you can very easily use two separate allocations for the
get and put areas, if that's what you want.

In this particular case, what I'd probably do is add an
std::vector< char > member to the class, then rewrite the
constructor to be
FoIpSocketStreambuf::FoIpSocketStreambuf()
: myBuffer( RWNET_BUFSIZE )
{
resetbuf() ;
}
and in resetbuf(), replace base() with &myBuffer[0], and ebuf()
with "&myBuffer[0] + myBuffer.size()".

> In the new C++ library setb(), ebuf(), and base() can no longer be
> directly called anymore. I can see in the new library that setb was
> replaced with setbuff or pubsetbuff, but I can't find anything that
> tells me what the member is doing when called.

pubsetbuf just calls setbuf, which is a virtual function. By
default, unless you've overridden it, it does nothing.

The pubsetbuf/setbuf interface is designed so that client code
can specify a buffer, in derived streambuf's where that might
make sense. Since your class provides a buffer itself, it
doesn't make sense, so you should just ignore them.

> For example is it calling/setting setp or setg. The next
> problem is what to replace the base() and ebuf() calls with.
> I can't find anything that does the same thing in the new
> library.

That's because there isn't any global buffer handling in the
standard library. (I don't remember any in the classical
iostream library either; it's possible that this is just a Rogue
Wave extension.) From the code you've posted, however, and some
familiarity with the streambuf naming conventions, it would seem
that all they do is set or return pointers to the beginning and
the end of some common buffer, shared between reading and
writing; given the code, it would also seem that they don't do
anything else; that you still have to position the pointers for
the get and the put areas. If you use an std::vector<char> as
your buffer, then basically, the two pointers correspond to
&buf[0] and &buf[0]+buf.size(). (If you don't use std::vector,
then just declare two pointers, and set and access them
manually.)

--
James Kanze (GABI Software) email:james.kanze(a)gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
From: John Kerich on
After debugging the old code to see what it was doing I found that the
that base() and ebuf() where returning the start and start
+RWNET_BUFSIZE addresses. The pioner calls to setg and setp where
null until it initialized them. For example:
p= 0x7e3228
setb(0x7E3228, 0x7e522C, 1)
base() -> 0x7E3228
ebuf() -> 0x7E522C
putStart -> 0x7e322C
setg(0x7e3228, 0x7e322C, 0x7e322C)
setp(0x7e422C, 0x7e522C)

The base() and ebuf() values seem to stay the same all the time, which
makes sense since the p array does change.

I decided not to do the &myBuffer[0] + myBuffer.size() to keep the
code as close to the orignal as possible so I added a mybase() and
myebuf() member to my class, as seen below, to do what the old base()
and ebuf() did, return the start and end +1 of the p array.

FoIpSocketStreambuf.h
#ifndef __FoIpSocketStreambuf_h
#define __FoIpSocketStreambuf_h
/
***************************************************************************
*
* FoIpSocketStreambuf.h
*

**************************************************************************/
/*
* SocketStreams: classes for iostream I/O using Sockets
*
*/

#include <streambuf>
#include <rw/network/RWSocket.h>
#include <rw/network/RWInetAddr.h>
#include <rw/network/RWInetHost.h>
#include <rw/network/RWInetPort.h>
#include <rw/network/RWSockAddr.h>
#include <rw/network/RWSockType.h>
#include <rw/network/RWSockAddrBase.h>

/*
* FoIpSocketStreambuf: A streambuf which is also a socket
* This code was cloned from RogueWaves RWPortalStreambuf
* changing their use of a RWPortal to our use of an RWSocket.
* This was done to implement Mcast/DataGram using net.h++. This
code
* is not using EcTypes and will not to ensure that the RW code
* does not break
*/

class FoIpSocketStreambuf : public std::streambuf {
public:
// Initialize and destroy the streambuf.
FoIpSocketStreambuf();

FoIpSocketStreambuf( RWSocket* p,
RWInetAddr inSendAddr,
RWSockAddr inRecvAddr);

~FoIpSocketStreambuf();

// Read/write using the socket
// once the buffer overflows. Here is
// where the action happens.
virtual int overflow(int = EOF);

virtual int underflow();


// Flush the buffer.
virtual int sync();

// Set the communications channel that we are writing into.
virtual void setSocket(RWSocket* p);

// Allow user to get at receive address.
// For UPD (DataGrams) this changes everytime
// data is receive on the socket.
RWSockAddr* getRecvAddr();

// Allow user to set send address.
// For UPD (DataGrams).
void setSendAddr(RWInetAddr inSendAddr);

// Return the start address of the buffer you allocated for setbuf
char * mybase();

//Save the start address of the buffer you allocated for setbuf
void mybase(char * new_base);

// Return end address + 1 of the buffer you allocated for setbuf
char * myebuf();

// Save the end address + 1 of the buffer you allocated for setbuf
void myebuf(char * new_ebuf);

// Are there bytes waiting to be proccess on the output stream?
int out_waiting();

protected:
// Here is where all the overflows and underflows go!
RWSocket* mySock;

// Mcast/DataGram stuff
RWInetAddr mySendAddr;

// Mcast/DataGram stuff
RWSockAddr myRecvAddr;

// Reset the get and put buffers
void resetbuf();

char * my_base;
char * my_ebuf;
};

#endif

In the class I added new member for the mybase and myebuf calls and
recreated the out_waiting() inline that was dropped in the new
streambuf library. The unbuffered() inline was replaced by
_C_is_unbuffered().

Hopefully it will work when I finally get to test it out in 6 or 8
months from now.

FoIpSocketStreambuf.C

include "FoIpSocketStreambuf.h"
#include <rw/rwerr.h>

static const int RWNET_BUFSIZE = 2*4096+4; // 1024 byte packets seems
ok

// resetbuf(): reset the buffers
// The streambuf's buffer consists of 4 bytes of putback area, then
// the get buffer, then the put buffer.
// This needs to be kept in synch with the buffer setting in
underflow.
//
void FoIpSocketStreambuf::resetbuf()
{
// The precondition implies that we don't allow unbuffered I/O (yet)
RWPRECONDITION( !myebuf() && !mybase() && myebuf()-mybase()>6 );


// The put area starts a bit more than half way along the buffer
char *putStart = mybase() + (myebuf()-mybase()-4)/2 + 4;

// Set the get pointer to the beginning of the buffer.
setg(mybase(), mybase()+4, mybase()+4);

// Set the put pointer from the start of put area to the end of
buffer
setp(putStart, myebuf());
}

FoIpSocketStreambuf::FoIpSocketStreambuf() :
my_base(NULL),
my_ebuf(NULL)
{
char *p = new char[RWNET_BUFSIZE];
if (p) { // hmmmm this seems broken with advent of exceptions
// Set the buffer area, from p to p + buffer size.
mybase(p);
myebuf(p+RWNET_BUFSIZE);

setbuf(p, RWNET_BUFSIZE); // dtor will delete buffer

resetbuf();
}
}

FoIpSocketStreambuf::FoIpSocketStreambuf(
RWSocket* s,
RWInetAddr inSendAddr,
RWSockAddr inRecvAddr) :
mySock(s),
mySendAddr(inSendAddr),
myRecvAddr(inRecvAddr),
my_base(NULL),
my_ebuf(NULL)
{
// allocate a buffer
char *p = new char[RWNET_BUFSIZE];
if (p) { // hmmmm this seems broken with advent of exceptions
// Set the buffer area, from p to p + buffer size.
mybase(p);
myebuf(p+RWNET_BUFSIZE);

setbuf(p, RWNET_BUFSIZE); // dtor will delete buffer

resetbuf();
}
}

FoIpSocketStreambuf::~FoIpSocketStreambuf()
{
// If there's any data to be written,
// force write on socket.
sync();
}

int FoIpSocketStreambuf::overflow(int c)
{
// Check that we're not buffered or not at start of buffer
if ( _C_is_unbuffered() || !mybase() )
{
if (c!=EOF)
{
char theChar = c;
mySock->sendto(&theChar,1,mySendAddr);
}
}
else
{
// Send data to mySendAddr, from start of put area, the number of
// unflushed characters.
mySock->sendto(pbase(),out_waiting(),mySendAddr);

// Init put pointers, start of put area, end of put area
setp(pbase(),epptr());
if (c!=EOF)
{
// Put one character.
sputc(c);
}
}
return 1;
}

int FoIpSocketStreambuf::underflow()
{
if (in_avail())
{
return (unsigned char) *gptr();
} // no action needed

int c = EOF;// return value - default to EOF

if (!_C_is_unbuffered() && mybase())
{ // this is buffered
sync(); // flush output

// set up get area buffer pointers. compute buffer pointers as in
// resetbuf
char *getBegin = mybase()+4;
char *getEnd = pbase();

// Get a buffer full of data
int count = mySock->recvfrom(getBegin,getEnd-
getBegin,&myRecvAddr);
setg(mybase(), getBegin, getBegin+count);

if (count)
{
c = (unsigned char) *gptr();
}

}
else
{ // not buffered
// Note that the throw below will not be thrown unless this class
// is changed because all destructors allocate a buffer.
RWTHROW( RWInternalErr("FoIpSocketStreambuf: Sorry - unbuffered
input not yet implemented") );
}

return c;
}

int FoIpSocketStreambuf::sync()
{
// If the number of characters waiting to be sent
// is greater than zero...
if (out_waiting())
{
// Force data to be written on socket
if (overflow(EOF) == EOF)
{
return EOF;
}
}
// Commented out the flushing of the input buffer.
// It seems weird to flush the input buffer for a portal, because
// you can never get that input back again. The ANSI std is
// not clear on what the correct approach is.
#if 0
else if (in_avail())
{
setg(eback(), gptr(), gptr());
setp(gptr(), gptr());
}
#endif
return 0;
}

void FoIpSocketStreambuf::setSocket(RWSocket* p)
{
sync();
mySock = p;
}


RWSockAddr*
FoIpSocketStreambuf::getRecvAddr()
{
return (&myRecvAddr);
}

void
FoIpSocketStreambuf::setSendAddr(
RWInetAddr inSendAddr)
{
mySendAddr = inSendAddr;
}

/*
FoIpSocketStreambuf::mybase

Description: Save the start address of the buffer you allocated for
setbuf

return - The start address of teh buffer you allocated for setbuf

*/
char *
FoIpSocketStreambuf::mybase ()
{
return my_base;
}

/*
FoIpSocketStreambuf::mybase

Description: Return the start address of the buffer you allocated
for setbuf

return - The start address of the buffer you allocated for setbuf

*/
void
FoIpSocketStreambuf::mybase (char * new_base)
{
my_base = new_base;
}

/*
FoIpSocketStreambuf::myebuf

Description: Return end address + 1 of the buffer you allocated for
setbuf

return - The end address + 1 of the buffer you allocated for setbuf

*/
char *
FoIpSocketStreambuf::myebuf()
{
return my_ebuf;
}

/*
FoIpSocketStreambuf::myebuf

Description: Save the end address + 1 of the buffer you allocated
for setbuf

return - The end address of the buffer you allocated for setbuf

*/
void
FoIpSocketStreambuf::myebuf (char * new_ebuf)
{
my_ebuf = new_ebuf;
}

/*
FoIpSocketStreambuf::out_waiting

Description: Are there bytes waiting to be proccess on the output
stream?

return - 0 = not waiting, >0 bytes waiting

*/
int
FoIpSocketStreambuf::out_waiting()
{
return pptr() ? pptr() - pbase() : 0;
}