diff --git a/src/3rdParty/CMakeLists.txt b/src/3rdParty/CMakeLists.txt index a79d60acb2..a42dfebb9a 100644 --- a/src/3rdParty/CMakeLists.txt +++ b/src/3rdParty/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory(FastSignals) + # Build SalomeMesh for all Platforms since heavily patched if (BUILD_SMESH) add_subdirectory(salomesmesh) diff --git a/src/3rdParty/FastSignals/.clang-format b/src/3rdParty/FastSignals/.clang-format new file mode 100644 index 0000000000..23ee80d649 --- /dev/null +++ b/src/3rdParty/FastSignals/.clang-format @@ -0,0 +1,102 @@ +--- +Language: Cpp +# BasedOnStyle: WebKit +AccessModifierOffset: -4 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: false +AlignOperands: false +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false +BreakBeforeBinaryOperators: All +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: true +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 0 +CommentPragmas: '^ IWYU pragma:' +BreakBeforeInheritanceComma: true +FixNamespaceComments: true +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +# FixNamespaceComments: false +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] +IncludeCategories: + - Regex: '^"(stdafx|PrecompiledHeader)' + Priority: -2 + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 4 + - Regex: '^(<|"(gtest|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 2 +IncludeIsMainRegex: '$' +IndentCaseLabels: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: 'BEGIN_MESSAGE_MAP_CUSTOM$|BEGIN_MSG_MAP$|BEGIN_SINK_MAP$|BEGIN_MESSAGE_MAP$|BOOST_FIXTURE_TEST_SUITE$|BOOST_AUTO_TEST_SUITE$|BEGIN_DLGRESIZE_MAP$|BEGIN_MSG_MAP_EX$|BEGIN_DDX_MAP$|BEGIN_COM_MAP$|BEGIN_CONNECTION_POINT_MAP$|WTL_BEGIN_LAYOUT_MAP$|WTL_BEGIN_LAYOUT_CONTAINER$' +MacroBlockEnd: 'END_MESSAGE_MAP_CUSTOM$|END_MSG_MAP$|END_SINK_MAP$|END_MESSAGE_MAP$|BOOST_AUTO_TEST_SUITE_END$|END_DLGRESIZE_MAP$|END_DDX_MAP$|END_COM_MAP$|END_CONNECTION_POINT_MAP$|WTL_END_LAYOUT_MAP$|WTL_END_LAYOUT_CONTAINER$' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 10000000 +PointerAlignment: Left +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +TabWidth: 4 +UseTab: Always +IndentPPDirectives: AfterHash +... diff --git a/src/3rdParty/FastSignals/.gitignore b/src/3rdParty/FastSignals/.gitignore new file mode 100644 index 0000000000..9952d2c02a --- /dev/null +++ b/src/3rdParty/FastSignals/.gitignore @@ -0,0 +1,367 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# ------------ +# Custom Rules + +build/ diff --git a/src/3rdParty/FastSignals/CMakeLists.txt b/src/3rdParty/FastSignals/CMakeLists.txt new file mode 100644 index 0000000000..3056332196 --- /dev/null +++ b/src/3rdParty/FastSignals/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.8 FATAL_ERROR) + +project(FastSignals) + +include(cmake/functions.cmake) + +add_subdirectory(libfastsignals) + +if(BUILD_FASTSIGNALS_TESTING) + add_subdirectory(tests/libfastsignals_stress_tests) + add_subdirectory(tests/libfastsignals_unit_tests) +endif() diff --git a/src/3rdParty/FastSignals/LICENSE b/src/3rdParty/FastSignals/LICENSE new file mode 100644 index 0000000000..28e1a7de0d --- /dev/null +++ b/src/3rdParty/FastSignals/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 iSpring Solutions Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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/src/3rdParty/FastSignals/README.md b/src/3rdParty/FastSignals/README.md new file mode 100644 index 0000000000..6b4eb3a258 --- /dev/null +++ b/src/3rdParty/FastSignals/README.md @@ -0,0 +1,17 @@ +# FastSignals + +Yet another C++ signals and slots library + +* Works as drop-in replacement for Boost.Signals2 with the same API + * Has better performance and more compact binary code +* Thread-safe in most operations, including concurrent connects/disconnects/emits +* Implemented with compact, pure C++17 code + +[![Build Status](https://travis-ci.org/ispringteam/FastSignals.svg?branch=master)](https://travis-ci.org/ispringteam/FastSignals) +[![License: MIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT) + +## Documentation + +* [Why FastSignals?](docs/why-fastsignals.md) +* [Simple Examples](docs/simple-examples.md) +* [Migration from Boost.Signals2](docs/migration-from-boost-signals2.md) diff --git a/src/3rdParty/FastSignals/cmake/functions.cmake b/src/3rdParty/FastSignals/cmake/functions.cmake new file mode 100644 index 0000000000..c3ef983788 --- /dev/null +++ b/src/3rdParty/FastSignals/cmake/functions.cmake @@ -0,0 +1,24 @@ +# Function to add a library target. +function(custom_add_library_from_dir TARGET) + # Gather files from the current directory + file(GLOB TARGET_SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/*.h") + add_library(${TARGET} ${TARGET_SRC}) +endfunction() + +# Function to add an executable target. +function(custom_add_executable_from_dir TARGET) + # Gather files from the current directory + file(GLOB TARGET_SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/*.h") + add_executable(${TARGET} ${TARGET_SRC}) +endfunction() + +# Function to add an executable target containing tests for a library. +function(custom_add_test_from_dir TARGET LIBRARY) + custom_add_executable_from_dir(${TARGET}) + # Add path to Catch framework header + target_include_directories(${TARGET} PRIVATE "${CMAKE_SOURCE_DIR}/libs/catch") + # Link with the library being tested + target_link_libraries(${TARGET} ${LIBRARY}) + # Register the executable with CMake as a test set + add_test(${TARGET} ${TARGET}) +endfunction() diff --git a/src/3rdParty/FastSignals/docs/bind_weak.md b/src/3rdParty/FastSignals/docs/bind_weak.md new file mode 100644 index 0000000000..338b3574a4 --- /dev/null +++ b/src/3rdParty/FastSignals/docs/bind_weak.md @@ -0,0 +1,94 @@ +# Function bind_weak + +## Usage + +* Use `fastsignals::bind_weak` instead of `std::bind` to ensure that nothing happens if method called when binded object already destroyed +* Pass pointer to T class method as first argument, `shared_ptr` or `weak_ptr` as second argument +* Example: `bind_weak(&Document::save(), document, std::placeholders::_1)`, where `document` is a `weak_ptr` or `shared_ptr` + +## Weak this idiom + +The `fastsignals::bind_weak(...)` function implements "weak this" idiom. This idiom helps to avoid dangling pointers and memory access wiolations in asynchronous and/or multithreaded programs. + +In the following example, we use weak this idiom to avoid using dangling pointer wehn calling `print()` method of the `Enityt`: + +```cpp +struct Entity : std::enable_shared_from_this +{ + int value = 42; + + void print() + { + std::cout << "print called, num = " << value << std::endl; + } + + std::function print_later() + { + // ! weak this idiom here ! + auto weak_this = weak_from_this(); + return [weak_this] { + if (auto shared_this = weak_this.lock()) + { + shared_this->print(); + } + }; + } +}; + +int main() +{ + auto entity = std::make_shared(); + auto print = entity->print_later(); + + // Prints OK. + print(); + + // Prints nothing - last shared_ptr to the Entity destroyed, so `weak_this.lock()` will return nullptr. + entity = nullptr; + print(); +} +``` + +## Using bind_weak to avoid signal receiver lifetime issues + +In the following example, `Entity::print()` method connected to the signal. Signal emmited once before and once after the `Entity` instance destroyed. However, no memory access violation happens: once `Entity` destoryed, no slot will be called because `bind_weak` doesn't call binded method if it cannot lock `std::weak_ptr` to binded object. The second `event()` expression just does nothing. + +```cpp +#include +#include +#include + +using VoidSignal = fastsignals::signal; +using VoidSlot = VoidSignal::slot_type; + +struct Entity : std::enable_shared_from_this +{ + int value = 42; + + VoidSlot get_print_slot() + { + // Here fastsignals::bind_weak() used instead of std::bind. + return fastsignals::bind_weak(&Entity::print, weak_from_this()); + } + + void print() + { + std::cout << "print called, num = " << value << std::endl; + } +}; + +int main() +{ + VoidSignal event; + auto entity = std::make_shared(); + event.connect(entity->get_print_slot()); + + // Here slot called - it prints `slot called, num = 42` + event(); + entity = nullptr; + + // Here nothing happens - no exception, no slot call. + event(); +} + +``` diff --git a/src/3rdParty/FastSignals/docs/migration-from-boost-signals2.md b/src/3rdParty/FastSignals/docs/migration-from-boost-signals2.md new file mode 100644 index 0000000000..72632caf74 --- /dev/null +++ b/src/3rdParty/FastSignals/docs/migration-from-boost-signals2.md @@ -0,0 +1,163 @@ +# Migration from Boost.Signals2 + +This guide helps to migrate large codebase from Boost.Signals2 to `FastSignals` signals/slots library. It helps to solve known migration issues in the right way. + +During migrations, you will probably face with following things: + +* You code uses `boost::signals2::` namespace and `` header directly +* You code uses third-party headers included implicitly by the `` header + +## Reasons migrate from Boost.Signals2 to FastSignals + +FastSignals API mostly compatible with Boost.Signals2 - there are differences, and all differences has their reasons explained below. + +Comparing to Boost.Signals2, FastSignals has following pros: + +* FastSignals is not header-only - so binary code will be more compact +* FastSignals implemented using C++17 with variadic templates, `constexpr if` and other modern metaprogramming techniques - so it compiles faster and, again, binary code will be more compact +* FastSignals probably will faster than Boost.Signals2 for your codebase because with FastSignals you don't pay for things that you don't use, with one exception: you always pay for the multithreading support + +## Step 1: Create header with aliases + +## Step 2: Rebuild and fix compile errors + +### 2.1 Add missing includes + +Boost.Signals2 is header-only library. It includes a lot of STL/Boost stuff while FastSignals does not: + +```cpp +#include +// Also includes std::map, boost::variant, boost::optional, etc. + +// Compiled OK even without `#include `! +std::map CreateMyMap(); +``` + +With FastSignals, you must include headers like `` manually. The following table shows which files should be included explicitly if you see compile erros after migration. + +| Class | Header | +|--------------------|:--------------------------------------:| +| std::map | `#include ` | +| boost::variant | `#include ` | +| boost::optional | `#include ` | +| boost::scoped_ptr | `#include ` | +| boost::noncopyable | `#include ` | +| boost::bind | `#include ` | +| boost::function | `#include ` | + +If you just want to compile you code, you can add following includes in you `signals.h` header: + +```cpp +// WARNING: [libfastsignals] we do not recommend to include following extra headers. +#include +#include +#include +#include +#include +#include +#include +``` + +### 2.2 Remove redundant returns for void signals + +With Boost.Signals2, following code compiled without any warning: + +```cpp +boost::signals2::signal event; +event.connect([] { + return true; +}); +``` + +With FastSignals, slot cannot return non-void value when for `signal`. You must fix your code: just remove returns from your slots or add lambdas to wrap slot and ignore it result. + +### 2.3 Replace track() and track_foreign() with bind_weak_ptr() + +Boost.Signals2 [can track connected objects lifetype](https://www.boost.org/doc/libs/1_55_0/doc/html/signals2/tutorial.html#idp204830936) using `track(...)` and `track_foreign(...)` methods. In the following example `Entity` created with `make_shared`, and `Entity::get_print_slot()` creates slot function which tracks weak pointer to Entity: + +```cpp +#include +#include +#include + +using VoidSignal = boost::signals2::signal; +using VoidSlot = VoidSignal::slot_type; + +struct Entity : std::enable_shared_from_this +{ + int value = 42; + + VoidSlot get_print_slot() + { + // Here track() tracks object itself. + return VoidSlot(std::bind(&Entity::print, this)).track_foreign(shared_from_this()); + } + + void print() + { + std::cout << "print called, num = " << value << std::endl; + } +}; + +int main() +{ + VoidSignal event; + auto entity = std::make_shared(); + event.connect(entity->get_print_slot()); + + // Here slot called - it prints `print called, num = 42` + event(); + entity = nullptr; + + // This call does nothing. + event(); +} +``` + +FastSignals uses another approach: `bind_weak` function: + +```cpp +#include +#include + +using VoidSignal = fastsignals::signal; +using VoidSlot = VoidSignal::slot_type; + +struct Entity : std::enable_shared_from_this +{ + int value = 42; + + VoidSlot get_print_slot() + { + // Here fastsignals::bind_weak() used instead of std::bind. + return fastsignals::bind_weak(&Entity::print, weak_from_this()); + } + + void print() + { + std::cout << "print called, num = " << value << std::endl; + } +}; + +int main() +{ + VoidSignal event; + auto entity = std::make_shared(); + event.connect(entity->get_print_slot()); + + // Here slot called - it prints `slot called, num = 42` + event(); + entity = nullptr; + + // Here nothing happens - no exception, no slot call. + event(); +} +``` + +### FastSignals Differences in Result Combiners + +## Step 3: Run Tests + +Run all automated tests that you have (unit tests, integration tests, system tests, stress tests, benchmarks, UI tests). + +Probably you will see no errors. If you see any, please report an issue. diff --git a/src/3rdParty/FastSignals/docs/simple-examples.md b/src/3rdParty/FastSignals/docs/simple-examples.md new file mode 100644 index 0000000000..3f067b347f --- /dev/null +++ b/src/3rdParty/FastSignals/docs/simple-examples.md @@ -0,0 +1,55 @@ +# Simple Examples + +>If you are not familar with Boost.Signals2, please read [Boost.Signals2: Connections](https://theboostcpplibraries.com/boost.signals2-connections) + +## Example with signal<> and connection + +```cpp +// Creates signal and connects 1 slot, calls 2 times, disconnects, calls again. +// Outputs: +// 13 +// 17 +#include "libfastsignals/signal.h" + +using namespace fastsignals; + +int main() +{ + signal valueChanged; + connection conn; + conn = valueChanged.connect([](int value) { + cout << value << endl; + }); + valueChanged(13); + valueChanged(17); + conn.disconnect(); + valueChanged(42); +} +``` + +## Example with scoped_connection + +```cpp +// Creates signal and connects 1 slot, calls 2 times, calls again after scoped_connection destroyed. +// - note: scoped_connection closes connection in destructor +// Outputs: +// 13 +// 17 +#include "libfastsignals/signal.h" + +using namespace fastsignals; + +int main() +{ + signal valueChanged; + { + scoped_connection conn; + conn = valueChanged.connect([](int value) { + cout << value << endl; + }); + valueChanged(13); + valueChanged(17); + } + valueChanged(42); +} +``` diff --git a/src/3rdParty/FastSignals/docs/why-fastsignals.md b/src/3rdParty/FastSignals/docs/why-fastsignals.md new file mode 100644 index 0000000000..36196221f4 --- /dev/null +++ b/src/3rdParty/FastSignals/docs/why-fastsignals.md @@ -0,0 +1,40 @@ +# Why FastSignals? + +FastSignals is a C++17 signals/slots implementation which API is compatible with Boost.Signals2. + +FastSignals pros: + +* Faster than Boost.Signals2 +* Has more compact binary code +* Has the same API as Boost.Signals2 + +FastSignals cons: + +* Supports only C++17 compatible compilers: Visual Studio 2017, modern Clang, modern GCC +* Lacks a few rarely used features presented in Boost.Signals2 + * No access to connection from slot with `signal::connect_extended` method + * No connected object tracking with `slot::track` method + * Use [bind_weak](bind_weak.md) instead + * No temporary signal blocking with `shared_connection_block` class + * Cannot disconnect equivalent slots since no `disconnect(slot)` function overload + * Any other API difference is a bug - please report it! + +See also [Migration from Boost.Signals2](migration-from-boost-signals2.md). + +## Benchmark results + +Directory `tests/libfastsignals_bench` contains simple benchmark with compares two signal/slot implementations: + +* Boost.Signals2 +* libfastsignals + +Benchmark compairs performance when signal emitted frequently with 0, 1 and 8 active connections. In these cases libfastsignals is 3-6 times faster. + +``` +*** Results: +measure emit_boost emit_fastsignals +emit_boost/0 1.00 3.00 +emit_boost/1 1.00 5.76 +emit_boost/8 1.00 3.70 +*** +``` diff --git a/src/3rdParty/FastSignals/libfastsignals/CMakeLists.txt b/src/3rdParty/FastSignals/libfastsignals/CMakeLists.txt new file mode 100644 index 0000000000..1ee88c092a --- /dev/null +++ b/src/3rdParty/FastSignals/libfastsignals/CMakeLists.txt @@ -0,0 +1,5 @@ + +file(GLOB LIBFASTSIGNALS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/include/*.h") +add_library(libfastsignals STATIC ${LIBFASTSIGNALS_SRC}) +set_target_properties(libfastsignals PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(libfastsignals INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") diff --git a/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/bind_weak.h b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/bind_weak.h new file mode 100644 index 0000000000..64d4b69f15 --- /dev/null +++ b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/bind_weak.h @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include + +namespace fastsignals +{ +namespace detail +{ +template +struct weak_binder +{ + using ConstMethodType = ReturnType (ClassType::*)(Args... args) const; + using NonConstMethodType = ReturnType (ClassType::*)(Args... args); + using MethodType = std::conditional_t; + using WeakPtrType = std::weak_ptr; + + weak_binder(MethodType pMethod, WeakPtrType&& pObject) + : m_pMethod(pMethod) + , m_pObject(std::move(pObject)) + { + } + + ReturnType operator()(Args... args) const + { + if (auto pThis = m_pObject.lock()) + { + return (pThis.get()->*m_pMethod)(std::forward(args)...); + } + return ReturnType(); + } + + MethodType m_pMethod; + WeakPtrType m_pObject; +}; +} // namespace detail + +/// Weak this binding of non-const methods. +template +decltype(auto) bind_weak(ReturnType (ClassType::*memberFn)(Params... args), std::shared_ptr const& pThis, Args... args) +{ + using weak_binder_alias = detail::weak_binder; + + weak_binder_alias invoker(memberFn, std::weak_ptr(pThis)); + return std::bind(invoker, args...); +} + +/// Weak this binding of const methods. +template +decltype(auto) bind_weak(ReturnType (ClassType::*memberFn)(Params... args) const, std::shared_ptr const& pThis, Args... args) +{ + using weak_binder_alias = detail::weak_binder; + + weak_binder_alias invoker(memberFn, std::weak_ptr(pThis)); + return std::bind(invoker, args...); +} + +/// Weak this binding of non-const methods. +template +decltype(auto) bind_weak(ReturnType (ClassType::*memberFn)(Params... args), std::weak_ptr pThis, Args... args) +{ + using weak_binder_alias = detail::weak_binder; + + weak_binder_alias invoker(memberFn, std::move(pThis)); + return std::bind(invoker, args...); +} + +/// Weak this binding of const methods. +template +decltype(auto) bind_weak(ReturnType (ClassType::*memberFn)(Params... args) const, std::weak_ptr pThis, Args... args) +{ + using weak_binder_alias = detail::weak_binder; + + weak_binder_alias invoker(memberFn, std::move(pThis)); + return std::bind(invoker, args...); +} + +} // namespace fastsignals diff --git a/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/combiners.h b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/combiners.h new file mode 100644 index 0000000000..3b83c42c0d --- /dev/null +++ b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/combiners.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +namespace fastsignals +{ + +/** + * This results combiner reduces results collection into last value of this collection. + * In other words, it keeps only result of the last slot call. + */ +template +class optional_last_value +{ +public: + using result_type = std::optional; + + template + void operator()(TRef&& value) + { + m_result = std::forward(value); + } + + result_type get_value() const + { + return m_result; + } + +private: + result_type m_result = {}; +}; + +template <> +class optional_last_value +{ +public: + using result_type = void; +}; + +} // namespace fastsignals diff --git a/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/connection.h b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/connection.h new file mode 100644 index 0000000000..2890f19d66 --- /dev/null +++ b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/connection.h @@ -0,0 +1,116 @@ +#pragma once + +#include "signal_impl.h" + +namespace fastsignals +{ + +// Connection keeps link between signal and slot and can disconnect them. +// Disconnect operation is thread-safe: any thread can disconnect while +// slots called on other thread. +// This class itself is not thread-safe: you can't use the same connection +// object from different threads at the same time. +class connection +{ +public: + connection() noexcept; + explicit connection(detail::signal_impl_weak_ptr storage, uint64_t id) noexcept; + connection(const connection& other) noexcept; + connection& operator=(const connection& other) noexcept; + connection(connection&& other) noexcept; + connection& operator=(connection&& other) noexcept; + ~connection() = default; + + bool connected() const noexcept; + void disconnect() noexcept; + +protected: + detail::signal_impl_weak_ptr m_storage; + uint64_t m_id = 0; +}; + +// Connection class that supports blocking callback execution +class advanced_connection : public connection +{ +public: + struct advanced_connection_impl + { + void block() noexcept; + void unblock() noexcept; + bool is_blocked() const noexcept; + + private: + std::atomic m_blockCounter {0}; + }; + using impl_ptr = std::shared_ptr; + + advanced_connection() noexcept; + explicit advanced_connection(connection&& conn, impl_ptr&& impl) noexcept; + advanced_connection(const advanced_connection&) noexcept; + advanced_connection& operator=(const advanced_connection&) noexcept; + advanced_connection(advanced_connection&& other) noexcept; + advanced_connection& operator=(advanced_connection&& other) noexcept; + ~advanced_connection() = default; + +protected: + impl_ptr m_impl; +}; + +// Blocks advanced connection, so its callback will not be executed +class shared_connection_block +{ +public: + shared_connection_block(const advanced_connection& connection = advanced_connection(), bool initially_blocked = true) noexcept; + shared_connection_block(const shared_connection_block& other) noexcept; + shared_connection_block(shared_connection_block&& other) noexcept; + shared_connection_block& operator=(const shared_connection_block& other) noexcept; + shared_connection_block& operator=(shared_connection_block&& other) noexcept; + ~shared_connection_block(); + + void block() noexcept; + void unblock() noexcept; + bool blocking() const noexcept; + +private: + void increment_if_blocked() const noexcept; + + std::weak_ptr m_connection; + std::atomic m_blocked {false}; +}; + +// Scoped connection keeps link between signal and slot and disconnects them in destructor. +// Scoped connection is movable, but not copyable. +class scoped_connection : public connection +{ +public: + scoped_connection() noexcept; + scoped_connection(const connection& conn) noexcept; + scoped_connection(connection&& conn) noexcept; + scoped_connection(const advanced_connection& conn) = delete; + scoped_connection(advanced_connection&& conn) noexcept = delete; + scoped_connection(const scoped_connection&) = delete; + scoped_connection& operator=(const scoped_connection&) = delete; + scoped_connection(scoped_connection&& other) noexcept; + scoped_connection& operator=(scoped_connection&& other) noexcept; + ~scoped_connection(); + + connection release() noexcept; +}; + +// scoped connection for advanced connections +class advanced_scoped_connection : public advanced_connection +{ +public: + advanced_scoped_connection() noexcept; + advanced_scoped_connection(const advanced_connection& conn) noexcept; + advanced_scoped_connection(advanced_connection&& conn) noexcept; + advanced_scoped_connection(const advanced_scoped_connection&) = delete; + advanced_scoped_connection& operator=(const advanced_scoped_connection&) = delete; + advanced_scoped_connection(advanced_scoped_connection&& other) noexcept; + advanced_scoped_connection& operator=(advanced_scoped_connection&& other) noexcept; + ~advanced_scoped_connection(); + + advanced_connection release() noexcept; +}; + +} // namespace fastsignals diff --git a/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/function.h b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/function.h new file mode 100644 index 0000000000..a4fcabb3ed --- /dev/null +++ b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/function.h @@ -0,0 +1,54 @@ +#pragma once + +#include "function_detail.h" + +namespace fastsignals +{ +// Derive your class from not_directly_callable to prevent function from wrapping it using its template constructor +// Useful if your class provides custom operator for casting to function +struct not_directly_callable +{ +}; + +template +using enable_if_callable_t = typename std::enable_if_t< + !std::is_same_v, Function> && !std::is_base_of_v> && std::is_same_v, Return>>; + +template +class function; + +// Compact function class - causes minimal code bloat when compiled. +// Replaces std::function in this library. +template +class function +{ +public: + function() = default; + + function(const function& other) = default; + function(function&& other) noexcept = default; + function& operator=(const function& other) = default; + function& operator=(function&& other) noexcept = default; + + template , Return, Arguments...>> + function(Fn&& function) noexcept(detail::is_noexcept_packed_function_init) + { + m_packed.init(std::forward(function)); + } + + Return operator()(Arguments&&... args) const + { + auto& proxy = m_packed.get(); + return proxy(std::forward(args)...); + } + + detail::packed_function release() noexcept + { + return std::move(m_packed); + } + +private: + detail::packed_function m_packed; +}; + +} // namespace fastsignals diff --git a/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/function_detail.h b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/function_detail.h new file mode 100644 index 0000000000..c3d09eae0e --- /dev/null +++ b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/function_detail.h @@ -0,0 +1,164 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace fastsignals::detail +{ +/// Buffer for callable object in-place construction, +/// helps to implement Small Buffer Optimization. +static constexpr size_t inplace_buffer_size = (sizeof(int) == sizeof(void*) ? 8 : 6) * sizeof(void*); + +/// Structure that has size enough to keep type "T" including vtable. +template +struct type_container +{ + T data; +}; + +/// Type that has enough space to keep type "T" (including vtable) with suitable alignment. +using function_buffer_t = std::aligned_storage_t; + +/// Constantly is true if callable fits function buffer, false otherwise. +template +inline constexpr bool fits_inplace_buffer = (sizeof(type_container) <= inplace_buffer_size); + +// clang-format off +/// Constantly is true if callable fits function buffer and can be safely moved, false otherwise +template +inline constexpr bool can_use_inplace_buffer = + fits_inplace_buffer && + std::is_nothrow_move_constructible_v; +// clang format on + +/// Type that is suitable to keep copy of callable object. +/// - equal to pointer-to-function if Callable is pointer-to-function +/// - otherwise removes const/volatile and references to allow copying callable. +template +using callable_copy_t = std::conditional_t>, + Callable, + std::remove_cv_t>>; + +class base_function_proxy +{ +public: + virtual ~base_function_proxy() = default; + virtual base_function_proxy* clone(void* buffer) const = 0; + virtual base_function_proxy* move(void* buffer) noexcept = 0; +}; + +template +class function_proxy; + +template +class function_proxy : public base_function_proxy +{ +public: + virtual Return operator()(Arguments&&...) = 0; +}; + +template +class function_proxy_impl final : public function_proxy +{ +public: + // If you see this error, probably your function returns value and you're trying to + // connect it to `signal`. Just remove return value from callback. + static_assert(std::is_same_v, Return>, + "cannot construct function<> class from callable object with different return type"); + + template + explicit function_proxy_impl(FunctionObject&& function) + : m_callable(std::forward(function)) + { + } + + Return operator()(Arguments&&... args) final + { + return m_callable(std::forward(args)...); + } + + base_function_proxy* clone(void* buffer) const final + { + if constexpr (can_use_inplace_buffer) + { + return new (buffer) function_proxy_impl(*this); + } + else + { + (void)buffer; + return new function_proxy_impl(*this); + } + } + + base_function_proxy* move(void* buffer) noexcept final + { + if constexpr (can_use_inplace_buffer) + { + base_function_proxy* moved = new (buffer) function_proxy_impl(std::move(*this)); + this->~function_proxy_impl(); + return moved; + } + else + { + (void)buffer; + return this; + } + } + +private: + callable_copy_t m_callable; +}; + +template +inline constexpr bool is_noexcept_packed_function_init = can_use_inplace_buffer>; + +class packed_function final +{ +public: + packed_function() = default; + packed_function(packed_function&& other) noexcept; + packed_function(const packed_function& other); + packed_function& operator=(packed_function&& other) noexcept; + packed_function& operator=(const packed_function& other); + ~packed_function() noexcept; + + // Initializes packed function. + // Cannot be called without reset(). + template + void init(Callable&& function) noexcept(is_noexcept_packed_function_init) + { + using proxy_t = function_proxy_impl; + + assert(m_proxy == nullptr); + if constexpr (can_use_inplace_buffer) + { + m_proxy = new (&m_buffer) proxy_t{ std::forward(function) }; + } + else + { + m_proxy = new proxy_t{ std::forward(function) }; + } + } + + template + function_proxy& get() const + { + return static_cast&>(unwrap()); + } + + void reset() noexcept; + +private: + base_function_proxy* move_proxy_from(packed_function&& other) noexcept; + base_function_proxy* clone_proxy_from(const packed_function &other); + base_function_proxy& unwrap() const; + bool is_buffer_allocated() const noexcept; + + function_buffer_t m_buffer[1] = {}; + base_function_proxy* m_proxy = nullptr; +}; + +} // namespace fastsignals::detail diff --git a/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/signal.h b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/signal.h new file mode 100644 index 0000000000..00a09e9f72 --- /dev/null +++ b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/signal.h @@ -0,0 +1,151 @@ +#pragma once + +#include "combiners.h" +#include "connection.h" +#include "function.h" +#include "signal_impl.h" +#include "type_traits.h" +#include + + +namespace fastsignals +{ +template class Combiner = optional_last_value> +class signal; + +struct advanced_tag +{ +}; + +/// Signal allows to fire events to many subscribers (slots). +/// In other words, it implements one-to-many relation between event and listeners. +/// Signal implements observable object from Observable pattern. +template class Combiner> +class signal : private not_directly_callable +{ +public: + using signature_type = Return(signal_arg_t...); + using slot_type = function; + using combiner_type = Combiner; + using result_type = typename combiner_type::result_type; + + signal() + : m_slots(std::make_shared()) + { + } + + /// No copy construction + signal(const signal&) = delete; + + /// Moves signal from other. Any operations on other except destruction, move, and swap are invalid + signal(signal&& other) = default; + + /// No copy assignment + signal& operator=(const signal&) = delete; + + /// Moves signal from other. Any operations on other except destruction, move, and swap are invalid + signal& operator=(signal&& other) = default; + + /** + * connect(slot) method subscribes slot to signal emission event. + * Each time you call signal as functor, all slots are also called with given arguments. + * @returns connection - object which manages signal-slot connection lifetime + */ + connection connect(slot_type slot) + { + const uint64_t id = m_slots->add(slot.release()); + return connection(m_slots, id); + } + + /** + * connect(slot, advanced_tag) method subscribes slot to signal emission event with the ability to temporarily block slot execution + * Each time you call signal as functor, all non-blocked slots are also called with given arguments. + * You can temporarily block slot execution using shared_connection_block + * @returns advanced_connection - object which manages signal-slot connection lifetime + */ + advanced_connection connect(slot_type slot, advanced_tag) + { + static_assert(std::is_void_v, "Advanced connect can only be used with slots returning void (implementation limitation)"); + auto conn_impl = std::make_shared(); + slot_type slot_impl = [this, slot, weak_conn_impl = std::weak_ptr(conn_impl)](signal_arg_t... args) { + (void)this; + auto conn_impl = weak_conn_impl.lock(); + if (!conn_impl || !conn_impl->is_blocked()) + { + slot(args...); + } + }; + auto conn = connect(std::move(slot_impl)); + return advanced_connection(std::move(conn), std::move(conn_impl)); + } + + /** + * disconnect_all_slots() method disconnects all slots from signal emission event. + */ + void disconnect_all_slots() noexcept + { + m_slots->remove_all(); + } + + /** + * num_slots() method returns number of slots attached to this singal + */ + [[nodiscard]] std::size_t num_slots() const noexcept + { + return m_slots->count(); + } + + /** + * empty() method returns true if signal has any slots attached + */ + [[nodiscard]] bool empty() const noexcept + { + return m_slots->count() == 0; + } + + /** + * operator(args...) calls all slots connected to this signal. + * Logically, it fires signal emission event. + */ + result_type operator()(signal_arg_t... args) const + { + return detail::signal_impl_ptr(m_slots)->invoke...>(args...); + } + + void swap(signal& other) noexcept + { + m_slots.swap(other.m_slots); + } + + /** + * Allows using signals as slots for another signal + */ + operator slot_type() const noexcept + { + return [weakSlots = detail::signal_impl_weak_ptr(m_slots)](signal_arg_t... args) { + if (auto slots = weakSlots.lock()) + { + return slots->invoke...>(args...); + } + }; + } + +private: + detail::signal_impl_ptr m_slots; +}; + +} // namespace fastsignals + +namespace std +{ + +// free swap function, findable by ADL +template class Combiner> +void swap( + ::fastsignals::signal& sig1, + ::fastsignals::signal& sig2) +{ + sig1.swap(sig2); +} + +} // namespace std diff --git a/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/signal_impl.h b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/signal_impl.h new file mode 100644 index 0000000000..e5042e1875 --- /dev/null +++ b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/signal_impl.h @@ -0,0 +1,59 @@ +#pragma once + +#include "function_detail.h" +#include "spin_mutex.h" +#include +#include + +namespace fastsignals::detail +{ + +class signal_impl +{ +public: + uint64_t add(packed_function fn); + + void remove(uint64_t id) noexcept; + + void remove_all() noexcept; + + size_t count() const noexcept; + + template + Result invoke(Args... args) const + { + packed_function slot; + size_t slotIndex = 0; + uint64_t slotId = 1; + + if constexpr (std::is_same_v) + { + while (get_next_slot(slot, slotIndex, slotId)) + { + slot.get()(std::forward(args)...); + } + } + else + { + Combiner combiner; + while (get_next_slot(slot, slotIndex, slotId)) + { + combiner(slot.get()(std::forward(args)...)); + } + return combiner.get_value(); + } + } + +private: + bool get_next_slot(packed_function& slot, size_t& expectedIndex, uint64_t& nextId) const; + + mutable spin_mutex m_mutex; + std::vector m_functions; + std::vector m_ids; + uint64_t m_nextId = 1; +}; + +using signal_impl_ptr = std::shared_ptr; +using signal_impl_weak_ptr = std::weak_ptr; + +} // namespace fastsignals::detail diff --git a/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/spin_mutex.h b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/spin_mutex.h new file mode 100644 index 0000000000..b8e4229cdb --- /dev/null +++ b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/spin_mutex.h @@ -0,0 +1,38 @@ +#pragma once +#include + +namespace fastsignals::detail +{ + +class spin_mutex +{ +public: + spin_mutex() = default; + spin_mutex(const spin_mutex&) = delete; + spin_mutex& operator=(const spin_mutex&) = delete; + spin_mutex(spin_mutex&&) = delete; + spin_mutex& operator=(spin_mutex&&) = delete; + + inline bool try_lock() noexcept + { + return !m_busy.test_and_set(std::memory_order_acquire); + } + + inline void lock() noexcept + { + while (!try_lock()) + { + /* do nothing */; + } + } + + inline void unlock() noexcept + { + m_busy.clear(std::memory_order_release); + } + +private: + std::atomic_flag m_busy = ATOMIC_FLAG_INIT; +}; + +} // namespace fastsignals::detail diff --git a/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/type_traits.h b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/type_traits.h new file mode 100644 index 0000000000..f0d6af11c4 --- /dev/null +++ b/src/3rdParty/FastSignals/libfastsignals/include/fastsignals/type_traits.h @@ -0,0 +1,24 @@ +#pragma once + +namespace fastsignals +{ +namespace detail +{ + +template +struct signal_arg +{ + using type = const T&; +}; + +template +struct signal_arg +{ + using type = U&; +}; +} // namespace detail + +template +using signal_arg_t = typename detail::signal_arg::type; + +} // namespace fastsignals diff --git a/src/3rdParty/FastSignals/libfastsignals/src/connection.cpp b/src/3rdParty/FastSignals/libfastsignals/src/connection.cpp new file mode 100644 index 0000000000..9a71745a1b --- /dev/null +++ b/src/3rdParty/FastSignals/libfastsignals/src/connection.cpp @@ -0,0 +1,251 @@ +#include "../include/fastsignals/connection.h" + +namespace fastsignals +{ +namespace +{ + +auto get_advanced_connection_impl(const advanced_connection& connection) noexcept +{ + struct advanced_connection_impl_getter : private advanced_connection + { + advanced_connection_impl_getter(const advanced_connection& connection) noexcept + : advanced_connection(connection) + { + } + using advanced_connection::m_impl; + }; + return advanced_connection_impl_getter(connection).m_impl; +} + +} // namespace + +connection::connection(connection&& other) noexcept + : m_storage(other.m_storage) + , m_id(other.m_id) +{ + other.m_storage.reset(); + other.m_id = 0; +} + +connection::connection(detail::signal_impl_weak_ptr storage, uint64_t id) noexcept + : m_storage(std::move(storage)) + , m_id(id) +{ +} + +connection::connection() noexcept = default; + +connection::connection(const connection& other) noexcept = default; + +connection& connection::operator=(connection&& other) noexcept +{ + m_storage = other.m_storage; + m_id = other.m_id; + other.m_storage.reset(); + other.m_id = 0; + return *this; +} + +connection& connection::operator=(const connection& other) noexcept = default; + +bool connection::connected() const noexcept +{ + return (m_id != 0); +} + +void connection::disconnect() noexcept +{ + if (auto storage = m_storage.lock()) + { + storage->remove(m_id); + m_storage.reset(); + } + m_id = 0; +} + +scoped_connection::scoped_connection(connection&& conn) noexcept + : connection(std::move(conn)) +{ +} + +scoped_connection::scoped_connection(const connection& conn) noexcept + : connection(conn) +{ +} + +scoped_connection::scoped_connection() noexcept = default; + +scoped_connection::scoped_connection(scoped_connection&& other) noexcept = default; + +scoped_connection& scoped_connection::operator=(scoped_connection&& other) noexcept +{ + disconnect(); + static_cast(*this) = std::move(other); + return *this; +} + +scoped_connection::~scoped_connection() +{ + disconnect(); +} + +connection scoped_connection::release() noexcept +{ + connection conn = std::move(static_cast(*this)); + return conn; +} + +bool advanced_connection::advanced_connection_impl::is_blocked() const noexcept +{ + return m_blockCounter.load(std::memory_order_acquire) != 0; +} + +void advanced_connection::advanced_connection_impl::block() noexcept +{ + ++m_blockCounter; +} + +void advanced_connection::advanced_connection_impl::unblock() noexcept +{ + --m_blockCounter; +} + +advanced_connection::advanced_connection() noexcept = default; + +advanced_connection::advanced_connection(connection&& conn, impl_ptr&& impl) noexcept + : connection(std::move(conn)) + , m_impl(std::move(impl)) +{ +} + +advanced_connection::advanced_connection(const advanced_connection&) noexcept = default; + +advanced_connection::advanced_connection(advanced_connection&& other) noexcept = default; + +advanced_connection& advanced_connection::operator=(const advanced_connection&) noexcept = default; + +advanced_connection& advanced_connection::operator=(advanced_connection&& other) noexcept = default; + +shared_connection_block::shared_connection_block(const advanced_connection& connection, bool initially_blocked) noexcept + : m_connection(get_advanced_connection_impl(connection)) +{ + if (initially_blocked) + { + block(); + } +} + +shared_connection_block::shared_connection_block(const shared_connection_block& other) noexcept + : m_connection(other.m_connection) + , m_blocked(other.m_blocked.load(std::memory_order_acquire)) +{ + increment_if_blocked(); +} + +shared_connection_block::shared_connection_block(shared_connection_block&& other) noexcept + : m_connection(other.m_connection) + , m_blocked(other.m_blocked.load(std::memory_order_acquire)) +{ + other.m_connection.reset(); + other.m_blocked.store(false, std::memory_order_release); +} + +shared_connection_block& shared_connection_block::operator=(const shared_connection_block& other) noexcept +{ + if (&other != this) + { + unblock(); + m_connection = other.m_connection; + m_blocked = other.m_blocked.load(std::memory_order_acquire); + increment_if_blocked(); + } + return *this; +} + +shared_connection_block& shared_connection_block::operator=(shared_connection_block&& other) noexcept +{ + if (&other != this) + { + unblock(); + m_connection = other.m_connection; + m_blocked = other.m_blocked.load(std::memory_order_acquire); + other.m_connection.reset(); + other.m_blocked.store(false, std::memory_order_release); + } + return *this; +} + +shared_connection_block::~shared_connection_block() +{ + unblock(); +} + +void shared_connection_block::block() noexcept +{ + bool blocked = false; + if (m_blocked.compare_exchange_strong(blocked, true, std::memory_order_acq_rel, std::memory_order_relaxed)) + { + if (auto connection = m_connection.lock()) + { + connection->block(); + } + } +} + +void shared_connection_block::unblock() noexcept +{ + bool blocked = true; + if (m_blocked.compare_exchange_strong(blocked, false, std::memory_order_acq_rel, std::memory_order_relaxed)) + { + if (auto connection = m_connection.lock()) + { + connection->unblock(); + } + } +} + +bool shared_connection_block::blocking() const noexcept +{ + return m_blocked; +} + +void shared_connection_block::increment_if_blocked() const noexcept +{ + if (m_blocked) + { + if (auto connection = m_connection.lock()) + { + connection->block(); + } + } +} + +advanced_scoped_connection::advanced_scoped_connection() noexcept = default; + +advanced_scoped_connection::advanced_scoped_connection(const advanced_connection& conn) noexcept + : advanced_connection(conn) +{ +} + +advanced_scoped_connection::advanced_scoped_connection(advanced_connection&& conn) noexcept + : advanced_connection(std::move(conn)) +{ +} + +advanced_scoped_connection::advanced_scoped_connection(advanced_scoped_connection&& other) noexcept = default; + +advanced_scoped_connection& advanced_scoped_connection::operator=(advanced_scoped_connection&& other) noexcept = default; + +advanced_scoped_connection::~advanced_scoped_connection() +{ + disconnect(); +} + +advanced_connection advanced_scoped_connection::release() noexcept +{ + advanced_connection conn = std::move(static_cast(*this)); + return conn; +} + +} // namespace fastsignals diff --git a/src/3rdParty/FastSignals/libfastsignals/src/function_detail.cpp b/src/3rdParty/FastSignals/libfastsignals/src/function_detail.cpp new file mode 100644 index 0000000000..c807e7c1d1 --- /dev/null +++ b/src/3rdParty/FastSignals/libfastsignals/src/function_detail.cpp @@ -0,0 +1,96 @@ +#include "../include/fastsignals/function_detail.h" +#include +#include + +namespace fastsignals::detail +{ + +packed_function::packed_function(packed_function&& other) noexcept + : m_proxy(move_proxy_from(std::move(other))) +{ +} + +packed_function::packed_function(const packed_function& other) + : m_proxy(clone_proxy_from(other)) +{ +} + +packed_function& packed_function::operator=(packed_function&& other) noexcept +{ + assert(this != &other); + reset(); + m_proxy = move_proxy_from(std::move(other)); + return *this; +} + +base_function_proxy* packed_function::move_proxy_from(packed_function&& other) noexcept +{ + auto proxy = other.m_proxy ? other.m_proxy->move(&m_buffer) : nullptr; + other.m_proxy = nullptr; + return proxy; +} + +base_function_proxy* packed_function::clone_proxy_from(const packed_function& other) +{ + return other.m_proxy ? other.m_proxy->clone(&m_buffer) : nullptr; +} + +packed_function& packed_function::operator=(const packed_function& other) +{ + if (this != &other) + { + if (other.is_buffer_allocated() && is_buffer_allocated()) + { + // "This" and "other" are using SBO. Safe assignment must use copy+move + *this = packed_function(other); + } + else + { + // Buffer is used either by "this" or by "other" or not used at all. + // If this uses buffer then other's proxy is null or allocated on heap, so clone won't overwrite buffer + // If this uses heap or null then other's proxy can safely use buffer because reset() won't access buffer + auto newProxy = clone_proxy_from(other); + reset(); + m_proxy = newProxy; + } + } + return *this; +} + +packed_function::~packed_function() noexcept +{ + reset(); +} + +void packed_function::reset() noexcept +{ + if (m_proxy != nullptr) + { + if (is_buffer_allocated()) + { + m_proxy->~base_function_proxy(); + } + else + { + delete m_proxy; + } + m_proxy = nullptr; + } +} + +base_function_proxy& packed_function::unwrap() const +{ + if (m_proxy == nullptr) + { + throw std::bad_function_call(); + } + return *m_proxy; +} + +bool packed_function::is_buffer_allocated() const noexcept +{ + return std::less_equal()(&m_buffer[0], m_proxy) + && std::less()(m_proxy, &m_buffer[1]); +} + +} // namespace fastsignals::detail diff --git a/src/3rdParty/FastSignals/libfastsignals/src/signal_impl.cpp b/src/3rdParty/FastSignals/libfastsignals/src/signal_impl.cpp new file mode 100644 index 0000000000..4c345f24d5 --- /dev/null +++ b/src/3rdParty/FastSignals/libfastsignals/src/signal_impl.cpp @@ -0,0 +1,84 @@ +#include "../include/fastsignals/signal_impl.h" +#include +#include + +namespace fastsignals::detail +{ + +uint64_t signal_impl::add(packed_function fn) +{ + std::lock_guard lock(m_mutex); + + m_functions.emplace_back(std::move(fn)); + + try + { + m_ids.emplace_back(m_nextId); + } + catch (const std::bad_alloc& /*e*/) + { + // Remove function since we failed to add its id + m_functions.pop_back(); + throw; + } + + return m_nextId++; +} + +void signal_impl::remove(uint64_t id) noexcept +{ + std::lock_guard lock(m_mutex); + + // We use binary search because ids array is always sorted. + auto it = std::lower_bound(m_ids.begin(), m_ids.end(), id); + if (it != m_ids.end() && *it == id) + { + size_t i = std::distance(m_ids.begin(), it); + m_ids.erase(m_ids.begin() + i); + m_functions.erase(m_functions.begin() + i); + } +} + +void signal_impl::remove_all() noexcept +{ + std::lock_guard lock(m_mutex); + m_functions.clear(); + m_ids.clear(); +} + +bool signal_impl::get_next_slot(packed_function& slot, size_t& expectedIndex, uint64_t& nextId) const +{ + // Slots always arranged by ID, so we can use a simple algorithm which avoids races: + // - on each step find first slot with ID >= slotId + // - after each call increment slotId + + std::lock_guard lock(m_mutex); + + // Avoid binary search if next slot wasn't moved between mutex locks. + if (expectedIndex >= m_ids.size() || m_ids[expectedIndex] != nextId) + { + auto it = (nextId < m_nextId) + ? std::lower_bound(m_ids.cbegin(), m_ids.cend(), nextId) + : m_ids.end(); + if (it == m_ids.end()) + { + return false; + } + expectedIndex = std::distance(m_ids.cbegin(), it); + } + + slot.reset(); + slot = m_functions[expectedIndex]; + nextId = (expectedIndex + 1 < m_ids.size()) ? m_ids[expectedIndex + 1] : m_ids[expectedIndex] + 1; + ++expectedIndex; + return true; +} + +size_t signal_impl::count() const noexcept +{ + std::lock_guard lock(m_mutex); + + return m_functions.size(); +} + +} // namespace fastsignals::detail diff --git a/src/3rdParty/FastSignals/tests/catch2/catch.hpp b/src/3rdParty/FastSignals/tests/catch2/catch.hpp new file mode 100644 index 0000000000..f4cb8f25a0 --- /dev/null +++ b/src/3rdParty/FastSignals/tests/catch2/catch.hpp @@ -0,0 +1,17976 @@ +/* + * 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