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; } 
1
  • You should avoid using memcpy in C++ and use std::copy instead. Commented Jul 29, 2017 at 7:28

2 Answers 2

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(); } 
Sign up to request clarification or add additional context in comments.

4 Comments

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, 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.
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, That will work. The only think you'll have to be concerned about is performance if you use these functions a lot.
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.

1 Comment

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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.