Program Listing for File macros.hpp

Return to documentation for file (SeQuant/core/utility/macros.hpp)

//
// Created by Eduard Valeyev on 7/31/23
//

#ifndef SEQUANT_CORE_UTILITY_MACROS_H
#define SEQUANT_CORE_UTILITY_MACROS_H

#include <SeQuant/core/utility/exception.hpp>

#include <cstdlib>
#include <iostream>
#include <source_location>
#include <sstream>
#include <utility>

/* detect C++ compiler id:
- ids taken from CMake
- macros are discussed at https://sourceforge.net/p/predef/wiki/Compilers/
*/
#define SEQUANT_CXX_COMPILER_ID_GNU 0
#define SEQUANT_CXX_COMPILER_ID_Clang 1
#define SEQUANT_CXX_COMPILER_ID_AppleClang 2
#define SEQUANT_CXX_COMPILER_ID_XLClang 3
#define SEQUANT_CXX_COMPILER_ID_Intel 4
#if defined(__INTEL_COMPILER_BUILD_DATE) /* macros like __ICC and even       \
                                            __INTEL_COMPILER can be affected \
                                            by command options like -no-icc */
#define SEQUANT_CXX_COMPILER_ID SEQUANT_CXX_COMPILER_ID_Intel
#define SEQUANT_CXX_COMPILER_IS_ICC 1
#endif
#if defined(__clang__) && !defined(SEQUANT_CXX_COMPILER_IS_ICC)
#define SEQUANT_CXX_COMPILER_IS_CLANG 1
#if defined(__apple_build_version__)
#define SEQUANT_CXX_COMPILER_ID SEQUANT_CXX_COMPILER_ID_AppleClang
#elif defined(__ibmxl__)
#define SEQUANT_CXX_COMPILER_ID SEQUANT_CXX_COMPILER_ID_XLClang
#else
#define SEQUANT_CXX_COMPILER_ID SEQUANT_CXX_COMPILER_ID_Clang
#endif
#endif
#if defined(__GNUG__) && !defined(SEQUANT_CXX_COMPILER_IS_ICC) && \
    !defined(SEQUANT_CXX_COMPILER_IS_CLANG)
#define SEQUANT_CXX_COMPILER_ID SEQUANT_CXX_COMPILER_ID_GNU
#define SEQUANT_CXX_COMPILER_IS_GCC 1
#endif

/* ----------- pragma helpers ---------------*/
#define SEQUANT_PRAGMA(x) _Pragma(#x)
/* same as SEQUANT_PRAGMA(x), but expands x */
#define SEQUANT_XPRAGMA(x) SEQUANT_PRAGMA(x)

#define SEQUANT_CONCAT_IMPL(x, y) x##y
/* "concats" a and b without a space in between */
#define SEQUANT_CONCAT(x, y) SEQUANT_CONCAT_IMPL(x, y)
/* "concats" a and b with a space in between */
#define SEQUANT_CONCAT_W_SPACE(a, b) a b
#if defined(SEQUANT_CXX_COMPILER_IS_CLANG)
#define SEQUANT_PRAGMA_CLANG(x) \
  SEQUANT_XPRAGMA(SEQUANT_CONCAT_W_SPACE(clang, x))
#else
#define SEQUANT_PRAGMA_CLANG(x)
#endif
#if defined(SEQUANT_CXX_COMPILER_IS_GCC)
#define SEQUANT_PRAGMA_GCC(x) SEQUANT_XPRAGMA(SEQUANT_CONCAT_W_SPACE(GCC, x))
#else
#define SEQUANT_PRAGMA_GCC(x)
#endif

/* Defines the default error checking behavior */
#define SEQUANT_ASSERT_THROW 2
#define SEQUANT_ASSERT_ABORT 3
#define SEQUANT_ASSERT_IGNORE 4
#define SEQUANT_STRINGIFY(x) #x
#define SEQUANT_ASSERT_BEHAVIOR \
  SEQUANT_CONCAT(SEQUANT_ASSERT_, SEQUANT_ASSERT_BEHAVIOR_)
#if SEQUANT_ASSERT_BEHAVIOR != SEQUANT_ASSERT_IGNORE
#define SEQUANT_ASSERT_ENABLED
#endif

namespace sequant {

#ifdef SEQUANT_ASSERT_ENABLED
[[noreturn]]
#endif
inline void
assert_failed([[maybe_unused]] const std::string &errmsg,
              [[maybe_unused]] const std::source_location location =
                  std::source_location::current()) {
#ifdef SEQUANT_ASSERT_ENABLED
#if SEQUANT_ASSERT_BEHAVIOR == SEQUANT_ASSERT_THROW
  std::ostringstream oss;
  oss
#elif SEQUANT_ASSERT_BEHAVIOR == SEQUANT_ASSERT_ABORT
  std::cerr
#endif  // SEQUANT_ASSERT_BEHAVIOR
      << errmsg << " at " << location.file_name() << ":" << location.line()
      << " in function '" << location.function_name() << "'";
#if SEQUANT_ASSERT_BEHAVIOR == SEQUANT_ASSERT_THROW
  throw sequant::Exception(oss.str());
#elif SEQUANT_ASSERT_BEHAVIOR == SEQUANT_ASSERT_ABORT
  std::cerr << std::endl;
  std::abort();
#endif  // SEQUANT_ASSERT_BEHAVIOR
#endif  // SEQUANT_ASSERT_ENABLED
}
}  // namespace sequant

#ifdef SEQUANT_ASSERT_ENABLED
#define SEQUANT_ASSERT_MESSAGE(EXPR, ...)                          \
  "SEQUANT_ASSERT(" SEQUANT_STRINGIFY(EXPR) ") failed" __VA_OPT__( \
      " with message '" __VA_ARGS__ "'")

#define SEQUANT_ASSERT(EXPR, ...)                                        \
  do {                                                                   \
    if (!(EXPR)) {                                                       \
      sequant::assert_failed(SEQUANT_ASSERT_MESSAGE(EXPR, __VA_ARGS__)); \
    }                                                                    \
  } while (0)
#else
#define SEQUANT_ASSERT(...) \
  do {                      \
  } while (0)
#endif

namespace sequant {
[[noreturn]] inline void abort_msg(
    const std::string &errmsg,
    const std::source_location location = std::source_location::current()) {
  std::cerr << errmsg << " at " << location.file_name() << ":"
            << location.line() << " in function '" << location.function_name()
            << "'";
  std::abort();
}
}  // namespace sequant

#define SEQUANT_ABORT(msg) sequant::abort_msg(msg)

#if defined(__cpp_lib_unreachable)

#define SEQUANT_UNREACHABLE_TOKEN std::unreachable()
#elif defined(SEQUANT_CXX_COMPILER_IS_GCC) || \
    defined(SEQUANT_CXX_COMPILER_IS_CLANG)
#define SEQUANT_UNREACHABLE_TOKEN __builtin_unreachable()
#else
// Compiler-independent fallback
#define SEQUANT_UNREACHABLE_TOKEN std::abort()
#endif

#define SEQUANT_UNREACHABLE                              \
  do {                                                   \
    SEQUANT_ASSERT(false && "reached unreachable code"); \
    SEQUANT_UNREACHABLE_TOKEN;                           \
  } while (0)

#endif  // SEQUANT_CORE_UTILITY_MACROS_H