Tag: language features

  • Quick Tip: Its Time to Avoid the Frustration of Single Return Types in C++

    When designing a new API one of the things I put a lot of thought into is how the user will know if the API call was successful or not. I don’t want to levy large error checking requirements on my users, but in C/C++ you can only return a single data type, so many APIs will pass the real output back through a referenced argument in the function prototype and a simple Boolean or error code as the return value. I find this clunky and hard to document, so I dug my heels in to find a better way.

    std::tuple and std::tie are two useful C++ features that can help you return multiple values from a function. std::tuple is a container that holds a tuple of values, while std::tie allows you to tie objects together so that they can be accessed as if they were one object. In this post, we’ll take a look at how to use these two features to make returning multiple values from a function easier.

    std::tuple and std::pair

    std::tuple (and std::pair) are C++ templates that allow you to combine two or more objects together and pass them around as if they were one. They are the clear choice for combining multiple outputs from a function into a single return data type. This creates clean, self-documenting code that is easy for a user to understand and follow.

    For example, let’s say we were dealing with a factory that created our objects. We’d have a creation method that looks something like this:

    std::shared_ptr<Object> MyFactory::create()
    {
      return std::make_shared<Object>();
    }
    Code language: C++ (cpp)

    One shortcoming here is that the create function does not to any error checking whatsoever, putting the entire burden on the user.

    A (slightly) improved version of the create method could be:

    bool MyFactory::create(std::shared_ptr<Object> &p)
    {
      p = std::make_shared<Object>();
      if (!p) return false;
      return true;
    }
    Code language: C++ (cpp)

    The user can now easily check the return value to determine whether or not the object was created successfully and perform additional processing. However, now they have to create the shared_ptr<T> object before calling the create function; and in addition to that, they also have to understand the argument p is not an input parameter, but rather they output parameter they are after in the first place.

    Instead, let’s make use of a std::pair to return both the created object as well as whether creation was successful.

    std::pair<std::shared_ptr<Object>, bool> MyFactory::create()
    {
      auto p = std::make_shared<Object>();
      return std::make_pair(p, !!p); // !!p same as static_cast<bool>(p) or 'if (p)'
    }
    Code language: C++ (cpp)

    How is this better? You may look at this and think that the user still has to grab the success value from the pair and that is absolutely correct. In this trivial case, creation is just a couple of lines. However, in a real-world scenario your function will likely be much more complex with many more errors to handle. Now, instead of levying that requirement on your user, you have captured all the error handling logic (and maybe reporting) internally. The user just has to check whether the returned data is valid or not via the Boolean in the pair.

    You can also just as easily extend this to return multiple values in a std::tuple:

    std::tuple<UUID, std::string, bool> createMsg(const std::string &msg, const int id)
    {
      UUID uuid = makeNewUUID();
      std::string outputmsg = std::to_string(id) + ": " + msg;
      return std::make_tuple(uuid, outputmsg, isUUIDValid(uuid));
    }
    Code language: C++ (cpp)

    Using std::tie to Access Return Values

    To access multiple return values, std::tie comes to the rescue. std::tie “ties” variable references together into a std::tuple. Accessing your multiple return values becomes straightforward at this point:

    // Factory Example
    std::shared_ptr<Object> obj;
    bool objvalid{false};
    std::tie(obj, objvalid) = MyFactory::create();
    if (objvalid) obj.work();
    
    // Message Example
    UUID lUuid;
    std::string msg;
    bool msgvalid{false};
    std::tie(lUuid, msg, msgvalid) = createMsg("Test message", 73);
    if (msgvalid) std::cout << lUuid << ": " << msg << std::endl;
    Code language: C++ (cpp)

    Conclusion

    The C++ Core Guidelines make it clear that passing back a std::tuple (or std::pair) is the preferred way to return multiple return values. It is also clear that if there are specific semantics to your return value that a class or structure object is best.

    std::tuple and std::pair provide a nice way to return multiple values from a function without having to resort to ugly workarounds. By using std::tie, we can make receiving the return value a breeze. What do you think? How will you use this in your next project?

  • Quick Tip: Improve Code Readability By Using C++17’s New Structured Bindings

    C++17 introduced a language feature called “structured bindings” which allows you to bind names to elements of another object easily. This makes your code more concise and easier to read, and also drives down maintenance costs. In this quick tip, we’ll take a look at how structured bindings work and give some examples of how you might use them in your own programs.

    Accessing std::tuple

    std::tuple is an extremely useful way to quickly combine multiple objects into a single object. I have used this often to combine various items that I want to serialize into a single byte stream for transmission somewhere (typically using MessagePack, for example see my code in the zRPC library). You can also use them effectively to return multiple values from a function (similar to std::pair, which is basically just a tuple of two objects).

    When using std::tuple the canonical way of gaining access to the members of the tuple is to use std::get<T> like so:

    // Given std::tuple<int, std::string, ExampleObject>
    const int i = std::get<0>(tpl);
    const std::string s = std::get<1>(tpl);
    const ExampleObject o = std::get<2>(tpl);
    Code language: C++ (cpp)

    This always felt clunky to me, yet the benefits of tuples were tremendous, so I just dealt with it.

    Structured Binding Approach

    Fast-forward to the C++17 standard and the ability to use structured bindings. These allow you to tie names to elements of any object, std::tuple included! Now, your access to the tuple becomes a single line:

    // Given std::tuple<int, std::string, ExampleObject>
    const auto [i,s,o] = tpl; // decltype(i) = int
                              // decltype(s)=std::string
                              // decltype(o)=ExampleObject
    Code language: C++ (cpp)

    So much cleaner and easier for the developer to read and follow!

    You can also get fancy with dealing with multiple return values from a function (see C++ Core Guidline F.21):

    ExampleObject obj;
    bool success{false};
    
    // Use structured binding to get object and success value
    // If creation succeeds, then process it
    if (auto [obj, success] = createObject(); success) processObject(obj);
    Code language: C++ (cpp)

    Structured bindings are a great new feature in C++17. They make your code more readable and maintainable, and they’re easier to parse for humans. I think you’ll find that they make your life a lot easier. What are some ways you see yourself using them in your own code?