4
\$\begingroup\$

As we know there is no standard way in compilers to standardize macros with compilers and different operating systems

So I created this library that contains standardized compiler, os, attributes macros and more.

Github Repository


Contents :

  • Architecture
    This section contains some macros that can detect more than 20 architecture.

  • Operating System
    This section contains some macros that can detect more than 66 operating system.

  • Compiler
    This section contains some macros that can detect more than 75 compiler and its version.

  • Library
    This section contains some macros that can detect more than 11 library and its version.

  • Standard
    This section contains the standard macros more than 35 macro.

  • Has Macros
    This section contains the __has macros more than 4 macros.

  • Attributes
    This section contains some standard and common attributes more than 13 attribute.

  • Controllers
    This section contains some macros that can manage some things in C/C++ more than 7 controllers.

  • Builtin
    This section contains some cross-platform builtin functions more than 4 functions.

  • Helpers Macros
    This section contains some macros that may be useful while writting this library.


Quick Examples

  • Architecture

    /*                  Print architecture name                   */
    cout << x_arch                    << endl; // 👉 "x86_64"
    
    /*       Check for specific architecture using ID/Macro       */
    #ifdef x_arch_<ID>
    ...
    
  • Operating System

    /*                 Print operating system name                */
    cout << x_os                      << endl; // 👉 "GNU/Linux"
    
    /*            Check for specific OS using ID/Macro            */
    #ifdef x_os_<ID>
    ...
    
  • Compiler

    /*                    Print compiler info                     */
    cout << x_compiler                << endl; // 👉 "GCC"
    cout << x_compiler_major          << endl; // 👉 12
    cout << x_compiler_minor          << endl; // 👉 1
    cout << x_compiler_patch          << endl; // 👉 0
    cout << x_compiler_version        << endl; // 👉 201392128
    cout << x_compiler_org_version    << endl; // 👉 12
    
    /*         Check for specific compiler using ID/Macro         */
    #ifdef x_compiler_<ID>
    ...
    
  • Library

    /*                       Print lib info                       */
    cout << x_lib                     << endl; // 👉 "GNU glibc"
    cout << x_lib_major               << endl; // 👉 2
    cout << x_lib_minor               << endl; // 👉 35
    cout << x_lib_patch               << endl; // 👉 0
    cout << x_lib_version             << endl; // 👉 35848192
    cout << x_lib_org_version         << endl; // 👉 2
    
    /*           Check for specific lib using ID/Macro            */
    #ifdef x_lib_<ID>
    ...
    
  • Standard

    /*                       Print some info                      */
    cout << x_std_CPP                 << endl; // 👉 20
    cout << x_std_FILE                << endl; // 👉 "path/main.cpp"
    cout << x_std_FUNC                << endl; // 👉 "main()"
    cout << x_std_LINE                << endl; // 👉 4
    cout << x_std_TIME                << endl; // 👉 "10:19:16"
    cout << x_std_DATE                << endl; // 👉 "Oct 30 2022"
    
    /*           Check for specific Version                       */
    #if x_std_CPP >= 20 // you can use (x_std_C) for c language
    ...
    
  • Has-Macros

    /*                    Check for Attribute                     */
    #if x_has_attr(noreturn)
    ...
    
    /*                    Check for Feature                       */
    #if x_has_feature(cxx_rvalue_references)
    ...
    
    /*                    Check for Include                       */
    #if x_has_include("iostream")
    ...
    
    /*                    Check for Builtin                       */
    #if x_has_builtin(__builtin_clz)
    ...
    
  • Attributes

    /*                    Usage of Attributes                     */
    x_attr_deprecated("old version")
    x_attr_inline
    x_attr_hidden
    void func(x_attr_maybe_unused int i = 0)
    ...
    
  • Controllers

    /*                   Manage Exceptions                        */
    x_constexpr void func() x_noexcept
    // IF C++ && x_exceptions  => constexpr void func() noexcept
    // ELSE                    => void func()
    ...
    
    /*                    Throw An Exception                      */
    x_throw(exception);
    // IF C++ && x_exceptions  => throw an exception
    // ELSE IF C++             => assert an error with exception message
    // ELSE                    => assert an error without        message
    
  • Builtin

    /*                      Usage of Builtin                      */
    cout << x_builtin_clz  (987654321) << endl; // 👉 2
    cout << x_builtin_clzll(987654321) << endl; // 👉 34
    cout << x_builtin_ctz  (987654321) << endl; // 👉 0
    cout << x_builtin_ctzll(987654321) << endl; // 👉 0
    
  • Helpers Macros

    /*                      C/C++ Static Cast                     */
    int var = x_scast(int, 0);
    
    /*        Version Normalization (major, minor, patch)         */
    #define version x_ver(9,3,7)
    
    /*        Check whether the language used is cpp or not       */
    #if x_is_cpp
    ...
    

CODE

It is best to see the code on GitHub

predefined.h

// predefined.h
#ifndef X___H_PREDEFINED
#define X___H_PREDEFINED



/**
 * @file      predefined.h
 * @brief     C++ Predefined Macros (Cross-Platform)
 * @author    [xeerx] by Maysara Elshewehy ([email protected])
 * @see       https://github.com/xeerx/cpp-predefined-macros
*/


#include "helper.h"
#include "architecture.h"
#include "os.h"
#include "compiler.h"
#include "library.h"
#include "standard.h"
#include "has.h"
#include "attributes.h"
#include "controllers.h"
#include "builtin.h"



#endif

helper.h

// helper.h
#ifndef X___H_PREDEFINED_HELPER
#define X___H_PREDEFINED_HELPER
 


// Check whether the language used is cpp or not
#ifdef __cplusplus
#define x_is_cpp 1
#else
#define x_is_cpp 0
#endif


// C/C++ Static Cast
#if x_is_cpp
#define x_scast(type,value) static_cast<type>(value)
#else
#define x_scast(type,value) (type)value
#endif


// Version Normalization
#define x_ver(major,minor,patch) (((major) << 24) + ((minor) << 16) + (patch))



#endif

architecture.h

// architecture.h
#ifndef X___H_PREDEFINED_ARCHITECTURE
#define X___H_PREDEFINED_ARCHITECTURE
 


/* -------------------------------------------------------------------------- */
/*                               Implementations                              */
/* -------------------------------------------------------------------------- */
#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
#define x_arch_X86_64 1
#define x_arch "x86_64"

#elif defined(__aarch64__)
#define x_arch_ARM64 1
#define x_arch "ARM64"

#elif defined(__arm__) || defined(__thumb__) || defined(__TARGET_ARCH_ARM) || defined(__TARGET_ARCH_THUMB) || defined(_ARM) || defined(_M_ARM) || defined(_M_ARMT) || defined(__arm) || defined(_M_ARM)defined(__ARM_ARCH_2__) || defined(__ARM_ARCH_3__) || defined(__ARM_ARCH_3M__) || defined(__ARM_ARCH_4T__) || defined(__TARGET_ARM_4T) || defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5E__) || defined(__ARM_ARCH_5T__) || defined(__ARM_ARCH_5TE__) || defined(__ARM_ARCH_5TEJ__) || defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) || defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7S__)
#define x_arch_ARM 1
#define x_arch "ARM"

#elif defined(i386) || defined(__i386) || defined(__i386__) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(__i386) || defined(__i386) || defined(__IA32__) || defined(_M_I86) || defined(_M_IX86) || defined(_M_IX86) || defined(__X86__) || defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) || defined(__I86__) || defined(__INTEL__) || defined(__386) || defined(_M_IX86) || defined(__I86__)
#define x_arch_I386 1
#define x_arch "i386"

#elif defined(__mips__) || defined(mips) || defined(_R3000) || defined(_R4000) || defined(_R5900) || defined(__mips) || defined(__mips) || defined(__MIPS__) || defined(_MIPS_ISA)defined(__mips)defined(_MIPS_ISA_MIPS1) || defined(_MIPS_ISA_MIPS1) || defined(_R3000) || defined(_MIPS_ISA_MIPS2) || defined(__MIPS_ISA2__) || defined(_R4000) || defined(_MIPS_ISA_MIPS3) || defined(__MIPS_ISA3__) || defined(_MIPS_ISA_MIPS4) || defined(__MIPS_ISA4__) || defined(_MIPS_ISA_MIPS4) || defined(__MIPS_ISA4__)
#define x_arch_MIPS 1
#define x_arch "MIPS"

#elif defined(__powerpc) || defined(__powerpc__) || defined(__powerpc64__) || defined(__POWERPC__) || defined(__ppc__) || defined(__ppc64__) || defined(__PPC__) || defined(__PPC64__) || defined(_ARCH_PPC) || defined(_ARCH_PPC64) || defined(_M_PPC) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PPC64) || defined(__PPCGECKO__) || defined(__PPCBROADWAY__) || defined(_XENON) || defined(__ppc) || defined(_M_PPC)defined(_ARCH_440) || defined(_ARCH_450) || defined(__ppc601__) || defined(_ARCH_601) || defined(__ppc603__) || defined(_ARCH_603) || defined(__ppc604__) || defined(_ARCH_604)
#define x_arch_POWERPC 1
#define x_arch "PowerPC"

#elif defined(__sparc) || defined(__sparc_v8__) || defined(__sparc_v9__) || defined(__sparcv8) || defined(__sparcv9) || defined(__sparcv8) || defined(__sparc_v8__) || defined(__sparcv9) || defined(__sparc_v9__)
#define x_arch_SPARC 1
#define x_arch "SPARC"

#elif defined(__alpha__) || defined(__alpha) || defined(_M_ALPHA) || defined(__alpha_ev4__) || defined(__alpha_ev5__) || defined(__alpha_ev6__)
#define x_arch_ALPHA 1
#define x_arch "Alpha"

#elif defined(__bfin) || defined(__BFIN__)
#define x_arch_BLACKFIN 1
#define x_arch "Blackfin"

#elif defined(__convex__) || defined(__convex_c1__) || defined(__convex_c2__) || defined(__convex_c32__) || defined(__convex_c34__) || defined(__convex_c38__)
#define x_arch_CONVEX 1
#define x_arch "Convex"

#elif defined(__epiphany__)
#define x_arch_EPIPHANY 1
#define x_arch "Epiphany"

#elif defined(__hppa__) || defined(__HPPA__) || defined(__hppa) || defined(_PA_RISC1_0) || defined(_PA_RISC1_1) || defined(__HPPA11__) || defined(__PA7100__) || defined(_PA_RISC2_0) || defined(__RISC2_0__) || defined(__HPPA20__) || defined(__PA8000__)
#define x_arch_HPPA 1
#define x_arch "HP/PA RISC"

#elif defined(__ia64__) || defined(_IA64) || defined(__IA64__) || defined(__ia64) || defined(_M_IA64) || defined(_M_IA64) || defined(_M_IA64) || defined(__itanium__) || defined(_M_IA64)
#define x_arch_IA64 1
#define x_arch "IA64"

#elif defined(__m68k__) ||  || defined(M68000) || defined(__MC68K__) || defined(__mc68000__) || defined(__MC68000__) || defined(__mc68010__) || defined(__mc68020__) || defined(__MC68020__) || defined(__mc68030__) || defined(__MC68030__) || defined(__mc68040__) || defined(__mc68060__) 
#define x_arch_M68K 1
#define x_arch "M68K"

#elif defined(pyr)
#define x_arch_PYRAMID 1
#define x_arch "Pyramid 9810"

#elif defined(__THW_RS6000) || defined(_IBMR2) || defined(_POWER) || defined(_ARCH_PWR) || defined(_ARCH_PWR2) || defined(_ARCH_PWR3) || defined(_ARCH_PWR4) || defined(__sparc__)
#define x_arch_RS6000 1
#define x_arch "RS/6000"

#elif defined(__sh__) || defined(__sh1__) || defined(__sh2__) || defined(__sh3__) || defined(__SH3__) || defined(__SH4__) || defined(__SH5__)
#define x_arch_SUPERH 1
#define x_arch "SuperH"

#elif defined(__370__) || defined(__THW_370__) || defined(__s390__) || defined(__s390x__) || defined(__zarch__) || defined(__SYSC_ZARCH__)
#define x_arch_SYSTEMZ 1
#define x_arch "SystemZ"

#elif defined(_TMS320C2XX) || defined(__TMS320C2000__) || defined(_TMS320C5X) || defined(__TMS320C55X__) || defined(_TMS320C6X) || defined(__TMS320C6X__) || defined(_TMS320C28X) || defined(_TMS320C5XX) || defined(__TMS320C55X__) || defined(_TMS320C6200) || defined(_TMS320C6400) || defined(_TMS320C6400_PLUS) || defined(_TMS320C6600) || defined(_TMS320C6700) || defined(_TMS320C6700_PLUS) || defined(_TMS320C6740)
#define x_arch_TMS320 1
#define x_arch "TMS320"

#elif defined(__TMS470__)
#define x_arch_TMS470 1
#define x_arch "TMS470"
#endif


/* -------------------------------------------------------------------------- */
/*                                    Check                                   */
/* -------------------------------------------------------------------------- */
#ifndef x_arch
#define x_arch ""
#endif



#endif

os.h

// os.h
#ifndef X___H_PREDEFINED_OS
#define X___H_PREDEFINED_OS



/* -------------------------------------------------------------------------- */
/*                               Implementations                              */
/* -------------------------------------------------------------------------- */
#if defined(__CYGWIN__)
#define x_os_CYGWIN 1
#define x_os "Cygwin"

#elif defined(_WIN32_WCE) || defined(WIN32_PLATFORM_HPC2000) || defined(WIN32_PLATFORM_HPCPRO) || defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)
#define x_os_WINDOWSCE 1
#define x_os "Windows CE"

#elif defined(_WIN16) || defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__)
#define x_os_WINDOWS 1
#define x_os "Windows"

#elif defined(__APPLE__) && defined(__MACH__)
#define x_os_APPLE 1
#define x_os "Apple (mac/i/tv)OS"

#elif defined(__ANDROID__) || defined(__ANDROID_API__)
#define x_os_ANDROID 1
#define x_os "Android"

#elif defined(__OpenBSD__) || defined(OpenBSD3_1) || defined(OpenBSD3_9)
#define x_os_OPENBSD 1
#define x_os "OpenBSD"

#elif defined(__NetBSD__) || defined(__NetBSD_Version__) || defined(NetBSD0_8) || defined(NetBSD0_9) || defined(NetBSD1_0) || defined(NetBSD1_0)
#define x_os_NETBSD 1
#define x_os "NetBSD"

#elif defined(__gnu_hurd__)
#define x_os_GNU_HURD 1
#define x_os "GNU/Hurd"

#elif defined(sun) || defined(__sun) || defined(__SunOS_5_7) || defined(__SunOS_5_8)
#define x_os_SOLARIS 1
#define x_os "Sun Solaris"

#elif defined(__FreeBSD_kernel__) && defined(__GLIBC__)
#define x_os_FREEBSD_KERNEL 1
#define x_os "GNU/FreeBSD Kernel"

#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD_version)
#define x_os_FREEBSD 1
#define x_os "FreeBSD"

#elif defined(MSDOS) || defined(__MSDOS__) || defined(_MSDOS) || defined(__DOS__)
#define x_os_MSDOS 1
#define x_os "MSDOS"

#elif defined(macintosh) || defined(Macintosh)
#define x_os_MACINTOSH 1
#define x_os "Macintosh"

#elif defined(__Lynx__)
#define x_os_LYNX 1
#define x_os "Lynx"

#elif defined(__minix)
#define x_os_MINIX 1
#define x_os "Minix"

#elif defined(__QNX__) || defined(__QNXNTO__) || defined(_NTO_VERSION) ||defined(BBNDK_VERSION_CURRENT)
#define x_os_QNX 1
#define x_os "QNX"

#elif defined(__bsdi__)
#define x_os_BSDI 1
#define x_os "BSD/OS"

#elif defined(__gnu_linux__)
#define x_os_GNU_LINUX 1
#define x_os "GNU/Linux"

#elif defined(BSD) || defined(BSD4_2) || defined(BSD4_3) || defined(BSD4_4) || defined(_SYSTYPE_BSD)
#define x_os_BSD 1
#define x_os "BSD"

#elif defined(__GNU__) || defined(__GLIBC__)
#define x_os_GNU 1
#define x_os "GNU"

#elif defined(__linux__) || defined(linux) || defined(__linux)
#define x_os_LINUX 1
#define x_os "Linux"

#elif defined(__unix__) || defined(__unix)
#define x_os_UNIX 1
#define x_os "Unix"

#elif defined(_AIX) || defined(__TOS_AIX__) || defined(_AIX3) || defined(_AIX32) || defined(_AIX41) || defined(_AIX43)
#define x_os_AIX 1
#define x_os "AIX"

#elif defined(UTS)
#define x_os_AMDAHL_UTS 1
#define x_os "Amdahl UTS"

#elif defined(AMIGA) || defined(__amigaos__)
#define x_os_AMIGA 1
#define x_os "AmigaOS"

#elif defined(aegis)
#define x_os_AEGIS 1
#define x_os "Apollo AEGIS"

#elif defined(apollo)
#define x_os_APOLLO 1
#define x_os "Apollo Domain/OS"

#elif defined(__BEOS__)
#define x_os_BEOS 1
#define x_os "BeOS"

#elif defined(__bg__) || defined(__bgq__) || defined(__THW_BLUEGENE__) || defined(__TOS_BGQ__)
#define x_os_BLUE_GENE 1
#define x_os "Blue Gene"

#elif defined(__convex__)
#define x_os_CONVEX 1
#define x_os "ConvexOS"

#elif defined(DGUX) || defined(__DGUX__) || defined(__dgux__)
#define x_os_DGUX 1
#define x_os "DG/UX"

#elif defined(__DragonFly__)
#define x_os_DRAGONFLY 1
#define x_os "DragonFly"

#elif defined(_SEQUENT_) || defined(sequent)
#define x_os_DYNIX 1
#define x_os "DYNIX/ptx (SEQUENT)"

#elif defined(__ECOS)
#define x_os_ECOS 1
#define x_os "eCos"

#elif defined(__EMX__)
#define x_os_EMX 1
#define x_os "EMX Environment"

#elif defined(__hiuxmpp)
#define x_os_HIUXMPP 1
#define x_os "HI-UX MPP"

#elif defined(_hpux) || defined(hpux) || defined(__hpux)
#define x_os_HPUX 1
#define x_os "HP-UX"

#elif defined(__OS400__)
#define x_os_IBM_OS_400 1
#define x_os "IBM OS 400"

#elif defined(__INTEGRITY)
#define x_os_INTEGRITY 1
#define x_os "INTEGRITY"

#elif defined(__INTERIX)
#define x_os_INTERIX 1
#define x_os "Interix Environment"

#elif defined(sgi) || defined(__sgi)
#define x_os_IRIX 1
#define x_os "IRIX (sgi)"

#elif defined(__OS9000) || defined(_OSK)
#define x_os_OS9000 1
#define x_os "Microware OS9"

#elif defined(__MORPHOS__)
#define x_os_MORPHOS 1
#define x_os "Morph OS"

#elif defined(mpeix) || defined(__mpexl)
#define x_os_MPEIX 1
#define x_os "MPE/ix"

#elif defined(__TANDEM)
#define x_os_TANDEM 1
#define x_os "NonStop (TANDEM)"

#elif defined(__nucleus__)
#define x_os_NUCLEUS 1
#define x_os "Nucleus RTOS"

#elif defined(OS2) || defined(_OS2) || defined(__OS2__) || defined(__TOS_OS2__)
#define x_os_OS2 1
#define x_os "OS/2"

#elif defined(__palmos__)
#define x_os_PALMOS 1
#define x_os "PalmOS"

#elif defined(EPLAN9)
#define x_os_PLAN9 1
#define x_os "Plan9 (EPLAN9)"

#elif defined(pyr)
#define x_os_PYRAMID 1
#define x_os "Pyramid DC/OSx"

#elif defined(sinux)
#define x_os_RELIANT 1
#define x_os "Reliant Unix (sinux)"

#elif defined(M_I386) || defined(M_XENIX) || defined(_SCO_DS)
#define x_os_M_I386 1
#define x_os "SCO OpenServer (M_I386)"

#elif defined(__VOS__)
#define x_os_STRATUS 1
#define x_os "Stratus VOS"

#elif defined(__sysv__) || defined(__SVR4) || defined(__svr4__) || defined(_SYSTYPE_SVR4)
#define x_os_SVR4 1
#define x_os "SVR4 Environment (sysv)"

#elif defined(__SYLLABLE__)
#define x_os_SYLLABLE 1
#define x_os "Syllable"

#elif defined(__SYMBIAN32__)
#define x_os_SYMBIAN 1
#define x_os "Symbian 32"

#elif defined(__osf__) || defined(__osf)
#define x_os_TRU64 1
#define x_os "Tru64 OSF/1"

#elif defined(ultrix) || defined(__ultrix) || defined(__ultrix__) || (defined(unix) && defined(vax))
#define x_os_ULTRIX 1
#define x_os "Ultrix"

#elif defined(_UNICOS)
#define x_os_UNICOS 1
#define x_os "UNICOS"

#elif defined(_CRAY) || defined(__crayx1)
#define x_os_CRAY 1
#define x_os "UNICOS/mp (CRAY)"

#elif defined(sco) || defined(_UNIXWARE7)
#define x_os_UNIXWARE 1
#define x_os "UnixWare (SCO)"

#elif defined(_UWIN)
#define x_os_UWIN 1
#define x_os "U/WIN Environment"

#elif defined(VMS) || defined(__VMS) ||defined(__VMS_VER)
#define x_os_VMS 1
#define x_os "VMS"

#elif defined(__VXWORKS__) || defined(__vxworks) || defined(_WRS_VXWORKS_MAJOR) || defined(_WRS_VXWORKS_MINOR) || defined(_WRS_VXWORKS_MAINT) || defined(__RTP__) || defined(_WRS_KERNEL)
#define x_os_VXWORKS 1
#define x_os "VxWorks"

#elif defined(_WINDU_SOURCE)
#define x_os_WINDU 1
#define x_os "Wind/U Environment (SOURCE)"

#elif defined(__MVS__) || defined(__HOS_MVS__) || defined(__TOS_MVS__)
#define x_os_ZOS 1
#define x_os "z/OS (MVS)"
#endif


/* -------------------------------------------------------------------------- */
/*                                    Check                                   */
/* -------------------------------------------------------------------------- */
#ifndef x_os
#define x_os ""
#endif



#endif

compiler.h

// compiler.h
#ifndef X___H_PREDEFINED_COMPILER
#define X___H_PREDEFINED_COMPILER



#include "helper.h"



/* -------------------------------------------------------------------------- */
/*                               Implementation                               */
/* -------------------------------------------------------------------------- */
#if defined(__GNUC__) || defined(__GNUC_MINOR__) || defined(__GNUC_PATCHLEVEL__) || defined(__GNUC_VERSION__)
#define x_compiler_GCC 1
#define x_compiler "GCC"
#define x_compiler_org_version __GNUC__
#define x_compiler_major x_scast(int,__GNUC__)
#define x_compiler_minor x_scast(int,__GNUC_MINOR__)
#ifdef __GNUC_PATCHLEVEL__
#define x_compiler_patch x_scast(int,__GNUC_PATCHLEVEL__)
#endif


#elif defined(__clang__) || defined(__clang_major__) || defined(__clang_minor__) || defined(__clang_patchlevel__)
#define x_compiler_CLANG 1
#define x_compiler "Clang"
#define x_compiler_org_version __clang_major__
#define x_compiler_major x_scast(int,__clang_major__)
#define x_compiler_minor x_scast(int,__clang_minor__)
#if defined(__clang_patchlevel__)
#define x_compiler_patch x_scast(int,__clang_patchlevel__)
#endif


#elif defined(_MSC_VER) || defined(_MSC_FULL_VER) || defined(_MSC_BUILD)
#define x_compiler_MICROSOFT_VISUAL 1
#define x_compiler "Microsoft Visual C++"
#define x_compiler_org_version _MSC_VER
#define x_compiler_major x_scast(int,(_MSC_VER / 100))
#define x_compiler_minor x_scast(int,(_MSC_VER % 100))
#ifdef _MSC_FULL_VER
#define x_compiler_patch x_scast(int,_MSC_FULL_VER % 100000)
#endif


#elif defined(__MINGW32__) || defined(__MINGW32_MAJOR_VERSION) || defined(__MINGW32_MINOR_VERSION)
#define x_compiler_MINGW 1
#define x_compiler "MinGW"
#define x_compiler_org_version __MINGW32_MAJOR_VERSION
#include <stdlib.h>
#define x_compiler_major x_scast(int,__MINGW32_MAJOR_VERSION)
#define x_compiler_minor x_scast(int,__MINGW32_MINOR_VERSION)


#elif defined(__MINGW64__) || defined(__MINGW64_VERSION_MAJOR) || defined(__MINGW64_VERSION_MINOR)
#define x_compiler_MINGW 1
#define x_compiler "MinGW"
#define x_compiler_org_version __MINGW64_VERSION_MAJOR
#include <stdlib.h>
#define x_compiler_major x_scast(int,__MINGW64_VERSION_MAJOR)
#define x_compiler_minor x_scast(int,__MINGW64_VERSION_MINOR)


#elif defined(__INTEL_COMPILER) || defined(__ICC) || defined(__ECC) || defined(__ICL) || defined(__INTEL_COMPILER_BUILD_DATE)
#define x_compiler_INTEL 1
#define x_compiler "Intel C/C++"
#define x_compiler_org_version __INTEL_COMPILER
#define x_compiler_major x_scast(int,(__INTEL_COMPILER / 100))
#define x_compiler_minor x_scast(int,(__INTEL_COMPILER / 10) % 10)
#define x_compiler_patch x_scast(int,__INTEL_COMPILER % 10)


#elif defined(__CC_ARM) || defined(__ARMCC_VERSION)
#define x_compiler_ARM_COMPILER 1
#define x_compiler "ARM Compiler"
#define x_compiler_org_version __ARMCC_VERSION
#define x_compiler_major x_scast(int,__ARMCC_VERSION / 100000)
#define x_compiler_minor x_scast(int,(__ARMCC_VERSION / 10000) % 10)
#define x_compiler_patch x_scast(int,(__ARMCC_VERSION / 1000) % 10)


#elif defined(__CMB__)
#define x_compiler_ALTIUM_MICROBLAZE 1
#define x_compiler "Altium MicroBlaze C"
#define x_compiler_org_version __BUILD__
#define x_compiler_major x_scast(int,__VERSION__ / 1000)
#define x_compiler_minor x_scast(int,__VERSION__ % 1000)
#define x_compiler_patch x_scast(int,__REVISION__)


#elif defined(__CHC__)
#define x_compiler_ALTIUM_C_TO_HARDWARE 1
#define x_compiler "Altium C-to-Hardware"
#define x_compiler_org_version __BUILD__
#define x_compiler_major x_scast(int,__VERSION__ / 1000)
#define x_compiler_minor x_scast(int,__VERSION__ % 1000)
#define x_compiler_patch x_scast(int,__REVISION__)


#elif defined(_UCC) || defined(_MAJOR_REV) || defined(_MINOR_REV)
#define x_compiler_ULTIMATE 1
#define x_compiler "Ultimate C/C++"
#define x_compiler_org_version _MAJOR_REV
#define x_compiler_major x_scast(int,_MAJOR_REV)
#define x_compiler_minor x_scast(int,_MINOR_REV)


#elif defined(AZTEC_C) || defined(__AZTEC_C__)
#define x_compiler_AZTEC 1
#define x_compiler "Aztec C"
#define x_compiler_org_version __VERSION
#define x_compiler_major x_scast(int,__VERSION / 100)
#define x_compiler_minor x_scast(int,__VERSION % 100)


#elif defined(__COMO__) || defined(__COMO_VERSION__)
#define x_compiler_COMEAU 1
#define x_compiler "Comeau C++"
#define x_compiler_org_version __COMO_VERSION__
#define x_compiler_major x_scast(int,__COMO_VERSION__ / 100)
#define x_compiler_minor x_scast(int,__COMO_VERSION__ % 100)


#elif defined(__DECC) || defined(__DECC_VER) || defined(__DECCXX) || defined(__DECCXX_VER) || defined(__VAXC) || defined(VAXC)
#define x_compiler_COMPAQ 1
#define x_compiler "Compaq C/C++"
#define x_compiler_org_version __DECC_VER
#define x_compiler_major x_scast(int,__DECC_VER / 10000000)
#define x_compiler_minor x_scast(int,(__DECC_VER / 100000) % 100)
#define x_compiler_patch x_scast(int,__DECC_VER % 10000)


#elif defined(_CRAYC)
#define x_compiler_CRAY 1
#define x_compiler "Cray C"
#define x_compiler_org_version _RELEASE
#define x_compiler_major x_scast(int,_RELEASE)
#define x_compiler_minor x_scast(int,_RELEASE_MINOR)


#elif defined(__DCC__)
#define x_compiler_DIAB 1
#define x_compiler "Diab C/C++"
#define x_compiler_org_version __VERSION_NUMBER__
#define x_compiler_major x_scast(int,__VERSION_NUMBER__ / 1000)
#define x_compiler_minor x_scast(int,(__VERSION_NUMBER__ / 100) % 10)
#define x_compiler_patch x_scast(int,__VERSION_NUMBER__ % 100)


#elif defined(__SYSC__) || defined(__SYSC_VER__)
#define x_compiler_DIGNUS_SYSTEMS 1
#define x_compiler "Dignus Systems C++"
#define x_compiler_org_version __SYSC_VER__
#define x_compiler_major x_scast(int,__SYSC_VER__ / 10000)
#define x_compiler_minor x_scast(int,(__SYSC_VER__ / 100) % 100)
#define x_compiler_patch x_scast(int,__SYSC_VER__ % 100)


#elif defined(__DJGPP__) || defined(__DJGPP_MINOR__) || defined(__GO32__)
#define x_compiler_DJGPP 1
#define x_compiler "DJGPP"
#define x_compiler_org_version __DJGPP__
#define x_compiler_major x_scast(int,__DJGPP__)
#define x_compiler_minor x_scast(int,__DJGPP_MINOR__)


#elif defined(__EDG__) || defined(__EDG_VERSION__)
#define x_compiler_EDG 1
#define x_compiler "EDG C++"
#define x_compiler_org_version __EDG_VERSION__
#define x_compiler_major x_scast(int,__EDG_VERSION__ / 100)
#define x_compiler_minor x_scast(int,__EDG_VERSION__ % 100)


#elif defined(__PATHCC__) || defined(__PATHCC_MINOR__) || defined(__PATHCC_PATCHLEVEL__)
#define x_compiler_EKOPATH 1
#define x_compiler "EKOPath"
#define x_compiler_org_version __PATHCC__
#define x_compiler_major x_scast(int,__PATHCC__)
#define x_compiler_minor x_scast(int,__PATHCC_MINOR__)
#define x_compiler_patch x_scast(int,__PATHCC_PATCHLEVEL__)


#elif defined(__ghs__) || defined(__GHS_VERSION_NUMBER__) || defined(__GHS_REVISION_DATE__) || defined(__GHS_VERSION_NUMBER__)
#define x_compiler_GREEN_HILL 1
#define x_compiler "Green Hill C/C++"
#define x_compiler_org_version __GHS_VERSION_NUMBER__
#define x_compiler_major x_scast(int,__GHS_VERSION_NUMBER__ / 100)
#define x_compiler_minor x_scast(int,(__GHS_VERSION_NUMBER__ / 10) % 10)
#define x_compiler_patch x_scast(int,__GHS_VERSION_NUMBER__ % 10)


#elif defined(__HP_aCC)
#define x_compiler_HP_ACPP 1
#define x_compiler "HP aC++"
#define x_compiler_org_version __HP_aCC
#define x_compiler_major x_scast(int,__HP_aCC / 10000)
#define x_compiler_minor x_scast(int,(__HP_aCC / 100) % 100)
#define x_compiler_patch x_scast(int,__HP_aCC % 100)


#elif defined(__IAR_SYSTEMS_ICC__) || defined(__VER__)
#define x_compiler_IAR 1
#define x_compiler "IAR C/C++"
#define x_compiler_org_version __VER__
#define x_compiler_major x_scast(int,__VER__ / 100)
#define x_compiler_minor x_scast(int,__VER__ % 100)


#elif defined(__IBMC__)
#define x_compiler_IBM_ZOS_C
#define x_compiler "IBM z/OS C"
#define x_compiler_org_version __IBMC__
#define x_compiler_major x_scast(int,(__IBMC__ / 1000) % 10)
#define x_compiler_minor x_scast(int,(__IBMC__ / 10)  % 100)


#elif defined(__IBMCPP__)
#define x_compiler_IBM_ZOS_CPP
#define x_compiler "IBM z/OS C++"
#define x_compiler_org_version __IBMCPP__
#define x_compiler_major x_scast(int,(__IBMCPP__ / 1000) % 10)
#define x_compiler_minor x_scast(int,(__IBMCPP__ / 10)  % 100)


#elif defined(__CA__) || defined(__KEIL__)
#define x_compiler_KEIL_CARM 1
#define x_compiler "KEIL CARM"
#define x_compiler_org_version __CA__
#define x_compiler_major x_scast(int,__CA__ / 100)
#define x_compiler_minor x_scast(int,__CA__ % 100)

#elif defined(__C166__)
#define x_compiler_KEIL_C166 1
#define x_compiler "KEIL C166"
#define x_compiler_org_version __C166__
#define x_compiler_major x_scast(int,__C166__ / 100)
#define x_compiler_minor x_scast(int,__C166__ % 100)


#elif defined(__C51__) || defined(__CX51__)
#define x_compiler_KEIL_C51 1
#define x_compiler "KEIL C51"
#define x_compiler_org_version __C51__
#define x_compiler_major x_scast(int,__C51__ / 100)
#define x_compiler_minor x_scast(int,__C51__ % 100)


#elif defined(__sgi) || defined(sgi) || defined(_SGI_COMPILER_VERSION)
#define x_compiler_MIPSPRO 1
#define x_compiler "MIPSPro"
#ifdef _SGI_COMPILER_VERSION
#define x_compiler_org_version _SGI_COMPILER_VERSION
#define x_compiler_major x_scast(int,_SGI_COMPILER_VERSION / 100)
#define x_compiler_minor x_scast(int,(_SGI_COMPILER_VERSION / 10) % 10)
#define x_compiler_patch x_scast(int,_SGI_COMPILER_VERSION % 10)
#else
#define x_compiler_org_version _COMPILER_VERSION
#define x_compiler_major x_scast(int,_COMPILER_VERSION / 100)
#define x_compiler_minor x_scast(int,(_COMPILER_VERSION / 10) % 10)
#define x_compiler_patch x_scast(int,_COMPILER_VERSION % 10)
#endif


#elif defined(__OPEN64__) || defined(__OPENCC__) || defined(__OPENCC_MINOR__) || defined(__OPENCC_PATCHLEVEL__)
#define x_compiler_OPEN64 1
#define x_compiler "Open64"
#define x_compiler_org_version __OPENCC__
#define x_compiler_major x_scast(int,__OPENCC__)
#define x_compiler_minor x_scast(int,__OPENCC_MINOR__   )
#define x_compiler_patch x_scast(int,__OPENCC_PATCHLEVEL__)


#elif defined(__POCC__) 
#define x_compiler_PALM 1
#define x_compiler "Palm C"
#define x_compiler_org_version __POCC__
#define x_compiler_major x_scast(int,__POCC__ / 100)
#define x_compiler_minor x_scast(int,__POCC__ % 100)


#elif defined(__PGI) || defined(__PGIC__) || defined(__PGIC_MINOR__) || defined(__PGIC_PATCHLEVEL__)
#define x_compiler_PORTLAND_GROUP 1
#define x_compiler "Portland Group"
#define x_compiler_org_version __PGIC__
#define x_compiler_major x_scast(int,__PGIC__)
#define x_compiler_minor x_scast(int,__PGIC_MINOR__ )
#define x_compiler_patch x_scast(int,__PGIC_PATCHLEVEL__)


#elif defined(SASC) || defined(__SASC) || defined(__SASC__)
#define x_compiler_SAS 1
#define x_compiler "SAS/C"
#define x_compiler_org_version __SASC__
#define x_compiler_major x_scast(int,__SASC__ / 100)
#define x_compiler_minor x_scast(int,__SASC__ % 100)


#elif defined(SDCC)
#define x_compiler_SDCC 1
#define x_compiler "Small Device C Compiler"
#define x_compiler_org_version SDCC
#define x_compiler_major x_scast(int,SDCC / 100)
#define x_compiler_minor x_scast(int,(SDCC / 10) % 10)
#define x_compiler_patch x_scast(int,SDCC % 10)


#elif defined(__TI_COMPILER_VERSION__) || defined(_TMS320C6X)
#define x_compiler_TEXAS_INSTRUMENTS 1
#define x_compiler "Texas Instruments C/C++"
#define x_compiler_org_version __TI_COMPILER_VERSION__
#define x_compiler_major x_scast(int,__TI_COMPILER_VERSION__ / 1000000)
#define x_compiler_minor x_scast(int,(__TI_COMPILER_VERSION__ / 1000) % 1000)
#define x_compiler_patch x_scast(int,__TI_COMPILER_VERSION__ % 1000)


#elif defined(__USLC__) || defined(__SCO_VERSION__) 
#define x_compiler_USL 1
#define x_compiler "USL C"
#define x_compiler_org_version __SCO_VERSION__
#define x_compiler_major x_scast(int,__SCO_VERSION__ / 100000000)
#define x_compiler_minor x_scast(int,(__SCO_VERSION__ / 1000000) % 100)


#elif defined(__WATCOMC__)
#define x_compiler_WATCOM 1
#define x_compiler "Watcom C++"
#define x_compiler_org_version __WATCOMC__
#define x_compiler_major x_scast(int,__WATCOMC__ / 100)
#define x_compiler_minor x_scast(int,__WATCOMC__ % 100)


#elif defined(__xlc__) || defined(__xlC__) || defined(__xlC_ver__)
#define x_compiler_IBM_XL 1
#define x_compiler "IBM XL C/C++"
#define x_compiler_org_version __IBMC__

#elif defined(ORA_PROC)
#define x_compiler_ORACLE_PRO 1
#define x_compiler "Oracle Pro C"
#define x_compiler_org_version ORA_PROC

#elif defined(__SUNPRO_CC)
#define x_compiler_SUN_PRO 1
#define x_compiler "Sun Pro"
#define x_compiler_org_version __SUNPRO_CC

#elif defined(__SUNPRO_C) 
#define x_compiler_SUN_PRO 1
#define x_compiler "Sun Pro"
#define x_compiler_org_version __SUNPRO_C

#elif defined(__llvm__)
#define x_compiler_LLVM 1
#define x_compiler "LLVM"
#define x_compiler_org_version __llvm__

#elif defined(__HP_cc)
#define x_compiler_HP_ANSI 1
#define x_compiler "HP ANSI C"
#define x_compiler_org_version __HP_cc

#elif defined(__TINYC__)
#define x_compiler_TINYC 1
#define x_compiler "Tiny C"
#define x_compiler_org_version __TINYC__

#elif defined(__MWERKS__) || defined(__CWCC__)
#define x_compiler_METROWERKS_CODEWARRIOR 1
#define x_compiler "Metrowerks CodeWarrior"
#define x_compiler_org_version __MWERKS__

#elif defined(__BORLANDC__) || defined(__CODEGEARC__)
#define x_compiler_BORLAND 1
#define x_compiler "Borland C++"
#define x_compiler_org_version __BORLANDC__

#elif defined(_ACC_)
#define x_compiler_ACC 1
#define x_compiler "ACC"
#define x_compiler_org_version NORMALIZE_VERSION(0, 0, 0)

#elif defined(__ACK__)
#define x_compiler_AMSTERDAM_COMPILER_KIT 1
#define x_compiler "Amsterdam Compiler Kit"
#define x_compiler_org_version __ACK__

#elif defined(__CC65__)
#define x_compiler_CC65 1
#define x_compiler "CC65"
#define x_compiler_org_version __VERSION

#elif defined(__convexc__)
#define x_compiler_CONVEX 1
#define x_compiler "Convex C"
#define x_compiler_org_version __convexc__

#elif defined(__COMPCERT__)
#define x_compiler_COMPCERT 1
#define x_compiler "CompCert"
#define x_compiler_org_version __COMPCERT__

#elif defined(__COVERITY__)
#define x_compiler_COVERITY 1
#define x_compiler "Coverity C/C++"
#define x_compiler_org_version __COVERITY__

#elif defined(_DICE)
#define x_compiler_DICE 1
#define x_compiler "DICE C"
#define x_compiler_org_version _DICE

#elif defined(__DMC__)
#define x_compiler_DIGITAL_MARS 1
#define x_compiler "Digital Mars"
#define x_compiler_org_version __DMC__

#elif defined(__FCC_VERSION)
#define x_compiler_FUJITSU 1
#define x_compiler "Fujitsu C++"
#define x_compiler_org_version __FCC_VERSION

#elif defined(__IMAGECRAFT__)
#define x_compiler_IMAGECRAFT 1
#define x_compiler "Imagecraft C"
#define x_compiler_org_version __IMAGECRAFT__

#elif defined(__KCC) || defined(__KCC_VERSION)
#define x_compiler_KAI 1
#define x_compiler "KAI C++"
#define x_compiler_org_version __KCC_VERSION

#elif defined(__LCC__)
#define x_compiler_LLC 1
#define x_compiler "LLC"
#define x_compiler_org_version __LCC__

#elif defined(__HIGHC__)
#define x_compiler_METAWARE_HIGH 1
#define x_compiler "MetaWare High C/C++"
#define x_compiler_org_version __HIGHC__

#elif defined(_MRI)
#define x_compiler_MICROTEC 1
#define x_compiler "Microtec C/C++"
#define x_compiler_org_version _MRI

#elif defined(__NDPC__) || defined(__NDPX__)
#define x_compiler_MICROWAY_NDP 1
#define x_compiler "Microway NDP C"
#define x_compiler_org_version __NDPC__

#elif defined(MIRACLE)
#define x_compiler_MIRACLE 1
#define x_compiler "Miracle"
#define x_compiler_org_version MIRACLE

#elif defined(__MRC__) || defined(MPW_C) || defined(MPW_CPLUS)
#define x_compiler_MPW 1
#define x_compiler "MPW"
#define x_compiler_org_version __MRC__

#elif defined(__CC_NORCROFT) || defined(__ARMCC_VERSION)
#define x_compiler_NORCROFT 1
#define x_compiler "Norcroft"
#define x_compiler_org_version __ARMCC_VERSION

#elif defined(__NWCC__)
#define x_compiler_NWCC 1
#define x_compiler "NWCC"
#define x_compiler_org_version __NWCC__

#elif defined(__PACIFIC__) || defined(_PACC_VER)
#define x_compiler_PACIFIC 1
#define x_compiler "Pacific"
#define x_compiler_org_version __PACIFIC__

#elif defined(__RENESAS__) || defined(__HITACHI__) || defined(__RENESAS_VERSION__) || defined(__HITACHI_VERSION__)
#define x_compiler_RENESAS 1
#define x_compiler "Renesas"
#define x_compiler_org_version __RENESAS_VERSION__

#elif defined(_SCO_DS)
#define x_compiler_SCO 1
#define x_compiler "SCO OpenServer"
#define x_compiler_org_version _SCO_DS

#elif defined(__SNC__)
#define x_compiler_SN 1
#define x_compiler "SN Compiler"
#define x_compiler_org_version __SNC__

#elif defined(__VOSC__)
#define x_compiler_STRATOS 1
#define x_compiler "Stratos VOS"
#define x_compiler_org_version __VOSC__

#elif defined(__SC__)
#define x_compiler_SYMANTEC 1
#define x_compiler "Symantec C"
#define x_compiler_org_version __SC__

#elif defined(__TenDRA__)
#define x_compiler_TENDRA 1
#define x_compiler "TenDRA C/C++"
#define x_compiler_org_version __TenDRA__

#elif defined(THINKC3)
#define x_compiler_THINKC 1
#define x_compiler "THINK C"
#define x_compiler_org_version THINKC3

#elif defined(THINKC4)
#define x_compiler_THINKC 1
#define x_compiler "THINK C"
#define x_compiler_org_version THINKC4

#elif defined(__TURBOC__)
#define x_compiler_TURBOS 1
#define x_compiler "Turbo C/C++"
#define x_compiler_org_version __TURBOC__

#elif defined(__VBCC__)
#define x_compiler_VBCC 1
#define x_compiler "VBCC"
#define x_compiler_org_version __VBCC__

#elif defined(__ZTC__)
#define x_compiler_ZORTECH 1
#define x_compiler "Zortech C++"
#define x_compiler_org_version __ZTC__
#endif


/* -------------------------------------------------------------------------- */
/*                                    Check                                   */
/* -------------------------------------------------------------------------- */
#ifndef x_compiler
#define x_compiler ""
#endif

#ifndef x_compiler_org_version
#define x_compiler_org_version 0
#endif

#ifndef x_compiler_major
#define x_compiler_major 0
#endif

#ifndef x_compiler_minor
#define x_compiler_minor 0
#endif

#ifndef x_compiler_patch
#define x_compiler_patch 0
#endif

#ifndef x_compiler_patch
#define x_compiler_patch 0
#endif

#ifndef x_compiler_version
#define x_compiler_version (x_ver(x_compiler_major, x_compiler_minor, x_compiler_patch))
#endif



#endif

library.h

// library.h
#ifndef X___H_PREDEFINED_LIBRARY
#define X___H_PREDEFINED_LIBRARY



#include "helper.h"



/* -------------------------------------------------------------------------- */
/*                               Implementation                               */
/* -------------------------------------------------------------------------- */
#if defined (__BIONIC__)
#define x_lib_BIONIC 1
#define x_lib "Bionic libc"
#define x_lib_major x_scast(int,0)
#define x_lib_minor x_scast(int,0)
#define x_lib_patch x_scast(int,0)
#define x_lib_org_version __BIONIC__

#elif defined (__GNU_LIBRARY__)
#define x_lib_GNU_GLIBC 1
#define x_lib "GNU glibc"
#define x_lib_major x_scast(int,__GLIBC__)
#define x_lib_minor x_scast(int,__GLIBC_MINOR__)
#define x_lib_org_version __GLIBC__

#elif defined (__KLIBC__)
#define x_lib_KLIBC 1
#define x_lib "klibc"
#define x_lib_major x_scast(int,__KLIBC__)
#define x_lib_minor x_scast(int,__KLIBC_MINOR__)
#define x_lib_patch x_scast(int,__KLIBC_PATCHLEVEL__)
#define x_lib_org_version __KLIBC__

#elif defined (__UCLIBC__)
#define x_lib_UCLIBC 1
#define x_lib "uClibc"
#define x_lib_major x_scast(int,__UCLIBC_MAJOR__)
#define x_lib_minor x_scast(int,__UCLIBC_MINOR__)
#define x_lib_patch x_scast(int,__UCLIBC_SUBLEVEL__)
#define x_lib_org_version __UCLIBC_MAJOR__

#elif defined (__CRTL_VER)
#define x_lib_VMS 1
#define x_lib "VMS libc"
#define x_lib_major x_scast(int,__CRTL_VER / 10000000)
#define x_lib_minor x_scast(int,(__CRTL_VER / 100000) % 100)
#define x_lib_patch x_scast(int,(__CRTL_VER / 100) % 100)
#define x_lib_org_version __CRTL_VER

#elif defined (__LIBREL__)
#define x_lib_ZOS_LIBC 1
#define x_lib "z/OS libc"
#define x_lib_org_version __LIBREL__

#elif defined (_CPPLIB_VER)
#define x_lib_CPPLIB 1
#define x_lib "Dinkumware"
#define x_lib_major x_scast(int,_CPPLIB_VER / 100)
#define x_lib_minor x_scast(int,_CPPLIB_VER % 100)
#define x_lib_org_version _CPPLIB_VER

#elif defined (__GLIBCPP__)
#define x_lib_GLIBCPP 1
#define x_lib "GNU libstdc++"
#define x_lib_major x_scast(int,__GLIBCXX__ / 10000)
#define x_lib_minor x_scast(int,(__GLIBCXX__ / 100) % 100)
#define x_lib_patch x_scast(int,__GLIBCXX__ % 100)
#define x_lib_org_version __GLIBCXX__

#elif defined (__INTEL_CXXLIB_ICC)
#define x_lib_INTEL_CXXLIB_ICC 1
#define x_lib "Intel C++ Run-Time Libraries"
#define x_lib_org_version __INTEL_CXXLIB_ICC

#elif defined (_LIBCPP_VERSION)
#define x_lib_LIBCPP 1
#define x_lib "libc++"
#define x_lib_major x_scast(int,_LIBCPP_VERSION / 1000)
#define x_lib_minor x_scast(int,_LIBCPP_VERSION % 1000)
#define x_lib_org_version _LIBCPP_VERSION

#elif defined (_MFC_VER)
#define x_lib_MFC 1
#define x_lib "Microsoft Foundation Classes"
#define x_lib_major x_scast(int,_MFC_VER / 100)
#define x_lib_minor x_scast(int,_MFC_VER % 100)
#define x_lib_org_version _MFC_VER
#endif

/* -------------------------------------------------------------------------- */
/*                                    Check                                   */
/* -------------------------------------------------------------------------- */
#ifndef x_lib
#define x_lib ""
#endif

#ifndef x_lib_org_version
#define x_lib_org_version 0
#endif

#ifndef x_lib_major
#define x_lib_major 0
#endif

#ifndef x_lib_minor
#define x_lib_minor 0
#endif

#ifndef x_lib_patch
#define x_lib_patch 0
#endif

#ifndef x_lib_patch
#define x_lib_patch 0
#endif

#ifndef x_lib_version
#define x_lib_version (x_ver(x_lib_major, x_lib_minor, x_lib_patch))
#endif


#endif

standard.h

// standard.h
#ifndef X___H_PREDEFINED_STANDARD
#define X___H_PREDEFINED_STANDARD



/* -------------------------------------------------------------------------- */
/*                                  Standards                                 */
/* -------------------------------------------------------------------------- */
#if defined(__STDC__)
    #define x_std_ISO __STDC__
#else
    #define x_std_ISO 0
#endif


#if defined(__STDC_HOSTED__)
    #define x_std_HOSTED __STDC_HOSTED__
#else
    #define x_std_HOSTED 0
#endif


#if defined(__OBJC__)
    #define x_std_OBJC __OBJC__
#else
    #define x_std_OBJC 0
#endif


#if defined(__ASSEMBLER__)
    #define x_std_ASSEMBLER __ASSEMBLER__
#else
    #define x_std_ASSEMBLER 0
#endif


#if defined(__STDC_NO_ATOMICS__)
    #define x_std_NO_ATOMICS __STDC_NO_ATOMICS__
#else
    #define x_std_NO_ATOMICS 0
#endif


#if defined(__STDC_NO_COMPLEX__)
    #define x_std_NO_COMPLEX __STDC_NO_COMPLEX__
#else
    #define x_std_NO_COMPLEX 0
#endif


#if defined(__STDC_NO_THREADS__)
    #define x_std_NO_THREADS __STDC_NO_THREADS__
#else
    #define x_std_NO_THREADS 0
#endif


#if defined(__STDCPP_THREADS__)
    #define x_std_THREADS __STDCPP_THREADS__
#else
    #define x_std_THREADS 0
#endif


#if defined(__STDC_NO_VLA__)
    #define x_std_NO_VLA __STDC_NO_VLA__
#else
    #define x_std_NO_VLA 0
#endif


#if defined(__embedded_cplusplus)
    #define x_std_EMBEDDED_CPP __embedded_cplusplus
#else
    #define x_std_EMBEDDED_CPP 0
#endif


#if defined(__cplusplus_cli)
    #define x_std_CLI_CPP __cplusplus_cli
#else
    #define x_std_CLI_CPP 0
#endif


#if defined(__STDC_VERSION__)
    #define x_std_C __STDC_VERSION__
#else
    #define x_std_C_VERSION 0
#endif


#if x_std_C_VERSION >= 201710L
    #define x_std_C 17
#elif x_std_C_VERSION >= 201112L
    #define x_std_C 11
#elif x_std_C_VERSION >= 199901L
    #define x_std_C 99
#elif x_std_C_VERSION >= 199409L
    #define x_std_C 94
#else
    #define x_std_C 0
#endif


#if defined(__cplusplus)
    #define x_std_CPP_VERSION __cplusplus
#else
    #define x_std_CPP 0
#endif


#if x_std_CPP_VERSION >= 202100L
    #define x_std_CPP 23
#elif x_std_CPP_VERSION >= 202002L
    #define x_std_CPP 20
#elif x_std_CPP_VERSION >= 201703L
    #define x_std_CPP 17
#elif x_std_CPP_VERSION >= 201402L
    #define x_std_CPP 14
#elif x_std_CPP_VERSION >= 201103L
    #define x_std_CPP 11
#elif x_std_CPP_VERSION >= 199711L
    #define x_std_CPP 98
#else
    #define x_std_CPP 0
#endif


/* -------------------------------------------------------------------------- */
/*                               Unix Standards                               */
/* -------------------------------------------------------------------------- */
#ifdef X_O_PREDEFINED_UNIX_STNDARDS
#include <unistd.h>

#if defined(_POSIX_VERSION)
    #define x_std_POSIX_VERSION _POSIX_VERSION
#else
    #define x_std_POSIX_VERSION 0
#endif


#if defined(_POSIX2_C_VERSION)
    #define x_std_POSIX2_C_VERSION _POSIX2_C_VERSION
#else
    #define x_std_POSIX2_C_VERSION 0
#endif


#if defined(_XOPEN_VERSION)
    #define x_std_XOPEN_VERSION _XOPEN_VERSION
#else
    #define x_std_XOPEN_VERSION 0
#endif


#if defined(_XOPEN_UNIX)
    #define x_std_XOPEN_UNIX _XOPEN_UNIX
#else
    #define x_std_XOPEN_UNIX 0
#endif
#endif


/* ------------------------------- Information ------------------------------ */
#if defined(__FILE__)
    #define x_std_FILE __FILE__
#else
    #define x_std_FILE ""
#endif


#if defined(__LINE__)
    #define x_std_LINE __LINE__
#else
    #define x_std_LINE 0
#endif


#if defined(__func__)
    #define x_std_FUNC __func__
#elif defined(__FUNCTION__)
    #define x_std_FUNC __FUNCTION__
#else
    #define x_std_FUNC ""
#endif


#if defined(__DATE__)
    #define x_std_DATE __DATE__
#else
    #define x_std_DATE ""
#endif


#if defined(__TIME__)
    #define x_std_TIME __TIME__
#else
    #define x_std_TIME ""
#endif


#if defined(__TIMESTAMP__)
    #define x_std_TIMESTAMP __TIMESTAMP__
#else
    #define x_std_TIMESTAMP ""
#endif



#endif

has.h

// has.h
#ifndef X___H_PREDEFINED_HAS
#define X___H_PREDEFINED_HAS



#if defined(__cplusplus) && defined(__has_cpp_attribute)
#define x_has_attr(x)       __has_cpp_attribute(x)

#elif defined(__STDC_VERSION__) && defined(__has_attribute)
#define x_has_attr(x)       __has_attribute(x)

#else
#define x_has_attr(x)       0
#endif

#ifdef __has_feature
#define x_has_feature(x)    __has_feature(x)
#else
#define x_has_feature(x)    0
#endif

#ifdef __has_include
#define x_has_include(x)    __has_include(x)
#else
#define x_has_include(x)    0
#endif

#ifdef __has_builtin
#define x_has_builtin(x)    __has_builtin(x)
#else
#define x_has_builtin(x)    0
#endif



#endif

attributes.h

// attributes.h
#ifndef X___H_PREDEFINED_ATTRIBUTES
#define X___H_PREDEFINED_ATTRIBUTES



#include "has.h"



/* -------------------------------------------------------------------------- */
/*                                  Standard                                  */
/* -------------------------------------------------------------------------- */
#if x_has_attr(assume)
#define x_attr_assume(x)                           [[assume(x)]]
#else
#define x_attr_assume(x)
#endif

#if x_has_attr(carries_dependency)
#define x_attr_carries_dependency                  [[carries_dependency]]
#else
#define x_attr_carries_dependency
#endif

#if x_has_attr(deprecated)
#define x_attr_deprecated(x)                       [[deprecated(x)]]
#else
#define x_attr_deprecated(x)
#endif


#if x_has_attr(fallthrough)
#define x_attr_fallthrough                         [[fallthrough]]
#else
#define x_attr_fallthrough
#endif

#if x_has_attr(likely)
#define x_attr_likely                              [[likely]]
#else
#define x_attr_likely
#endif

#if x_has_attr(unlikely)
#define x_attr_unlikely                            [[unlikely]]
#else
#define x_attr_unlikely
#endif

#if x_has_attr(maybe_unused)
#define x_attr_maybe_unused                        [[maybe_unused]]
#else
#define x_attr_maybe_unused
#endif

#if x_has_attr(nodiscard)
#define x_attr_nodiscard                           [[nodiscard]]
#else
#define x_attr_nodiscard
#endif

#if x_has_attr(noreturn)
#define x_attr_noreturn                            [[noreturn]]
#else
#define x_attr_noreturn
#endif


/* -------------------------------------------------------------------------- */
/*                             Inline | No-inline                             */
/* -------------------------------------------------------------------------- */
#if x_has_attr(always_inline)
#define x_attr_inline                             [[always_inline]]
#elif defined(_MSC_VER) || defined(_MSC_FULL_VER)
#define x_attr_inline                             __forceinline
#else
#define x_attr_inline                            inline
#endif

#if x_has_attr(noinline)
#define x_attr_noinline                           [[noinline]]
#else
#define x_attr_noinline                          
#endif


/* -------------------------------------------------------------------------- */
/*                                 Visibility                                 */
/* -------------------------------------------------------------------------- */
#if defined _WIN32 || defined __CYGWIN__
  #ifdef BUILDING_DLL
    #ifdef __GNUC__
      #define x_attr_visible __attribute__((dllexport))
    #else
      #define x_attr_visible __declspec(dllexport)
    #endif
  #else
    #ifdef __GNUC__
      #define x_attr_visible __attribute__((dllimport))
    #else
      #define x_attr_visible __declspec(dllimport)
    #endif
  #endif
  #define x_attr_hidden
#else
  #if __GNUC__ >= 4
    #define x_attr_visible __attribute__ ((visibility ("default")))
    #define x_attr_hidden  __attribute__ ((visibility ("hidden")))
  #else
    #define x_attr_visible
    #define x_attr_hidden
  #endif
#endif



#endif

controllers.h

// controllers.h
#ifndef X___H_PREDEFINED_CONTROLLERS
#define X___H_PREDEFINED_CONTROLLERS



#include "standard.h"
#include "attributes.h"



/* -------------------------------------------------------------------------- */
/*                                  Constexpr                                 */
/* -------------------------------------------------------------------------- */
#if x_is_cpp && (defined(__cpp_constexpr) && (!defined(x_use_constexpr) || (defined(x_use_constexpr) &&  x_use_constexpr)))

#ifdef x_use_constexpr
#undef x_use_constexpr
#endif

#define x_use_constexpr     1
#define x_constexpr         constexpr
#define x_if_constexpr      if constexpr

#else
#define x_constexpr
#define x_if_constexpr      if
#endif


/* -------------------------------------------------------------------------- */
/*                                 Exceptions                                 */
/* -------------------------------------------------------------------------- */
#if x_is_cpp && ( !defined(x_exceptions) || (defined(x_exceptions) &&  x_exceptions))

#ifdef x_exceptions
#undef x_exceptions
#endif

#define x_exceptions        1
#define x_noexcept          noexcept

#else
#define x_exceptions        0
#define x_noexcept          
#endif


/* -------------------------------------------------------------------------- */
/*                                   Assert                                   */
/* -------------------------------------------------------------------------- */
#if x_is_cpp
#include <cstdio>       // std::fprintf
#include <exception>    // std::terminate
#else
#include <stdio.h>      // fprintf
#include <stdlib.h>     // exit
#endif

namespace xrx::__detail
{
    /**
     * @brief Print the assert information and terminate the program
     * 
     * @param file      file name
     * @param line      line number
     * @param message   message to print
    */
    x_attr_hidden x_attr_maybe_unused x_attr_noreturn void assert_fail(const char* file, int line, const char* message) 
    {
        #if x_is_cpp
            std::fprintf(stderr, "Assertion Failed\n[FILE] %s [LINE] %d [MSG]: %s ", file, line, message);
            std::terminate();
        #else
            fprintf(stderr, "Assertion Failed\n[FILE] %s [LINE] %d [MSG]: %s ", file, line, message);
            exit(0);
        #endif
    }
    
    /**
     * @brief Ignore unused parameters to avoiding the compiler warning
     * 
     * @tparam T parameters to ignore
    */
    template <typename... T> x_attr_hidden x_attr_maybe_unused x_constexpr void ignore_unused(const T &...) {}
}

#if !defined(x_assert) || (defined(x_assert) &&  x_assert)

#ifdef x_assert
#undef x_assert
#endif

#ifdef NDEBUG
#define x_assert(x, m) xrx::__detail::ignore_unused((x), (m))
#else
#define x_assert(x, m) ((x) ? (void) 0 : xrx::__detail::assert_fail(x_std_FILE, x_std_LINE, (m)))
#endif

#endif


/* -------------------------------------------------------------------------- */
/*                                    Throw                                   */
/* -------------------------------------------------------------------------- */
#if x_exceptions
#define x_throw(x) throw x
#else
#if x_is_cpp
#define x_throw(x) do { x_assert(false, (x).what()); } while (false)
#else
#define x_throw(x) do { x_assert(false,         ""); } while (false)
#endif
#endif



#endif

builtin.h

// builtin.h
#ifndef X___H_PREDEFINED_BUILTIN
#define X___H_PREDEFINED_BUILTIN



#include "helper.h"
#include "has.h"


#if x_has_builtin(__builtin_clz)
#define x_builtin_clz(x)        __builtin_clz   (x)
#else
#define x_builtin_ctz(x)        0
#endif

#if x_has_builtin(__builtin_clzll)
#define x_builtin_clzll(x)      __builtin_clzll (x)
#else
#define x_builtin_ctzll(x)      0
#endif

#if x_has_builtin(__builtin_clz)
#define x_builtin_ctz(x)        __builtin_ctz   (x)
#else
#define x_builtin_ctz(x)        0
#endif

#if x_has_builtin(__builtin_ctzll)
#define x_builtin_ctzll(x)      __builtin_ctzll (x)
#else
#define x_builtin_ctzll(x)      0
#endif



#endif

Updates

  • v2.0.0 (thanks @indi)

    • CHANGE extension of include files to .hpp instead of .h

    • DELETE x_scast macro (no need to it)

    • DELETE x_is_cpp macro (no need to it)

    • DELETE X_STD_C_VERSION and X_STD_C macros (no need to it in c++)

    • UPDATE xrx::__detail namespace to xrx::detail

    • UPDATE xrx::__detail namespace to xrx::detail

    • FIX compiler detection in compiler.hpp (the order was wrong)

    • USE cstdlib instead of stdlib.h

    • RENAME all macros (change to uppercase)

    • ADD warnings if can't detect architecture, compiler, os, ...

    • DETECT compiler before the architectur (it was reversed)

    • FIX some typo's

    • FIX detection of visibility attributes (i did my best here)

    • DELETE builtin.hpp (It's really just nonsense)

    • The library has been completely destined for C++

\$\endgroup\$
2

2 Answers 2

7
\$\begingroup\$

Design review

There are other libraries that do more or less what you’re trying to do, in whole or in part (for example Boost.Config). Even so, I’m skeptical about why most of this stuff would be useful. The reasons why are complex and vary from macro to macro, so I’ll cover them in the code review.

Even though this library is apparently intended to be both a C and C++ library, I am going to be reviewing it wholly from a C++ perspective.

This is not a C++ library

You call this a C++ library… but I think that’s false advertising. This is a C library that can also be used in C++.

The evidence is manifold. .h is for C headers, not C++ (for that, you should use .hpp). Granted, .h is also used for dual C/C++ headers, which these are, so .h doesn’t mean it’s not C++.

There are also a bunch of macros that just don’t make any sense in C++, like x_scast.

There are also a couple of C library includes (<stdlib.h>). These are supported in C++ only for interoperability with C code, but otherwise should not be used.

The fact that this library will “work” in C++ does not make it a C++ library. C++ supports C code… but it doesn’t magically change C code into C++ code.

The only clue that this is actually intended to be a C++ library is in controllers.h, where you have a namespace defined… but then, bizarrely, within that namespace, still do #if x_is_cpp as if that can possibly not be true. This seems more evidence of confusion than of C++ to me.

None of those should be macros in C++

If this were a C++ library, almost all of these things should not be macros. Except for a few things, all of them should be constexpr constants. Some of them should be constexpr bools (like x_is_cpp). Some should be ints or other numbers (most of the version numbers). Others should be C-strings, or string views, or some other kind of constexpr string.

This is not just a stylistic concern either. The future of C++ is most likely a modular one, and macros do not play well with modules. A macro-based library might still make sense in C++20… but will likely make a lot less sense in C++23… and even less sense in future standards.

But of course, if this is not actually a C++ library, but rather a C library that works in C++, then sure, macros are your only option.

Several of the macros don’t make sense in C++

What is the purpose of x_constexpr (or x_if_constexpr) or x_throw?

If this is a C++ library… and if it is, it must be at least C++17… then you obviously have constexpr (and throw).

And you can’t just turn these things on and off, because that radically changes the meaning of code. For example:

auto func_1()
{
    try
    {
        // What happens here? Shrug. Function might continue and return
        // normally... or the whole program may crash.
        x_throw(std::runtime_error{"whatever"});
    }
    catch (std::exception const&)
    {
    }
}

auto func_2()
{
    try
    {
        // And what happens here? Depending on a number of factors, the
        // function might continue and return normally... or the program
        // may crash... or it might not compile at all!
        x_throw(1);
    }
    catch (int)
    {
    }
}

And you can’t just replace if constexpr with if. That’s absurd. Do you even know what if constexpr does?

So what purpose do these macros serve?

In fact, for many of the macros, they seem just to exist for existing’s sake. They have a vague sense that they might be useful… but I don’t think you’ve given very serious thought as to how or where. What might have helped is some real-world use cases showing where the macros might be useful.

(Some of the macros don’t even make sense in C)

What exactly is the point of macros like x_std_FILE? Where in C++… or C!… would __FILE__ not be defined?

The macro names are all unacceptable

My biggest issue with this library is that there’s just no way I could ever use it in any C++ project I’ve ever contributed to, for the simple reason that all of your macros are lowercase. It has been industry standard for decades (at least in C++; I won’t speak for C) that macros should be ALL_CAPS (also known as “screaming snake case”). This is a very common requirement in coding standards, and is even one of the core guidelines.

Now, macros in general are evil in modern C++; the fact that your library is all macros is a smell right off the bat. But if you must use macros, making them lowercase is just about the worst thing you can do, because it invites clashes with other identifiers.

And speaking of identifiers, every one of your include guard macros is illegal. Double underscores are illegal in identifiers.

It doesn’t even work

I even checked the GitHub repository, and there is not even a single test to check that any of these macros even work. And you know what happens when you don’t test your code?

It doesn’t work.

(FTR, in that Compiler Explorer link I have extracted just the test that detects GCC or Clang… compiled it with Clang 15.0.0… and the output happily prints that it was using “GCC version 4”.)

Oh, I can see in the README that the library was supposedly tested on “GCC 12.1” and “Clang 15.0.0”. I can only presume that “tested” there just means means “it compiled”… because, clearly it hasn’t been actually tested.

Granted, this isn’t exactly the kind of library you can test with basic unit testing techniques. To test this properly, you would need to take advantage of a CI facility that runs tests on multiple platforms, and configure the tests for each platform to do string checks for the expected stuff (OS name, compiler name, etc.). None of this is trivial… but it’s clearly necessary, because right now the only reason you think your library works is starry-eyed faith. I’d bet you have literally not even the foggiest idea of whether it actually correctly detects Comeau running on AIX on PowerPC hardware.

Code review

Now let’s get into the actual code.

predefined.h

Nothing really to say here.

helper.h

// Check whether the language used is cpp or not
#ifdef __cplusplus
#define x_is_cpp 1
#else
#define x_is_cpp 0
#endif

But what is the point?

As mentioned in the design review, if this really is a C++ library, then… this has to be the most pointless check possible. If you’re using a C++ library, then, yes, “is cpp”. Always. Always, always, always. If you’re writing C++ code, and “! is cpp”… then something is seriously goddamn wrong with the universe.

But okay, as I said, I don’t think this is a C++ library at all; I think it is a C library that works in C++, in which case, sure, it could be used in either C or C++, and it is perfectly reasonable for other code that could be situated in either C or C++ to want to know what’s what.

But even so… why would anyone prefer to write #if x_is_cpp rather than #if defined(__cplusplus)? What is gained? It’s a few chars shorter, sure… but it’s for a test that should be rare in any case, and more importantly, #if defined(__cplusplus) will always work, whereas I could break x_is_cpp trivially by redefining it as often as I please.

// C/C++ Static Cast
#if x_is_cpp
#define x_scast(type,value) static_cast<type>(value)
#else
#define x_scast(type,value) (type)value
#endif

Aside from the fact that this is silly and wrong in C++ (because the two casts do very different things), this macro is terrible for another reason… it gives different results for C and C++. See for yourself (compiler on the left is C, right is C++).

architecture.h

/* -------------------------------------------------------------------------- */
/*                                    Check                                   */
/* -------------------------------------------------------------------------- */
#ifndef x_arch
#define x_arch ""
#endif

This says “check” but… doesn’t actually check anything. If the architecture (or OS, or compiler, etc.) just isn’t supported by the library, I would at least hope for a warning, if not a full-on error.

This is what I was getting at when I said I was skeptical of the purpose for this library. I mean, presumably I’m using this library for a reason, right? I want to know the architecture. If I can’t… I should at least be informed of that. If I really don’t care that I can’t know the architecture, then why would I bother adding a whole library dependency for something I don’t really need?

And, really, this is the crux of the problem. If you’re writing code that depends on particular standard library implementations or compilers… you’re writing bad code. For example, if you want to use std::midpoint(), but you discover that it was only added in GCC 9, this is bad code:

#if x_compiler_GCC and x_compiler_major >= 9
    return std::midpoint(a, b);
#else
    // Hand-rolled implementation...
#endif

This is better:

#if defined(__cpp_lib_interpolate)
    return std::midpoint(a, b);
#else
    // Hand-rolled implementation...
#endif

Why? Because it is better to test for features rather than versions. This is why C++20 added feature testing, for features going all the way back to C++11.

Before C++20, build systems usually provided this information. For example, this is basically the whole raison d’être for Autotools. Let’s put a pin in this point for now.

Now it does make sense to version sniff if you are dealing with a bug. For example:

#if defined(__clang__) and (__clang_major__ < 13) and defined(__GLIBCXX__) and defined(_WIN32)
    // Clang 12 on Windows using libstdc++ has a bug where call_once()
    // segfaults. So here you'd use some alternative.
#else
    std::call_once(flag, func);
#endif

// OR //////////////////////////////////////////////////////////////////
#if defined(__clang__) and (__clang_major__ < 13) and defined(__GLIBCXX__) and defined(_WIN32)
    #define INDI_BUGFIX_CALL_ONCE(...) indi::call_once_workaround(__VA_ARGS__);
#else
    #define INDI_BUGFIX_CALL_ONCE(...) std::call_once(__VA_ARGS__);
#endif

// Usage:
INDI_BUGFIX_CALL_ONCE(flag, func);

But the thing is… those kinds of bugs and workarounds are very rare, and very specific situations. I write bleeding edge code—I’m already using some C++23 features—and I can count on one hand the number of times I’ve needed workaround like this for the whole year. It’s hardly the kind of thing you need a whole third-party library to deal with.

And even when I did need these kinds of workarounds… I mean, what really do your macros help with?

// How is:
#if defined(x_compiler_CLANG) and (x_compiler_major < 13) and defined(x_lib_GLIBCPP) and defined(x_os_WINDOWS)
// better than:
#if defined(__clang__) and (__clang_major__ < 13) and defined(__GLIBCXX__) and defined(_WIN32)
// ?

// ... and not just better, but so much better it's worth another
// dependency in a project?

As for architecture and OS sniffing, you might suppose that it would be useful for supporting multiple platforms by wrapping platform-specific code in portable utilities:

auto foo()
{
#if defined(x_os_WINDOWS)
    // Some Windows-specific code
#elif defined(x_os_LINUX)
    // Some Linux-specific code
#else
    #error unsupported OS
#endif
}

Except even that is bad practice. The proper thing to do is to separate your architecture-dependent code into files, and use the build system to decide which file to use. In other words, instead of the above, you would do:

// in project file .../windows/foo.cpp: ////////////////////////////////
auto foo()
{
    // Some Windows-specific code
}

// in project file .../linux/foo.cpp: //////////////////////////////////
auto foo()
{
    // Some Linux-specific code
}

And when you do that, as you can see, there is no more need for preprocessor shenanigans.

(You may be wondering why splitting platform-specific code into separate files and using the build system is better than using the preprocessor to sniff and conditionally-compile. Explaining that in detail would take far too much wordage for a simple code review, but I’ll give you some starting points you can dig deeper into if you care: 1. especially in bigger projects with more contributors, it is far more likely that the Windows coders and Linux coders will be entirely different groups of people, so it makes sense to keep their responsibilities separate; and 2. it eliminates the possibility of someone doing a tweak for the Windows implementation, yet it having an effect on the Linux implementation, creating all kinds of confusion and misery (“how can there be a regression?! no-one even touched the Linux code!!!”).)

So I ask, yet again… what is the purpose of these macros? What are they for? If you eliminate bad practices (version-sniffing, code with chunks that are conditionally-compiled or not), then all you are really left with is… I dunno, I guess printing the platform in debugging files? But, no, not even that, because core dumps will have all that info, and more, and be far more reliable.

What are they for? What project have you worked on where you needed these kinds of macros? Why? These are the kinds of questions that should be answered by the documentation, but it just seems like it’s been blindly assumed they should be maybe-i-dunno-i-guess-kinda-useful-somewhere-somehow.

Finally, I should point out that you are sniffing out the architecture before sniffing the compiler. That seems back-asswards. Those architecture identifiers, like __amd64__, __aarch64__, and so on… they don’t just pop into existence out of nowhere. They are defined by the compiler. So you need to know the compiler first… and then you can query the target architecture. Doing it the other way around is dangerous, because, since it is the compiler that defines everything, you are at its mercy if it doesn’t do what you expect. For example, suppose some compiler decides that “ARM” should refer to the C++ Annotated Reference Manual, which was the standard-before-the-standard, and that _ARM means you are using that standard… which means you will now incorrectly detect ARM hardware.

(Also, I suspect you may have some typos, at least in your i386 detection, because there is a lot of repetition in there—a good indicator of copy-paste errors. But of course, there’s no way to be sure whether it works or not, because nothing is tested.)

os.h

#elif defined(_WIN16) || defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__)
#define x_os_WINDOWS 1
#define x_os "Windows"

#elif defined(__APPLE__) && defined(__MACH__)
#define x_os_APPLE 1
#define x_os "Apple (mac/i/tv)OS"

There seems little point in sniffing the OS if one is going to be so blasé about it. macOS and iOS are very different beasts. As are Windows 11, Windows Server, Windows Phone, and Windows 98.

compiler.h

As I already pointed out, Clang also defines GCC’s macros, which means that Clang will never be detected.

library.h

Your libstdc++ detection will always fail.

This is yet another case of failure in a situation where “[T]his library has been tested”.

Granted, it only says the library has been “tested”… it doesn’t actually say the tests passed. So maybe the joke’s on me.

standard.h

#if defined(__STDC__)
    #define x_std_ISO __STDC__
#else
    #define x_std_ISO 0
#endif

But why?

Why would anyone want to do #if x_std_ISO rather than #if defined(__STDC__)? The latter is a few more chars, but it’s universal and standard, and can’t be fucked with (by redefinition, for example).

Same goes for all the other macros here, but especially the ones like this:

#if defined(__FILE__)
    #define x_std_FILE __FILE__
#else
    #define x_std_FILE ""
#endif

Seriously, why is x_std_FILE better than __FILE__?

has.h

I’m having a hard time seeing the usefulness of anything here, for various reasons.

Let’s start with x_has_attr:

#if defined(__cplusplus) && defined(__has_cpp_attribute)
#define x_has_attr(x)       __has_cpp_attribute(x)

#elif defined(__STDC_VERSION__) && defined(__has_attribute)
#define x_has_attr(x)       __has_attribute(x)

#else
#define x_has_attr(x)       0
#endif

So in C++, x_has_attr(x) is defined to __has_cpp_attribute(x). In C, it’s defined to __has_attribute(x).

The problem? In C, it should be __has_c_attribute(x). In both C and C++, __has_attribute(x) is something entirely different from __has_cpp_attribute(x) or __has_c_attribute(x). Depending on the compiler/version/whatever, __has_attribute(foo) could be true while __has_cpp_attribute(foo) is false (if something like __attribute__((foo)) is supported, but not [[foo]]), or vice versa. By conflating the two, you’re just creating confusion.

And worse, you’ve taken away the ability to check whether the test even works! With __has_cpp_attribute, I can do:

#if defined(__has_cpp_attribute)
    // Now I can do as many __has_cpp_attribute checks as I want:

    // I can do optional attributes
    #if __has_cpp_attribute(whatever)
        #define INDI_ATTRIB_WHATEVER [[whatever]]
    #else
        #define INDI_ATTRIB_WHATEVER
    #endif

    // I can do required attributes
    #if not __has_cpp_attribute(whatever)
        #error need whatever attribute
    #endif

    // I can do substitutions
    #if __has_cpp_attribute(whatever)
        #define INDI_ATTRIB_WHATEVER [[whatever]]
    #elif __has_cpp_attribute(other)
        #define INDI_ATTRIB_WHATEVER [[other]]
    #elif __has_attribute(whatever)
        #define INDI_ATTRIB_WHATEVER __attribute__((other))
    #endif
#endif

But with x_has_attr…:

// I can't do optional attributes
#if x_has_attr(whatever)
    #define INDI_ATTRIB_WHATEVER [[whatever]]
    // or should it be __attribute__((whatever))?
#else
    // is it that we don't have the [[whatever]] attribute?...
    // or is it that we can't check for attributes?
#endif

// I can't do required attributes
// (same as above)

// And I can't do substitutions
#if x_has_attr(whatever)
    #define INDI_ATTRIB_WHATEVER [[whatever]]
    // or should it be __attribute__((whatever))?
#elif x_has_attr(other)
    // Did the previous check fail because of a missing [[whatever]]
    // attribute... or a missing attribute check function?
    #define INDI_ATTRIB_WHATEVER [[other]]
#elif ???
    // How do I even check for C++-style attributes versus compiler-
    // style attributes?
#endif

What is the point of wrapping a standard functionality… and making it less useful?

Similar problems exist for x_has_include.

x_has_builtin is problematic because __has_builtin is different on Clang and GCC. You have created an unportable portable wrapper.

And __has_feature is a Clang-only thing as far as I know, which makes it practically useless. (If a x_has_feature test fails, what does that mean? That we don’t have the feature? Or that we’re just not using Clang?)

attributes.h

/* -------------------------------------------------------------------------- */
/*                                 Visibility                                 */
/* -------------------------------------------------------------------------- */
#if defined _WIN32 || defined __CYGWIN__
  #ifdef BUILDING_DLL

Here’s the problem: DLLs can link with other DLLs. That means in a single compile, you may be importing symbols from DLL-1, and exporting them from DLL-2. You can’t simply rely on a single preprocessor macro (like BUILDING_DLL)… you need a different macro for each DLL (BUILDING_DLL_1 for DLL-1 and BUILDING_DLL_2 for DLL-2, for example).

So this whole visibility section just… won’t work.

controllers.h

#if x_is_cpp && (defined(__cpp_constexpr) && (!defined(x_use_constexpr) || (defined(x_use_constexpr) &&  x_use_constexpr)))

#ifdef x_use_constexpr
#undef x_use_constexpr
#endif

#define x_use_constexpr     1
#define x_constexpr         constexpr
#define x_if_constexpr      if constexpr

#else
#define x_constexpr
#define x_if_constexpr      if
#endif

I’m at a complete loss trying to figure out how the above could ever be useful. But that seems to be the general trend: “solutions” that are looking for a problem, rather than having a specific problem to solve.

#if x_is_cpp && ( !defined(x_exceptions) || (defined(x_exceptions) &&  x_exceptions))

#ifdef x_exceptions
#undef x_exceptions
#endif

#define x_exceptions        1
#define x_noexcept          noexcept

#else
#define x_exceptions        0
#define x_noexcept          
#endif

Again, this is just nonsense. You can’t just… “turn exceptions off” by taking away noexcept.

How would that even work anyway?

template <typename T>
concept non_throwing_foo =
    requires (T& t)
    {
        { t.foo() } x_noexcept;
    };

struct a { auto foo() x_noexcept { return 0; } };
struct b { auto foo()            { throw 0; }  };

std::cout << non_throwing_foo<a> << "\n";
std::cout << non_throwing_foo<b> << "\n";

// Will print either:
//  true
//  false
// or:
//  true
//  true

Now your code that depends on a no-fail foo() function will be in trouble.

Or even more catastrophic, what about this:

#include <fstream>
#include <iostream>

#include "predefined.h"

// This function should never be run!!!
auto launch_the_nukes() -> char const*
{
    auto f = std::ofstream{"IF_YOU_SEE_THIS_FILE_THE_WORLD_IS_ON_FIRE"};
    return "boom.";
}

auto main() -> int
{
    std::cout << "If all is well, you will the number zero: "
              << x_noexcept(launch_the_nukes())
              << "\n";
}

// Will either print:
//  If all is well, you will the number zero: 0
// or:
//  If all is well, you will the number zero: boom.
// and if the latter, will have created a scary file... but could well
// have erased your entire hard drive, or worse.

Things are also weird with the assert stuff. First you include either C++ or C headers depending on the language… but then immediately after there’s this:

namespace xrx::__detail
{
    /**
     * @brief Print the assert information and terminate the program
     * 
     * @param file      file name
     * @param line      line number
     * @param message   message to print
    */
    x_attr_hidden x_attr_maybe_unused x_attr_noreturn void assert_fail(const char* file, int line, const char* message) 
    {
        #if x_is_cpp
            std::fprintf(stderr, "Assertion Failed\n[FILE] %s [LINE] %d [MSG]: %s ", file, line, message);
            std::terminate();
        #else
            fprintf(stderr, "Assertion Failed\n[FILE] %s [LINE] %d [MSG]: %s ", file, line, message);
            exit(0);
        #endif
    }
    
    /**
     * @brief Ignore unused parameters to avoiding the compiler warning
     * 
     * @tparam T parameters to ignore
    */
    template <typename... T> x_attr_hidden x_attr_maybe_unused x_constexpr void ignore_unused(const T &...) {}
}

which is clearly C++, notwithstanding the little bit in the middle of the (namespaced) function.

Now, firstly: __detail is an illegal identifier.

Second, the function assert_fail() does not look like a function template, and nor is it constexpr, so I can’t figure out how it would even compile in anything but the most trivial programs. It seems to me that it would violate the ODR. It should probably be declared inline… but better yet, not be defined in the header at all.

Third, the only real difference between the two functions seems to be that the “C++ version” calls terminate(), while the “C version” calls exit(), but… and here’s what really makes me raise an eyebrow… both options are wrong.

terminate() is specifically for situations where exception handling has failed for one reason or another. It is not meant for generally terminating the program. It just happens that the default behaviour is to call abort().

And while you should never use exit() in C++, it is fine to use in C… but in that case it means you are ending the program… NORMALLY. Which is kinda the opposite of crashing due to an assertion failing. In that case, you want the program to end… ABnormally. The standard assert() macro, when triggered, calls abort().

See the pattern?

So there is no purpose to the C/C++ split in assert_fail(). You might as well just write:

    x_attr_hidden x_attr_maybe_unused x_attr_noreturn void assert_fail(const char* file, int line, const char* message) 
    {
        fprintf(stderr, "Assertion Failed\n[FILE] %s [LINE] %d [MSG]: %s ", file, line, message);
        abort();
    }

Except, if you do that, you’ve literally just re-implemented the standard assert(). Except the standard assert() is better, because it also includes __func__, and it does not require a message.

builtin.h

Okay, so:

#include <iostream>

#include "predefined.h"

constexpr auto secret_number = ???;

auto main() -> int
{
    std::cout << x_builtin_clz(secret_number) << "\n";
}

The above program prints “0”.

Now, riddle me this. Is the most significant bit of my secret number 1? Or… does the compiler not support __builtin_clz?

Isn’t that kind of an important thing to know? Isn’t silently returning a valid result (zero) when a function doesn’t work… like… a really, really goddamn terrible idea?

Summary

Okay, so where did things really go off the rails here? As I see it, there are two main problems.

You didn’t write code that you needed

I suppose there are some people out there who just write code because it’s aesthetically pleasing, but most programmers write code because they have a problem they need solved.

And that’s the problem here: you didn’t have a problem you needed solved. You just… assumed that other people have this problem, and… figured you’d solve it for them, without even bothering to try to understand it.

You’re going to waste a lot of your time and energy if you’re coding for imaginary people. Your time would be better spent either asking around to see what real people need, or… better yet… do some coding yourself, and see what itches you need scratched.

NOBODY needs a library that detects “Pyramid 9810” hardware. Seriously, do you even know what that is? I had to Google it because I seriously suspected René Rivera was taking the piss and making up architectures just to screw with people. But no, it was a real architecture… from 1985–1987. From a company that hasn’t been around since 1995. That architecture not only predates standard C++, it almost predates C++ completely. That’s so old, they might have actually named the structures at Giza after it.

You do not need to detect 30+ year-old Pyramid 9810 minicomputer architecture, running discontinued Unix knock-off operating systems. Nor do you need to detect Zortech C++. (You know what that is? That was the first (commercial) C++ compiler for Windows… and no, not Windows Vista, or Windows NT, or even Windows 95. Not… even… Windows… 3.0 (which is arguably the first “real” Windows)! This compiler was released in 1988 for Windows 2.0.) Nor will any users of your library ever need to detect those things?

So why would you bother to write those checks? That’s just dead code for you; nothing more than a maintenance burden, for no benefit.

And did you ever, in any of your coding endeavours, find you had a need for a function that either returns the result of __builtin_clz, or zero? No? Then why did you write it?

And did you ever find that the standard __FILE__ macro just… wasn’t working for some reason or another? And that it could be fixed by… redefining it? No? Then, again, why?

If your answer is a shrug and a “well, maybe somebody might find it useful…?” No. Just, no. That’s not how you do software development. You find an itch, and you scratch it. And there are plenty of itches to scratch.

You want to write a good C++ library? Find a real problem to solve. Either look at what real C++ programmers are griping about, and say they really need. Or—and this is really the best option—just try doing some projects yourself, ones that actually feed your interests, and find out what you need.

You didn’t test your code

I know your library’s README says it was “tested” on two different operating systems, and three different compilers. But I don’t think your definition of “tested” is the same as the dictionary’s, because when most people hear something was “tested” on a given platform, they assume that someone actually looked at the output and confirmed it was what it’s supposed to be.

I would suggest reading up on testing in software development, including things like unit testing, integration testing, and test-driven development. Find a C++ unit testing framework you like, like Boost.Test, or Catch2, then learn it, live it, love it.

Any code that isn’t tested is garbage. I can’t, and won’t, ever use untested code in any projects I am responsible for, or contributing to. No one with even half a neuron of sense would. And, to be clear, I am using my definition of “tested” here… not yours. “Tested” means there are unit and/or integration tests that actual confirm the code’s behaviour is what it’s supposed to be… and those tests have been run, and passed. Learning how to properly test your code is a crucial skill. The “best” coder in the world who can’t test their code is less than useless for me on a project, whereas even a mediocre coder who knows how to test well will be invaluable.

Unfortunately, this particular library is very challenging to test, because the whole point of it is detecting hundreds of different platforms… which means you need to test it on hundreds of different platforms. There are free continuous integration services, like GitHub actions… but none that include anything even approaching the number of different platforms you’ll need to test on. (I believe GitHub actions only include Linux, Windows, and macOS. So, like, no IRIX or Plan 9.)

Nevertheless, you must learn how to properly test your C++ code, so you may want to start with a different project in order to learn testing.

\$\endgroup\$
6
  • \$\begingroup\$ At first, thank you very much. I have updated the library for the second version you can check it out (based on your instructions). \$\endgroup\$ Nov 5, 2022 at 13:34
  • 1
    \$\begingroup\$ #if defined(__cplusplus) will always work, whereas I could break x_is_cpp trivially by redefining it as often as I please.” That's not true, you can redefine __cplusplus just as well. \$\endgroup\$
    – G. Sliepen
    Nov 5, 2022 at 14:23
  • \$\begingroup\$ That is not correct. Redefining any predefined macro (including __cplusplus) is undefined behaviour. \$\endgroup\$
    – indi
    Nov 5, 2022 at 21:56
  • \$\begingroup\$ It might be undefined behaviour, but that is not a guarantee that it can never be redefined. So "will always work" is false. Try it in GCC. I'm not saying one shouldn't use __cplusplus directly, I'm in full agreement on that :) \$\endgroup\$
    – G. Sliepen
    Nov 6, 2022 at 16:05
  • 1
    \$\begingroup\$ Put another way, I shouldn’t have to say “#if defined(__cplusplus) will always work EXCEPT WHEN YOU’VE TRIGGERED UNDEFINED BEHAVIOUR”, because NOTHING can be guaranteed to work when undefined behaviour has been triggered. AB-SO-LUTE-LY ⓃⓄⓉⒽⒾⓃⒼ. Even int i = 0; assert(i == 0); is no longer guaranteed. So if you say “I did UB on this compiler, and it seemed to work!”, my only response is… 🤷🏼. You could just as easily have said “I did UB, and my laptop turned into a lemon meringue pie”… my response would be the same: 🤷🏼. (Well, that’s a lie… I might ask for a slice.) \$\endgroup\$
    – indi
    Nov 6, 2022 at 19:30
2
\$\begingroup\$

A few things that jump out at me here.

The x_scast Macro

#if x_is_cpp
#define x_scast(type,value) static_cast<type>(value)
#else
#define x_scast(type,value) (type)value
#endif

The C version of this is buggy. Try this:

int test_value = x_scast(int, 5.5/0.5); // Is 10, not 11!

This is because x_scast(int, 5.5/0.5) expands to (int)5.5/0.5. Because the cast has higher precedence than arithmetic operators, it rounds off 5.5 to the int value 5, then divides by 0.5 to get a double value of 10.0. This then gets coerced to an int by the assignment. (GCC or Clang will at least give you a warning that the type is not what you might have expected it to be, if you specify -Wconversion.)

A version that solves the immediate problem would be,

#define x_scast(type,value) ((type)(value))

However, this has very limited utility. x_scast(type, value) is longer to type than the C-style cast, behaves differently when compiled in C or C++ (for instance, casting away const-ness will succeed in C but fail in C++) and doesn’t really offer any advantages.

The Assertions

I am not sure why this is here at all, but there are a number of problems with the implementation as well.

Looking at the assertion handler,

namespace xrx::__detail
{
    /**
     * @brief Print the assert information and terminate the program
     * 
     * @param file      file name
     * @param line      line number
     * @param message   message to print
    */
    x_attr_hidden x_attr_maybe_unused x_attr_noreturn void assert_fail(const char* file, int line, const char* message) 
    {
        #if x_is_cpp
            std::fprintf(stderr, "Assertion Failed\n[FILE] %s [LINE] %d [MSG]: %s ", file, line, message);
            std::terminate();
        #else
            fprintf(stderr, "Assertion Failed\n[FILE] %s [LINE] %d [MSG]: %s ", file, line, message);
            exit(0);
        #endif
    }
    
    /**
     * @brief Ignore unused parameters to avoiding the compiler warning
     * 
     * @tparam T parameters to ignore
    */
    template <typename... T> x_attr_hidden x_attr_maybe_unused x_constexpr void ignore_unused(const T &...) {}
}

There are a lot of things wrong with this.

It’s defined in a header file, so that every execution unit that includes it will duplicate the code. But why would anyone ever want to compile multiple copies of this code into their executable? It’s completely identical, and there’s no reason to optimize the path where an assertion fails, because that should never happen in production code. (And even when it does, it only runs once.)

The behavior is completely different between C and C++.

There is already an assert() in the standard library that is absolutely portable, does the same thing in both languages, and is easier to use.

There’s a redundant check to see whether you’re compiling as C or C++ in the middle of a C++ namespace that could not possibly compile in C, and even an alternative branch for C that cannot possibly be used.

For that matter, it uses #ifdef checks to see whether it should be calling fprintf or std::fprintf, but since the code is within a namespace block anyway, you could just have declared using std::fprintf; within that block.

Also, the code is buggy. Since you don’t flush stdout before writing to stderr, an implementation that buffers stdout could mix the streams and garble the output. In C++, there is also the possibility that stdout and stderr might not be in sync with cout and cerr, so you would want to flush both libraries’ streams.

OS Detection

These macros generally would not be useful to me. For example,

#elif defined(_WIN16) || defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__)

When would I ever want to know that I’m compiling for some version of Windows, but not whether it’s 16-bit, 32-bit or 64-bit? Those have different APIs.

At a glance, some of these defines are incorrect as well. It’s been a while since I needed to patch in a bugfix for SunOS, but this library would detect it as Solaris. And they’re very different: SunOS was BSD-flavored, and Solaris started out as System V-flavored. Or if you were developing for this system today, you would either be using Oracle Solaris or illumos, which this library would not detect.

The biggest problem, though, is that the library is unaware that many implementations define feature-test macros for compatibility, checks in the wrong order, and stops looking. This affects both OSes based on BSD or System V, as well as compilers including ICX, ICC, Clang and GCC, many of which report the wrong result.

You seem to have searched for lists of feature-test macros for historical OSes, and thrown them in all in, willy-nilly, without testing. The ones I’ve coded on myself, I know are wrong.

ISA Detection

I can see more of a use case for some of these: different compilers that all have different feature-test macros for Intel CPUs, but all support <immintrin.h>.

However, if I’m checking the architecture in order to use intrinsics or hand-coded assembly, I would nearly always want to know which ISA extensions I can use. If all I know is that it’s some 64-bit Intel or AMD chip, that does not tell me what vector instructions I can use in my tight inner loop, nor whether I can use cmpxchg16b on my 128-bit atomic data structure.

Honestly, though, I find myself needing to less and less. Compilers will now generate cmpxchg16b instructions for appropriately-sized types with a proper alignas specifier, and have gotten much better at vectorizing loops.

This becomes especially crucial for the versions of ARM, which are completely different architectures incompatible with each other. It would also be crucial to adding support for RISC-V (strangely omitted, while Cray supercomputers from the 1980s are theoretically supported?) since anything you would ever want to test for on RISC-V is in some optional extension of the ISA.

I also see that the ARM64 detection breaks on MSVC, which uses a different macro than you check for.

The closest thing I could think of to a project where this would have come in handy for, where I went with a fairly-complicated block to insert one intrinsic for Intel CPUs, a different intrinsic on MSVC for 64-bit ARM, and inline asm for 65-bit ARM on gcc/clang. However, because I needed different source code for ARM on different compilers, a single cross-platform feature-test macro would have been useless to me. I actually wanted to distinguish between ARM64 on MSVC versus gcc!

In Conclusion

This would be a lot more useful if you picked some practical use cases and focused on making those simpler to write. I’d suggest you drop everything that’s already standardized and every bit of historical esoterica that you cannot actually test. I don’t own a thirty-year-old Cray supercomputer either, but I bet you there would be some kind of compatibility problem if I ever tried to use it on one.

I also highly recommend taking out every attempt to define a new common interface between C and C++ that actually behaves differently on both anyway—especially when the C version runs just fine on a C++ compiler.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.