1

So I've browsed a bunch of other threads, but none pertain to the questions I have on this exact topic. I am writing a C++ library to do socket communication. I have a TCP socket class which handles all operations on a TCP socket (setup, read/write, etc). I want to be able to read and write data to/from a TCP socket object using the << and >> operators. For example, I want to be able to write, say, a double to the socket in this fashion:

double x;
TcpSocket *sock = new TcpSocket();

//socket setup stuff here...

sock << x;

At the moment, I have two templated overloaded operator functions within the TcpSocket class:

template<typename T>
TcpSocket operator<<(TcpSocket sock, T &val)
{
    unsigned char bytesOfVal[] = //parse bytes of val here...

    sock.Write(bytesOfVal, sizeof(bytesOfVal));
}

-

template<typename T>
TcpSocket operator>>(TcpSocket sock, T &val)
{
    std::stringstream ss;
    byte buf[sizeof(val)];

    sock.Read(buf, sizeof(buf));
    ss.str(std::string(buf));

    ss >> val;
}

where sock.Read( ) and sock.Write( ) issue the system calls read( ) and write( ) using a socket file descriptor (not really relevant but just FYI). These functions are templated in an attempt to be able to get the bytes for almost any data type in one place.

So my questions are:

  • Will these overloaded operator functions work in the way I want them to?
  • If it works, can I chain these together, such as:

    sock << 45.8 << 33.9 << "numbers";

========================================================================= Here are my updated operator functions:

//Write to socket
template<typename T>
TcpSocket& operator<<(TcpSocket& sock, T &val)
{
    size_t buflen = sizeof(val);
    unsigned char buf[buflen];

    memcpy(buf, &val, buflen);

    sock.Write(buf, buflen);

    return sock;
}

and

//Read from socket
template<typename T>
TcpSocket& operator>>(TcpSocket &sock, T &val)
{
    size_t buflen = sizeof(val);
    unsigned char buf[buflen];
    int numBytesRead = 0;

    numBytesRead = sock.Read(buf, buflen);

    memcpy(&val, buf, numBytesRead);

    return sock;
} 
Timmah339
  • 37
  • 1
  • 9

2 Answers2

3

For your function to work at all, you'll need to use references to TcpSocket for the input argument as well as the return type. Also val needs to be T const&, not T&.

template<typename T>
TcpSocket& operator<<(TcpSocket& sock, T const&val)
{
    unsigned char bytesOfVal[] = //parse bytes of val here...

    sock.Write(bytesOfVal, sizeof(bytesOfVal));
    return sock;
}

Now, you can use:

sock << 45.8 << 33.9 << "numbers";

If you also want to be able to use:

sock << 45.8 << 33.9 << "numbers" << std::endl;

you'll need another function.

TcpSocket& operator<<(TcpSocket& soc,
                      std::ostream&(*f)(std::ostream&))
{
    std::ostringstream s;
    s << f;
    return sock << s.str();
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • After looking over your suggestions (and the libsocket project on github and their use of overloaded << >> operators), I came up with the two functions I've attached to my original post. I'm pretty sure they will work for any data type, as well as cascading variables such as: 'tcpSocket << x << y;' I am concerned about passing strings to them such as: 'tcpSocket << "hello";' Would there be a problem sending that string using my updated operator functions? – Timmah339 Jun 19 '15 at 20:40
  • @Timmah339, using `size_t buflen = sizeof(val);` is worrisome. That will work for POD types but not for `char*`, `std::string` and other STL containers. You'll have to rethink that strategy. – R Sahu Jun 19 '15 at 21:43
  • Thanks, I didn't think about that problem. I suppose it would be a better solution to just stuff `val` into a stringstream and use `.c_str` in my call to `read()` or `write()`. I'd just have to get the length of that data – Timmah339 Jun 20 '15 at 15:18
  • @Timmah339, That will work. The only think you'll have to be concerned about is performance if you use these functions a lot. – R Sahu Jun 20 '15 at 17:04
0

The code you propose

template<typename T>
TcpSocket operator<<(TcpSocket sock, T &val)
{
    unsigned char bytesOfVal[] = //parse bytes of val here...

    sock.Write(bytesOfVal, sizeof(bytesOfVal));
}

has another major flaw (apart the one signalled by Sahu): it will write your data in binary form and won't work ad one would expect from a std::ostream.

For example

TcpSocket socket ;
socket << std::string("Hello") ;

won't write 'Hello' but some not well undestandable data (size? pointer to actual text? depends on implementation).

I suggest you to write your own stream buf. In this way you can use all use all the exiting std::ostream stuff.

std::ostream ost ;
ost.imbue(new my_sockect_streambuf) ;

// or std::ostream ost(new my_socket_strembuf) ; ost << 1.2 << "text" << std::endl ;

You can find references here :How do I create my own ostream/streambuf? (I consider your question nearly a duplicate of this one) or here: http://www.mr-edd.co.uk/blog/beginners_guide_streambuf.

May be is too much, but I consider this the way to go.

Community
  • 1
  • 1
marom
  • 5,064
  • 10
  • 14
  • Thanks for the suggestion! I'll keep this in mind, but I'd prefer not to go through the trouble of writing a custom stream buffer and such. – Timmah339 Jun 19 '15 at 20:33