Adopting some newer C++ features

I’ve been continuously updating my skills in C++, adopting features from newer features of the language, like from the version C++17. For fun and learning, I’ve been updating some older apps to use these newer features, and implementing some new tools adopting newer features like std::variant, algorithms (instead of traditional loops) and attributes. Some examples below.

Attributes

Instead of commenting in a switch/case structure that fallthrough is OK, use the [[fallthrough]] attribute:

      switch (argc) {
         case 4:
            outputFileName = argv[3];
            [[fallthrough]];
            
         case 3:

Reader is then aware that the missing break; is not actually missing by accident, but intentional. Improves code readability and quality, and silences the compiler warning about the missing break.

To make sure the caller of the function handles the return value, use the [[nodiscard]] attribute:

[[nodiscard]]
int readFile(const std::string & fileName, std::vector<std::string> & entries);

Compiler will warn you that the return value is not handled. This again improves code quality.

nodiscard attribute warns you that essential return value is not handled.

Using using instead of typedef

I wanted to use a shorter name for a complex data structure. Usually done with typedef. Instead, using the using keyword, the one used usually with namespaces, is neat:

using queue_package_type = std::map<std::string, std::pair<int,int>>;
queue_package_type queuePackageCounts;

Or similarily:

using NodeContainer = std::vector<NodeView>;
NodeContainer nodes;
// ...
SPConfigurator::NodeContainer nodes = configurator->getNodes();
std::for_each(std::begin(nodes), std::end(nodes), [this](const NodeView & node) {
   std::string description = node.getInputAddressWithPort() + "\t" + node.getName() + "\t" + node.getOutputAddressWithPort();
   QString logEntry = QString::fromStdString(description);
   ui->LogView->appendPlainText(logEntry);
});

Small thing but makes better looking code, in my opinion. When working with templates, The alias declaration with using is compatible with templates, whereas the C style typedef is not.

Algorithms

In a recent post, I mentioned algorithms like std::iota and std::shuffle, useful in generating test data. When handling containers (vectors, lists), the “old way” is to use either indexes or iterators to handle the items. Implementing these carelessly may lead to bugs. The better alternative is to use algorithms from the standard library, readily developed and rigorously tested, also considering performance. An example from a small tool app I recently made, which searches if id values read from one file are contained in lines read from another file:

std::for_each(std::begin(indexes), std::end(indexes), [&matchCount, &dataEntries, &output](const std::string & index) {
   std::any_of(std::begin(dataEntries), std::end(dataEntries), [&matchCount, &index, &output](const std::string & dataEntry) {
      if (dataEntry.find(index) != std::string::npos) {
         *output << matchCount+1 << "   " << dataEntry << std::endl;
         matchCount++;
         return true; // Not returning from the app but from the lambda function.
      }
      return false;   // Not returning from the app but from the lambda function.
   });
});

std::for_each replaces loops created by using iterators (or indexes to the container), and when some additional logic is needed, std::any_of is a nice solution to end the search when a match is found.

A bit more complicated example, using std::find_if, std::all_of and a boolean predicate object assisting in the search when calling std::find_if. In this example (full source code is here), there is a composite design pattern implemented for handling hierarchical key-value -pairs. The code sample below implements removing a specific key-value pair from the object hierarchy.

/**
 A helper struct to assist in finding an Entity with a given name. Used
 in EntityComposite::remove(const std::string &) to find an Entity with a given name.
 */
struct ElementNameMatches {
   ElementNameMatches(const std::pair<std::string,std::string> & nameValue) {
      searchNameValue = nameValue;
   }
   std::pair<std::string,std::string> searchNameValue;
   bool operator() (const Entity * e) {
      return (e->getName() == searchNameValue.first && e->getValue() == searchNameValue.second);
   }
};

/**
 Removes and deletes a child entity from this Entity.
 If the child is not an immediate child of this entity, then it is given
 to the children to be removed from there, if it is found.
 If the child is a Composite, removes and deletes the children too.
 @param nameValue A child with the equal name and value properties to remove from this entity.
 @return Returns true if the entity was removed, otherwise false.
 */
bool EntityComposite::remove(const std::pair<std::string,std::string> & nameValue) {
   bool returnValue = false;
   auto iter = std::find_if(children.begin(), children.end(), ElementNameMatches(nameValue));
   if (iter != children.end()) {
      Entity * entity = *iter;
      children.remove(*iter);
      delete entity;
      returnValue = true;
   } else {
      // child was not an immediate child. Check if one of the children (or their child) has the child.
      // Use a lambda function to go through the children to find and delete the child.
      // std::all_of can be stopped when the child is found by returning false from the lambda.
      std::all_of(children.begin(), children.end(), [nameValue, &returnValue](Entity * entity) {
         if (entity->remove(nameValue)) {
            returnValue = true;
            return false;
         } else {
            return true;
         }
      });
   }
   return returnValue;
}

// And then call remove() like this, for example, with 
// key "customer", and value "Antti Juustila":
newComposite->remove({"customer", "Antti Juustila"});

What you get is more robust code without your own bugs implemented in “custom” loops with indexes and iterators.

std::variant from C++17

What if your app has some data that can be manipulated in two formats? For example, first you get the data from the network in JSON, and then later you parse the JSON string and create an application specific object holding that parsed data. Later on, you again export the data from the internal object type to JSON to be send over to the network.

You could implement this so that you have both the JSON/string object and the application internal class object in memory. Then just add logic to know which currently has the data and should be used, and ignore the other variable until it is needed. An alternative is to use the good old union to handle this, if you want to save memory. This could be quite complicated to implement.

C++17 provides a more well managed option — std::variant. When using union, you have to keep track what the union contains, but using the variant, it knows which type of object it is currently holding and you can check that.

Following the scenario above, a class could have a member variable holding the JSON in a string, or alternatively, after parsing it, in an application specific object, within an unique pointer assisting with memory management:

std::variant<std::string, std::unique_ptr<DataItem>> payload;

In the class containing the payload member variable, you can initialise it to an empty string:

Package::Package()
: payload("")

Then you can provide setters to change from one representation of the data to another:

// Set the data to be a JSON string:
void Package::setPayload(const std::string & d) {
   payload = d;
}
// ...or a DataItem object, parsed from the string:
void Package::setPayload(std::unique_ptr<DataItem> item) {
   payload = std::move(item);
}

When you access the data to use it somewhere, you can check what is actually stored in the variant and return it. If the representation is not the one requested, return an empty value or null pointer to indicate to the caller that the requested representation of the data is not available currently:

// Get the string, using std::get_if:
const std::string & Package::getPayloadString() const {
   auto item = std::get_if<std::string>(&payload);
   if (item) {
      return *item;
   }
   return emptyString;
}
// Get the DataItem, using std::get_if
const DataItem * Package::getPayloadObject() const {
   auto item = std::get_if<std::unique_ptr<DataItem>>(&payload);
   if (item) {
      return item->get();
   }
   return nullptr;
}

Next I’d like to take a look at how to use the new async programming features of C++, as well as the Boost asio library…