diff --git a/.github/actions/cmake/build/action.yml b/.github/actions/cmake/build/action.yml
index 4c089a30c..782c04b96 100644
--- a/.github/actions/cmake/build/action.yml
+++ b/.github/actions/cmake/build/action.yml
@@ -11,7 +11,8 @@ runs:
-DCMAKE_INSTALL_PREFIX:PATH=${INSTALL_PREFIX:-/usr} \
-DCMAKE_INSTALL_RUNSTATEDIR:PATH=${INSTALL_RUNSTATEDIR:-/run} \
-DCMAKE_INSTALL_SYSCONFDIR:PATH=${INSTALL_SYSCONFDIR:-/etc} \
- -DENABLE_TESTS:BOOL=ON
+ -DENABLE_TESTS:BOOL=ON \
+ ${EXTRA_CMAKE_OPTIONS:-}
shell: bash --noprofile --norc -euxo pipefail {0}
- name: Build `mod_tile`
diff --git a/.github/workflows/sanitizers-and-warnings.yml b/.github/workflows/sanitizers-and-warnings.yml
new file mode 100644
index 000000000..eccbd1180
--- /dev/null
+++ b/.github/workflows/sanitizers-and-warnings.yml
@@ -0,0 +1,113 @@
+---
+name: Sanitizers & Warnings
+
+on:
+ pull_request:
+ push:
+
+jobs:
+ Warnings:
+ name: >-
+ ${{ matrix.image }}
+ (-Werror)
+ (${{ matrix.compiler }})
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ build_system:
+ - CMake
+ compiler:
+ - GNU
+ - LLVM
+ image:
+ - "ubuntu:24.04"
+ fail-fast: false
+ container:
+ env:
+ CC: ${{ matrix.compiler == 'LLVM' && 'clang' || 'gcc' }}
+ CXX: ${{ matrix.compiler == 'LLVM' && 'clang++' || 'g++' }}
+ EXTRA_CMAKE_OPTIONS: -DENABLE_WARNINGS:BOOL=ON -DENABLE_WERROR:BOOL=ON
+ image: ${{ matrix.image }}
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v6
+
+ - name: Install dependencies
+ uses: ./.github/actions/dependencies/install
+
+ - name: Build `mod_tile`
+ uses: ./.github/actions/cmake/build
+
+ - name: Test `mod_tile`
+ uses: ./.github/actions/cmake/test
+
+ Sanitizers:
+ name: >-
+ ${{ matrix.name }}
+ (${{ matrix.compiler }})
+ # Run on the host rather than in a container so that `vm.mmap_rnd_bits` can be
+ # lowered, which ThreadSanitizer requires on recent kernels
+ runs-on: ubuntu-24.04
+ strategy:
+ matrix:
+ build_system:
+ - CMake
+ compiler:
+ - GNU
+ sanitizers:
+ - address,undefined
+ - thread
+ include:
+ - sanitizers: address,undefined
+ name: ASan + UBSan
+ # Includes the Apache/renderd integration tests (the ASan runtime is preloaded into httpd)
+ test_options: --exclude-regex 'clear_dirs_.+|remove_tile_.+' --output-on-failure
+ - sanitizers: thread
+ name: TSan
+ # TSan cannot be preloaded into the system httpd, so only the unit test executables are run
+ test_options: --tests-regex '_test$|^unit_tests$' --output-on-failure
+ fail-fast: false
+ env:
+ ASAN_OPTIONS: detect_leaks=0
+ BUILD_TYPE: Debug
+ CC: gcc
+ CXX: g++
+ EXTRA_CMAKE_OPTIONS: -DENABLE_SANITIZERS:STRING=${{ matrix.sanitizers }}
+ TSAN_OPTIONS: halt_on_error=1:second_deadlock_stack=1:suppressions=${{ github.workspace }}/tests/tsan.supp
+ UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v6
+
+ - name: Lower `vm.mmap_rnd_bits` for ThreadSanitizer
+ run: sudo sysctl vm.mmap_rnd_bits=28
+ if: matrix.sanitizers == 'thread'
+
+ - name: Install dependencies
+ uses: ./.github/actions/dependencies/install
+
+ # Unlike in containers, installing the packages on the host starts their system services,
+ # whose default ports (e.g. memcached on 11211) collide with the test services
+ - name: Stop system `apache2` & `memcached` services
+ run: sudo systemctl stop apache2 memcached || true
+
+ - name: Build `mod_tile`
+ uses: ./.github/actions/cmake/build
+
+ - name: Test `mod_tile`
+ uses: ./.github/actions/cmake/test
+ with:
+ options: ${{ matrix.test_options }}
+
+ # Services started by the integration tests (httpd, renderd) report sanitizer errors to
+ # their own log files and may still exit "successfully", so check those logs too
+ - name: Check service logs for sanitizer reports
+ run: |
+ if [ ! -d build/tests/logs ]; then
+ exit 0
+ fi
+ if grep -rlaE 'ERROR: AddressSanitizer|runtime error:|WARNING: ThreadSanitizer' build/tests/logs; then
+ grep -raEA30 'ERROR: AddressSanitizer|runtime error:|WARNING: ThreadSanitizer' build/tests/logs
+ exit 1
+ fi
+ shell: bash --noprofile --norc -euo pipefail {0}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ffbc85e73..8e1a03fec 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,7 +4,7 @@
#
#-----------------------------------------------------------------------------
-cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.15 FATAL_ERROR)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
#-----------------------------------------------------------------------------
@@ -21,17 +21,20 @@ project(mod_tile
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_STANDARD 11 CACHE STRING "Sets the C++ standard.")
+set(CMAKE_CXX_STANDARD 14 CACHE STRING "Sets the C++ standard (C++14 minimum; Catch2 v3 requires it).")
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(THREADS_PREFER_PTHREAD_FLAG ON)
set(CMAKE_INSTALL_MODULESDIR CACHE PATH "Apache HTTP Server module installation directory")
-set(ENABLE_MAN ON CACHE BOOL "Build man pages")
-set(ENABLE_TESTS OFF CACHE BOOL "Build test suite")
-set(USE_CAIRO ON CACHE BOOL "Add cairo support if available (for `store_ro_composite.c` backend)")
-set(USE_CURL ON CACHE BOOL "Add curl support if available (for `store_ro_http_proxy.c` backend)")
-set(USE_MEMCACHED ON CACHE BOOL "Add memcached support if available (for `store_memcached.c` backend)")
-set(USE_RADOS ON CACHE BOOL "Add rados support if available (for `store_rados.c` backend)")
+option(ENABLE_MAN "Build man pages" ON)
+option(ENABLE_TESTS "Build test suite" OFF)
+option(ENABLE_WARNINGS "Compile with extra warnings (-Wall -Wextra)" OFF)
+option(ENABLE_WERROR "Treat compiler warnings as errors (requires ENABLE_WARNINGS)" OFF)
+set(ENABLE_SANITIZERS "" CACHE STRING "Comma-separated sanitizers to build with, e.g. 'address,undefined' or 'thread'")
+option(USE_CAIRO "Add cairo support if available (for `store_ro_composite.c` backend)" ON)
+option(USE_CURL "Add curl support if available (for `store_ro_http_proxy.c` backend)" ON)
+option(USE_MEMCACHED "Add memcached support if available (for `store_memcached.c` backend)" ON)
+option(USE_RADOS "Add rados support if available (for `store_rados.c` backend)" ON)
set(MALLOC_LIB "libc" CACHE STRING "Memory Management Library for `renderd`")
set_property(CACHE MALLOC_LIB PROPERTY STRINGS "libc" "jemalloc" "mimalloc" "tcmalloc")
@@ -41,9 +44,11 @@ set_property(CACHE MALLOC_LIB PROPERTY STRINGS "libc" "jemalloc" "mimalloc" "tcm
#
#-----------------------------------------------------------------------------
+include(FeatureSummary)
include(GNUInstallDirs)
# Packages
+find_package(PkgConfig REQUIRED)
find_package(ICU REQUIRED uc)
find_package(Threads REQUIRED)
@@ -55,6 +60,10 @@ find_package(LIBMAPNIK 3 REQUIRED)
if(LIBMAPNIK_VERSION VERSION_GREATER_EQUAL 4)
set(CMAKE_CXX_STANDARD 17)
+elseif(CMAKE_CXX_STANDARD LESS 14)
+ # e.g. an existing build directory whose cache still holds the old default of 11
+ message(WARNING "CMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} is too old (Catch2 v3 needs C++14), using 14")
+ set(CMAKE_CXX_STANDARD 14)
endif()
if(USE_CURL)
@@ -171,8 +180,12 @@ set(RENDERD_TILE_DIR "${CMAKE_INSTALL_LOCALSTATEDIR}/cache/renderd/tiles")
set(RENDERD_PIDFILE "${RENDERD_RUN_DIR}/renderd.pid")
set(RENDERD_SOCKET "${RENDERD_RUN_DIR}/renderd.sock")
-set(TILE_LOAD_DIRECTORY "${HTTPD_SYSCONFDIR}")
-set(TILE_LOAD_FILENAME "tile.load")
+if(NOT DEFINED CACHE{TILE_LOAD_DIRECTORY})
+ set(TILE_LOAD_DIRECTORY "${HTTPD_SYSCONFDIR}")
+endif()
+if(NOT DEFINED CACHE{TILE_LOAD_FILENAME})
+ set(TILE_LOAD_FILENAME "tile.load")
+endif()
set(VERSION "${PROJECT_VERSION}")
#-----------------------------------------------------------------------------
@@ -182,9 +195,10 @@ set(VERSION "${PROJECT_VERSION}")
#-----------------------------------------------------------------------------
# include/config.h.in
+# (generated into the build tree so that multiple build directories do not clobber each other)
configure_file(
${PROJECT_SOURCE_DIR}/includes/config.h.in
- ${PROJECT_SOURCE_DIR}/includes/config.h
+ ${PROJECT_BINARY_DIR}/includes/config.h
)
# etc/apache2/tile.load.in
@@ -199,6 +213,32 @@ configure_file(
${PROJECT_BINARY_DIR}/renderd.conf
)
+#-----------------------------------------------------------------------------
+#
+# Compiler flags
+#
+#-----------------------------------------------------------------------------
+
+if(ENABLE_WARNINGS)
+ add_compile_options(-Wall -Wextra -Wno-unused-parameter)
+ # Log messages and storage IDs are deliberately truncated to PATH_MAX-sized buffers,
+ # so GCC's -Wformat-truncation heuristic only produces false positives here.
+ include(CheckCCompilerFlag)
+ check_c_compiler_flag(-Wformat-truncation HAVE_WFORMAT_TRUNCATION)
+ if(HAVE_WFORMAT_TRUNCATION)
+ add_compile_options(-Wno-format-truncation)
+ endif()
+ if(ENABLE_WERROR)
+ add_compile_options(-Werror)
+ endif()
+endif()
+
+if(ENABLE_SANITIZERS)
+ message(STATUS "Building with sanitizers: ${ENABLE_SANITIZERS}")
+ add_compile_options(-fsanitize=${ENABLE_SANITIZERS} -fno-omit-frame-pointer)
+ add_link_options(-fsanitize=${ENABLE_SANITIZERS})
+endif()
+
#-----------------------------------------------------------------------------
#
# Build
@@ -214,7 +254,11 @@ add_subdirectory(src)
#-----------------------------------------------------------------------------
# Determine install destination for 'etc/apache2/tile.load.in'
-if(EXISTS "/etc/os-release")
+# (auto-detected from the build host unless TILE_LOAD_DIRECTORY / TILE_LOAD_FILENAME are set explicitly,
+# which packagers and cross-compilers should do)
+if(DEFINED CACHE{TILE_LOAD_DIRECTORY} OR DEFINED CACHE{TILE_LOAD_FILENAME})
+ message(STATUS "Using user-supplied TILE_LOAD_DIRECTORY / TILE_LOAD_FILENAME")
+elseif(EXISTS "/etc/os-release")
execute_process(COMMAND sh -c ". /etc/os-release && echo $ID"
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE DISTRO_ID
@@ -320,3 +364,16 @@ if(ENABLE_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
+
+#-----------------------------------------------------------------------------
+#
+# Summary
+#
+#-----------------------------------------------------------------------------
+
+add_feature_info(cairo CAIRO_FOUND "store_ro_composite backend")
+add_feature_info(curl CURL_FOUND "store_ro_http_proxy backend")
+add_feature_info(memcached LIBMEMCACHED_FOUND "store_memcached backend")
+add_feature_info(rados LIBRADOS_FOUND "store_rados backend")
+add_feature_info(tests ENABLE_TESTS "Catch2 test suite")
+feature_summary(WHAT ENABLED_FEATURES DISABLED_FEATURES)
diff --git a/CMakePresets.json b/CMakePresets.json
new file mode 100644
index 000000000..cdd8ea94e
--- /dev/null
+++ b/CMakePresets.json
@@ -0,0 +1,84 @@
+{
+ "version": 3,
+ "cmakeMinimumRequired": {
+ "major": 3,
+ "minor": 21,
+ "patch": 0
+ },
+ "configurePresets": [
+ {
+ "name": "default",
+ "displayName": "Release",
+ "description": "Release build installed under /usr with FHS paths",
+ "binaryDir": "${sourceDir}/build/${presetName}",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Release",
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
+ "CMAKE_INSTALL_LOCALSTATEDIR": "/var",
+ "CMAKE_INSTALL_PREFIX": "/usr",
+ "CMAKE_INSTALL_RUNSTATEDIR": "/run",
+ "CMAKE_INSTALL_SYSCONFDIR": "/etc"
+ }
+ },
+ {
+ "name": "dev",
+ "displayName": "Development",
+ "description": "Debug build with tests and extra warnings",
+ "inherits": "default",
+ "cacheVariables": {
+ "CMAKE_BUILD_TYPE": "Debug",
+ "ENABLE_TESTS": "ON",
+ "ENABLE_WARNINGS": "ON"
+ }
+ },
+ {
+ "name": "asan",
+ "displayName": "AddressSanitizer + UBSan",
+ "description": "Debug build with tests, instrumented with ASan and UBSan",
+ "inherits": "dev",
+ "cacheVariables": {
+ "ENABLE_SANITIZERS": "address,undefined"
+ }
+ },
+ {
+ "name": "tsan",
+ "displayName": "ThreadSanitizer",
+ "description": "Debug build with tests, instrumented with TSan (for renderd threading)",
+ "inherits": "dev",
+ "cacheVariables": {
+ "ENABLE_SANITIZERS": "thread"
+ }
+ }
+ ],
+ "buildPresets": [
+ { "name": "default", "configurePreset": "default" },
+ { "name": "dev", "configurePreset": "dev" },
+ { "name": "asan", "configurePreset": "asan" },
+ { "name": "tsan", "configurePreset": "tsan" }
+ ],
+ "testPresets": [
+ {
+ "name": "dev",
+ "configurePreset": "dev",
+ "output": { "outputOnFailure": true }
+ },
+ {
+ "name": "asan",
+ "configurePreset": "asan",
+ "output": { "outputOnFailure": true },
+ "environment": {
+ "ASAN_OPTIONS": "detect_leaks=0",
+ "UBSAN_OPTIONS": "halt_on_error=1:print_stacktrace=1"
+ }
+ },
+ {
+ "name": "tsan",
+ "configurePreset": "tsan",
+ "output": { "outputOnFailure": true },
+ "filter": { "include": { "name": "_test$|^unit_tests$" } },
+ "environment": {
+ "TSAN_OPTIONS": "suppressions=${sourceDir}/tests/tsan.supp"
+ }
+ }
+ ]
+}
diff --git a/Makefile.am b/Makefile.am
index aa31f1c09..08483e64d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,7 +2,7 @@ AUTOMAKE_OPTIONS = foreign
ACLOCAL_AMFLAGS = -I m4
-AM_CPPFLAGS = $(PTHREAD_CFLAGS) $(GLIB_CFLAGS)
+AM_CPPFLAGS = $(PTHREAD_CFLAGS) $(GLIB_CFLAGS) $(INIPARSER_CFLAGS)
STORE_SOURCES = \
src/g_logger.c \
@@ -98,7 +98,9 @@ render_speedtest_LDADD = $(PTHREAD_CFLAGS) $(GLIB_LIBS) $(INIPARSER_LDFLAGS)
#convert_meta_SOURCES = src/dir_utils.c src/store.c src/convert_meta.c
-noinst_LIBRARIES = catch_main.o catch_test_common.o
+noinst_LIBRARIES = catch2.o catch_main.o catch_test_common.o
+catch2_o_SOURCES = tests/catch/catch_amalgamated.cpp
+catch2_o_CPPFLAGS = -DCATCH_AMALGAMATED_CUSTOM_MAIN
catch_main_o_SOURCES = tests/catch_main.cpp
catch_test_common_o_SOURCES = tests/catch_test_common.cpp
@@ -107,32 +109,32 @@ gen_tile_test_SOURCES = \
$(renderd_SOURCES)
gen_tile_test_CFLAGS = -DMAIN_ALREADY_DEFINED
gen_tile_test_CXXFLAGS = $(renderd_CXXFLAGS)
-gen_tile_test_LDADD = $(renderd_LDADD) catch_test_common.o
+gen_tile_test_LDADD = $(renderd_LDADD) catch_test_common.o catch2.o
renderd_test_SOURCES = \
tests/renderd_test.cpp
-renderd_test_LDADD = $(GLIB_LIBS) catch_main.o catch_test_common.o
+renderd_test_LDADD = $(GLIB_LIBS) catch_main.o catch_test_common.o catch2.o
render_expired_test_SOURCES = \
tests/render_expired_test.cpp
-render_expired_test_LDADD = $(GLIB_LIBS) catch_main.o catch_test_common.o
+render_expired_test_LDADD = $(GLIB_LIBS) catch_main.o catch_test_common.o catch2.o
render_list_test_SOURCES = \
tests/render_list_test.cpp
-render_list_test_LDADD = $(GLIB_LIBS) catch_main.o catch_test_common.o
+render_list_test_LDADD = $(GLIB_LIBS) catch_main.o catch_test_common.o catch2.o
render_old_test_SOURCES = \
tests/render_old_test.cpp
-render_old_test_LDADD = $(GLIB_LIBS) catch_main.o catch_test_common.o
+render_old_test_LDADD = $(GLIB_LIBS) catch_main.o catch_test_common.o catch2.o
render_speedtest_test_SOURCES = \
tests/render_speedtest_test.cpp
-render_speedtest_test_LDADD = $(GLIB_LIBS) catch_main.o catch_test_common.o
+render_speedtest_test_LDADD = $(GLIB_LIBS) catch_main.o catch_test_common.o catch2.o
unit_tests_SOURCES = \
tests/unit_tests.cpp
unit_tests_CXXFLAGS = -Isrc
-unit_tests_LDADD = $(GLIB_LIBS) $(INIPARSER_LDFLAGS) $(MAPNIK_LDFLAGS) $(PTHREAD_CFLAGS) catch_main.o catch_test_common.o
+unit_tests_LDADD = $(GLIB_LIBS) $(INIPARSER_LDFLAGS) $(MAPNIK_LDFLAGS) $(PTHREAD_CFLAGS) catch_main.o catch_test_common.o catch2.o
CLEANFILES=*.slo mod_tile.la stderr.out src/*.slo src/*.lo src/.libs/* src/*.la
diff --git a/README.rst b/README.rst
index 77b27e348..0e5550388 100644
--- a/README.rst
+++ b/README.rst
@@ -27,7 +27,7 @@ Dependencies
* `macOS`
* `Supported Build Systems`
* `CMake `__
- * `GNU Autotools `__
+ * `GNU Autotools `__ (deprecated, use CMake)
* `Runtime/Build Dependencies`
* `Apache 2 HTTP webserver `__
* `Cairo 2D graphics library (optional) `__
diff --git a/configure.ac b/configure.ac
index b024890ba..29664817a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,14 +1,18 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
-# Define mod_tile version number
-m4_define([mod_tile_version], [0.8.1])
+# NOTE: The Autotools build is deprecated; CMake is the supported build system.
+
+# mod_tile version number, taken from CMakeLists.txt so it is only defined in one place
+m4_define([mod_tile_version],
+ m4_esyscmd_s([sed -n 's/^ VERSION \([0-9][0-9.]*\)$/\1/p' CMakeLists.txt]))
+m4_if(mod_tile_version, [], [m4_fatal([Could not read the project version from CMakeLists.txt])])
AC_PREREQ([2.61])
AX_CONFIG_NICE
AC_INIT([mod_tile],
[mod_tile_version],
- [http://trac.openstreetmap.org])
+ [https://github.com/openstreetmap/mod_tile/issues])
AM_INIT_AUTOMAKE([subdir-objects])
LT_INIT
AC_CONFIG_SRCDIR([src/mod_tile.c])
@@ -64,10 +68,15 @@ AC_CHECK_LIB(rados, rados_version, [
LIBRADOS_LDFLAGS='-lrados'
AC_SUBST(LIBRADOS_LDFLAGS)
][])
-AC_CHECK_LIB(iniparser, iniparser_load, [
- INIPARSER_LDFLAGS='-liniparser'
+PKG_CHECK_MODULES([INIPARSER], [iniparser], [
+ INIPARSER_LDFLAGS="${INIPARSER_LIBS}"
AC_SUBST(INIPARSER_LDFLAGS)
-], [AC_MSG_ERROR([Unable to find libiniparser])])
+], [
+ AC_CHECK_LIB(iniparser, iniparser_load, [
+ INIPARSER_LDFLAGS='-liniparser'
+ AC_SUBST(INIPARSER_LDFLAGS)
+ ], [AC_MSG_ERROR([Unable to find libiniparser])])
+])
AC_CHECK_FUNCS([bzero gethostbyname gettimeofday inet_ntoa memset mkdir pow select socket strchr strdup strerror strrchr strstr strtol strtoul utime],[],[AC_MSG_ERROR([One of the required functions was not found])])
AC_CHECK_FUNCS([daemon getloadavg],[],[])
@@ -91,3 +100,9 @@ fi
AC_CONFIG_FILES(Makefile)
AC_OUTPUT
+
+AC_MSG_WARN([
+ The Autotools build of mod_tile is deprecated and may be removed in a future
+ release. Please use CMake instead, e.g.:
+ cmake -B build -S . && cmake --build build
+ or one of the presets listed by: cmake --list-presets])
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c182ac8d7..2cab3a484 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -4,6 +4,8 @@
#
#-----------------------------------------------------------------------------
+# Generated config.h lives in the build tree and must be found first
+include_directories(BEFORE ${PROJECT_BINARY_DIR}/includes)
include_directories(${PROJECT_SOURCE_DIR}/includes)
include_directories(SYSTEM
@@ -21,38 +23,15 @@ include_directories(SYSTEM
${LIBRADOS_INCLUDE_DIRS}
)
-link_directories(${CMAKE_LIBRARY_PATH})
-
-set(COMMON_SRCS
- g_logger.c
- sys_utils.c
-)
set(COMMON_LIBRARIES
${GLIB_LIBRARIES}
Threads::Threads
)
-
-set(RENDER_SRCS
- ${COMMON_SRCS}
- protocol_helper.c
- renderd_config.c
-)
set(RENDER_LIBRARIES
${COMMON_LIBRARIES}
${INIPARSER_LIBRARIES}
${MATH_LIBRARY}
)
-
-set(STORE_SRCS
- store.c
- store_file.c
- store_file_utils.c
- store_memcached.c
- store_null.c
- store_rados.c
- store_ro_composite.c
- store_ro_http_proxy.c
-)
set(STORE_LIBRARIES
${CAIRO_LIBRARIES}
${CURL_LIBRARIES}
@@ -65,6 +44,82 @@ if(NOT MALLOC_LIB STREQUAL "libc")
list(PREPEND RENDER_LIBRARIES ${MALLOC_LIBRARY})
endif()
+#-----------------------------------------------------------------------------
+#
+# Internal object libraries
+#
+# Sources shared between several targets are compiled once here rather than
+# once per target. They are built position-independent so that they can also
+# be linked into the `mod_tile.so` Apache module.
+#
+#-----------------------------------------------------------------------------
+
+# Logging & system helpers (used by everything)
+add_library(common_objs OBJECT
+ g_logger.c
+ sys_utils.c
+)
+
+# renderd.conf parsing (used by everything)
+add_library(config_objs OBJECT
+ renderd_config.c
+)
+
+# renderd socket protocol (used by renderd and the render_* clients)
+add_library(protocol_objs OBJECT
+ protocol_helper.c
+)
+
+# Client-side render request queue (used by the render_* clients)
+add_library(submit_queue_objs OBJECT
+ render_submit_queue.c
+)
+
+# Metatile path helpers (used by render_old without the full storage layer)
+add_library(store_file_utils_objs OBJECT
+ store_file_utils.c
+)
+
+# Pluggable storage backends
+add_library(store_objs OBJECT
+ store.c
+ store_file.c
+ store_memcached.c
+ store_null.c
+ store_rados.c
+ store_ro_composite.c
+ store_ro_http_proxy.c
+)
+
+# Mapnik rendering core (everything in renderd apart from `main()`)
+add_library(renderd_core_objs OBJECT
+ cache_expire.c
+ daemon_compat.c
+ gen_tile.cpp
+ metatile.cpp
+ parameterize_style.cpp
+ request_queue.c
+)
+
+set_target_properties(
+ common_objs
+ config_objs
+ protocol_objs
+ renderd_core_objs
+ store_file_utils_objs
+ store_objs
+ submit_queue_objs
+ PROPERTIES POSITION_INDEPENDENT_CODE ON
+)
+
+set(COMMON_OBJS $)
+set(CONFIG_OBJS $)
+set(PROTOCOL_OBJS $)
+set(SUBMIT_QUEUE_OBJS $)
+set(RENDER_OBJS ${COMMON_OBJS} ${CONFIG_OBJS} ${PROTOCOL_OBJS})
+set(STORE_OBJS $ $)
+set(RENDERD_CORE_OBJS $)
+
#-----------------------------------------------------------------------------
#
# Installed targets
@@ -78,10 +133,10 @@ endif()
#-----------------------------------------------------------------------------
set(mod_tile_SRCS
- ${COMMON_SRCS}
- ${STORE_SRCS}
+ ${COMMON_OBJS}
+ ${CONFIG_OBJS}
+ ${STORE_OBJS}
mod_tile.c
- renderd_config.c
)
set(mod_tile_LIBS
${COMMON_LIBRARIES}
@@ -104,10 +159,10 @@ set_target_properties(mod_tile PROPERTIES PREFIX "" SUFFIX ".so")
#-----------------------------------------------------------------------------
set(render_expired_SRCS
- ${RENDER_SRCS}
- ${STORE_SRCS}
+ ${RENDER_OBJS}
+ ${STORE_OBJS}
+ ${SUBMIT_QUEUE_OBJS}
render_expired.c
- render_submit_queue.c
)
set(render_expired_LIBS
${RENDER_LIBRARIES}
@@ -123,10 +178,10 @@ target_link_libraries(render_expired ${render_expired_LIBS})
#-----------------------------------------------------------------------------
set(render_list_SRCS
- ${RENDER_SRCS}
- ${STORE_SRCS}
+ ${RENDER_OBJS}
+ ${STORE_OBJS}
+ ${SUBMIT_QUEUE_OBJS}
render_list.c
- render_submit_queue.c
)
set(render_list_LIBS
${RENDER_LIBRARIES}
@@ -142,10 +197,10 @@ target_link_libraries(render_list ${render_list_LIBS})
#-----------------------------------------------------------------------------
set(render_old_SRCS
- ${RENDER_SRCS}
+ ${RENDER_OBJS}
+ ${SUBMIT_QUEUE_OBJS}
+ $
render_old.c
- render_submit_queue.c
- store_file_utils.c
)
set(render_old_LIBS
${RENDER_LIBRARIES}
@@ -160,9 +215,9 @@ target_link_libraries(render_old ${render_old_LIBS})
#-----------------------------------------------------------------------------
set(render_speedtest_SRCS
- ${RENDER_SRCS}
+ ${RENDER_OBJS}
+ ${SUBMIT_QUEUE_OBJS}
render_speedtest.cpp
- render_submit_queue.c
)
set(render_speedtest_LIBS
${RENDER_LIBRARIES}
@@ -177,15 +232,10 @@ target_link_libraries(render_speedtest ${render_speedtest_LIBS})
#-----------------------------------------------------------------------------
set(renderd_SRCS
- ${RENDER_SRCS}
- ${STORE_SRCS}
- cache_expire.c
- daemon_compat.c
- gen_tile.cpp
- metatile.cpp
- parameterize_style.cpp
+ ${RENDER_OBJS}
+ ${STORE_OBJS}
+ ${RENDERD_CORE_OBJS}
renderd.c
- request_queue.c
)
set(renderd_LIBS
${ICU_LIBRARIES}
@@ -210,6 +260,7 @@ if(ENABLE_TESTS)
#-----------------------------------------------------------------------------
set(gen_tile_test_SRCS
+ $
$
${renderd_SRCS}
${PROJECT_SOURCE_DIR}/tests/gen_tile_test.cpp
@@ -232,11 +283,13 @@ target_link_libraries(gen_tile_test ${gen_tile_test_LIBS})
#-----------------------------------------------------------------------------
set(unit_tests_SRCS
+ $
$
${PROJECT_SOURCE_DIR}/tests/unit_tests.cpp
)
set(unit_tests_LIBS
${COMMON_LIBRARIES}
+ ${ICU_LIBRARIES}
${INIPARSER_LIBRARIES}
${LIBMAPNIK_LIBRARIES}
)
diff --git a/src/render_expired.c b/src/render_expired.c
index 1f5a80a21..4bb7d524c 100644
--- a/src/render_expired.c
+++ b/src/render_expired.c
@@ -38,17 +38,33 @@
// we'd still only use 4^17 bits = 2 GB RAM (plus a little for the lower zoom
// levels) - this saves us the hassle of working with a tree structure.
-#define TILE_REQUESTED(z, x, y) \
- (tile_requested[z][((x) * twopow[z] + (y)) / (8 * sizeof(int))] >> (((x) * twopow[z] + (y)) % (8 * sizeof(int)))) & 0x01
-#define SET_TILE_REQUESTED(z, x, y) \
- tile_requested[z][((x) * twopow[z] + (y)) / (8 * sizeof(int))] |= (0x01 << (((x) * twopow[z] + (y)) % (8 * sizeof(int))));
+// Set of metatiles already requested, so each is only queued once.
+// Keys are (z, x, y) packed into a gint64; this only uses memory for tiles actually
+// seen, unlike a per-zoom bitmap (which needed ~2 GiB at --max-zoom 20).
+static GHashTable *tile_requested;
+
+static gint64 tile_key(int z, int x, int y)
+{
+ return ((gint64)z << 48) | ((gint64)(x & 0xFFFFFF) << 24) | (gint64)(y & 0xFFFFFF);
+}
+
+static int tile_is_requested(int z, int x, int y)
+{
+ gint64 key = tile_key(z, x, y);
+ return g_hash_table_contains(tile_requested, &key);
+}
+
+static void set_tile_requested(int z, int x, int y)
+{
+ gint64 *key = g_new(gint64, 1);
+ *key = tile_key(z, x, y);
+ g_hash_table_add(tile_requested, key);
+}
// tile marking arrays
-unsigned int **tile_requested;
// "two raised to the power of [...]" - don't trust pow() to be efficient
// for base 2
-unsigned long long twopow[MAX_ZOOM];
void display_rate(struct timeval start, struct timeval end, int num)
{
@@ -298,19 +314,7 @@ int main(int argc, char **argv)
// initialise arrays for tile markings
- tile_requested = (unsigned int **)malloc((max_zoom - excess_zoomlevels + 1) * sizeof(unsigned int *));
-
- for (int i = 0; i <= max_zoom - excess_zoomlevels; i++) {
- // initialize twopow array
- twopow[i] = (i == 0) ? 1 : twopow[i - 1] * 2;
- unsigned long long fourpow = twopow[i] * twopow[i];
- tile_requested[i] = (unsigned int *)calloc((fourpow / METATILE) + 1, 1);
-
- if (NULL == tile_requested[i]) {
- g_logger(G_LOG_LEVEL_CRITICAL, "not enough memory available");
- return 1;
- }
- }
+ tile_requested = g_hash_table_new_full(g_int64_hash, g_int64_equal, g_free, NULL);
store = init_storage_backend(tile_dir);
@@ -410,7 +414,7 @@ int main(int argc, char **argv)
// renderd does keep a list internally to avoid enqueuing the same tile
// twice but in case it has already rendered the tile we don't want to
// cause extra work.
- if (TILE_REQUESTED(z - excess_zoomlevels, x >> excess_zoomlevels, y >> excess_zoomlevels)) {
+ if (tile_is_requested(z - excess_zoomlevels, x >> excess_zoomlevels, y >> excess_zoomlevels)) {
if (verbose) {
g_logger(G_LOG_LEVEL_MESSAGE, "Already requested metatile containing '%d/%d/%d', moving on to next input line", z, x, y);
}
@@ -425,7 +429,7 @@ int main(int argc, char **argv)
// mark tile as requested. (do this even if, below, the tile is not
// actually requested due to not being present on disk, to avoid
// unnecessary later stat'ing).
- SET_TILE_REQUESTED(z - excess_zoomlevels, x >> excess_zoomlevels, y >> excess_zoomlevels);
+ set_tile_requested(z - excess_zoomlevels, x >> excess_zoomlevels, y >> excess_zoomlevels);
// commented out - seems to cause problems in MT environment,
// trying to write to already-closed file
@@ -492,11 +496,7 @@ int main(int argc, char **argv)
store->close_storage(store);
free(store);
- for (int i = 0; i <= max_zoom - excess_zoomlevels; i++) {
- free(tile_requested[i]);
- }
-
- free(tile_requested);
+ g_hash_table_destroy(tile_requested);
gettimeofday(&end, NULL);
g_logger(G_LOG_LEVEL_MESSAGE, "Read and expanded %i tiles from list.", num_read);
diff --git a/src/renderd.c b/src/renderd.c
index 6ad8e5209..11929fe61 100644
--- a/src/renderd.c
+++ b/src/renderd.c
@@ -165,14 +165,14 @@ enum protoCmd rx_request(struct protocol *req, int fd)
void request_exit(void)
{
- // Any write to the exit pipe will trigger a graceful exit
+ // Any write to the exit pipe will trigger a graceful exit.
+ // This function is called from a signal handler, so only async-signal-safe
+ // functions (write(2)) may be used here — no g_logger, no strerror.
char c = 0;
-
- g_logger(G_LOG_LEVEL_INFO, "Sending exit request");
-
- if (write(exit_pipe_fd, &c, sizeof(c)) < 0) {
- g_logger(G_LOG_LEVEL_ERROR, "Failed to write to the exit pipe: %s", strerror(errno));
- }
+ // Nothing useful can be done on failure here; assign to discard (GCC ignores a (void) cast
+ // for functions declared warn_unused_result)
+ ssize_t ignored = write(exit_pipe_fd, &c, sizeof(c));
+ (void)ignored;
}
void process_loop(int listen_fd)
@@ -637,7 +637,20 @@ void *slave_thread(void * arg)
struct protocol * req_slave;
req_slave = (struct protocol *)malloc(sizeof(struct protocol));
+
+ if (req_slave == NULL) {
+ g_logger(G_LOG_LEVEL_ERROR, "slave_thread: failed to allocate memory for req_slave");
+ return NULL;
+ }
+
resp = (struct protocol *)malloc(sizeof(struct protocol));
+
+ if (resp == NULL) {
+ g_logger(G_LOG_LEVEL_ERROR, "slave_thread: failed to allocate memory for resp");
+ free(req_slave);
+ return NULL;
+ }
+
bzero(req_slave, sizeof(struct protocol));
bzero(resp, sizeof(struct protocol));
@@ -718,10 +731,27 @@ void *slave_thread(void * arg)
retry = 10;
while ((ret_size < sizeof(struct protocol)) && (retry > 0)) {
- ret_size = recv(pfd, resp + ret_size, (sizeof(struct protocol)
- - ret_size), 0);
+ ssize_t n = recv(pfd, (char *)resp + ret_size,
+ sizeof(struct protocol) - ret_size, 0);
+
+ if (n < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
- if ((errno == EPIPE) || ret_size == 0) {
+ if (errno == EPIPE) {
+ close(pfd);
+ pfd = FD_INVALID;
+ ret_size = 0;
+ g_logger(G_LOG_LEVEL_ERROR, "Pipe to Renderd slave closed");
+ break;
+ }
+
+ retry--;
+ continue;
+ }
+
+ if (n == 0) {
close(pfd);
pfd = FD_INVALID;
ret_size = 0;
@@ -729,7 +759,7 @@ void *slave_thread(void * arg)
break;
}
- retry--;
+ ret_size += n;
}
if (ret_size < sizeof(struct protocol)) {
@@ -921,6 +951,12 @@ int main(int argc, char **argv)
render_threads = (pthread_t *) malloc(sizeof(pthread_t) * config->num_threads);
+ if (render_threads == NULL) {
+ g_logger(G_LOG_LEVEL_CRITICAL, "Failed to allocate memory for render threads");
+ close(fd);
+ return 7;
+ }
+
for (i = 0; i < config->num_threads; i++) {
if (pthread_create(&render_threads[i], NULL, render_thread, (void *)maps)) {
g_logger(G_LOG_LEVEL_CRITICAL, "Could not spawn rendering thread");
@@ -934,6 +970,12 @@ int main(int argc, char **argv)
k = 0;
slave_threads = (pthread_t *) malloc(sizeof(pthread_t) * num_slave_threads);
+ if (slave_threads == NULL) {
+ g_logger(G_LOG_LEVEL_CRITICAL, "Failed to allocate memory for slave threads");
+ close(fd);
+ return 7;
+ }
+
for (i = 1; i < MAX_SLAVES; i++) {
for (j = 0; j < config_slaves[i].num_threads; j++) {
if (pthread_create(&slave_threads[k++], NULL, slave_thread, (void *) &config_slaves[i])) {
diff --git a/src/request_queue.c b/src/request_queue.c
index 832d2e788..6bb893993 100644
--- a/src/request_queue.c
+++ b/src/request_queue.c
@@ -491,6 +491,15 @@ struct request_queue * request_queue_init()
queue->renderHead.next = queue->renderHead.prev = &(queue->renderHead);
queue->hashidxSize = HASHIDX_SIZE;
queue->item_hashidx = (struct item_idx *) malloc(sizeof(struct item_idx) * queue->hashidxSize);
+
+ if (queue->item_hashidx == NULL) {
+ g_logger(G_LOG_LEVEL_ERROR, "Failed to allocate memory for request queue hash index");
+ pthread_cond_destroy(&(queue->qCond));
+ pthread_mutex_destroy(&(queue->qLock));
+ free(queue);
+ return NULL;
+ }
+
bzero(queue->item_hashidx, sizeof(struct item_idx) * queue->hashidxSize);
return queue;
diff --git a/src/store_file.c b/src/store_file.c
index 5464cf715..07b8f6d26 100644
--- a/src/store_file.c
+++ b/src/store_file.c
@@ -74,6 +74,11 @@ static int file_tile_read(struct storage_backend * store, const char *xmlconfig,
struct meta_layout *m = (struct meta_layout *)malloc(header_len);
size_t file_offset, tile_size;
+ if (m == NULL) {
+ snprintf(log_msg, PATH_MAX - 1, "Failed to allocate memory for metatile header\n");
+ return -1;
+ }
+
meta_offset = xyzo_to_meta(path, sizeof(path), (char *)(store->storage_ctx), xmlconfig, options, x, y, z);
fd = open(path, O_RDONLY);
@@ -219,6 +224,12 @@ static int file_metatile_write(struct storage_backend * store, const char *xmlco
g_logger(G_LOG_LEVEL_DEBUG, "Creating and writing a metatile to %s", meta_path);
tmp = (char *)malloc(sizeof(char) * strlen(meta_path) + 24);
+
+ if (tmp == NULL) {
+ g_logger(G_LOG_LEVEL_ERROR, "file_metatile_write: failed to allocate memory for temp path");
+ return -1;
+ }
+
snprintf(tmp, strlen(meta_path) + 24, "%s.%lu", meta_path, (unsigned long) pthread_self());
if (mkdirp(tmp)) {
@@ -246,7 +257,14 @@ static int file_metatile_write(struct storage_backend * store, const char *xmlco
}
close(fd);
- rename(tmp, meta_path);
+
+ if (rename(tmp, meta_path) != 0) {
+ g_logger(G_LOG_LEVEL_WARNING, "Error renaming temp file %s to %s: %s", tmp, meta_path, strerror(errno));
+ unlink(tmp);
+ free(tmp);
+ return -1;
+ }
+
free(tmp);
return sz;
diff --git a/src/store_memcached.c b/src/store_memcached.c
index 981b8b102..07546f035 100644
--- a/src/store_memcached.c
+++ b/src/store_memcached.c
@@ -80,6 +80,11 @@ static int memcached_tile_read(struct storage_backend * store, const char *xmlco
memcached_return_t rc;
char * buf_raw;
+ if (m == NULL) {
+ snprintf(log_msg, 1024, "Failed to allocate memory for metatile header\n");
+ return -1;
+ }
+
mask = METATILE - 1;
meta_offset = (x & mask) * METATILE + (y & mask);
@@ -235,7 +240,6 @@ static int memcached_metatile_expire(struct storage_backend * store, const char
char * buf;
size_t len;
uint32_t flags;
- uint64_t cas;
memcached_return_t rc;
//TODO: deal with options
@@ -246,11 +250,11 @@ static int memcached_metatile_expire(struct storage_backend * store, const char
return -1;
}
- //cas = memcached_result_cas(&rc);
-
((struct stat_info *)buf)->expired = 1;
- rc = memcached_cas(store->storage_ctx, meta_path, strlen(meta_path), buf, len, 0, flags, cas);
+ // memcached_get() does not return a CAS value, so a compare-and-swap cannot be used here
+ // (the previous code passed an uninitialised CAS token). Replace the existing item instead.
+ rc = memcached_replace(store->storage_ctx, meta_path, strlen(meta_path), buf, len, 0, flags);
if (rc != MEMCACHED_SUCCESS) {
free(buf);
diff --git a/src/store_rados.c b/src/store_rados.c
index c057a804e..4b5281143 100644
--- a/src/store_rados.c
+++ b/src/store_rados.c
@@ -134,6 +134,11 @@ static int rados_tile_read(struct storage_backend * store, const char *xmlconfig
int err;
char * buf_raw;
+ if (m == NULL) {
+ snprintf(log_msg, 1024, "Failed to allocate memory for metatile header\n");
+ return -1;
+ }
+
mask = METATILE - 1;
meta_offset = (x & mask) * METATILE + (y & mask);
@@ -234,6 +239,11 @@ static int rados_metatile_write(struct storage_backend * store, const char *xmlc
char * buf2 = malloc(sz2);
int err;
+ if (buf2 == NULL) {
+ g_logger(G_LOG_LEVEL_ERROR, "rados_metatile_write: failed to allocate memory for write buffer");
+ return -1;
+ }
+
tile_stat.expired = 0;
tile_stat.size = sz;
tile_stat.mtime = time(NULL);
@@ -352,7 +362,7 @@ struct storage_backend * init_storage_rados(const char * connection_string)
int err;
int i;
- if (ctx == NULL) {
+ if (ctx == NULL || store == NULL) {
free(ctx);
free(store);
return NULL;
diff --git a/src/store_ro_composite.c b/src/store_ro_composite.c
index 8faf4b4d6..959cf0560 100644
--- a/src/store_ro_composite.c
+++ b/src/store_ro_composite.c
@@ -202,8 +202,12 @@ static int ro_composite_metatile_expire(struct storage_backend * store, const ch
static int ro_composite_close_storage(struct storage_backend * store)
{
struct ro_composite_ctx * ctx = (struct ro_composite_ctx *)(store->storage_ctx);
+ // close_storage() no longer frees the backend itself (#524), so free the
+ // two inner backends here, as the callers do for the outer one
ctx->store_primary->close_storage(ctx->store_primary);
+ free(ctx->store_primary);
ctx->store_secondary->close_storage(ctx->store_secondary);
+ free(ctx->store_secondary);
if (ctx->cache.tile) {
free(ctx->cache.tile);
@@ -226,7 +230,7 @@ struct storage_backend * init_storage_ro_composite(const char * connection_strin
return NULL;
#else
struct storage_backend * store = malloc(sizeof(struct storage_backend));
- struct ro_composite_ctx * ctx = malloc(sizeof(struct ro_composite_ctx));
+ struct ro_composite_ctx * ctx = calloc(1, sizeof(struct ro_composite_ctx));
char * connection_string_primary;
char * connection_string_secondary;
char * tmp;
@@ -248,7 +252,23 @@ struct storage_backend * init_storage_ro_composite(const char * connection_strin
}
connection_string_secondary = strstr(connection_string, "}{");
+
+ if (connection_string_secondary == NULL) {
+ g_logger(G_LOG_LEVEL_ERROR, "init_storage_ro_composite: malformed connection string, missing '}{' separator: %s", connection_string);
+ free(ctx);
+ free(store);
+ return NULL;
+ }
+
connection_string_primary = malloc(strlen(connection_string) - strlen("composite:{") - strlen(connection_string_secondary) + 1);
+
+ if (connection_string_primary == NULL) {
+ g_logger(G_LOG_LEVEL_ERROR, "init_storage_ro_composite: failed to allocate memory for primary connection string");
+ free(ctx);
+ free(store);
+ return NULL;
+ }
+
memcpy(connection_string_primary, connection_string + strlen("composite:{"), strlen(connection_string) - strlen("composite:{") - strlen(connection_string_secondary));
connection_string_primary[strlen(connection_string) - strlen("composite:{") - strlen(connection_string_secondary)] = 0;
connection_string_secondary = strdup(connection_string_secondary + 2);
@@ -258,30 +278,61 @@ struct storage_backend * init_storage_ro_composite(const char * connection_strin
g_logger(G_LOG_LEVEL_DEBUG, "init_storage_ro_composite: Secondary storage backend: %s", connection_string_secondary);
tmp = strstr(connection_string_primary, ",");
+
+ if (tmp == NULL) {
+ g_logger(G_LOG_LEVEL_ERROR, "init_storage_ro_composite: malformed primary connection string, missing ',' separator: %s", connection_string_primary);
+ free(connection_string_primary);
+ free(connection_string_secondary);
+ free(ctx);
+ free(store);
+ return NULL;
+ }
+
memcpy(ctx->xmlconfig_primary, connection_string_primary, tmp - connection_string_primary);
ctx->xmlconfig_primary[tmp - connection_string_primary] = 0;
ctx->store_primary = init_storage_backend(tmp + 1);
if (ctx->store_primary == NULL) {
g_logger(G_LOG_LEVEL_ERROR, "init_storage_ro_composite: failed to initialise primary storage backend");
+ free(connection_string_primary);
+ free(connection_string_secondary);
free(ctx);
free(store);
return NULL;
}
tmp = strstr(connection_string_secondary, ",");
+
+ if (tmp == NULL) {
+ g_logger(G_LOG_LEVEL_ERROR, "init_storage_ro_composite: malformed secondary connection string, missing ',' separator: %s", connection_string_secondary);
+ free(connection_string_primary);
+ free(connection_string_secondary);
+ ctx->store_primary->close_storage(ctx->store_primary);
+ free(ctx->store_primary);
+ free(ctx);
+ free(store);
+ return NULL;
+ }
+
memcpy(ctx->xmlconfig_secondary, connection_string_secondary, tmp - connection_string_secondary);
ctx->xmlconfig_secondary[tmp - connection_string_secondary] = 0;
ctx->store_secondary = init_storage_backend(tmp + 1);
if (ctx->store_secondary == NULL) {
g_logger(G_LOG_LEVEL_ERROR, "init_storage_ro_composite: failed to initialise secondary storage backend");
+ free(connection_string_primary);
+ free(connection_string_secondary);
ctx->store_primary->close_storage(ctx->store_primary);
+ free(ctx->store_primary);
free(ctx);
free(store);
return NULL;
}
+ // The backends keep their own copies of what they need from these
+ free(connection_string_primary);
+ free(connection_string_secondary);
+
ctx->render_size = 256;
store->storage_ctx = ctx;
diff --git a/src/store_ro_http_proxy.c b/src/store_ro_http_proxy.c
index 69925fb7e..61b0e098a 100644
--- a/src/store_ro_http_proxy.c
+++ b/src/store_ro_http_proxy.c
@@ -116,6 +116,7 @@ static int ro_http_proxy_tile_retrieve(struct storage_backend * store, const cha
if (res != CURLE_OK) {
g_logger(G_LOG_LEVEL_ERROR, "ro_http_proxy_tile_fetch: failed to retrieve file: %s", curl_easy_strerror(res));
+ free(chunk.memory);
ctx->cache.x = -1;
ctx->cache.y = -1;
ctx->cache.z = -1;
@@ -126,6 +127,7 @@ static int ro_http_proxy_tile_retrieve(struct storage_backend * store, const cha
if (res != CURLE_OK) {
g_logger(G_LOG_LEVEL_ERROR, "ro_http_proxy_tile_fetch: failed to retrieve HTTP code: %s", curl_easy_strerror(res));
+ free(chunk.memory);
ctx->cache.x = -1;
ctx->cache.y = -1;
ctx->cache.z = -1;
@@ -150,19 +152,31 @@ static int ro_http_proxy_tile_retrieve(struct storage_backend * store, const cha
case 404: {
if (ctx->cache.tile != NULL) {
free(ctx->cache.tile);
+ ctx->cache.tile = NULL;
}
+ free(chunk.memory);
ctx->cache.st_stat.size = -1;
ctx->cache.st_stat.expired = 0;
break;
}
- }
+ default: {
+ // Do not cache anything for unexpected responses, otherwise the previously
+ // cached tile would be served for these coordinates
+ g_logger(G_LOG_LEVEL_ERROR, "ro_http_proxy_tile_fetch: unexpected HTTP response code %li", httpCode);
+ free(chunk.memory);
+ ctx->cache.x = -1;
+ ctx->cache.y = -1;
+ ctx->cache.z = -1;
+ return -1;
+ }
+ }
ctx->cache.x = x;
ctx->cache.y = y;
ctx->cache.z = z;
- strcpy(ctx->cache.xmlname, xmlconfig);
+ snprintf(ctx->cache.xmlname, sizeof(ctx->cache.xmlname), "%s", xmlconfig);
return 1;
}
}
@@ -172,7 +186,12 @@ static int ro_http_proxy_tile_read(struct storage_backend * store, const char *x
struct ro_http_proxy_ctx * ctx = (struct ro_http_proxy_ctx *)(store->storage_ctx);
if (ro_http_proxy_tile_retrieve(store, xmlconfig, options, x, y, z) > 0) {
- if (ctx->cache.st_stat.size > sz) {
+ if (ctx->cache.st_stat.size < 0) {
+ // Cached 404
+ return -1;
+ }
+
+ if ((size_t)ctx->cache.st_stat.size > sz) {
g_logger(G_LOG_LEVEL_ERROR, "ro_http_proxy_tile_read: size was too big, overrun %lu %li", sz, ctx->cache.st_stat.size);
return -1;
}
@@ -299,6 +318,7 @@ struct storage_backend * init_storage_ro_http_proxy(const char * connection_stri
if (res != CURLE_OK) {
g_logger(G_LOG_LEVEL_ERROR, "init_storage_ro_http_proxy: failed to initialise global curl: %s", curl_easy_strerror(res));
+ free(ctx->baseurl);
free(ctx);
free(store);
return NULL;
@@ -308,6 +328,7 @@ struct storage_backend * init_storage_ro_http_proxy(const char * connection_stri
if (!ctx->ctx) {
g_logger(G_LOG_LEVEL_ERROR, "init_storage_ro_http_proxy: failed to initialise curl");
+ free(ctx->baseurl);
free(ctx);
free(store);
return NULL;
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index be26e98ae..b75418648 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1277,6 +1277,20 @@ endforeach()
#
#-----------------------------------------------------------------------------
+#-----------------------------------------------------------------------------
+#
+# catch2_o (vendored Catch2 v3 implementation, compiled once)
+#
+#-----------------------------------------------------------------------------
+
+add_library(catch2_o OBJECT
+ ${PROJECT_SOURCE_DIR}/tests/catch/catch_amalgamated.cpp
+)
+# All test binaries provide their own main() (catch_main.cpp or gen_tile_test.cpp)
+target_compile_definitions(catch2_o PUBLIC CATCH_AMALGAMATED_CUSTOM_MAIN)
+# Third-party code: do not apply the project's warning flags to it
+target_compile_options(catch2_o PRIVATE -w)
+
#-----------------------------------------------------------------------------
#
# catch_main_o
@@ -1319,6 +1333,7 @@ add_compile_definitions(
PROJECT_BINARY_DIR="${PROJECT_BINARY_DIR}/src"
RENDERD_CONF="${PROJECT_SOURCE_DIR}/etc/renderd/renderd.conf.examples"
)
+include_directories(BEFORE ${PROJECT_BINARY_DIR}/includes)
include_directories(${PROJECT_SOURCE_DIR}/includes)
link_libraries(${GLIB_LIBRARIES})
@@ -1330,6 +1345,7 @@ link_libraries(${GLIB_LIBRARIES})
add_executable(render_expired_test
$
+ $
render_expired_test.cpp
)
@@ -1341,6 +1357,7 @@ add_executable(render_expired_test
add_executable(render_list_test
$
+ $
render_list_test.cpp
)
@@ -1352,6 +1369,7 @@ add_executable(render_list_test
add_executable(render_old_test
$
+ $
render_old_test.cpp
)
@@ -1363,6 +1381,7 @@ add_executable(render_old_test
add_executable(render_speedtest_test
$
+ $
render_speedtest_test.cpp
)
@@ -1374,5 +1393,45 @@ add_executable(render_speedtest_test
add_executable(renderd_test
$
+ $
renderd_test.cpp
)
+
+#-----------------------------------------------------------------------------
+#
+# Sanitizer runtime preloading
+#
+# The integration tests load the instrumented `mod_tile.so` into the system's
+# (uninstrumented) `httpd`, which ASan/TSan refuse unless their runtime is the
+# first library loaded. Preload it for all tests when building with sanitizers.
+#
+#-----------------------------------------------------------------------------
+
+if(ENABLE_SANITIZERS)
+ set(SANITIZER_PRELOAD "")
+ # (TSan cannot be preloaded into uninstrumented processes, so only ASan is handled here;
+ # with ENABLE_SANITIZERS=thread run only the unit test executables: ctest -R "_test$|^unit_tests$")
+ foreach(SANITIZER_RUNTIME address:asan)
+ string(REPLACE ":" ";" SANITIZER_RUNTIME "${SANITIZER_RUNTIME}")
+ list(GET SANITIZER_RUNTIME 0 SANITIZER_NAME)
+ list(GET SANITIZER_RUNTIME 1 SANITIZER_LIB)
+ if(ENABLE_SANITIZERS MATCHES "${SANITIZER_NAME}")
+ execute_process(COMMAND ${CMAKE_C_COMPILER} -print-file-name=lib${SANITIZER_LIB}.so
+ OUTPUT_VARIABLE SANITIZER_LIB_PATH
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+ if(IS_ABSOLUTE "${SANITIZER_LIB_PATH}" AND EXISTS "${SANITIZER_LIB_PATH}")
+ list(APPEND SANITIZER_PRELOAD "${SANITIZER_LIB_PATH}")
+ else()
+ message(WARNING "Could not locate lib${SANITIZER_LIB}.so; httpd-based tests will fail with ${SANITIZER_NAME}")
+ endif()
+ endif()
+ endforeach()
+
+ if(SANITIZER_PRELOAD)
+ string(REPLACE ";" ":" SANITIZER_PRELOAD "${SANITIZER_PRELOAD}")
+ message(STATUS "Preloading '${SANITIZER_PRELOAD}' for tests")
+ get_property(ALL_TESTS DIRECTORY PROPERTY TESTS)
+ set_property(TEST ${ALL_TESTS} APPEND PROPERTY ENVIRONMENT "LD_PRELOAD=${SANITIZER_PRELOAD}")
+ endif()
+endif()
diff --git a/tests/catch/LICENSE.txt b/tests/catch/LICENSE.txt
new file mode 100644
index 000000000..36b7cd93c
--- /dev/null
+++ b/tests/catch/LICENSE.txt
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/tests/catch/README b/tests/catch/README
new file mode 100644
index 000000000..12cda4ff2
--- /dev/null
+++ b/tests/catch/README
@@ -0,0 +1,6 @@
+Catch2 v3.16.0 amalgamated build
+Source: https://github.com/catchorg/Catch2/tree/v3.16.0/extras
+Commit: 317ac1ed4c0bb6e6b91eafc817e05c488feffcb3
+
+To update, replace catch_amalgamated.hpp/.cpp with the files from the
+extras/ directory of the desired Catch2 release tag.
diff --git a/tests/catch/catch.hpp b/tests/catch/catch.hpp
deleted file mode 100644
index 9b309bddc..000000000
--- a/tests/catch/catch.hpp
+++ /dev/null
@@ -1,17976 +0,0 @@
-/*
- * Catch v2.13.10
- * Generated: 2022-10-16 11:01:23.452308
- * ----------------------------------------------------------
- * This file has been merged from multiple headers. Please don't edit it directly
- * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved.
- *
- * Distributed under the Boost Software License, Version 1.0. (See accompanying
- * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- */
-#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
-#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
-// start catch.hpp
-
-
-#define CATCH_VERSION_MAJOR 2
-#define CATCH_VERSION_MINOR 13
-#define CATCH_VERSION_PATCH 10
-
-#ifdef __clang__
-# pragma clang system_header
-#elif defined __GNUC__
-# pragma GCC system_header
-#endif
-
-// start catch_suppress_warnings.h
-
-#ifdef __clang__
-# ifdef __ICC // icpc defines the __clang__ macro
-# pragma warning(push)
-# pragma warning(disable: 161 1682)
-# else // __ICC
-# pragma clang diagnostic push
-# pragma clang diagnostic ignored "-Wpadded"
-# pragma clang diagnostic ignored "-Wswitch-enum"
-# pragma clang diagnostic ignored "-Wcovered-switch-default"
-# endif
-#elif defined __GNUC__
- // Because REQUIREs trigger GCC's -Wparentheses, and because still
- // supported version of g++ have only buggy support for _Pragmas,
- // Wparentheses have to be suppressed globally.
-# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details
-
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wunused-variable"
-# pragma GCC diagnostic ignored "-Wpadded"
-#endif
-// end catch_suppress_warnings.h
-#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
-# define CATCH_IMPL
-# define CATCH_CONFIG_ALL_PARTS
-#endif
-
-// In the impl file, we want to have access to all parts of the headers
-// Can also be used to sanely support PCHs
-#if defined(CATCH_CONFIG_ALL_PARTS)
-# define CATCH_CONFIG_EXTERNAL_INTERFACES
-# if defined(CATCH_CONFIG_DISABLE_MATCHERS)
-# undef CATCH_CONFIG_DISABLE_MATCHERS
-# endif
-# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
-# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
-# endif
-#endif
-
-#if !defined(CATCH_CONFIG_IMPL_ONLY)
-// start catch_platform.h
-
-// See e.g.:
-// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html
-#ifdef __APPLE__
-# include
-# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \
- (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1)
-# define CATCH_PLATFORM_MAC
-# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
-# define CATCH_PLATFORM_IPHONE
-# endif
-
-#elif defined(linux) || defined(__linux) || defined(__linux__)
-# define CATCH_PLATFORM_LINUX
-
-#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
-# define CATCH_PLATFORM_WINDOWS
-#endif
-
-// end catch_platform.h
-
-#ifdef CATCH_IMPL
-# ifndef CLARA_CONFIG_MAIN
-# define CLARA_CONFIG_MAIN_NOT_DEFINED
-# define CLARA_CONFIG_MAIN
-# endif
-#endif
-
-// start catch_user_interfaces.h
-
-namespace Catch {
- unsigned int rngSeed();
-}
-
-// end catch_user_interfaces.h
-// start catch_tag_alias_autoregistrar.h
-
-// start catch_common.h
-
-// start catch_compiler_capabilities.h
-
-// Detect a number of compiler features - by compiler
-// The following features are defined:
-//
-// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
-// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
-// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
-// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled?
-// ****************
-// Note to maintainers: if new toggles are added please document them
-// in configuration.md, too
-// ****************
-
-// In general each macro has a _NO_ form
-// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature.
-// Many features, at point of detection, define an _INTERNAL_ macro, so they
-// can be combined, en-mass, with the _NO_ forms later.
-
-#ifdef __cplusplus
-
-# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L)
-# define CATCH_CPP14_OR_GREATER
-# endif
-
-# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
-# define CATCH_CPP17_OR_GREATER
-# endif
-
-#endif
-
-// Only GCC compiler should be used in this block, so other compilers trying to
-// mask themselves as GCC should be ignored.
-#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
-# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
-# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
-
-# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
-
-#endif
-
-#if defined(__clang__)
-
-# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" )
-# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" )
-
-// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug
-// which results in calls to destructors being emitted for each temporary,
-// without a matching initialization. In practice, this can result in something
-// like `std::string::~string` being called on an uninitialized value.
-//
-// For example, this code will likely segfault under IBM XL:
-// ```
-// REQUIRE(std::string("12") + "34" == "1234")
-// ```
-//
-// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented.
-# if !defined(__ibmxl__) && !defined(__CUDACC__)
-# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */
-# endif
-
-# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
- _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
-
-# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
- _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
-
-# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
- _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
-
-# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
- _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
-
-# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
- _Pragma( "clang diagnostic ignored \"-Wunused-template\"" )
-
-#endif // __clang__
-
-////////////////////////////////////////////////////////////////////////////////
-// Assume that non-Windows platforms support posix signals by default
-#if !defined(CATCH_PLATFORM_WINDOWS)
- #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// We know some environments not to support full POSIX signals
-#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__)
- #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
-#endif
-
-#ifdef __OS400__
-# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
-# define CATCH_CONFIG_COLOUR_NONE
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Android somehow still does not support std::to_string
-#if defined(__ANDROID__)
-# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
-# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Not all Windows environments support SEH properly
-#if defined(__MINGW32__)
-# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// PS4
-#if defined(__ORBIS__)
-# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Cygwin
-#ifdef __CYGWIN__
-
-// Required for some versions of Cygwin to declare gettimeofday
-// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
-# define _BSD_SOURCE
-// some versions of cygwin (most) do not support std::to_string. Use the libstd check.
-// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813
-# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \
- && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
-
-# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
-
-# endif
-#endif // __CYGWIN__
-
-////////////////////////////////////////////////////////////////////////////////
-// Visual C++
-#if defined(_MSC_VER)
-
-// Universal Windows platform does not support SEH
-// Or console colours (or console at all...)
-# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
-# define CATCH_CONFIG_COLOUR_NONE
-# else
-# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
-# endif
-
-# if !defined(__clang__) // Handle Clang masquerading for msvc
-
-// MSVC traditional preprocessor needs some workaround for __VA_ARGS__
-// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
-// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
-# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
-# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-# endif // MSVC_TRADITIONAL
-
-// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop`
-# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) )
-# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) )
-# endif // __clang__
-
-#endif // _MSC_VER
-
-#if defined(_REENTRANT) || defined(_MSC_VER)
-// Enable async processing, as -pthread is specified or no additional linking is required
-# define CATCH_INTERNAL_CONFIG_USE_ASYNC
-#endif // _MSC_VER
-
-////////////////////////////////////////////////////////////////////////////////
-// Check if we are compiled with -fno-exceptions or equivalent
-#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)
-# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// DJGPP
-#ifdef __DJGPP__
-# define CATCH_INTERNAL_CONFIG_NO_WCHAR
-#endif // __DJGPP__
-
-////////////////////////////////////////////////////////////////////////////////
-// Embarcadero C++Build
-#if defined(__BORLANDC__)
- #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-
-// Use of __COUNTER__ is suppressed during code analysis in
-// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly
-// handled by it.
-// Otherwise all supported compilers support COUNTER macro,
-// but user still might want to turn it off
-#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
- #define CATCH_INTERNAL_CONFIG_COUNTER
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-
-// RTX is a special version of Windows that is real time.
-// This means that it is detected as Windows, but does not provide
-// the same set of capabilities as real Windows does.
-#if defined(UNDER_RTSS) || defined(RTX64_BUILD)
- #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
- #define CATCH_INTERNAL_CONFIG_NO_ASYNC
- #define CATCH_CONFIG_COLOUR_NONE
-#endif
-
-#if !defined(_GLIBCXX_USE_C99_MATH_TR1)
-#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER
-#endif
-
-// Various stdlib support checks that require __has_include
-#if defined(__has_include)
- // Check if string_view is available and usable
- #if __has_include() && defined(CATCH_CPP17_OR_GREATER)
- # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
- #endif
-
- // Check if optional is available and usable
- # if __has_include() && defined(CATCH_CPP17_OR_GREATER)
- # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
- # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER)
-
- // Check if byte is available and usable
- # if __has_include() && defined(CATCH_CPP17_OR_GREATER)
- # include
- # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0)
- # define CATCH_INTERNAL_CONFIG_CPP17_BYTE
- # endif
- # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER)
-
- // Check if variant is available and usable
- # if __has_include() && defined(CATCH_CPP17_OR_GREATER)
- # if defined(__clang__) && (__clang_major__ < 8)
- // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
- // fix should be in clang 8, workaround in libstdc++ 8.2
- # include
- # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
- # define CATCH_CONFIG_NO_CPP17_VARIANT
- # else
- # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
- # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
- # else
- # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
- # endif // defined(__clang__) && (__clang_major__ < 8)
- # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER)
-#endif // defined(__has_include)
-
-#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
-# define CATCH_CONFIG_COUNTER
-#endif
-#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH)
-# define CATCH_CONFIG_WINDOWS_SEH
-#endif
-// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
-#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
-# define CATCH_CONFIG_POSIX_SIGNALS
-#endif
-// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions.
-#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR)
-# define CATCH_CONFIG_WCHAR
-#endif
-
-#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING)
-# define CATCH_CONFIG_CPP11_TO_STRING
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL)
-# define CATCH_CONFIG_CPP17_OPTIONAL
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW)
-# define CATCH_CONFIG_CPP17_STRING_VIEW
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT)
-# define CATCH_CONFIG_CPP17_VARIANT
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE)
-# define CATCH_CONFIG_CPP17_BYTE
-#endif
-
-#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
-# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE)
-# define CATCH_CONFIG_NEW_CAPTURE
-#endif
-
-#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
-# define CATCH_CONFIG_DISABLE_EXCEPTIONS
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN)
-# define CATCH_CONFIG_POLYFILL_ISNAN
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC)
-# define CATCH_CONFIG_USE_ASYNC
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE)
-# define CATCH_CONFIG_ANDROID_LOGWRITE
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
-# define CATCH_CONFIG_GLOBAL_NEXTAFTER
-#endif
-
-// Even if we do not think the compiler has that warning, we still have
-// to provide a macro that can be used by the code.
-#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION)
-# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
-#endif
-#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION)
-# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
-#endif
-#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
-# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
-#endif
-#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
-# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
-#endif
-#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS)
-# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
-#endif
-#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
-# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
-#endif
-
-// The goal of this macro is to avoid evaluation of the arguments, but
-// still have the compiler warn on problems inside...
-#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN)
-# define CATCH_INTERNAL_IGNORE_BUT_WARN(...)
-#endif
-
-#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10)
-# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
-#elif defined(__clang__) && (__clang_major__ < 5)
-# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
-#endif
-
-#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS)
-# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
-#endif
-
-#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
-#define CATCH_TRY if ((true))
-#define CATCH_CATCH_ALL if ((false))
-#define CATCH_CATCH_ANON(type) if ((false))
-#else
-#define CATCH_TRY try
-#define CATCH_CATCH_ALL catch (...)
-#define CATCH_CATCH_ANON(type) catch (type)
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR)
-#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#endif
-
-// end catch_compiler_capabilities.h
-#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
-#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
-#ifdef CATCH_CONFIG_COUNTER
-# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
-#else
-# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
-#endif
-
-#include
-#include
-#include
-
-// We need a dummy global operator<< so we can bring it into Catch namespace later
-struct Catch_global_namespace_dummy {};
-std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
-
-namespace Catch {
-
- struct CaseSensitive { enum Choice {
- Yes,
- No
- }; };
-
- class NonCopyable {
- NonCopyable( NonCopyable const& ) = delete;
- NonCopyable( NonCopyable && ) = delete;
- NonCopyable& operator = ( NonCopyable const& ) = delete;
- NonCopyable& operator = ( NonCopyable && ) = delete;
-
- protected:
- NonCopyable();
- virtual ~NonCopyable();
- };
-
- struct SourceLineInfo {
-
- SourceLineInfo() = delete;
- SourceLineInfo( char const* _file, std::size_t _line ) noexcept
- : file( _file ),
- line( _line )
- {}
-
- SourceLineInfo( SourceLineInfo const& other ) = default;
- SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
- SourceLineInfo( SourceLineInfo&& ) noexcept = default;
- SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default;
-
- bool empty() const noexcept { return file[0] == '\0'; }
- bool operator == ( SourceLineInfo const& other ) const noexcept;
- bool operator < ( SourceLineInfo const& other ) const noexcept;
-
- char const* file;
- std::size_t line;
- };
-
- std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
-
- // Bring in operator<< from global namespace into Catch namespace
- // This is necessary because the overload of operator<< above makes
- // lookup stop at namespace Catch
- using ::operator<<;
-
- // Use this in variadic streaming macros to allow
- // >> +StreamEndStop
- // as well as
- // >> stuff +StreamEndStop
- struct StreamEndStop {
- std::string operator+() const;
- };
- template
- T const& operator + ( T const& value, StreamEndStop ) {
- return value;
- }
-}
-
-#define CATCH_INTERNAL_LINEINFO \
- ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) )
-
-// end catch_common.h
-namespace Catch {
-
- struct RegistrarForTagAliases {
- RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
- };
-
-} // end namespace Catch
-
-#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
-
-// end catch_tag_alias_autoregistrar.h
-// start catch_test_registry.h
-
-// start catch_interfaces_testcase.h
-
-#include
-
-namespace Catch {
-
- class TestSpec;
-
- struct ITestInvoker {
- virtual void invoke () const = 0;
- virtual ~ITestInvoker();
- };
-
- class TestCase;
- struct IConfig;
-
- struct ITestCaseRegistry {
- virtual ~ITestCaseRegistry();
- virtual std::vector const& getAllTests() const = 0;
- virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0;
- };
-
- bool isThrowSafe( TestCase const& testCase, IConfig const& config );
- bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
- std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config );
- std::vector const& getAllTestCasesSorted( IConfig const& config );
-
-}
-
-// end catch_interfaces_testcase.h
-// start catch_stringref.h
-
-#include
-#include
-#include
-#include
-
-namespace Catch {
-
- /// A non-owning string class (similar to the forthcoming std::string_view)
- /// Note that, because a StringRef may be a substring of another string,
- /// it may not be null terminated.
- class StringRef {
- public:
- using size_type = std::size_t;
- using const_iterator = const char*;
-
- private:
- static constexpr char const* const s_empty = "";
-
- char const* m_start = s_empty;
- size_type m_size = 0;
-
- public: // construction
- constexpr StringRef() noexcept = default;
-
- StringRef( char const* rawChars ) noexcept;
-
- constexpr StringRef( char const* rawChars, size_type size ) noexcept
- : m_start( rawChars ),
- m_size( size )
- {}
-
- StringRef( std::string const& stdString ) noexcept
- : m_start( stdString.c_str() ),
- m_size( stdString.size() )
- {}
-
- explicit operator std::string() const {
- return std::string(m_start, m_size);
- }
-
- public: // operators
- auto operator == ( StringRef const& other ) const noexcept -> bool;
- auto operator != (StringRef const& other) const noexcept -> bool {
- return !(*this == other);
- }
-
- auto operator[] ( size_type index ) const noexcept -> char {
- assert(index < m_size);
- return m_start[index];
- }
-
- public: // named queries
- constexpr auto empty() const noexcept -> bool {
- return m_size == 0;
- }
- constexpr auto size() const noexcept -> size_type {
- return m_size;
- }
-
- // Returns the current start pointer. If the StringRef is not
- // null-terminated, throws std::domain_exception
- auto c_str() const -> char const*;
-
- public: // substrings and searches
- // Returns a substring of [start, start + length).
- // If start + length > size(), then the substring is [start, size()).
- // If start > size(), then the substring is empty.
- auto substr( size_type start, size_type length ) const noexcept -> StringRef;
-
- // Returns the current start pointer. May not be null-terminated.
- auto data() const noexcept -> char const*;
-
- constexpr auto isNullTerminated() const noexcept -> bool {
- return m_start[m_size] == '\0';
- }
-
- public: // iterators
- constexpr const_iterator begin() const { return m_start; }
- constexpr const_iterator end() const { return m_start + m_size; }
- };
-
- auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&;
- auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
-
- constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
- return StringRef( rawChars, size );
- }
-} // namespace Catch
-
-constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
- return Catch::StringRef( rawChars, size );
-}
-
-// end catch_stringref.h
-// start catch_preprocessor.hpp
-
-
-#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__
-#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__)))
-#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__)))
-#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__)))
-#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__)))
-#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__)))
-
-#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__
-// MSVC needs more evaluations
-#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__)))
-#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__))
-#else
-#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__)
-#endif
-
-#define CATCH_REC_END(...)
-#define CATCH_REC_OUT
-
-#define CATCH_EMPTY()
-#define CATCH_DEFER(id) id CATCH_EMPTY()
-
-#define CATCH_REC_GET_END2() 0, CATCH_REC_END
-#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2
-#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1
-#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT
-#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0)
-#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next)
-
-#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
-#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ )
-#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
-
-#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
-#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ )
-#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
-
-// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results,
-// and passes userdata as the first parameter to each invocation,
-// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c)
-#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
-
-#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
-
-#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param)
-#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__
-#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__
-#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
-#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__)
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__
-#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param))
-#else
-// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
-#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__)
-#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__
-#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1)
-#endif
-
-#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__
-#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name)
-
-#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__)
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper())
-#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
-#else
-#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper()))
-#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
-#endif
-
-#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\
- CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__)
-
-#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0)
-#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1)
-#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2)
-#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3)
-#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4)
-#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5)
-#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6)
-#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7)
-#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8)
-#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9)
-#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10)
-
-#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
-
-#define INTERNAL_CATCH_TYPE_GEN\
- template struct TypeList {};\
- template\
- constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\
- template class...> struct TemplateTypeList{};\
- template class...Cs>\
- constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\
- template\
- struct append;\
- template\
- struct rewrap;\
- template class, typename...>\
- struct create;\
- template class, typename>\
- struct convert;\
- \
- template \
- struct append { using type = T; };\
- template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\
- struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\
- template< template class L1, typename...E1, typename...Rest>\
- struct append, TypeList, Rest...> { using type = L1; };\
- \
- template< template class Container, template class List, typename...elems>\
- struct rewrap, List> { using type = TypeList>; };\
- template< template class Container, template class List, class...Elems, typename...Elements>\
- struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\
- \
- template class Final, template< typename...> class...Containers, typename...Types>\
- struct create, TypeList> { using type = typename append, typename rewrap, Types...>::type...>::type; };\
- template class Final, template class List, typename...Ts>\
- struct convert> { using type = typename append,TypeList...>::type; };
-
-#define INTERNAL_CATCH_NTTP_1(signature, ...)\
- template struct Nttp{};\
- template\
- constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
- template class...> struct NttpTemplateTypeList{};\
- template class...Cs>\
- constexpr auto get_wrapper() noexcept -> NttpTemplateTypeList { return {}; } \
- \
- template< template class Container, template class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
- struct rewrap, List<__VA_ARGS__>> { using type = TypeList>; };\
- template< template class Container, template class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\
- struct rewrap, List<__VA_ARGS__>, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\
- template class Final, template class...Containers, typename...Types>\
- struct create, TypeList> { using type = typename append, typename rewrap, Types...>::type...>::type; };
-
-#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName)
-#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
- template\
- static void TestName()
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...)\
- template\
- static void TestName()
-
-#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName)
-#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature)\
- template\
- static void TestName()
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature,...)\
- template\
- static void TestName()
-
-#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\
- template\
- void reg_test(TypeList, Catch::NameAndTags nameAndTags)\
- {\
- Catch::AutoReg( Catch::makeTestInvoker(&TestFunc), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
- }
-
-#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\
- template\
- void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags)\
- {\
- Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
- }
-
-#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...)\
- template\
- void reg_test(TypeList, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
- {\
- Catch::AutoReg( Catch::makeTestInvoker(&TestName::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
- }
-
-#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...)\
- template\
- void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
- {\
- Catch::AutoReg( Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
- }
-
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName)
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, signature)\
- template \
- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
- void test();\
- }
-
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...)\
- template \
- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \
- void test();\
- }
-
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName)
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature)\
- template \
- void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName::test()
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...)\
- template \
- void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<__VA_ARGS__>::test()
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_NTTP_0
-#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0)
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)
-#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)
-#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)
-#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)
-#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)
-#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)
-#else
-#define INTERNAL_CATCH_NTTP_0(signature)
-#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1,INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_0)( __VA_ARGS__))
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__))
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__))
-#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__))
-#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__))
-#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__))
-#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__))
-#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__))
-#endif
-
-// end catch_preprocessor.hpp
-// start catch_meta.hpp
-
-
-#include
-
-namespace Catch {
- template
- struct always_false : std::false_type {};
-
- template struct true_given : std::true_type {};
- struct is_callable_tester {
- template
- true_given()(std::declval()...))> static test(int);
- template
- std::false_type static test(...);
- };
-
- template
- struct is_callable;
-
- template
- struct is_callable : decltype(is_callable_tester::test(0)) {};
-
-#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703
- // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is
- // replaced with std::invoke_result here.
- template
- using FunctionReturnType = std::remove_reference_t>>;
-#else
- // Keep ::type here because we still support C++11
- template
- using FunctionReturnType = typename std::remove_reference::type>::type>::type;
-#endif
-
-} // namespace Catch
-
-namespace mpl_{
- struct na;
-}
-
-// end catch_meta.hpp
-namespace Catch {
-
-template
-class TestInvokerAsMethod : public ITestInvoker {
- void (C::*m_testAsMethod)();
-public:
- TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {}
-
- void invoke() const override {
- C obj;
- (obj.*m_testAsMethod)();
- }
-};
-
-auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*;
-
-template
-auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* {
- return new(std::nothrow) TestInvokerAsMethod( testAsMethod );
-}
-
-struct NameAndTags {
- NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept;
- StringRef name;
- StringRef tags;
-};
-
-struct AutoReg : NonCopyable {
- AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept;
- ~AutoReg();
-};
-
-} // end namespace Catch
-
-#if defined(CATCH_CONFIG_DISABLE)
- #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \
- static void TestName()
- #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
- namespace{ \
- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
- void test(); \
- }; \
- } \
- void TestName::test()
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( TestName, TestFunc, Name, Tags, Signature, ... ) \
- INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature))
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
- namespace{ \
- namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
- INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
- } \
- } \
- INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
-
- #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ )
- #else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
- #endif
-
- #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ )
- #else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
- #endif
-
- #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
- #else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
- #endif
-
- #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
- #else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
- #endif
-#endif
-
- ///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
- static void TestName(); \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
- static void TestName()
- #define INTERNAL_CATCH_TESTCASE( ... ) \
- INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), __VA_ARGS__ )
-
- ///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
-
- ///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- namespace{ \
- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
- void test(); \
- }; \
- Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
- } \
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
- void TestName::test()
- #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
- INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), ClassName, __VA_ARGS__ )
-
- ///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
-
- ///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
- INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
- namespace {\
- namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
- INTERNAL_CATCH_TYPE_GEN\
- INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
- INTERNAL_CATCH_NTTP_REG_GEN(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))\
- template \
- struct TestName{\
- TestName(){\
- int index = 0; \
- constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
- using expander = int[];\
- (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
- }\
- };\
- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
- TestName();\
- return 0;\
- }();\
- }\
- }\
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
- INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ )
-#else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
-#endif
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ )
-#else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
-#endif
-
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
- template static void TestFuncName(); \
- namespace {\
- namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
- INTERNAL_CATCH_TYPE_GEN \
- INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \
- template \
- struct TestName { \
- void reg_tests() { \
- int index = 0; \
- using expander = int[]; \
- constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
- constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
- constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */\
- } \
- }; \
- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
- using TestInit = typename create()), TypeList>::type; \
- TestInit t; \
- t.reg_tests(); \
- return 0; \
- }(); \
- } \
- } \
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
- template \
- static void TestFuncName()
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
- INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename T,__VA_ARGS__)
-#else
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename T, __VA_ARGS__ ) )
-#endif
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
- INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__)
-#else
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
-#endif
-
- #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
- template static void TestFunc(); \
- namespace {\
- namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
- INTERNAL_CATCH_TYPE_GEN\
- template \
- struct TestName { \
- void reg_tests() { \
- int index = 0; \
- using expander = int[]; \
- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\
- } \
- };\
- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
- using TestInit = typename convert::type; \
- TestInit t; \
- t.reg_tests(); \
- return 0; \
- }(); \
- }}\
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
- template \
- static void TestFunc()
-
- #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \
- INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, TmplList )
-
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
- namespace {\
- namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
- INTERNAL_CATCH_TYPE_GEN\
- INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
- INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
- INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))\
- template \
- struct TestNameClass{\
- TestNameClass(){\
- int index = 0; \
- constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
- using expander = int[];\
- (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
- }\
- };\
- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
- TestNameClass();\
- return 0;\
- }();\
- }\
- }\
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
- INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
-#else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
-#endif
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
-#else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
-#endif
-
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
- template \
- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \
- void test();\
- };\
- namespace {\
- namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\
- INTERNAL_CATCH_TYPE_GEN \
- INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
- template\
- struct TestNameClass{\
- void reg_tests(){\
- int index = 0;\
- using expander = int[];\
- constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
- constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
- constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */ \
- }\
- };\
- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
- using TestInit = typename create()), TypeList>::type;\
- TestInit t;\
- t.reg_tests();\
- return 0;\
- }(); \
- }\
- }\
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
- template \
- void TestName::test()
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
- INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T, __VA_ARGS__ )
-#else
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) )
-#endif
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
- INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, Signature, __VA_ARGS__ )
-#else
- #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) )
-#endif
-
- #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \
- CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
- CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
- template \
- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \
- void test();\
- };\
- namespace {\
- namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
- INTERNAL_CATCH_TYPE_GEN\
- template\
- struct TestNameClass{\
- void reg_tests(){\
- int index = 0;\
- using expander = int[];\
- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
- }\
- };\
- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
- using TestInit = typename convert::type;\
- TestInit t;\
- t.reg_tests();\
- return 0;\
- }(); \
- }}\
- CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
- template \
- void TestName::test()
-
-#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \
- INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, TmplList )
-
-// end catch_test_registry.h
-// start catch_capture.hpp
-
-// start catch_assertionhandler.h
-
-// start catch_assertioninfo.h
-
-// start catch_result_type.h
-
-namespace Catch {
-
- // ResultWas::OfType enum
- struct ResultWas { enum OfType {
- Unknown = -1,
- Ok = 0,
- Info = 1,
- Warning = 2,
-
- FailureBit = 0x10,
-
- ExpressionFailed = FailureBit | 1,
- ExplicitFailure = FailureBit | 2,
-
- Exception = 0x100 | FailureBit,
-
- ThrewException = Exception | 1,
- DidntThrowException = Exception | 2,
-
- FatalErrorCondition = 0x200 | FailureBit
-
- }; };
-
- bool isOk( ResultWas::OfType resultType );
- bool isJustInfo( int flags );
-
- // ResultDisposition::Flags enum
- struct ResultDisposition { enum Flags {
- Normal = 0x01,
-
- ContinueOnFailure = 0x02, // Failures fail test, but execution continues
- FalseTest = 0x04, // Prefix expression with !
- SuppressFail = 0x08 // Failures are reported but do not fail the test
- }; };
-
- ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs );
-
- bool shouldContinueOnFailure( int flags );
- inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
- bool shouldSuppressFailure( int flags );
-
-} // end namespace Catch
-
-// end catch_result_type.h
-namespace Catch {
-
- struct AssertionInfo
- {
- StringRef macroName;
- SourceLineInfo lineInfo;
- StringRef capturedExpression;
- ResultDisposition::Flags resultDisposition;
-
- // We want to delete this constructor but a compiler bug in 4.8 means
- // the struct is then treated as non-aggregate
- //AssertionInfo() = delete;
- };
-
-} // end namespace Catch
-
-// end catch_assertioninfo.h
-// start catch_decomposer.h
-
-// start catch_tostring.h
-
-#include
-#include
-#include
-#include
-// start catch_stream.h
-
-#include
-#include
-#include
-
-namespace Catch {
-
- std::ostream& cout();
- std::ostream& cerr();
- std::ostream& clog();
-
- class StringRef;
-
- struct IStream {
- virtual ~IStream();
- virtual std::ostream& stream() const = 0;
- };
-
- auto makeStream( StringRef const &filename ) -> IStream const*;
-
- class ReusableStringStream : NonCopyable {
- std::size_t m_index;
- std::ostream* m_oss;
- public:
- ReusableStringStream();
- ~ReusableStringStream();
-
- auto str() const -> std::string;
-
- template
- auto operator << ( T const& value ) -> ReusableStringStream& {
- *m_oss << value;
- return *this;
- }
- auto get() -> std::ostream& { return *m_oss; }
- };
-}
-
-// end catch_stream.h
-// start catch_interfaces_enum_values_registry.h
-
-#include
-
-namespace Catch {
-
- namespace Detail {
- struct EnumInfo {
- StringRef m_name;
- std::vector> m_values;
-
- ~EnumInfo();
-
- StringRef lookup( int value ) const;
- };
- } // namespace Detail
-
- struct IMutableEnumValuesRegistry {
- virtual ~IMutableEnumValuesRegistry();
-
- virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector const& values ) = 0;
-
- template
- Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list values ) {
- static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int");
- std::vector intValues;
- intValues.reserve( values.size() );
- for( auto enumValue : values )
- intValues.push_back( static_cast( enumValue ) );
- return registerEnum( enumName, allEnums, intValues );
- }
- };
-
-} // Catch
-
-// end catch_interfaces_enum_values_registry.h
-
-#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
-#include
-#endif
-
-#ifdef __OBJC__
-// start catch_objc_arc.hpp
-
-#import
-
-#ifdef __has_feature
-#define CATCH_ARC_ENABLED __has_feature(objc_arc)
-#else
-#define CATCH_ARC_ENABLED 0
-#endif
-
-void arcSafeRelease( NSObject* obj );
-id performOptionalSelector( id obj, SEL sel );
-
-#if !CATCH_ARC_ENABLED
-inline void arcSafeRelease( NSObject* obj ) {
- [obj release];
-}
-inline id performOptionalSelector( id obj, SEL sel ) {
- if( [obj respondsToSelector: sel] )
- return [obj performSelector: sel];
- return nil;
-}
-#define CATCH_UNSAFE_UNRETAINED
-#define CATCH_ARC_STRONG
-#else
-inline void arcSafeRelease( NSObject* ){}
-inline id performOptionalSelector( id obj, SEL sel ) {
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
-#endif
- if( [obj respondsToSelector: sel] )
- return [obj performSelector: sel];
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
- return nil;
-}
-#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
-#define CATCH_ARC_STRONG __strong
-#endif
-
-// end catch_objc_arc.hpp
-#endif
-
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless
-#endif
-
-namespace Catch {
- namespace Detail {
-
- extern const std::string unprintableString;
-
- std::string rawMemoryToString( const void *object, std::size_t size );
-
- template
- std::string rawMemoryToString( const T& object ) {
- return rawMemoryToString( &object, sizeof(object) );
- }
-
- template
- class IsStreamInsertable {
- template
- static auto test(int)
- -> decltype(std::declval() << std::declval(), std::true_type());
-
- template
- static auto test(...)->std::false_type;
-
- public:
- static const bool value = decltype(test(0))::value;
- };
-
- template
- std::string convertUnknownEnumToString( E e );
-
- template
- typename std::enable_if<
- !std::is_enum::value && !std::is_base_of::value,
- std::string>::type convertUnstreamable( T const& ) {
- return Detail::unprintableString;
- }
- template
- typename std::enable_if<
- !std::is_enum::value && std::is_base_of::value,
- std::string>::type convertUnstreamable(T const& ex) {
- return ex.what();
- }
-
- template
- typename std::enable_if<
- std::is_enum::value
- , std::string>::type convertUnstreamable( T const& value ) {
- return convertUnknownEnumToString( value );
- }
-
-#if defined(_MANAGED)
- //! Convert a CLR string to a utf8 std::string
- template
- std::string clrReferenceToString( T^ ref ) {
- if (ref == nullptr)
- return std::string("null");
- auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString());
- cli::pin_ptr p = &bytes[0];
- return std::string(reinterpret_cast(p), bytes->Length);
- }
-#endif
-
- } // namespace Detail
-
- // If we decide for C++14, change these to enable_if_ts
- template
- struct StringMaker {
- template
- static
- typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type
- convert(const Fake& value) {
- ReusableStringStream rss;
- // NB: call using the function-like syntax to avoid ambiguity with
- // user-defined templated operator<< under clang.
- rss.operator<<(value);
- return rss.str();
- }
-
- template
- static
- typename std::enable_if::value, std::string>::type
- convert( const Fake& value ) {
-#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER)
- return Detail::convertUnstreamable(value);
-#else
- return CATCH_CONFIG_FALLBACK_STRINGIFIER(value);
-#endif
- }
- };
-
- namespace Detail {
-
- // This function dispatches all stringification requests inside of Catch.
- // Should be preferably called fully qualified, like ::Catch::Detail::stringify
- template
- std::string stringify(const T& e) {
- return ::Catch::StringMaker::type>::type>::convert(e);
- }
-
- template
- std::string convertUnknownEnumToString( E e ) {
- return ::Catch::Detail::stringify(static_cast::type>(e));
- }
-
-#if defined(_MANAGED)
- template
- std::string stringify( T^ e ) {
- return ::Catch::StringMaker::convert(e);
- }
-#endif
-
- } // namespace Detail
-
- // Some predefined specializations
-
- template<>
- struct StringMaker {
- static std::string convert(const std::string& str);
- };
-
-#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
- template<>
- struct StringMaker {
- static std::string convert(std::string_view str);
- };
-#endif
-
- template<>
- struct StringMaker {
- static std::string convert(char const * str);
- };
- template<>
- struct StringMaker {
- static std::string convert(char * str);
- };
-
-#ifdef CATCH_CONFIG_WCHAR
- template<>
- struct StringMaker {
- static std::string convert(const std::wstring& wstr);
- };
-
-# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
- template<>
- struct StringMaker {
- static std::string convert(std::wstring_view str);
- };
-# endif
-
- template<>
- struct StringMaker {
- static std::string convert(wchar_t const * str);
- };
- template<>
- struct StringMaker {
- static std::string convert(wchar_t * str);
- };
-#endif
-
- // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer,
- // while keeping string semantics?
- template
- struct StringMaker {
- static std::string convert(char const* str) {
- return ::Catch::Detail::stringify(std::string{ str });
- }
- };
- template
- struct StringMaker {
- static std::string convert(signed char const* str) {
- return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) });
- }
- };
- template
- struct StringMaker {
- static std::string convert(unsigned char const* str) {
- return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) });
- }
- };
-
-#if defined(CATCH_CONFIG_CPP17_BYTE)
- template<>
- struct StringMaker {
- static std::string convert(std::byte value);
- };
-#endif // defined(CATCH_CONFIG_CPP17_BYTE)
- template<>
- struct StringMaker {
- static std::string convert(int value);
- };
- template<>
- struct StringMaker {
- static std::string convert(long value);
- };
- template<>
- struct StringMaker {
- static std::string convert(long long value);
- };
- template<>
- struct StringMaker {
- static std::string convert(unsigned int value);
- };
- template<>
- struct StringMaker {
- static std::string convert(unsigned long value);
- };
- template<>
- struct StringMaker {
- static std::string convert(unsigned long long value);
- };
-
- template<>
- struct StringMaker {
- static std::string convert(bool b);
- };
-
- template<>
- struct StringMaker {
- static std::string convert(char c);
- };
- template<>
- struct StringMaker {
- static std::string convert(signed char c);
- };
- template<>
- struct StringMaker {
- static std::string convert(unsigned char c);
- };
-
- template<>
- struct StringMaker {
- static std::string convert(std::nullptr_t);
- };
-
- template<>
- struct StringMaker