Skip to content

Commit

Permalink
Extract platform and feature detection to a separate file and add mac…
Browse files Browse the repository at this point in the history
…ro support for a number of C++20/23 features (#93)

---------

Co-authored-by: Jeremy Rifkin <[email protected]>
  • Loading branch information
Rinzii and jeremy-rifkin authored May 7, 2024
1 parent 6af3014 commit 71617b4
Show file tree
Hide file tree
Showing 5 changed files with 313 additions and 97 deletions.
14 changes: 10 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@ on:
push:
pull_request:

# TODO: Check different standards?

jobs:
build-linux:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
compiler: [g++-10, clang++-14]
cxx_version: [ cxx_std_17, cxx_std_20 ]
target: [Debug, Release]
steps:
- uses: actions/checkout@v2
Expand All @@ -24,7 +23,8 @@ jobs:
cd build
cmake .. \
-DCMAKE_BUILD_TYPE=${{matrix.target}} \
-DCMAKE_CXX_COMPILER=${{matrix.compiler}}
-DCMAKE_CXX_COMPILER=${{matrix.compiler}} \
-DLIBASSERT_DESIRED_CXX_STANDARD="${{matrix.cxx_version}}"
make -j
build-macos:
runs-on: macos-14
Expand All @@ -33,6 +33,7 @@ jobs:
matrix:
compiler: [g++-12, clang++]
c_compiler: [g++-12, clang++]
cxx_version: [ cxx_std_17, cxx_std_20 ]
target: [Debug, Release]
steps:
- uses: actions/checkout@v2
Expand All @@ -42,14 +43,16 @@ jobs:
cd build
cmake .. \
-DCMAKE_BUILD_TYPE=${{matrix.target}} \
-DCMAKE_CXX_COMPILER=${{matrix.compiler}}
-DCMAKE_CXX_COMPILER=${{matrix.compiler}} \
-DLIBASSERT_DESIRED_CXX_STANDARD="${{matrix.cxx_version}}"
make -j
build-windows:
runs-on: windows-2022
strategy:
fail-fast: false
matrix:
compiler: [cl, clang++]
cxx_version: [ cxx_std_17, cxx_std_20 ]
target: [Debug, Release]
steps:
- uses: actions/checkout@v2
Expand All @@ -62,13 +65,15 @@ jobs:
cmake .. `
-DCMAKE_BUILD_TYPE=${{matrix.target}} `
-DCMAKE_CXX_COMPILER=${{matrix.compiler}} `
-DLIBASSERT_DESIRED_CXX_STANDARD="${{matrix.cxx_version}}" `
msbuild .\libassert.sln
build-mingw:
runs-on: windows-2022
strategy:
fail-fast: false
matrix:
compiler: [g++]
cxx_version: [ cxx_std_17, cxx_std_20 ]
target: [Debug, Release]
steps:
- uses: actions/checkout@v2
Expand All @@ -81,5 +86,6 @@ jobs:
cmake .. `
-DCMAKE_BUILD_TYPE=${{matrix.target}} `
-DCMAKE_CXX_COMPILER=${{matrix.compiler}} `
-DLIBASSERT_DESIRED_CXX_STANDARD="${{matrix.cxx_version}}" `
"-GUnix Makefiles"
make -j
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
.vs
.vscode
.cache
.idea
scratch
build*
cmake-build-*
__pycache__
compile_commands.json
CMakeUserPresets.json
21 changes: 16 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ include(cmake/PreventInSourceBuilds.cmake)
# used to support find_package
set(package_name "libassert")


# create base project
project(
libassert
Expand Down Expand Up @@ -87,6 +88,7 @@ target_sources(
${target_name} PRIVATE
# include
include/libassert/assert.hpp
include/libassert/platform.hpp
)

# add /src files to target
Expand Down Expand Up @@ -152,11 +154,20 @@ target_include_directories(
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
)

# require C++17 support
target_compile_features(
${target_name}
PUBLIC cxx_std_17
)
# If the user has not defined a C++ standard, default to C++17
if (NOT DEFINED LIBASSERT_DESIRED_CXX_STANDARD)
target_compile_features(
${target_name}
PUBLIC cxx_std_17
)
message(STATUS "libassert: Using cxx_std_17 for target ${target_name}")
else()
target_compile_features(
${target_name}
PUBLIC ${LIBASSERT_DESIRED_CXX_STANDARD}
)
message(STATUS "libassert: Using ${LIBASSERT_DESIRED_CXX_STANDARD} for target ${target_name}")
endif()

if(LIBASSERT_SANITIZER_BUILD)
add_compile_options(-fsanitize=address)
Expand Down
127 changes: 39 additions & 88 deletions include/libassert/assert.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,9 @@
#include <expected>
#endif

#if defined(_MSVC_LANG) && _MSVC_LANG < 201703L
#error "libassert requires C++17 or newer"
#elif !defined(_MSVC_LANG) && __cplusplus < 201703L
#pragma error "libassert requires C++17 or newer"
#endif
#include "libassert/platform.hpp"

#if __cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
#if LIBASSERT_STD_VER >= 20
#include <compare>
#endif

Expand All @@ -56,81 +52,6 @@
// Block comments are used to create some visual separation and try to break the library into more manageable parts.
// I've tried as much as I can to keep logically connected parts together but there is some bootstrapping necessary.

// =====================================================================================================================
// || Preprocessor stuff ||
// =====================================================================================================================

#ifdef _WIN32
#define LIBASSERT_EXPORT_ATTR __declspec(dllexport)
#define LIBASSERT_IMPORT_ATTR __declspec(dllimport)
#else
#define LIBASSERT_EXPORT_ATTR __attribute__((visibility("default")))
#define LIBASSERT_IMPORT_ATTR __attribute__((visibility("default")))
#endif

#ifdef LIBASSERT_STATIC_DEFINE
#define LIBASSERT_EXPORT
#define LIBASSERT_NO_EXPORT
#else
#ifndef LIBASSERT_EXPORT
#ifdef libassert_lib_EXPORTS
/* We are building this library */
#define LIBASSERT_EXPORT LIBASSERT_EXPORT_ATTR
#else
/* We are using this library */
#define LIBASSERT_EXPORT LIBASSERT_IMPORT_ATTR
#endif
#endif
#endif

#define LIBASSERT_IS_CLANG 0
#define LIBASSERT_IS_GCC 0
#define LIBASSERT_IS_MSVC 0

#if defined(__clang__)
#undef LIBASSERT_IS_CLANG
#define LIBASSERT_IS_CLANG 1
#elif defined(__GNUC__) || defined(__GNUG__)
#undef LIBASSERT_IS_GCC
#define LIBASSERT_IS_GCC 1
#elif defined(_MSC_VER)
#undef LIBASSERT_IS_MSVC
#define LIBASSERT_IS_MSVC 1
#include <iso646.h> // alternative operator tokens are standard but msvc requires the include or /permissive- or /Za
#else
#error "Libassert: Unsupported compiler"
#endif

#if LIBASSERT_IS_CLANG || LIBASSERT_IS_GCC
#define LIBASSERT_PFUNC __extension__ __PRETTY_FUNCTION__
#define LIBASSERT_ATTR_COLD [[gnu::cold]]
#define LIBASSERT_ATTR_NOINLINE [[gnu::noinline]]
#define LIBASSERT_UNREACHABLE_CALL __builtin_unreachable()
#else
#define LIBASSERT_PFUNC __FUNCSIG__
#define LIBASSERT_ATTR_COLD
#define LIBASSERT_ATTR_NOINLINE __declspec(noinline)
#define LIBASSERT_UNREACHABLE_CALL __assume(false)
#endif

#if LIBASSERT_IS_MSVC
#define LIBASSERT_STRONG_EXPECT(expr, value) (expr)
#elif defined(__clang__) && __clang_major__ >= 11 || __GNUC__ >= 9
#define LIBASSERT_STRONG_EXPECT(expr, value) __builtin_expect_with_probability((expr), (value), 1)
#else
#define LIBASSERT_STRONG_EXPECT(expr, value) __builtin_expect((expr), (value))
#endif

// deal with gcc shenanigans
// at one point their std::string's move assignment was not noexcept even in c++17
// https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html
#if defined(_GLIBCXX_USE_CXX11_ABI)
// NOLINTNEXTLINE(misc-include-cleaner)
#define LIBASSERT_GCC_ISNT_STUPID _GLIBCXX_USE_CXX11_ABI
#else
// assume others target new abi by default - homework
#define LIBASSERT_GCC_ISNT_STUPID 1
#endif

// always_false is just convenient to use here
#define LIBASSERT_PHONY_USE(E) ((void)libassert::detail::always_false<decltype(E)>)
Expand All @@ -143,13 +64,6 @@
#pragma warning(disable: 4251; disable: 4275)
#endif

#if LIBASSERT_IS_GCC || __cplusplus >= 2020002L
// __VA_OPT__ needed for GCC, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=44317
#define LIBASSERT_VA_ARGS(...) __VA_OPT__(,) __VA_ARGS__
#else
// clang properly eats the comma with ##__VA_ARGS__
#define LIBASSERT_VA_ARGS(...) , ##__VA_ARGS__
#endif

// =====================================================================================================================
// || Core utilities ||
Expand Down Expand Up @@ -1849,8 +1763,45 @@ namespace libassert {
#define assert_val(expr, ...) LIBASSERT_INVOKE_VAL(expr, true, true, "assert_val", assertion, , __VA_ARGS__)
#endif

// Wrapper macro to allow support for C++26's user generated static_assert messages.
// The backup message version also allows for the user to provide a backup version that will
// be used if the compiler does not support user generated messages.
// More info on user generated static_assert's
// can be found here: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2741r1.pdf
//
// Currently the functionality works as such. If we are in a C++26 environment, the user generated message will be used.
// If we are not in a C++26 environment, then either the static_assert will be used without a message or the backup message.
// TODO: Maybe give these a better name? Ideally one that is shorter and more descriptive?
// TODO: Maybe add a helper to make passing user generated static_assert messages easier?
#if defined(__cpp_static_assert) && __cpp_static_assert >= 202306L
#ifdef LIBASSERT_LOWERCASE
#define libassert_user_static_assert(cond, constant) static_assert(cond, constant)
#define libassert_user_static_assert_backup_msg(cond, msg, constant) static_assert(cond, constant)
#define user_static_assert(cond, constant) static_assert(cond, constant)
#define user_static_assert_backup_msg(cond, msg, constant) static_assert(cond, constant)
#else
#define LIBASSERT_USER_STATIC_ASSERT(cond, constant) static_assert(cond, constant)
#define LIBASSERT_USER_STATIC_ASSERT_BACKUP_MSG(cond, msg, constant) static_assert(cond, constant)
#define USER_STATIC_ASSERT(cond, constant) static_assert(cond, constant)
#define USER_STATIC_ASSERT_BACKUP_MSG(cond, msg, constant) static_assert(cond, constant)
#endif
#else
#ifdef LIBASSERT_LOWERCASE
#define libassert_user_static_assert(cond, constant) static_assert(cond)
#define libassert_user_static_assert_backup_msg(cond, msg, constant) static_assert(cond, msg)
#define user_static_assert(cond, constant) static_assert(cond)
#define user_static_assert_backup_msg(cond, msg, constant) static_assert(cond, msg)
#else
#define LIBASSERT_USER_STATIC_ASSERT(cond, constant) static_assert(cond)
#define LIBASSERT_USER_STATIC_ASSERT_BACKUP_MSG(cond, msg, constant) static_assert(cond, msg)
#define USER_STATIC_ASSERT(cond, constant) static_assert(cond)
#define USER_STATIC_ASSERT_BACKUP_MSG(cond, msg, constant) static_assert(cond, msg)
#endif
#endif


#endif // LIBASSERT_HPP

// Intentionally done outside the include guard. Libc++ leaks `assert` (among other things), so the include for
// assert.hpp should go after other includes when using -DLIBASSERT_LOWERCASE.
#ifdef LIBASSERT_LOWERCASE
Expand Down
Loading

0 comments on commit 71617b4

Please sign in to comment.