1.2. SDK core elements

This chapter outlines the common, core elements and conventions used throughout the SDK, for example errors, exceptions, and handling asynchronous operations.

1.2.1. Boost C++ library

The Proximie SDK minimises the use of external libraries for reasons of simplicity and mitigation of security/vulnerability risks. We use various Boost libraries as they are highly respected, robust, well-tested and comprehensively assessed for vulnerabilities. The sections in this chapter will give details when they utilise Boost for features and functionality.

1.2.2. Error handling

Exceptions

The Proximie SDK only throws exceptions for truly exceptional or critical conditions such as out of memory. Otherwise, errors are either returned from function calls or as parameters in callbacks etc.

Error codes

The SDK uses error codes based on Boost’s extensible error reporting (see https://www.boost.org/doc/libs/release/libs/system/) using error_code. This is aliased into the Proximie namespace as Proximie::error_code for convenience. Error codes allow domain-specific errors to be defined and then handled by the client application. Both Boost and STL error codes work in the same way; they encapsulate an error category and a numeric error value.

Error codes can simply be if-tested to check if there is an error value:

void onCompleted(Proximie::error_code error) {
    if (!error) {
        // Successful, no error
        return;
    }

    // Handle error...
}

All error categories defined in the Proximie SDK have a static function to obtain the category, allowing error handlers to test the category for an error:

    if (error.category() == Proximie::PxMedia::PxMediaErrorCategory::category()) {
        // Handle media error...

Otherwise, each error category code can be type-safely compared:

    if (error == Proximie::PxMedia::PxMediaErrorCode::InvalidSession) {
        // Handle invalid media session...

Outcomes and results

Where a function call may return a failure condition, the SDK uses Boost’s Outcome library (see https://www.boost.org/doc/libs/release/libs/outcome/). Again, this is aliased into the Proximie namespace as Proximie::outcome for convenience. An outcome result represents either a successful return (which may be some value type or simply void) or an error in the form of an error_code as discussed above.

Refer to the Boost documentation for details, but briefly, given some result-return function:

Proximie::outcome::result<int> getSomeIntResult();

The result is [[nodiscard]], which allows the compiler to detect if the caller accidentally forget to check the return value. You can check for a successful result with a simple if on the result itself, or with has_error and has_value functions.

    auto result = getSomeIntResult();
    if (result) {
        int value = result.value();
        // Do something with the value...
    } else {
        Proximie::error_code error = result.error();
        // Handle the error...
    }
    if (result.has_error()) {
        Proximie::error_code error = result.error();
        // Handle the error...
    }
    if (result.has_value()) {
        int value = result.value();
        // Do something with the value...
    }

1.2.3. Object lifetimes

The Proximie SDK makes extensive use of STL “smart pointers”, namely std::shared_ptr and std::unique_ptr. A common pattern for SDK classes to have private constructors and a static factory create function that returns the smart pointer.

The SDK makes use of the concept of “not-null” pointers, which are part of the C++ Core Guidelines. See the section on not-null in the guidelines. The SDK implementation is taken from the Guidelines support library.

Due to the asynchronous nature of the SDK, shared pointers are commonly used for objects that instigate asynchronous operations. This allows an object’s creator to free their local pointer without causing the object to be destroyed whilst an async operation is still in flight. The operations themselves keep the object alive, and when all references are freed (i.e. there are guaranteed no more async operations), the object can safely be freed. This is why some object types that one might expect to use unique (i.e. one and only one owner) pointers, need to use shared pointers.

1.2.4. Asynchronous I/O

The SDK provides a broad set of features, many of which are asynchronous - e.g. HTTP requests, WebSockets, communication with various Proximie services and so on.

Underpinning the async functionality is Boost’s Asio library (see https://live.boost.org/doc/libs/release/doc/html/boost_asio.html). For the most part SDK clients do not need to be concerned by the underlying implementation.

For more details, see Async operations & threading.