UTL

Collection of self-contained header-only libraries for C++17

View on GitHub

utl::predef

<- to README.md

<- to implementation.hpp

predef module contains macros and constant expressions for detecting compilation details, while also providing several helper macros for codegen.

It uses known implementation-defined macros to deduce compilation details and abstracts them away behind a unified API.

[!Note] There exists a very similar boost library with the same name, it supports more exotic platforms, but has a different API and doesn’t provide some of the utilities defined in this module.

Definitions

// Compiler detection
#define UTL_PREDEF_COMPILER_IS_MSVC // only one of these macros will be defined
#define UTL_PREDEF_COMPILER_IS_GCC
#define UTL_PREDEF_COMPILER_IS_CLANG
#define UTL_PREDEF_COMPILER_IS_LLVM
#define UTL_PREDEF_COMPILER_IS_ICC
#define UTL_PREDEF_COMPILER_IS_PGI
#define UTL_PREDEF_COMPILER_IS_IBMCPP
#define UTL_PREDEF_COMPILER_IS_NVCC
#define UTL_PREDEF_COMPILER_IS_UNKNOWN

constexpr std::string_view compiler_name;
constexpr std::string_view compiler_full_name;

// Platform detection
#define UTL_PREDEF_PLATFORM_IS_WINDOWS_X64 // only one of these macros will be defined
#define UTL_PREDEF_PLATFORM_IS_WINDOWS_X32
#define UTL_PREDEF_PLATFORM_IS_CYGWIN
#define UTL_PREDEF_PLATFORM_IS_ANDROID
#define UTL_PREDEF_PLATFORM_IS_LINUX
#define UTL_PREDEF_PLATFORM_IS_UNIX
#define UTL_PREDEF_PLATFORM_IS_MACOS
#define UTL_PREDEF_PLATFORM_IS_UNKNOWN

constexpr std::string_view platform_name;

// Architecture detection
#define UTL_PREDEF_ARCHITECTURE_IS_X86_64 // only one of these macros will be defined
#define UTL_PREDEF_ARCHITECTURE_IS_X86_32
#define UTL_PREDEF_ARCHITECTURE_IS_ARM
#define UTL_PREDEF_ARCHITECTURE_IS_UNKNOWN

constexpr std::string_view architecture_name;

// Language standard detection
#define UTL_PREDEF_STANDARD_IS_23_PLUS // multiple of these macros can be defined
#define UTL_PREDEF_STANDARD_IS_20_PLUS
#define UTL_PREDEF_STANDARD_IS_17_PLUS
#define UTL_PREDEF_STANDARD_IS_14_PLUS
#define UTL_PREDEF_STANDARD_IS_11_PLUS
#define UTL_DEFINE_STANDARD_IS_UNKNOWN

constexpr std::string_view standard_name;

// Compilation mode detection
#define UTL_PREDEF_MODE_IS_DEBUG

constexpr bool debug;

// Optimization macros
#define UTL_PREDEF_FORCE_INLINE
#define UTL_PREDEF_ASSUME
[[noreturn]] void unreachable();

// Other utils
std::string compilation_summary();

#define UTL_PREDEF_VA_ARGS_COUNT(...) // size of __VA_ARGS__ in variadic macros

#define UTL_PREDEF_ENUM_WITH_STRING_CONVERSION(enum_name, ...)

#define UTL_PREDEF_IS_FUNCTION_DEFINED_TRAIT(function_name, return_type, ...)

Methods

Compiler detection

// Compiler detection
#define UTL_PREDEF_COMPILER_IS_MSVC
#define UTL_PREDEF_COMPILER_IS_GCC
#define UTL_PREDEF_COMPILER_IS_CLANG
#define UTL_PREDEF_COMPILER_IS_LLVM
#define UTL_PREDEF_COMPILER_IS_ICC
#define UTL_PREDEF_COMPILER_IS_PGI
#define UTL_PREDEF_COMPILER_IS_IBMCPP
#define UTL_PREDEF_COMPILER_IS_NVCC
#define UTL_PREDEF_COMPILER_IS_UNKNOWN

Only one of these macros will be defined. The macro that gets defined corresponds to the detected compiler. If no other option is suitable, unknown is used as a fallback.

This is useful for compiler-specific conditional compilation.

constexpr std::string_view compiler_name;

constexpr string that evaluates to the abbreviated name of the detected compiler.

Possible values: MSVC, GCC, clang, LLVM, ICC, PGI, IBMCPP, NVCC, <unknown>.

constexpr std::string_view compiler_full_name;

constexpr string that evaluates to the full name of the detected compiler.

Possible values: Microsoft Visual C++ Compiler, GNU C/C++ Compiler, Clang Compiler, LLVM Compiler, Inter C/C++ Compiler, Portland Group C/C++ Compiler, IBM XL C/C++ Compiler, Nvidia Cuda Compiler Driver, <unknown>.

Platform detection

// Platform detection
#define UTL_PREDEF_PLATFORM_IS_WINDOWS_X64
#define UTL_PREDEF_PLATFORM_IS_WINDOWS_X32
#define UTL_PREDEF_PLATFORM_IS_CYGWIN
#define UTL_PREDEF_PLATFORM_IS_ANDROID
#define UTL_PREDEF_PLATFORM_IS_LINUX
#define UTL_PREDEF_PLATFORM_IS_UNIX
#define UTL_PREDEF_PLATFORM_IS_MACOS
#define UTL_PREDEF_PLATFORM_IS_UNKNOWN

Only one of these macros will be defined. The macro that gets defined corresponds to the detected platform. If no other option is suitable, unknown is used as a fallback.

This is useful for platform-specific conditional compilation.

constexpr std::string_view platform_name;

constexpr string that evaluates to the name of the detected platform.

Possible values: Windows64, Windows32, Windows (CYGWIN), Android, Linux, Unix-like OS, MacOS, <unknown>.

Architecture detection

#define UTL_PREDEF_ARCHITECTURE_IS_X86_64
#define UTL_PREDEF_ARCHITECTURE_IS_X86_32
#define UTL_PREDEF_ARCHITECTURE_IS_ARM
#define UTL_PREDEF_ARCHITECTURE_IS_UNKNOWN

Only one of these macros will be defined. The macro that gets defined corresponds to the detected CPU architecture. If no other option is suitable, unknown is used as a fallback.

This is useful for architecture-specific conditional compilation.

constexpr std::string_view architecture_name;

constexpr string that evaluates to the name of the detected CPU architecture.

Possible values: x86-64, x86-32, ARM, <unknown>

Language standard detection

#define UTL_PREDEF_STANDARD_IS_23_PLUS
#define UTL_PREDEF_STANDARD_IS_20_PLUS
#define UTL_PREDEF_STANDARD_IS_17_PLUS
#define UTL_PREDEF_STANDARD_IS_14_PLUS
#define UTL_PREDEF_STANDARD_IS_11_PLUS
#define UTL_DEFINE_STANDARD_IS_UNKNOWN

Multiple of these macros can be defined. Macro that get defined correspond to the available C++ standard. If no other option is suitable, unknown is used as a fallback.

This is useful for conditional compilation based on available standard.

constexpr std::string_view standard_name;

constexpr string that evaluates to the name of the detected C++ standard. Possible values: C++23, C++20, C++17, C++14, C++11, <unknown>.

Note: Considering that this is a C++17 library, there should be no feasible way to get values below C++17, however they are still provided for the sake of implementation completeness, shall the source code be copied directly.

Compilation mode detection

#define UTL_PREDEF_MODE_IS_DEBUG

Defined when compiling in debug mode. Works as an alias for _DEBUG, provided for the sake of feature completeness.

constexpr bool debug;

constexpr bool that evaluates to true when compiling in debug mode.

This is useful for if constexpr conditional compilation of debug code.

Optimization macros

#define UTL_PREDEF_FORCE_INLINE

Hints (MSVC) or forces (GCC, clang and ICX) function inlining using compiler built-ins.

Note: Compiles to nothing if there is no suitable compiler support.

#define UTL_PREDEF_ASSUME(condition)

Equivalent to C++23 [[assume(condition)]] that supports earlier standards using MSVC and clang built-ins.

Invokes undefined behavior if statement condition evaluates to false, which provides compiler with additional optimization opportunities since implementations may assume that undefined behavior can never happen and the statement always holds.

Note: Compiles to nothing if there is no suitable compiler support.

[[noreturn]] void unreachable();

Equivalent to C++23 std::unreachable that supports earlier standards using MSVC, clang and GCC built-ins.

Compiler implementation may use this to optimize impossible code branches away.

Note: Compiles to nothing if there is no suitable compiler support.

Other utils

UTL_PREDEF_VA_ARGS_COUNT(...)

Returns number of comma-separated arguments passed. Used mainly to deduce the size of __VA_ARGS__ in variadic macros.

Note: Since macro evaluates arbitrary preprocessor text, language construct with additional comma should be surrounded by parentheses aka (std::pair<int, int>{4, 5}), since std::pair<int, int>{4, 5} from preprocessor perspective can be interpreted as 3 separate args std::pair<int, ` int>{4 and 5}`.

UTL_PREDEF_ENUM_WITH_STRING_CONVERSION(enum_name, ...)

Declares namespace enum_name that contains enumeration enum_name with members ... and methods enum_name::to_string(), enum_name::from_string() that convert enum values to corresponding std::string and back.

If no valid convertion to enum exists for arg, enum_name::from_string(arg) returns enum_name::_count.

UTL_PREDEF_IS_FUNCTION_DEFINED_TRAIT(function_name, return_type, ...)

Declares integral constant named is_function_defined_##function_name, which returns on compile-time whether function function_name() with return type return_type that accepts arguments with types ... exists or not.

Used mainly to detect presence of platform- or library-specific functions, which allows constexpr if logic based on existing methods without resorting to macros.

Note 1: Like a regular template class, this macro should be declared outside of functions.

Note 2: The usefulness of this trait is rather dubious, considering it can’t be used to conditionally compile platform-specific code with if constexpr since that would still require all branches to use existing identifiers, however since that somewhat arcane thing is already implemented there is little reason to take it out of the library, perhaps it might prove useful in some context later on.

Examples

Conditional compilation

[ Run this code ]

// Portable thread-safe 'localtime()' implementation
std::time_t timer = std::time(nullptr);
std::tm     time_moment{};

#if defined(UTL_PREDEF_COMPILER_IS_GCC) || defined(UTL_PREDEF_COMPILER_IS_CLANG)
localtime_r(&timer, &time_moment);
#elif defined(UTL_PREDEF_COMPILER_IS_MSVC)
localtime_s(&time_moment, &timer);
#else
static std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
time_moment = *std::localtime(&timer);
#endif

// GCC/clang/MSVC will usually complain when using 'std::localtime()' directly
// and recommend a thread-safe platform-specific version of it.
// Here, we choose appropriate version automatically and fallback onto a
// mutex-guarded implementation otherwise

// Get formatted date from localtime
std::array<char, sizeof("yyyy-mm-dd HH:MM:SS")> buffer;
std::strftime(buffer.data(), buffer.size(), "%Y-%m-%d %H:%M:%S", &time_moment);

std::cout << "Current time is: "<< buffer.data();

Output:

Current time is: 2024-12-01 03:39:46

Compilation summary

[ Run this code ]

std::cout << utl::predef::compilation_summary();

Output:

Compiler:          GNU C/C++ Compiler
Platform:          Linux
Architecture:      x86-64
Compiled in DEBUG: false
Compiled under OS: true
Compilation date:  Dec  1 2024 03:47:20

Counting Variadic Macro Args

[ Run this code ]

#define VARIADIC_MACRO(...) UTL_PREDEF_VA_ARGS_COUNT(__VA_ARGS__)

constexpr int args = VARIADIC_MACRO(1, 2., "3", A, B, (std::pair<int, int>{4, 5}), "12,3");

std::cout << "Size of __VA_ARGS__: " << args << "\n";

Output:

Size of __VA_ARGS__: 7

Declaring enum with string conversion

[ Run this code ]

// Outside of function
UTL_PREDEF_ENUM_WITH_STRING_CONVERSION(Sides, LEFT, RIGHT, TOP, BOTTOM)

// Inside the function
std::cout
    << "(enum -> string) conversion:\n"
    << Sides::BOTTOM << " -> " << Sides::to_string(Sides::BOTTOM) << "\n"
    << "(string -> enum) conversion:\n"
    << "BOTTOM" << " -> " << Sides::from_string("BOTTOM") << "\n";

Output:

(enum -> string) conversion:
3 -> BOTTOM
(string -> enum) conversion:
BOTTOM -> 3

Detecting existence of a function signature

[ Run this code ]

// Outside the function
UTL_PREDEF_IS_FUNCTION_DEFINED(localtime_s, int, tm*,  const time_t*)
UTL_PREDEF_IS_FUNCTION_DEFINED(localtime_r, tm*,  const time_t*, tm*)

// ...

// Inside the function
constexpr bool localtime_s_is_defined = is_function_defined_localtime_s::value;
constexpr bool localtime_r_is_defined = is_function_defined_localtime_r::value;

std::cout
    << std::boolalpha
    << "Windows localtime_s() present: " << localtime_s_is_defined << "\n"
    << "Linux   localtime_r() present: " << localtime_r_is_defined << "\n";

// Do some specific logic based on existing function
if constexpr (localtime_s_is_defined) {
    std::cout << "\n~ Some localtime_s()-specific logic ~";
}
if constexpr (localtime_r_is_defined) {
    std::cout << "\n~ Some localtime_r()-specific logic ~";
}

Output:

Windows localtime_s() present: false
Linux   localtime_r() present: true

~ Some localtime_r()-specific logic ~