Spike To Learn
As one of the responsibilities on my last project, I had to interface one of our radars to a 10+ year old, legacy, command-and-control system built by another company. My C++11 code was required to receive commands from the control system and send radar data to it via UDP datagrams over a LAN that connects the two systems together.
It’s unfortunate, but we won’t get a standard C++ networking library until the next version of the C++ standard gets ratified in 2017. However, instead of using the low-level C sockets API to implement the interface functionality, I chose to use the facilities of the Poco::Net library.
Poco is a portable, well written, and nicely documented set of multi-function, open source C++ libraries. Among other nice, higher level, TCP/IP and http networking functionality, Poco::Net provides a thin wrapper layer around the native OS C-language sockets API.
Since I had never used the Poco::Net API before, I decided to spike off the main trail and write a little test program to learn the API before integrating the API calls directly into my production code. I use the “Spike To Learn” best practice whenever I can.
Here is the finished and prettied-up spike program for your viewing pleasure:
#include <string> #include <iostream> #include "Poco/Net/SocketAddress.h" #include "Poco/Net/DatagramSocket.h" using Poco::Net::SocketAddress; using Poco::Net::DatagramSocket; int main() { //simulate a UDP legacy app bound to port 15001 SocketAddress legacyNodeAddr{"localhost", 15001}; DatagramSocket legacyApp{legacyNodeAddr}; //create & bind //simulate my UDP app bound to port 15002 SocketAddress myAddr{"localhost", 15002}; DatagramSocket myApp{myAddr}; //create & bind //myApp creates & transmits a message //encapsulated in a UDP datagram to the legacyApp char myAppTxBuff[]{"Hello legacyApp"}; auto msgSize = sizeof(myAppTxBuff); myApp.sendTo(myAppTxBuff, msgSize, legacyNodeAddr); //legacyApp receives a message //from myApp and prints its payload //to the console char legacyAppRxBuff[msgSize]; legacyApp.receiveBytes(legacyAppRxBuff, msgSize); std::cout << std::string{legacyAppRxBuff} << std::endl; //legacyApp creates & transmits a message //to myApp char legacyAppTxBuff[]{"Hello myApp!"}; msgSize = sizeof(legacyAppTxBuff); legacyApp.sendTo(legacyAppTxBuff, msgSize, myAddr); //myApp receives a message //from legacyApp and prints its payload //to the console char myAppRxBuff[msgSize]; myApp.receiveBytes(myAppRxBuff, msgSize); std::cout << std::string{myAppRxBuff} << std::endl; }
As you can see, I used the Poco::Net::SocketAddress and Poco::Net::DatagramSocket classes to simulate the bi-directional messaging between myApp and the legacyApp on one machine. The code first transmits the text message “Hello legacyApp!” from myApp to the legacyApp; and then it transmits the text message “Hello myApp!” from the legacyApp to myApp.
Lest you think the program doesn’t work 🙂 , here is the console output after the final compile & run cycle:
As a side note, notice that I used C++11 brace-initializers to uniformly initialize every object in the code – except for one: the msgSize object. I had to fallback and use the “=” form of initialization because “auto” does not yet play well with brace-initializers. For who knows why, the type of obj in a statement of the form “auto obj{5};” is deduced to be a std::initializer_list<T> instead of a simple int. I think this strange inconvenience will be fixed in C++17. It may have been fixed already in the recently ratified C++14 standard, but I don’t know for sure. Do you?
Which compiler are you using? Not all compilers are created equal, ISO notwithstanding…
Hi Guy. I’m using GCC 4.8.3 over cygwin64 on Windows 8.1. Poco comes with the cygwin distribution.
I’m fairly sure that the auto-with-braces mismatch is a language specification issue. I recall seeing Scott Meyers complaining about it in one of his video talks.
Ah, Poco doesn’t have a build script for VS2013. Can I be bothered… Looks nice…
That’s ringing a bell actually. I love auto, but I tend to initialise by assignment using constructs like
auto foo = Type{bar};
or
auto foo2 = bar();
and relying on the compiler eliding the assignment.
Nothing wrong with that personal style. I’d prefer, if auto did indeed work with braces: auto foo{bar}; and auto foo{bar()};. Until auto plays nice with brace initializers, I don’t think the ideal of “uniform initialization” can really be fully achieved.