Portable module

DDS discovery datacentric memory execution serialization platform protocol transport portable system toolchain

Safe DDS’ portable module provides a unique point of access to the complete set of Safe DDS’ dependencies. Following this approach, the dependency of Safe DDS on the C++ standard library is both limited and encapsulated. To facilitate customization, it is possible to override the contents of the include/portable folder, allowing for modifications or replacements of any headers found within this directory. This strategy enables the utilization of a custom C++ standard library, or even a tailored implementation to suit specific requirements.

The list of dependencies that can be overwritten or customized in Safe DDS are:

portable/BitConstants.hpp

By default it is implemented assuming a 32-bit architecture.

Needs to provide

Description

Default value

BITS_IN_LONG

Number of bits in a long

32

BITS_IN_BYTE

Number of bits in a byte

8

SHIFT_DIVISION_32

Shift division by 32

5

SHIFT_24

Shift by 24 bits

24

SHIFT_16

Shift by 16 bits

16

SHIFT_8

Shift by 8 bits

8

MASK_24_TO_32

Mask selecting bits 24th to 31st

0xFF000000

MASK_16_TO_24

Mask selecting bits 16th to 23rd

0x00FF0000

MASK_8_TO_16

Mask selecting bits 8th to 15th

0x0000FF00

MASK_0_TO_8

Mask selecting bits 0th to 7th

0x000000FF

portable/CLZ.hpp

An implementation of the counting leading zeros function.

Needs to provide

Description

Default value

clz

Count leading zeros

__builtin_clz

portable/Endianness.hpp

Endianness related definitions.

Needs to provide

Description

Default value

MACHINE_ENDIANNESS

Machine endianness as portable::Endianness

Using __BYTE_ORDER__ and __ORDER_BIG_ENDIAN__

portable/TypeTraits.hpp

Dependencies with type traits to check type structure and/or serialization possibilities using memory copy.

Needs to provide

Description

Default value

IsStandardLayout

Check if a type is standard layout

std::is_standard_layout

IsSerializableByCopy

Check if a type is serializable by copy

Using IsStandardLayout and safe_sizeof

portable/MD5.hpp

An implementation of the MD5 hash algorithm.

Needs to provide

Description

Default value

MD5

MD5 hash algorithm

By default it is implemented as defined in portable::MD5.

portable/New.hpp

A safe_new function to construct objects dynamically.

Needs to provide

Description

Default value

safe_new

Safe new in-place operator

Using placement new operator

portable/StdInt.hpp

Definition of common types and type bounds.

Needs to provide

Description

Default value

int[N]_t and uint[N]_t

Signed and unsigned integer types

std::int[N]_t and std::uint[N]_t

INT[N]_MIN, INT[N]_MAX and UINT[N]_MAX

Minimum and maximum values for integer types

std::INT[N]_MIN, std::INT[N]_MAX and std::UINT[N]_MAX

__BYTE_ORDER__

Byte order

__BYTE_ORDER__

__ORDER_BIG_ENDIAN__

Big endian order

__ORDER_BIG_ENDIAN__

uintptr_t

Unsigned integer type capable of holding a pointer

std::uintptr_t

portable/String.hpp

Definition of C standard library functions.

Needs to provide

Description

Default value

safe_memcmp

Compare memory

memcmp

safe_memcpy

Copy memory

memcpy

safe_memcpy_reverse

Copy memory in reverse

Custom implementation

safe_memset

Set memory

memset

safe_memclear

Clear memory

safe_memset

safe_memmove

Move memory

memmove

portable/Logger.hpp

Definition of the portable::Logger class and logging macros. For more information see Logging module.

Needs to provide

Description

Default value

SAFEDDS_LOG_DEBUG, SAFEDDS_LOG_INFO, SAFEDDS_LOG_WARNING and SAFEDDS_LOG_ERROR

Logging macros

vprintf-based implementation

portable/SafeCasts.hpp

Definition of safe casting functions and utilities.

Needs to provide

Description

Default value

safe_offsetof

Offset (in bytes) of a member returned as an uint32_t

Using offsetof

Override portable module

Overriding the contents of the include/portable folder is done by providing a custom include directory. To illustrate this process, consider the example of overriding the usage of the builtin __builtin_clz() in the default implementation of portable::clz(). This can be accomplished by rewriting the file include/portable/CLZ.hpp as include_overrides/safedds/portable/CLZ.hpp and subsequently incorporating it into the toolchain as follows:

# Add the custom include folder to override library headers
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include_overrides)

Logging module

Safe DDS provides a logging mechanism based on a set of macros. The logging macros shall be defined in include/portable/Logger.hpp and are used throughout the codebase.

For reference, the following macros are defined:

  • SAFEDDS_LOG_DEBUG: Debug level logging.

  • SAFEDDS_LOG_INFO: Information level logging.

  • SAFEDDS_LOG_WARNING: Warning level logging.

  • SAFEDDS_LOG_ERROR: Error level logging.

A portable::BaseLogger class is provided to ease formatting of log messages. This class provides a set of implemented operator<< functions for Safe DDS types and exposes a virtual void logger_print(const char *msg, ...) function that shall be overwritten to provide an endpoint for logging strings.

By default, the portable::Logger class is implemented using <cstdio> to print messages to the standard output.

An example of a custom logger as the following one can be injected into the library by means of overriding the portable module:

#include <safedds/portable/BaseLogger.hpp>

class CustomLogger :
    public portable::BaseLogger
{
    CustomLogger(
            portable::LogLevel level)
        : portable::BaseLogger(level)
    {
    }

    //! BaseLogger implementation.
    void logger_print(
            const char* format,
            ...) noexcept override
    {
        // Will print info, warning and error messages in screen
        // and store debug messages in a file

        va_list args;
        va_start(args, format);

        if (level_ == portable::LogLevel::DEBUG)
        {
            char buffer[1000];
            vsprintf(buffer, format, args);
            save_in_file("debug.log", buffer);
        }
        else
        {
            vprintf(format, args);
        }

        va_end(args);
    }

};

//! Log macro info.
#define SAFEDDS_LOG_INFO(message) \
    CustomLogger(portable::LogLevel::INFO) << message;

//! Log macro warning.
#define SAFEDDS_LOG_WARNING(message) \
    CustomLogger(portable::LogLevel::WARNING) << message;

//! Log macro error.
#define SAFEDDS_LOG_ERROR(message) \
    CustomLogger(portable::LogLevel::ERROR) << message;

//! Log macro debug.
#define SAFEDDS_LOG_DEBUG(message) \
    CustomLogger(portable::LogLevel::DEBUG) << message;