pax_global_header00006660000000000000000000000064144571327530014525gustar00rootroot0000000000000052 comment=4aeb70a2f05560338a919e3f2c9a9208274c7a7a intrusive_shared_ptr-1.4/000077500000000000000000000000001445713275300156345ustar00rootroot00000000000000intrusive_shared_ptr-1.4/.gitattributes000066400000000000000000000000351445713275300205250ustar00rootroot00000000000000doc/* linguist-documentation intrusive_shared_ptr-1.4/.github/000077500000000000000000000000001445713275300171745ustar00rootroot00000000000000intrusive_shared_ptr-1.4/.github/workflows/000077500000000000000000000000001445713275300212315ustar00rootroot00000000000000intrusive_shared_ptr-1.4/.github/workflows/publish.yml000066400000000000000000000026401445713275300234240ustar00rootroot00000000000000name: Publish on: push: tags: - 'v*' jobs: main: runs-on: ubuntu-latest permissions: write-all steps: - name: checkout uses: actions/checkout@v3 with: fetch-depth: 0 - name: Zip Headers shell: bash working-directory: ${{github.workspace}} run: tar -czf intrusive_shared_ptr.tar.gz --transform 's/inc/intrusive_shared_ptr\/inc/' inc - id: get-release-name uses: actions/github-script@v6 with: script: | const tagName = context.ref.substring(context.ref.lastIndexOf('/') + 2) core.setOutput('name', tagName) - name: Release uses: actions/create-release@v1 id: create_release with: draft: true prerelease: false release_name: ${{ steps.get-release-name.outputs.name }} tag_name: ${{ github.ref }} body: ...edit me... env: GITHUB_TOKEN: ${{ github.token }} - name: Upload headers uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ github.token }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} asset_path: ${{github.workspace}}/intrusive_shared_ptr.tar.gz asset_name: intrusive_shared_ptr-${{ steps.get-release-name.outputs.name }}.tar.gz asset_content_type: application/gzip intrusive_shared_ptr-1.4/.github/workflows/test.yml000066400000000000000000000010551445713275300227340ustar00rootroot00000000000000name: Test on: push: paths-ignore: - 'README.md' - '.gitignore' - 'LICENSE' - 'CHANGELOG.md' - 'doc/**' - 'tools/**' jobs: build: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [macos-latest, ubuntu-latest, windows-latest] steps: - uses: actions/checkout@v3 - name: Configure run: | cmake -S . -B build -DCMAKE_BUILD_TYPE=Release - name: Build and Test run: | cmake --build build --config Release --target run-test intrusive_shared_ptr-1.4/.gitignore000066400000000000000000000000461445713275300176240ustar00rootroot00000000000000.DS_Store .vscode .vs /build/ /**/out/intrusive_shared_ptr-1.4/CHANGELOG.md000066400000000000000000000022471445713275300174520ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## Unreleased ## [1.4] - 2023-07-22 ### Changed - Updated CMake configuration to modernize it and allow local installation ## [1.3] - 2023-03-31 ### Added - Added single threaded mode support to `ref_counted` ### Changed - Updated documentation ## [1.2] - 2023-03-17 ### Added - Pre-defined specialization for Python object and type pointer for use in Python extensions. See python_ptr.h header for details. ## [1.1] - 2022-06-09 ### Changed - CMake configuration modified to mark this library sources as PRIVATE rather than INTERFACE. This plays nicer with IDEs and avoid polluting library clients with its headers ## [1.0] - 2022-05-22 ### Added - First release [1.0]: https://github.com/gershnik/intrusive_shared_ptr/releases/v1.0 [1.1]: https://github.com/gershnik/intrusive_shared_ptr/releases/v1.1 [1.2]: https://github.com/gershnik/intrusive_shared_ptr/releases/v1.2 [1.3]: https://github.com/gershnik/intrusive_shared_ptr/releases/v1.3 [1.4]: https://github.com/gershnik/intrusive_shared_ptr/releases/v1.4 intrusive_shared_ptr-1.4/CMakeLists.txt000066400000000000000000000026131445713275300203760ustar00rootroot00000000000000# # Copyright 2004 Eugene Gershnik # # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file or at # https://github.com/gershnik/intrusive_shared_ptr/blob/master/LICENSE.txt # cmake_minimum_required(VERSION 3.25) file(READ VERSION ISPTR_VERSION) if (NOT ISPTR_VERSION) message(FATAL_ERROR "Cannot determine library version (VERSION file not found)") endif() string(STRIP ${ISPTR_VERSION} ISPTR_VERSION) project(isptr VERSION ${ISPTR_VERSION} LANGUAGES CXX) set(SRCDIR ${CMAKE_CURRENT_LIST_DIR}) set(LIBNAME isptr) add_library(${LIBNAME} INTERFACE) target_include_directories(${LIBNAME} INTERFACE $ $ # /include ) set(PUBLIC_HEADERS ${SRCDIR}/inc/intrusive_shared_ptr/intrusive_shared_ptr.h ${SRCDIR}/inc/intrusive_shared_ptr/apple_cf_ptr.h ${SRCDIR}/inc/intrusive_shared_ptr/com_ptr.h ${SRCDIR}/inc/intrusive_shared_ptr/python_ptr.h ${SRCDIR}/inc/intrusive_shared_ptr/refcnt_ptr.h ${SRCDIR}/inc/intrusive_shared_ptr/ref_counted.h ) target_sources(${LIBNAME} INTERFACE FILE_SET HEADERS BASE_DIRS ${SRCDIR}/inc FILES ${PUBLIC_HEADERS} PRIVATE ${PUBLIC_HEADERS} ) add_library(intrusive-shared-ptr ALIAS isptr) add_library(isptr::isptr ALIAS isptr) if (PROJECT_IS_TOP_LEVEL) include(cmake/install.cmake) add_subdirectory(test) endif()intrusive_shared_ptr-1.4/LICENSE.txt000066400000000000000000000020601445713275300174550ustar00rootroot00000000000000MIT License Copyright (c) 2004 Eugene Gershnik 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. intrusive_shared_ptr-1.4/README.md000066400000000000000000000421641445713275300171220ustar00rootroot00000000000000# Intro This is yet another implementation of an intrusive [reference counting](http://en.wikipedia.org/wiki/Reference_counting) [smart pointer](http://en.wikipedia.org/wiki/Smart_pointer), highly configurable reference counted base class and adapters. The code requires C++17 or above compiler. It is known to work with:
Xcode 11 or above
Microsoft Visual Studio 2019 or above
GCC 7.4.0 or above
Documentation and formal tests are work in progress. - [Why bother?](#why-bother) - [Named conversions from raw pointers](#named-conversions-from-raw-pointers) - [No ADL](#no-adl) - [Decent support for output parameters](#decent-support-for-output-parameters) - [Support for operator->*](#support-for-operator-) - [Atomic access](#atomic-access) - [Trivial ABI](#trivial-abi) - [Correct implementation of a "reference counted base" class](#correct-implementation-of-a-reference-counted-base-class) - [Support for weak pointers](#support-for-weak-pointers) - [Integration](#integration) - [CMake via FetchContent](#cmake-via-fetchcontent) - [Building and installing on your system](#building-and-installing-on-your-system) - [Basic use](#basic-use) - [CMake package](#cmake-package) - [Via pkg-config](#via-pkg-config) - [Copying to your sources](#copying-to-your-sources) - [Usage](#usage) - [Using provided base classes](#using-provided-base-classes) - [Supporting weak pointers](#supporting-weak-pointers) - [Using with Apple CoreFoundation types](#using-with-apple-corefoundation-types) - [Using with Microsoft COM interfaces](#using-with-microsoft-com-interfaces) - [Using with Python objects](#using-with-python-objects) - [Using with non-reference counted types](#using-with-non-reference-counted-types) - [Atomic operations](#atomic-operations) - [Constexpr functionality](#constexpr-functionality) - [Reference](#reference) ## Why bother? There are multiple other intrusive smart pointers available including one from [Boost](https://www.boost.org/doc/libs/1_71_0/libs/smart_ptr/doc/html/smart_ptr.html#intrusive_ptr) and nowadays there is even a [proposal](http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0468r0.html) to add one to standard library C++ 2023, so why create another one? Unfortunately, as far as I can tell, all existing implementations, and that includes the standard library proposal at the time of this writing, suffer from numerous deficiencies that make them hard or annoying to use in real life code. The most serious problems addressed here are as follows ### Named conversions from raw pointers All other libraries offer a conversion in the form `smart_ptr(T * p);` In my opinions this is an extremely bad design. When looking at a call like `smart_ptr(foo())` can you quickly tell whether this adds a reference count or "attaches" the smart pointer to a raw one? That's right, you cannot! The answer depends on the smart pointer implementation or even on specific traits used. This makes it invisible and hard to predict at the **call site** and 100% guarantees that someone will make a wrong assumption. In my experience, almost all reference counting bugs happen on the **boundary** between C++ and C code where such conversions are abundant. Just like any form of dangerous cast this one has to be **explicit** in calling code (as an aside, ObjectiveC ARC did it right with their explicit and visible `__bridge` casts between raw and smart pointers). Note that having a boolean argument (like what Boost and many other implementations do) in constructor isn't a solution. Can you quickly tell what `smart_ptr(p, true)` does? Is it "true, add reference" or "true, copy it"? This library uses named functions to perform conversion. You see exactly what is being done at the call site. ### No ADL Many libraries use [ADL](https://en.wikipedia.org/wiki/Argument-dependent_name_lookup) to find "add reference" and "release reference" functions for the underlying type. That is they have expressions like `add_ref(p)` in their implementation, and expect a function named `add_ref` that accepts pointer to the underlying type is supposed to be found via ADL. This solution is great in many cases but it breaks when working with some C types like Apple's `CTypeRef`. This one is actually a typedef to `void *` so if you have an `add_ref` that accepts it, you have just made every unrelated `void *` reference counted (with very bad results if you accidentally put a wrong one into a smart pointer). A better approach to defining how reference counting is done is to pass a traits class to the smart pointer. (The standard library proposal gets this one right). This library uses traits ### Decent support for output parameters Often times you need to pass smart pointer as an output parameter to a C function that takes `T **` Many other smart pointers either - ignore this scenario, requiring you to introduce extra raw pointer and unsafe code, or - overload `operator&` which is a horrendously bad idea (it breaks lots of generic code which assumes that `&foo` gives an address of foo, not something else) The right solution is to have a proxy class convertible to `T **`. The standard library proposal addresses this problem via generic `out_ptr` that can work with any smart pointer. If done right, this might be the best solution but the relevant code is not yet widely available anywhere. This library currently uses an inner proxy class and a `get_output_param()` method. ### Support for `operator->*` This might seem to be a minor thing but is really annoying in generic code. For some reason no smart pointers bother to provide `operator->*` so that pointers to members could be accessed via the same syntax as for raw pointers. In non-generic code you can always work around it via `(*p).*whatever` but in generic code this is not an option. ### Atomic access Sometimes you need to operate on smart pointers atomically. To the best of my knowledge no library currently provides this functionality. This library provides a specialization of `std::atomic>` extending to it the normal `std::atomic` semantics. ### Trivial ABI When built with CLang compiler `intrusive_shared_ptr` is marked with [\[\[clang::trivial_abi\]\]](https://clang.llvm.org/docs/AttributeReference.html#trivial-abi) attribute. A good description of what this attribute does and why it is important for performance can be found [here](https://quuxplusone.github.io/blog/2018/05/02/trivial-abi-101/). Another take on the performance issue as a comment on standard library proposal can be found [here](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1351r0.html#params). [This page](doc/trivial_abi.md) contains details on why this is a good idea and why concerns about order of destruction do not really matter here. ### Correct implementation of a "reference counted base" class This is not directly a problem with smart pointers but with a base class often provided together with them to implement an intrusively counted class. Very often they contain subtle bugs (see ['A note on implementing reference counted objects'](doc/reference_counting.md) for more details). It is also tricky to create a base class that can work well for different requirements without compromising efficiency. ### Support for weak pointers Continuing on the base class theme, when doing intrusive reference counting, supporting (or not) weak pointers is the responsibility of the counted class. Supporting weak pointers also usually involves tradeoffs in terms of performance or memory consumption. This library allows to enable a decent implementation of weak pointers via policy based design. ## Integration ### CMake via FetchContent ```cmake include(FetchContent) ... FetchContent_Declare(isptr GIT_REPOSITORY https://github.com/gershnik/intrusive_shared_ptr.git GIT_TAG v1.4 #use the tag, branch or sha you need GIT_SHALLOW TRUE ) ... FetchContent_MakeAvailable(isptr) ... target_link_libraries(mytarget PRIVATE isptr::isptr ) ``` > ℹ️ _[What is FetchContent?](https://cmake.org/cmake/help/latest/module/FetchContent.html)_ ### Building and installing on your system You can also build and install this library on your system using CMake. 1. Download or clone this repository into SOME_PATH 2. On command line: ```bash cd SOME_PATH cmake -S . -B build cmake --build build #Optional #cmake --build build --target run-test #install to /usr/local sudo cmake --install build #or for a different prefix #cmake --install build --prefix /usr ``` Once the library has been installed it can be used int the following ways: #### Basic use Set the include directory to `/include` where `` is the install prefix from above. #### CMake package ```cmake find_package(isptr) target_link_libraries(mytarget PRIVATE isptr::isptr ) ``` #### Via `pkg-config` Add the output of `pkg-config --cflags isptr` to your compiler flags. Note that the default installation prefix `/usr/local` might not be in the list of places your `pkg-config` looks into. If so you might need to do: ```bash export PKG_CONFIG_PATH=/usr/local/share/pkgconfig ``` before running `pkg-config` ### Copying to your sources You can also simply download the headers of this repository from [Releases](https://github.com/gershnik/intrusive_shared_ptr/releases) page (named `intrusive_shared_ptr-X.Y.tar.gz`), unpack it somewhere in your source tree and add it to your include path. ## Usage All the types in this library are declared in `namespace isptr`. For brevity the namespace is omitted below. Add `isptr::` prefix to all the type or use `using` declaration in your own code. The header `` provides a template ```cpp template class intrusive_shared_ptr; ``` Where `T` is the type of the pointee and `Traits` a class that should provide 2 static functions that look like this ```cpp static void add_ref(SomeType * ptr) noexcept { //increment reference count. ptr is guaranteed to be non-nullptr } static void sub_ref(SomeType * ptr) noexcept { //decrement reference count. ptr is guaranteed to be non-nullptr } ``` `SomeType *` should be a pointer type to which `T *` is convertible to. It is possible to make `add_ref` and `sub_ref` templates, if desired, though this is usually not necessary. To create `intrusive_shared_ptr` from a raw `T *` there are 2 functions: ```cpp //pass the smart pointer in without changing the reference count template intrusive_shared_ptr intrusive_shared_ptr::noref(T * p) noexcept; //adopt the pointer and bump the reference count template intrusive_shared_ptr intrusive_shared_ptr::ref(T * p) noexcept ``` It is possible to use `intrusive_shared_ptr` directly but the name is long and ugly so a better approach is to wrap in a typedef and wrapper functions like this ```cpp struct my_type {}; struct my_intrusive_traits { static void add_ref(my_type * ptr) noexcept; //implement static void sub_ref(my_type * ptr) noexcept; //implement }; template using my_ptr = intrusive_shared_ptr; template my_ptr my_retain_func(T * ptr) { return my_ptr::ref(ptr); } template my_ptr my_attach_func(T * ptr) { return my_ptr::noref(ptr); } ``` The library provides such wrappers for some common scenarios. If you fully control the definition of `my_type` then it is possible to simplify things even further with header `refcnt_ptr.h`. It adapts `intrusive_shared_ptr` to traits exposed as inner type `refcnt_ptr_traits`. You can use it like this: ```cpp #include struct my_type { struct refcnt_ptr_traits { static void add_ref(my_type * ptr) noexcept; //implement static void sub_ref(my_type * ptr) noexcept; //implement }; }; //now you can use refcnt_ptr for the pointer type and refcnt_attach and refcnt_retain free functions e.g. //create from raw pointer (created with count 1) foo raw = new my_type(); refcnt_ptr p1 = refcnt_attach(raw); //create directly auto p1 = make_refcnt(); //assign from raw pointer bumping reference count refcnt_ptr p2; p2 = refcnt_retain(raw); ``` ### Using provided base classes To implement `my_type` above the library provides a base class you can inherit from which will do the right thing. ```cpp #include #include class foo : ref_counted { friend ref_counted; public: void method(); private: ~foo() noexcept = default; //prevent manual deletion }; //you can use auto to declare p1, p2 and p3. The full type is spelled out for //demonstration purposes only //attach from raw pointer (created with count 1) refcnt_ptr p1 = refcnt_attach(new foo()); //create directly refcnt_ptr p2 = make_refcnt(); //assign from raw pointer bumping reference count foo * raw = ... refcnt_ptr p3 = refcnt_retain(raw); ``` The type of the reference count is `int` by default. If you need to you can customize it. ```cpp class tiny : ref_counted //use char as count type { friend ref_counted; char c; }; static_assert(sizeof(tiny) == 2); ``` More details can be found in [this document](doc/ref_counted.md) ### Supporting weak pointers If you want to support weak pointers you need to tell `ref_counted` about it. Since weak pointers include overhead even if you never create one by default they are disabled. ```cpp #include #include class foo : weak_ref_counted //alias for ref_counted { void method(); }; refcnt_ptr p1 = refcnt_attach(new foo()); foo::weak_ptr w1 = p1->get_weak_ptr(); refcnt_ptr p2 = w1->lock(); ``` Note that you cannot customize the type of reference count if you support weak pointers - it will always be `intptr_t`. More details can be found in [this document](doc/ref_counted.md) ### Using with Apple CoreFoundation types ```cpp #include //Use auto in real code. Type is spelled out for clarity cf_ptr str = cf_attach(CFStringCreateWithCString(nullptr, "Hello", kCFStringEncodingUTF8)); std::cout << CFStringGetLength(str.get()); CFArrayRef raw = ...; //Use auto in real code. cf_ptr array = cf_retain(raw); ``` ### Using with Microsoft COM interfaces ```cpp #include com_shared_ptr pStream; CreateStreamOnHGlobal(nullptr, true, pStream.get_output_param()); pStream->Write(....); ``` ### Using with Python objects ```cpp #include auto str = py_attach(PyUnicode_FromString("Hello")); std::cout << PyUnicode_GetLength(str.get()); ``` ### Using with non-reference counted types On occasion when you have a code that uses intrusive reference counting a lot you might need to handle a type which you cannot modify and which is not by itself reference counted. In such situation you can use an adapter (if you prefer derivation) or wrapper (if you prefer containment) that makes it such Adapter: ```cpp #include using counted_map = ref_counted_adapter>; auto ptr = make_refcnt(); (*ptr)["abc"] = 7; std::cout << ptr->size(); using weakly_counted_map = weak_ref_counted_adapter>; auto ptr1 = make_refcnt(); (*ptr1)["abc"] = 7; std::cout << ptr1->size(); foo::weak_ptr w1 = p1->get_weak_ptr(); refcnt_ptr p2 = w1->lock(); ``` Wrapper: ```cpp #include using counted_map = ref_counted_wrapper>; auto ptr = make_refcnt(); ptr->wrapped()["abc"] = 7; std::cout << ptr->wrapped().size(); using weakly_counted_map = weak_ref_counted_wrapper>; auto ptr1 = make_refcnt(); ptr1->wrapped()["abc"] = 7; std::cout << ptr1->wrapped().size(); foo::weak_ptr w1 = p1->get_weak_ptr(); refcnt_ptr p2 = w1->lock(); ``` ### Atomic operations The library provides a partial specialization ```cpp template std::atomic>; ``` which exposes normal `std::atomic` functionality. For example: ```cpp using my_ptr = intrusive_shared_ptr; using my_atomic_ptr = std::atomic; my_ptr ptr = ...; my_atomic_ptr aptr = ptr; ptr = aptr.load(); //or ptr = aptr; aptr.store(ptr); //or aptr = ptr; my_ptr ptr1 = aptr.exchange(ptr); //etc. ``` ## Constexpr functionality When built with C++20 compiler `intrusive_shared_ptr` is fully constexpr capable. You can do things like ```cpp using my_ptr = intrusive_shared_ptr; constexpr my_ptr foo; ``` Due to non-default destructors this functionality is not available on C++17 ## Reference * [intrusive_shared_ptr.h](doc/intrusive_shared_ptr.md) * [refcnt_ptr.h](doc/refcnt_ptr.md) * [ref_counted.h](doc/ref_counted.md) intrusive_shared_ptr-1.4/VERSION000066400000000000000000000000041445713275300166760ustar00rootroot000000000000001.4 intrusive_shared_ptr-1.4/cmake/000077500000000000000000000000001445713275300167145ustar00rootroot00000000000000intrusive_shared_ptr-1.4/cmake/install.cmake000066400000000000000000000026741445713275300213750ustar00rootroot00000000000000# # Copyright 2004 Eugene Gershnik # # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file or at # https://github.com/gershnik/intrusive_shared_ptr/blob/master/LICENSE.txt # include(GNUInstallDirs) include(CMakePackageConfigHelpers) install(TARGETS isptr EXPORT isptr FILE_SET HEADERS DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(EXPORT isptr NAMESPACE isptr:: FILE isptr-exports.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/isptr) configure_package_config_file( ${CMAKE_CURRENT_LIST_DIR}/isptr-config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/isptr-config.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/isptr ) write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/isptr-config-version.cmake COMPATIBILITY SameMajorVersion ARCH_INDEPENDENT ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/isptr-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/isptr-config-version.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/isptr ) file(RELATIVE_PATH FROM_PCFILEDIR_TO_PREFIX ${CMAKE_INSTALL_FULL_DATAROOTDIR}/isptr ${CMAKE_INSTALL_PREFIX}) string(REGEX REPLACE "/+$" "" FROM_PCFILEDIR_TO_PREFIX "${FROM_PCFILEDIR_TO_PREFIX}") configure_file( ${CMAKE_CURRENT_LIST_DIR}/isptr.pc.in ${CMAKE_CURRENT_BINARY_DIR}/isptr.pc @ONLY ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/isptr.pc DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig )intrusive_shared_ptr-1.4/cmake/isptr-config.cmake.in000066400000000000000000000005671445713275300227370ustar00rootroot00000000000000# # Copyright 2004 Eugene Gershnik # # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file or at # https://github.com/gershnik/intrusive_shared_ptr/blob/master/LICENSE.txt # @PACKAGE_INIT@ # Avoid repeatedly including the targets if(NOT TARGET isptr::isptr) include(${CMAKE_CURRENT_LIST_DIR}/isptr-exports.cmake) endif() intrusive_shared_ptr-1.4/cmake/isptr.pc.in000066400000000000000000000004311445713275300210040ustar00rootroot00000000000000prefix=${pcfiledir}/@FROM_PCFILEDIR_TO_PREFIX@ exec_prefix=${prefix} includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ Name: isptr Description: Yet another implementation of an intrusive reference counting for C++ Version: @PROJECT_VERSION@ Requires: Cflags: -I${includedir} Libs: intrusive_shared_ptr-1.4/doc/000077500000000000000000000000001445713275300164015ustar00rootroot00000000000000intrusive_shared_ptr-1.4/doc/intrusive_shared_ptr.md000066400000000000000000000151611445713275300231720ustar00rootroot00000000000000# Header `` ## Class isptr::intrusive_shared_ptr ```cpp template class ISPTR_TRIVIAL_ABI intrusive_shared_ptr; ``` Macro `ISPTR_TRIVIAL_ABI` expands to `[[clang::trivial_abi]]` when compiled under clang. ### Traits requirements Traits must expose two static methods with the following signatures: ```cpp add_ref(T *) noexcept sub_ref(T *) noexcept ``` Return value of either method is ignored. The argument is never `nullptr`. ### Namespace Types - `template constexpr bool are_intrusive_shared_traits` checks whether given Traits satisfy the traits requirements above for the given type T. - `template using is_intrusive_shared_ptr = ...` type trait that is `std::true_type` or `std::false_type` depending on whether T is a valid `intrusive_shared_ptr` type. - `template bool constexpr is_intrusive_shared_ptr_v = is_intrusive_shared_ptr::value;` ### Nested Types ```cpp using pointer = T *; using element_type = T; using traits_type = Traits; ``` ### Operations #### Construction/assignment/destruction - Default constructor. `noexcept`, produces a null pointer - Constructor from `nullptr`. Same as above - Copy constructor. `noexcept`. Accepts: - Objects of the same type and traits - Objects that point to `Y` such as `Y *` is convertible to `T *` using the same or different traits. - Move constructor. `noexcept`. Accepts: - Objects of the same type and traits. No changes to reference count are performed. - Objects that point to `Y` such as `Y *` is convertible to `T *` using the same or different traits. If the source and destination traits are the same no changes to source reference count are performed. - Copy assignment. `noexcept`. Accepts: - Objects of the same type and traits - Objects that point to `Y` such as `Y *` is convertible to `T *` using the same or different traits. - Move assignment. `noexcept`. Accepts: - Objects of the same type and traits - Objects that point to `Y` such as `Y *` is convertible to `T *` using the same or different traits. If the source and destination traits are the same no changes to source reference count are performed. - Destructor. `noexcept`. Non-virtual. - `static intrusive_shared_ptr noref() noexcept` Creates a smart pointer from a raw pointer without modifying the reference count. - `static intrusive_shared_ptr ref() noexcept` Creates a smart pointer from a raw pointer and increments reference count. #### Instance methods - `T * get() const noexcept` Returns the stored pointer. Reference count is not modified. - `T * operator->() const noexcept` Returns the stored pointer. Reference count is not modified. - `T & operator*() const noexcept` Returns the reference to pointee. Undefined if stored pointer is nullptr - `template M & operator->*(M T::*memptr) const noexcept` Returns the result of `'stored pointer'->*memptr` This allows access via pointee's pointer to members - `explicit operator bool() const noexcept` true if the pointer is non null - `output_param get_output_param() noexcept` returns a temporary object that exposes `operator T**() && noexcept`. This can be passed as an output parameter to C functions that return a reference counted pointer. The destructor of the temporary will fill this pointer with the returned result. - `T * release() noexcept` Releases the stored pointer. The object is set to null and calling code assumes ownership of the pointer. No adjustment is made to the reference count. - `void reset() noexcept` Clears the stored pointer. The reference count is decremented. - `void swap(intrusive_shared_ptr & other) noexcept` and
`friend void swap(intrusive_shared_ptr & lhs, intrusive_shared_ptr & rhs) noexcept` (ADL only) Perform swap of pointers of the same type #### Namespace methods - Equality: `==`, `!=`. All `noexcept`. Work between `intrusive_shared_ptr` of compatible types and any traits as well as raw pointers and nullptr. - Comparisons: `<`, `<=`, `>=`, `>`. All `noexcept`. On C++20 these are replaced by `<=>`. Work between `intrusive_shared_ptr` of compatible types and any traits as well as raw pointers. - Namespace method: `template friend std::basic_ostream & operator<<(std::basic_ostream & str, const intrusive_shared_ptr & ptr)` (ADL only) outputs the stored pointer value to a stream - `Dest intrusive_const_cast(intrusive_shared_ptr src) noexcept`. Performs an equivalent of `const_cast` on passed argument. The destination type needs to be a valid `intrusive_shared_ptr` type whose underlying type is convertible from source's via `const_cast`. - `Dest intrusive_static_cast(intrusive_shared_ptr src) noexcept`. Performs an equivalent of `static_cast` on passed argument. The destination type needs to be a valid `intrusive_shared_ptr` type whose underlying type is convertible from source's via `static_cast`. - `Dest intrusive_dynamic_cast(intrusive_shared_ptr src) noexcept`. Performs an equivalent of `dynamic_cast` on passed argument. The destination type needs to be a valid `intrusive_shared_ptr` type whose underlying type is convertible from source's via `dynamic_cast`. Returns a null result if underlying `dynamic_cast` fails. ## Class std::atomic Provides the standard set of `std::atomic` functionality: - `using value_type = isptr::intrusive_shared_ptr` - `static constexpr bool is_always_lock_free` - `constexpr atomic() noexcept`. Initializes with null pointer. - `atomic(value_type desired) noexcept` - `atomic(const atomic&) = delete` Deleted! - `void operator=(const atomic&) = delete` Deleted! - `~atomic() noexcept` - `void operator=(value_type desired) noexcept` - `operator value_type() const noexcept` - `value_type load(memory_order order = memory_order_seq_cst) const noexcept` - `void store(value_type desired, memory_order order = memory_order_seq_cst) noexcept` - `value_type exchange(value_type desired, memory_order order = memory_order_seq_cst) noexcept` - `bool compare_exchange_strong(value_type & expected, value_type desired, memory_order success, memory_order failure) noexcept` - `bool compare_exchange_strong(value_type & expected, value_type desired, memory_order order = memory_order_seq_cst) noexcept` - `bool compare_exchange_weak(value_type & expected, value_type desired, memory_order success, memory_order failure) noexcept` - `bool compare_exchange_weak(value_type & expected, value_type desired, memory_order order = memory_order_seq_cst) noexcept` - `bool is_lock_free() const noexcept` intrusive_shared_ptr-1.4/doc/ref_counted.md000066400000000000000000000400471445713275300212250ustar00rootroot00000000000000# Header `` - [Class isptr::ref_counted](#class-isptrref_counted) - [Customization](#customization) - [Usage](#usage) - [Rules for constructors and destructors](#rules-for-constructors-and-destructors) - [Limitations](#limitations) - [Class isptr::weak_reference](#class-isptrweak_reference) - [Customization](#customization) - [Usage](#usage) - [Types](#types) - [Methods](#methods) - [Class isptr::ref_counted_adapter](#class-isptrref_counted_adapter) - [Methods](#methods) - [Class isptr::ref_counted_wrapper](#class-isptrref_counted_wrapper) - [Members](#members) - [Methods](#methods) ## Class isptr::ref_counted ```cpp template> class ref_counted; template using weak_ref_counted = ref_counted; ``` `ref_counted` is meant to be used as base class for any class you want to make reference counted. It is supposed to be used in conjunction with [`refcnt_ptr`](refcnt_ptr.md) specialization of `intrusive_shared_ptr` but, of course, can also be used via manual reference counting calls or other smart pointers if desired. It uses CRTP to access the derived class, avoiding the need for and overhead of virtual functions. Thus, the first template parameter: `Derived` must be the name of the derived class. Other template parameters have reasonable defaults and allow customization of reference counting functionality. These are described below. ### Customization `ref_counted` can be customized in the following ways: * Support for weak references. Default: no. Weak reference support adds some performance overhead even for object which never have weak reference taken so it is not enabled by default. In addition weak reference support requires the reference count type to be `intptr_t` which might be wasteful for many applications where `int` is sufficient. * Single-threaded mode. Default: no. In single threaded mode reference count manipulations are not thread safe so you cannot share `refcnt_ptr`s across threads. This results in increased performance though, and might be worthwhile in some scenarios. * Type of the reference count. Can only be customized if weak references are not supported. Default: `int`. If weak references are supported the count type is always `intptr_t`. You can reduce the size of derived classes by using a smaller type if it can fit the largest expected count. This type must be a signed integral type. * Many methods of `ref_counted` are accessed via CRTP calls to `Dervied`. This allows "overriding" them in your derived class to modify or augment their functionality. * In particular, `destroy()` member function is called to actually destroy the instance when the reference count drops to 0. The base `ref_counted` implementation invokes `delete` on `Derived` pointer. Overriding this function in derived class allows you to handle objects allocated in a different way. The template parameter `Flags` is a set of flags of type `ref_counted_flags`. These can be bitwise OR-ed to combine. By default no flags are set. Currently only two flags are defined: * `ref_counted_flags::provide_weak_references` - enables weak references * `ref_counted_flags::single_threaded` - enables single threaded mode More flags may be added in the future. The template parameter CountType can be specified to indicate the type of reference count (if `ref_counted_flags::provide_weak_references` is not set). It must be a signed integral type. For convenience the library provides the following typedefs: - `template weak_ref_counted` is a `ref_counted` with `ref_counted_flags::provide_weak_references` flag. - `template ref_counted_st` is `ref_counted` with `ref_counted_flags::single_threaed` flag. - `template weak_ref_counted_st` is a `ref_counted` with both `provide_weak_references` and `single_threaed` flags set. ### Usage You use `ref_counted` by deriving from it and making it a friend. You probably want to make your destructor private or protected (if your class is itself a base). Reference counted object should only be destroyed via reference counting, not via direct `delete` calls. Example: ```cpp class foo : public ref_counted { friend ref_counted; private: ~foo() noexcept = default; }; ``` ### Rules for constructors and destructors In your class derived from `ref_counted` you must follow these rules: - **Never increment reference count (by creating `refcnt_ptr` from `this` for example or any other means) in destructor.** Doing so will trigger an assert in debug builds and cause undefined behavior in release ones. - It is generally ok to create weak references to `this` in destructor. - Never give out `this` (by creating `refcnt_ptr` from `this` for example or any other means) or a weak reference to `this` in a constructor **if any subsequent code in constructor can exit via an exception**. If such exception is thrown your object will fail to be created leaving the outside code holding an invalid pointer. This is a general C++ rule. Usage of reference counting does not negate it. - It is ok and supported to do reference counting in constructor **if any subsequent code in constructor does not exit via exception** - Your destructor must not throw exceptions. Another general C++ rule made even more relevant here, since the reference counting machinery in `ref_counted` does not expect exception to be thrown and will break in this case. - If your class *is* a base then the destructor needs to be virtual - `ref_counted` will invoke destructor of `Derived` template argument. (If you propagate most derived class as `Derived` argument then this obviously does not apply). ### Limitations When not using weak references the implementation of `ref_counted` is standard compliant and portable (modulo bugs and compiler deficiencies of course). The only practical limitation is that total number of outstanding references to an object must be between 0 and `std::numeric_limits::max()`. When using weak references `ref_counted` relies on multiplexing pointers and reference count in the same memory location. This mechanism should work provided: * The platform uses 2s complement arithmetic * `alignof(intptr_t) > 1` * When cast to `uintptr_t` the value of a pointer to an object with alignment bigger than 1 has the lowest bit 0 (e.g. is odd). All these conditions seem to be true for all platforms these days but who knows... ``` ### Types `ref_counted` declares the following public types: * `ref_counted_base` - a synonym for itself. This allows to refer to the type from its derived types without caring about specific template arguments like `derived::ref_counted_base` * `weak_value_type` - if weak references are enabled this is the type of weak reference object this class exposes. Otherwise `void` * `weak_ptr` - if weak references are enabled a synonym for `refcnt_ptr`. Otherwise `void` * `const_weak_ptr` - if weak references are enabled a synonym for `refcnt_ptr`. Otherwise `void`. ### Constants * `static constexpr bool provides_weak_references` - `true` if this class provides weak references * `static constexpr bool single_threaded` - `true` if this class is single threaded ### Methods Unless specified otherwise all methods of this class are `noexcept`. * Copy and move constructors are **deleted**. This is a base class and slicing is meaningless. * Copy and move assignment operators are similarly **deleted** * Protected default constructor. Reference count is set to 1 in it. * Protected destructor. * Protected `void destroy() const noexcept`. Called when reference count drops to 0 to destroy the object. The default implementation calls `delete` on derived class pointer. Can be overridden in a derived class. * Public `void add_ref() const noexcept`. Increments reference count. Can be overridden in a derived class. * Public `void sub_ref() const noexcept`. Decrements reference count and destroys the object if it reaches 0. Can be overridden in a derived class. * If the class supports weak references then two additional public methods are available
`weak_ptr get_weak_ptr()` and
`const_weak_ptr get_weak_ptr() const`.
These methods are **not** `noexcept`. Weak reference "control block" is created lazily when a weak reference is requested for the first time. If memory allocation or customized weak reference class constructor throws these methods will throw. Subsequent calls will be `noexcept`. * Protected `const weak_value_type * get_weak_value() const`. Only meaningful if the class supports weak references. This is the actual method that retrieves raw pointer to weak reference used to implement `get_weak_ptr`. Not `nonexcept`. Can be overridden in a derived class. * Protected `weak_value_type * make_weak_reference(intptr_t count) const`. Only meaningful if the class supports weak references. This method is called to create weak reference (control block) when one is needed for the first time. The returned pointer has its own reference count already incremented (as appropriate for a method returning raw pointer). Can be overridden in a derived class. ### Customizing weak reference type It is possible to customize weak reference type used by your class by "overriding" `make_weak_reference`. `ref_counted` looks via CRTP for a function with the following signature in your derived class: ```cpp some_type * make_weak_reference(intptr_t count) const ``` where some_type must be a class derived from `isptr::weak_reference` (see below). If such function exists it will be called instead of the one provided by `ref_counted` itself and the type it returns will be the weak reference type. ## Class isptr::weak_reference ```cpp template class weak_reference; ``` `weak_reference` represents a weak reference to a class derived from `ref_counted`. It usually is used as-is but can be used as a base class for customized weak references. `Owner` template parameter is the actual class the weak reference is a reference to. Internally `weak_reference` is a "control block" for a `ref_counted`. It is reference counted itself and also manages the count for the referenced object. `weak_reference` objects are created on-demand when a first weak reference is requested from `ref_counted` ### Customization You can derive your own class from `weak_reference` and use it in conjunction with your class derived from `ref_counted`. See [Customizing weak reference type](#customizing-weak-reference-type) for details. Many methods of `weak_reference` are accessed via CRTP calls to the derived class. This allows "overriding" them in your derived class to modify or augment their functionality. In particular, `destroy()` member function is called to actually destroy the instance when the reference count drops to 0. The base `weak_reference` implementation invokes `delete` on your class pointer. Overriding this function in derived class allows you to handle objects allocated in a different way. ### Usage Usually there is no need to explicitly mention `weak_reference` in your code. The type is accessible as `weak_value_type` typedef of your `ref_counted`-derived class. If you use `refcnt_ptr` you can convert between weak and strong pointers in the following fashion: ```cpp auto original = refcnt_attach(new your_object()); auto weak = weak_cast(original); //or your_object::weak_ptr weak = weak_cast(original); auto strong = strong_cast(weak); assert(strong == original || strong == nullptr); ``` You can also use member functions instead of `weak_cast`/`strong_cast` ```cpp auto original = refcnt_attach(new your_object()); auto weak = original->get_weak_ptr(); //or your_object::weak_ptr weak = original->get_weak_ptr(); auto strong = weak->lock(); assert(strong == original || strong == nullptr); ``` Note that `const`-ness propagates between strong and weak pointers. A strong pointer to const yields weak pointer to const and vice versa. ### Types `weak_reference` declares the following public types * `strong_value_type` - a synonym for `Owner` * `strong_ptr` - a synonym for `refcnt_ptr` * `const_strong_ptr` - a synonym for `refcnt_ptr` ### Methods Unless specified otherwise all methods of this class are `noexcept`. * Copy and move constructors are **deleted**. Copying of weak references is meaningless. * Copy and move assignment operators are similarly **deleted** * Protected constructor `constexpr weak_reference(intptr_t initial_strong, Owner * owner) noexcept` The `initial_strong` parameter is the initial value for the `Owner`s reference count. Since `weak_reference` objects are created on-demand the referent's count can be any value that was reached prior to `weak_reference` creation. The `owner` is a raw pointer to the `Owner` object. * Protected destructor. * Protected `void destroy() const noexcept`. Called when object's own reference count drops to 0 to destroy the object. The default implementation calls `delete` on derived class pointer. Can be overridden in a derived class. * Public `void add_ref() const noexcept`. Increments object's own reference count. Can be overridden in a derived class. * Public `void sub_ref() const noexcept`. Decrements object's own reference count and destroys the object if it reaches 0. Note that the `Owner` always holds a reference to its `weak_reference` (control block) so it will only be destroyed after the `Owner` object. Can be overridden in a derived class. * Public
`const_strong_ptr lock() const noexcept` and
`strong_ptr lock() noexcept`
Obtain a strong reference to `Owner`. The return value is a `null` smart pointer if the owner no longer exists. * Protected
`void add_owner_ref() noexcept` and
`void sub_owner_ref() noexcept`
These manage reference count of the `Owner`. Can be overridden in a derived class. * Protected `strong_value_type * lock_owner() const noexcept` This method does the actual locking and returns a pointer to owner (with reference count incremented) or `null`. Can be overridden in a derived class. * Protected `void on_owner_destruction() const noexcept`. This method does nothing. It can be re-declared in a derived class. It is invoked *after* the `Owner` object has been destroyed - that is when the number of strong references to it becomes 0. This provides a customization point for derived classes in case they need to clean up some data associated with the owner. Note that the method is called after owner's destruction so you cannot access or resurrect owner from it. ## Class isptr::ref_counted_adapter ```cpp template> class ref_counted_adapter; template using weak_ref_counted_adapter = ref_counted_adapter; ``` This class publicly derives from a non-reference counted class `T` and a `ref_counted`. The rest template parameters are forwarded to `ref_counted`. ### Methods * Public constructor. Perfectly forwards to constructor of `T`. `noexcept` if `T`'s constructor is. * Protected destructor. ## Class isptr::ref_counted_wrapper ```cpp template> class ref_counted_wrapper; template using weak_ref_counted_wrapper = ref_counted_wrapper; ``` This class stores (wraps) a member of class `T` and derives form `ref_counted`. The rest template parameters are forwarded to `ref_counted`. ### Members * Public `T wrapped`. The wrapped instance of `T`. ### Methods * Public constructor. Perfectly forwards to constructor of `wrapped`. `noexcept` if `T`'s constructor is. * Protected destructor. intrusive_shared_ptr-1.4/doc/refcnt_ptr.md000066400000000000000000000042461445713275300210770ustar00rootroot00000000000000# Header `` - [Class isptr::refcnt_ptr](#class-isptrrefcnt_ptr) - [Namespace methods](#namespace-methods) ## Class isptr::refcnt_ptr ```cpp template using refcnt_ptr = intrusive_shared_ptr; ``` `refcnt_ptr` is a specialization of [`intrusive_shared_ptr`](intrusive_shared_ptr.md) for a common case where you fully control the pointee's class and can provide traits as a nested type named `refcnt_ptr_traits`. A [`ref_counted`](ref_counted.md) base class provides such an implementation so you can easily use `ref_counted` derived classes with `refcnt_ptr`. ## Namespace methods * `template constexpr refcnt_ptr refcnt_retain(T * ptr) noexcept`. Creates `refcnt_ptr` from a raw pointer and increments the reference count. * `template constexpr refcnt_ptr refcnt_attach(T * ptr) noexcept`. Creates `refcnt_ptr` from a raw pointer without incrementing the reference count. * `template refcnt_ptr make_refcnt(Args &&... args)`. A convenience function that creates an instance of `T` via `new` and forwards the arguments to its constructor. Equivalent to `refcnt_attach(new T(args))`. * `template refcnt_ptr weak_cast(const refcnt_ptr & src)` and
`template refcnt_ptr weak_cast(const refcnt_ptr & src)`.
If `T` provides a type called `weak_value_type` and a method `get_weak_ptr()` it is assumed to support weak references. These functions provide a convenient "cast" conversion from a strong to a weak pointer wrapping the call to `get_weak_ptr()`. * `template refcnt_ptr strong_cast(const refcnt_ptr & src) noexcept` and
`template refcnt_ptr strong_cast(const refcnt_ptr & src) noexcept`
If `T` provides a type called `strong_value_type` and a method `lock()` it is assumed to be a weak reference. These functions provide a convenient "cast" conversion from a weak to a strong pointer wrapping the call to `lock()`. intrusive_shared_ptr-1.4/doc/reference_counting.md000066400000000000000000000200061445713275300225650ustar00rootroot00000000000000# A note on implementing reference counted objects Making an object reference counted can be tricky. There are some fundamental problems, tricky situations and some design choices that need to be taken care of. The basics are pretty well known. People usually create a class along the following lines ```cpp class foo { public: void add_ref() noexcept { ++m_count; } void sub_ref() noexcept { if (--m_count == 0) delete this; } private: atomic m_count; }; ``` So far so good but why add_ref and sub_ref are not const? A reference count is really not a part of the object state. Consider that you might want to have `smart_ptr` in your code and such smart pointer will have to call these methods on a const object. So here is the first revision ```cpp class foo { public: void add_ref() const noexcept { ++m_count; } void sub_ref() const noexcept { if (--m_count == 0) delete this; } private: mutable atomic m_count; }; ``` Now the first fundamental problem. What should the reference count be initialized to in object constructor: 1 or 0? People who use flawed smart pointers with a simple constructor `smart_ptr(T * p)` that always bumps the count tend to like 0. This way the object gets the desired one as soon as you stuff it into a smart_ptr. Unfortunately, this turns out to be a bad idea from two different angles. One is performance. Having to always increment count right after construction is a performance penalty. A small penalty but penalty nonetheless. Starting from 1 avoids it. The second problem is that while in the body of the constructor you have an object with a 0 reference count. Now, the body of the constructor is a tricky place. Consider what happens if you create a smart pointer out of `this` within the constructor. This can happen if you call a function that expects a smart pointer parameter (e.g. `register_callback(smart_ptr(this))`) and the function does not store the pointer. In this case the count is bumped to 1 when the pointer is created but then goes to 0 when it is destroyed. And this invokes the destructor from inside the constructor. Bang! You've got a nasty piece of undefined behavior happening. But wait, is creating a smart pointer from `this` in constructor a good idea? Suppose you have the following situation: ```cpp foo::foo(): base_class(...) { register_callback(smart_ptr(this)); function_that_may_throw_exception(); //other initialization } ``` and `function_that_may_throw_exception` actually throws. In this case external code possesses a pointer to an object that failed to construct. Its base classes are destroyed and it is dead from C++ point of view. Nevertheless, the external code will have a live pointer to it and, worse, invoke destructor when reference count goes to 0. Note that there is nothing special about this situation - the same bad effect can be achieved with raw pointers if we give `this` away before an exception is thrown. However, using smart pointers can provide false sense of security here. The general rule for any C++ class is: Do not give `this` away to be stored for later use if the constructor can throw after you do so. Assuming you follow this rule giving this away can be legal and legitimate - but only if you start counting from 1. To conclude, starting from 1 is faster and allows some techniques that starting from 0 doesn't. With this is mind here is a revised foo ```cpp class foo { public: void add_ref() const noexcept { ++m_count; } void sub_ref() const noexcept { if (--m_count == 0) delete this; } private: mutable atomic m_count = 1; }; ``` Note that having this convention requires you to use ```cpp intrusive_shared_ptr p = intrusive_shared_ptr::noref(new foo); ``` to **attach** newly created object rather than bump its count. But we are not off the hook yet. Now consider what happens in destructor. When we enter it, the reference count is already 0 but what if call some function there too and pass it a smart pointer created from `this`? The count will be bumped again then go to 0 and you will have `delete this` running the second time. Perhaps, the solution is again to make the count 1? If this is the case for constructor perhaps it makes sense for destructor too? This would also be a bad idea. First of all bumping the count for all objects even if they don't care, is, again, a performance penalty. Second consider what does it mean to add a reference to an object that is undergoing destruction. Conceivably the code that added the reference can then store the pointer expecting the object to be safely alive. Then it can try to use it later but the object has been already destroyed. Once you think more about it you realize that this is the famous "finalize resurrection" problem (https://en.wikipedia.org/wiki/Object_resurrection) in another form. Unfortunately, or rather fortunately, C++ doesn't allow you to "abandon" or "postpone" destruction. Once the destructor has been entered the object will become dead. It is possible to re-invent the whole notion of finalizer (a separate function called before the destructor) and resurrection for reference counting but doing so is a lot of work and doing it correctly is very tricky. The experience with finalizers in other languages is not encouraging. Instead, consider why would you ever need to give out a reference to an object from a destructor. Constructor case is obvious: you might want to register for some callback or notification from somewhere else. You might think that in a destructor it would be the opposite: deregister the object, but this is wrong. The very fact that you *are* in a destructor means that no place else in the code has any knowledge of the object. There is nowhere to deregister or disconnect from. (It is certainly possible that other places have *weak* references to the object, but those do not concern us here - there is no issue in creating a *weak* pointer to `this` in destructor and deregistering *that*.) With this in mind, the only sane approach seems to be to disallow adding a reference to an object that is being destroyed, period. Any attempt to do so likely indicates a design or implementation mistake. You could detect this situation (bumping the count from 0) and call `terminate()` or, do it in debug mode only via `assert`. ```cpp class foo { public: void add_ref() const noexcept { [[maybe_unused]] auto value = ++m_count; assert(value > 1); } void sub_ref() const noexcept { if (--m_count == 0) delete this; } private: mutable atomic m_count = 1; }; ``` Now this is the bare minimum for a reference counted class but there are a few more things to take care of. First the destructor declaration. If this class is going to be a base class for a class hierarchy then the destructor must be virtual and protected (to avoid manual deletion not done via reference counting). ```cpp class foo { //... as above protected: virtual ~foo() noexcept = default; }; ``` (If you use CRTP you can avoid `virtual` here - this is beyond the scope of this article) Alternatively if the class is standalone it should be final with a private non-virtual destructor. ```cpp class foo final { //... as above private: ~foo() noexcept = default; }; ``` The reference counting itself can also be made more efficient. Operators `++` and `--` perform a full fence whereas we don't really need it. A better approach is ```cpp class foo { public: void add_ref() const noexcept { [[maybe_unused]] auto old_value = m_count.fetch_add(1, std::memory_order_relaxed); assert(old_value > 0); } void sub_ref() const noexcept { if (m_count.fetch_sub(1, std::memory_order_release) == 1) { std::atomic_thread_fence(std::memory_order_acquire); delete this; } } protected: virtual ~foo() noexcept = default; private: mutable atomic m_count = 1; }; ``` intrusive_shared_ptr-1.4/doc/trivial_abi.md000066400000000000000000000065271445713275300212220ustar00rootroot00000000000000When built with CLang compiler `intrusive_shared_ptr` is marked with [\[\[clang::trivial_abi\]\]](https://clang.llvm.org/docs/AttributeReference.html#trivial-abi) attribute. A good description of what this attribute does and why it is important for performance can be found [here](https://quuxplusone.github.io/blog/2018/05/02/trivial-abi-101/). Another take on the performance issue as a comment on standard library proposal can be found [here](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1351r0.html#params). However, using trivial ABI with a type that has non trivial destructor (and of course a smart pointer destructor is non-trivial - it has to decrement reference count!) immediately raises serious objections. Such use makes the destructor run *out of order* inside the called function whereas destructors of other parameters run after the function exits. While on theoretical level this is indeed wrong, the interesting question is whether it can ever matter in practice. An important observation is that the fact that destructor runs out of order only really matters in one circumstance - when the smart pointer object holds the last reference to pointee and its destructor causes the pointee to be destroyed. This out of order destruction can be, in principle, observed by outside code. Or can it? If the smart pointer passed as a function argument holds the last reference then, for some other code to observe the pointee demise, it would need to refer to it via an unsafe raw pointer and only while the smart pointer is alive. Something like this ```cpp T * raw = ...; struct nasty { nasty(T * p): _p(p) {} ~nasty() { //use p-> here } }; foo(smart_ptr(intrusive_noref(raw)), nasty(raw)); ``` Assuming **left to right order of evaluation** for function arguments, this indeed will do bad things in `nasty` destructor. This is indeed what happens with clang on x64 MacOS. But wait a minute, even though there is a [proposal](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0145r3.pdf) to fix the order of function arguments evaluation this is not yet part of the standard and you cannot rely on a specific order here. The code is actually broken even without trivial ABI. And indeed it is on GCC 7.4 on x64 Ubuntu. Even if the order of evaluation becomes fixed in some future C++ standard, I am sure you will agree that the code like above is, well, nasty and shouldn't exist. What about the other non-destructive cases where only reference count is modified without object destruction? In principle, this is also problematic as the value of the count can be observed. However, this is even less of an issue in practice. In all intrusive reference counted systems the specific value of reference count is meaningless, can change at any point from any thread and, in general, developers are always cautioned from even looking at it for non debugging purposes. Chances of having code somewhere that would do something wrong if the count is decremented inside, rather than outside of a function are exactly 0. So should performance of every smart pointer argument passing be penalized to handle some esoteric condition that never happens in real code? My answer is no, and this is why this library uses the trivial ABI when available. If an when the standard C++ provides a better solution for wrapper classes this decision can be revisited. intrusive_shared_ptr-1.4/inc/000077500000000000000000000000001445713275300164055ustar00rootroot00000000000000intrusive_shared_ptr-1.4/inc/intrusive_shared_ptr/000077500000000000000000000000001445713275300226505ustar00rootroot00000000000000intrusive_shared_ptr-1.4/inc/intrusive_shared_ptr/apple_cf_ptr.h000066400000000000000000000017401445713275300254610ustar00rootroot00000000000000/* Copyright 2004 Eugene Gershnik Use of this source code is governed by a BSD-style license that can be found in the LICENSE file or at https://github.com/gershnik/intrusive_shared_ptr/blob/master/LICENSE.txt */ #ifndef HEADER_APPLE_CF_PTR_H_INCLUDED #define HEADER_APPLE_CF_PTR_H_INCLUDED #if (defined(__APPLE__) && defined(__MACH__)) #include #include namespace isptr { struct cf_traits { static void add_ref(CFTypeRef ptr) noexcept { CFRetain(ptr); } static void sub_ref(CFTypeRef ptr) noexcept { CFRelease(ptr); } }; template using cf_ptr = intrusive_shared_ptr, cf_traits>; template cf_ptr cf_retain(T * ptr) { return cf_ptr::ref(ptr); } template cf_ptr cf_attach(T * ptr) { return cf_ptr::noref(ptr); } } #endif #endif intrusive_shared_ptr-1.4/inc/intrusive_shared_ptr/com_ptr.h000066400000000000000000000020761445713275300244710ustar00rootroot00000000000000/* Copyright 2004 Eugene Gershnik Use of this source code is governed by a BSD-style license that can be found in the LICENSE file or at https://github.com/gershnik/intrusive_shared_ptr/blob/master/LICENSE.txt */ #ifndef HEADER_COM_PTR_H_INCLUDED #define HEADER_COM_PTR_H_INCLUDED #if defined(_WIN32) #include #include namespace isptr { struct com_traits { template static std::enable_if_t, void> add_ref(T * ptr) noexcept { ptr->AddRef(); } template static std::enable_if_t, void> sub_ref(T * ptr) noexcept { ptr->Release(); } }; template using com_shared_ptr = intrusive_shared_ptr; template com_shared_ptr com_retain(T * ptr) { return com_shared_ptr::ref(ptr); } template com_shared_ptr com_attach(T * ptr) { return com_shared_ptr::noref(ptr); } } #endif #endif intrusive_shared_ptr-1.4/inc/intrusive_shared_ptr/common.h000066400000000000000000000021231445713275300243070ustar00rootroot00000000000000/* Copyright 2023 Eugene Gershnik Use of this source code is governed by a BSD-style license that can be found in the LICENSE file or at https://github.com/gershnik/intrusive_shared_ptr/blob/master/LICENSE.txt */ #ifndef HEADER_ISPTR_COMMON_H_INCLUDED #define HEADER_ISPTR_COMMON_H_INCLUDED #if __cpp_constexpr >= 201907L #define ISPTR_CONSTEXPR_SINCE_CPP20 constexpr #else #define ISPTR_CONSTEXPR_SINCE_CPP20 #endif #if __cpp_impl_three_way_comparison >= 201907L #include #define ISPTR_USE_SPACESHIP_OPERATOR 1 #else #define ISPTR_USE_SPACESHIP_OPERATOR 0 #endif #ifdef _MSC_VER #define ISPTR_ALWAYS_INLINE __forceinline #define ISPTR_TRIVIAL_ABI #elif defined(__clang__) #define ISPTR_ALWAYS_INLINE [[gnu::always_inline]] inline #define ISPTR_TRIVIAL_ABI [[clang::trivial_abi]] #elif defined (__GNUC__) #define ISPTR_ALWAYS_INLINE [[gnu::always_inline]] inline #define ISPTR_TRIVIAL_ABI #endif namespace isptr::internal { template static constexpr bool dependent_bool = Val; } #endif intrusive_shared_ptr-1.4/inc/intrusive_shared_ptr/intrusive_shared_ptr.h000066400000000000000000000445561445713275300273020ustar00rootroot00000000000000/* Copyright 2004 Eugene Gershnik Use of this source code is governed by a BSD-style license that can be found in the LICENSE file or at https://github.com/gershnik/intrusive_shared_ptr/blob/master/LICENSE.txt */ #ifndef HEADER_INTRUSIVE_SHARED_PTR_H_INCLUDED #define HEADER_INTRUSIVE_SHARED_PTR_H_INCLUDED #include #include #include #include namespace isptr { namespace internal { struct add_ref_detector { template auto operator()(Traits * , T * p) noexcept(noexcept(Traits::add_ref(p))) -> decltype(Traits::add_ref(p)); }; struct sub_ref_detector { template auto operator()(Traits *, T * p) noexcept(noexcept(Traits::sub_ref(p))) -> decltype(Traits::sub_ref(p)); }; } template constexpr bool are_intrusive_shared_traits = std::is_nothrow_invocable_v && std::is_nothrow_invocable_v; template class ISPTR_TRIVIAL_ABI intrusive_shared_ptr { static_assert(are_intrusive_shared_traits, "Invalid Traits for type T"); friend std::atomic>; public: using pointer = T *; using element_type = T; using traits_type = Traits; private: class output_param { friend class intrusive_shared_ptr; public: ISPTR_CONSTEXPR_SINCE_CPP20 ~output_param() noexcept { if (m_p != m_owner->get()) *m_owner = intrusive_shared_ptr::noref(m_p); } constexpr operator T**() && noexcept { return &m_p; } private: constexpr output_param(intrusive_shared_ptr & owner, T * p) noexcept : m_owner(&owner), m_p(p) {} constexpr output_param(output_param && src) noexcept = default; output_param(const output_param &) = delete; void operator=(const output_param &) = delete; void operator=(output_param &&) = delete; private: intrusive_shared_ptr * m_owner; T * m_p; }; public: static constexpr intrusive_shared_ptr noref(T * p) noexcept { return intrusive_shared_ptr(p); } static constexpr intrusive_shared_ptr ref(T * p) noexcept { intrusive_shared_ptr::do_add_ref(p); return intrusive_shared_ptr(p); } constexpr intrusive_shared_ptr() noexcept : m_p(nullptr) {} constexpr intrusive_shared_ptr(std::nullptr_t) noexcept : m_p(nullptr) {} constexpr intrusive_shared_ptr(const intrusive_shared_ptr & src) noexcept : m_p(src.m_p) { this->do_add_ref(this->m_p); } constexpr intrusive_shared_ptr(intrusive_shared_ptr && src) noexcept : m_p(src.release()) { } constexpr intrusive_shared_ptr & operator=(const intrusive_shared_ptr & src) noexcept { T * temp = this->m_p; this->m_p = src.m_p; this->do_add_ref(this->m_p); this->do_sub_ref(temp); return *this; } constexpr intrusive_shared_ptr & operator=(intrusive_shared_ptr && src) noexcept { T * new_val = src.release(); //this must come second so it is nullptr if src is us T * old_val = this->m_p; this->m_p = new_val; this->do_sub_ref(old_val); return *this; } template, void>> constexpr intrusive_shared_ptr(const intrusive_shared_ptr & src) noexcept : m_p(src.get()) { this->do_add_ref(this->m_p); } template, void>> constexpr intrusive_shared_ptr(intrusive_shared_ptr && src) noexcept : m_p(src.release()) {} template, void>> constexpr intrusive_shared_ptr(intrusive_shared_ptr && src) noexcept : m_p(src.get()) { this->do_add_ref(this->m_p); src.reset(); } template, void>> constexpr intrusive_shared_ptr & operator=(const intrusive_shared_ptr & src) noexcept { T * temp = this->m_p; this->m_p = src.get(); this->do_add_ref(this->m_p); this->do_sub_ref(temp); return *this; } template, void>> constexpr intrusive_shared_ptr & operator=(intrusive_shared_ptr && src) noexcept { this->do_sub_ref(this->m_p); this->m_p = src.release(); return *this; } template, void>> constexpr intrusive_shared_ptr & operator=(intrusive_shared_ptr && src) noexcept { this->do_sub_ref(this->m_p); this->m_p = src.get(); this->do_add_ref(this->m_p); src.reset(); return *this; } ISPTR_CONSTEXPR_SINCE_CPP20 ~intrusive_shared_ptr() noexcept { this->reset(); } constexpr T * get() const noexcept { return this->m_p; } constexpr T * operator->() const noexcept { return this->m_p; } template constexpr std::enable_if_t, X &> operator*() const noexcept { return *this->m_p; } template constexpr std::enable_if_t, M &> operator->*(M X::*memptr) const noexcept { return this->m_p->*memptr; } constexpr explicit operator bool() const noexcept { return this->m_p; } constexpr output_param get_output_param() noexcept { return output_param(*this, this->m_p); } constexpr T * release() noexcept { T * p = this->m_p; this->m_p = nullptr; return p; } ISPTR_ALWAYS_INLINE //GCC refuses to inline this otherwise constexpr void reset() noexcept { this->do_sub_ref(this->m_p); this->m_p = nullptr; } constexpr void swap(intrusive_shared_ptr & other) noexcept { T * temp = this->m_p; this->m_p = other.m_p; other.m_p = temp; } friend constexpr void swap(intrusive_shared_ptr & lhs, intrusive_shared_ptr & rhs) noexcept { lhs.swap(rhs); } template friend constexpr bool operator==(const intrusive_shared_ptr& lhs, const intrusive_shared_ptr& rhs) noexcept { return lhs.m_p == rhs.get(); } template friend constexpr bool operator==(const intrusive_shared_ptr& lhs, const Y* rhs) noexcept { return lhs.m_p == rhs; } template friend constexpr bool operator==(const Y* lhs, const intrusive_shared_ptr& rhs) noexcept { return lhs == rhs.m_p; } friend constexpr bool operator==(const intrusive_shared_ptr& lhs, std::nullptr_t) noexcept { return lhs.m_p == nullptr; } friend constexpr bool operator==(std::nullptr_t, const intrusive_shared_ptr& rhs) noexcept { return nullptr == rhs.m_p; } template friend constexpr bool operator!=(const intrusive_shared_ptr& lhs, const intrusive_shared_ptr& rhs) noexcept { return !(lhs == rhs); } template friend constexpr bool operator!=(const intrusive_shared_ptr& lhs, const Y* rhs) noexcept { return !(lhs == rhs); } template friend constexpr bool operator!=(const Y* lhs, const intrusive_shared_ptr& rhs) noexcept { return !(lhs == rhs); } friend constexpr bool operator!=(const intrusive_shared_ptr& lhs, std::nullptr_t) noexcept { return !(lhs == nullptr); } friend constexpr bool operator!=(std::nullptr_t, const intrusive_shared_ptr& rhs) noexcept { return !(nullptr == rhs); } #if ISPTR_USE_SPACESHIP_OPERATOR template friend constexpr auto operator<=>(const intrusive_shared_ptr & lhs, const intrusive_shared_ptr & rhs) noexcept { return lhs.m_p <=> rhs.get(); } template friend constexpr auto operator<=>(const intrusive_shared_ptr & lhs, const Y * rhs) noexcept { return lhs.m_p <=> rhs; } template friend constexpr auto operator<=>(const Y * lhs, const intrusive_shared_ptr & rhs) noexcept { return lhs <=> rhs.m_p; } #else template friend constexpr bool operator<(const intrusive_shared_ptr & lhs, const intrusive_shared_ptr & rhs) noexcept { return lhs.m_p < rhs.get(); } template friend constexpr bool operator<(const intrusive_shared_ptr & lhs, const Y * rhs) noexcept { return lhs.m_p < rhs; } template friend constexpr bool operator<(const Y * lhs, const intrusive_shared_ptr & rhs) noexcept { return lhs < rhs.m_p; } template friend constexpr bool operator<=(const intrusive_shared_ptr & lhs, const intrusive_shared_ptr & rhs) noexcept { return lhs.m_p <= rhs.get(); } template friend constexpr bool operator<=(const intrusive_shared_ptr & lhs, const Y * rhs) noexcept { return lhs.m_p <= rhs; } template friend constexpr bool operator<=(const Y * lhs, const intrusive_shared_ptr & rhs) noexcept { return lhs <= rhs.m_p; } template friend constexpr bool operator>(const intrusive_shared_ptr & lhs, const intrusive_shared_ptr & rhs) noexcept { return !(lhs <= rhs); } template friend constexpr bool operator>(const intrusive_shared_ptr & lhs, const Y * rhs) noexcept { return !(lhs <= rhs); } template friend constexpr bool operator>(const Y * lhs, const intrusive_shared_ptr & rhs) noexcept { return !(lhs <= rhs); } template friend constexpr bool operator>=(const intrusive_shared_ptr & lhs, const intrusive_shared_ptr & rhs) noexcept { return !(lhs < rhs); } template friend constexpr bool operator>=(const intrusive_shared_ptr & lhs, const Y * rhs) noexcept { return !(lhs < rhs); } template friend constexpr bool operator>=(const Y * lhs, const intrusive_shared_ptr & rhs) noexcept { return !(lhs < rhs); } #endif template friend std::basic_ostream & operator<<(std::basic_ostream & str, const intrusive_shared_ptr & ptr) { return str << ptr.m_p; } private: constexpr intrusive_shared_ptr(T * ptr) noexcept : m_p(ptr) { } static constexpr void do_add_ref(T * p) noexcept { if (p) Traits::add_ref(p); } static constexpr void do_sub_ref(T * p) noexcept { if (p) Traits::sub_ref(p); } private: T * m_p; }; template std::false_type is_intrusive_shared_ptr_helper(const T &); template std::true_type is_intrusive_shared_ptr_helper(const intrusive_shared_ptr &); template using is_intrusive_shared_ptr = decltype(is_intrusive_shared_ptr_helper(std::declval())); template bool constexpr is_intrusive_shared_ptr_v = is_intrusive_shared_ptr::value; template inline constexpr std::enable_if_t, Dest> intrusive_const_cast(intrusive_shared_ptr p) noexcept { return Dest::noref(const_cast(p.release())); } template inline constexpr std::enable_if_t, Dest> intrusive_dynamic_cast(intrusive_shared_ptr p) noexcept { auto res = dynamic_cast(p.get()); if (res) { p.release(); return Dest::noref(res); } return Dest(); } template inline constexpr std::enable_if_t, Dest> intrusive_static_cast(intrusive_shared_ptr p) noexcept { return Dest::noref(static_cast(p.release())); } } namespace std { template class atomic> { public: using value_type = isptr::intrusive_shared_ptr; public: static constexpr bool is_always_lock_free = std::atomic::is_always_lock_free; constexpr atomic() noexcept = default; atomic(value_type desired) noexcept : m_p(desired.m_p) { desired.m_p = nullptr; } atomic(const atomic&) = delete; void operator=(const atomic&) = delete; ~atomic() noexcept { value_type::do_sub_ref(this->m_p.load(memory_order_acquire)); } void operator=(value_type desired) noexcept { this->store(std::move(desired)); } operator value_type() const noexcept { return this->load(); } value_type load(memory_order order = memory_order_seq_cst) const noexcept { T * ret = this->m_p.load(order); return value_type::ref(ret); } void store(value_type desired, memory_order order = memory_order_seq_cst) noexcept { exchange(std::move(desired), order); } value_type exchange(value_type desired, memory_order order = memory_order_seq_cst) noexcept { T * ret = this->m_p.exchange(desired.m_p, order); desired.m_p = nullptr; return value_type::noref(ret); } bool compare_exchange_strong(value_type & expected, value_type desired, memory_order success, memory_order failure) noexcept { T * saved_expected = expected.m_p; bool ret = this->m_p.compare_exchange_strong(expected.m_p, desired.m_p, success, failure); return post_compare_exchange(ret, saved_expected, expected, desired); } bool compare_exchange_strong(value_type & expected, value_type desired, memory_order order = memory_order_seq_cst) noexcept { T * saved_expected = expected.m_p; bool ret = this->m_p.compare_exchange_strong(expected.m_p, desired.m_p, order); return post_compare_exchange(ret, saved_expected, expected, desired); } bool compare_exchange_weak(value_type & expected, value_type desired, memory_order success, memory_order failure) noexcept { T * saved_expected = expected.m_p; bool ret = this->m_p.compare_exchange_weak(expected.m_p, desired.m_p, success, failure); return post_compare_exchange(ret, saved_expected, expected, desired); } bool compare_exchange_weak(value_type & expected, value_type desired, memory_order order = memory_order_seq_cst) noexcept { T * saved_expected = expected.m_p; bool ret = this->m_p.compare_exchange_weak(expected.m_p, desired.m_p, order); return post_compare_exchange(ret, saved_expected, expected, desired); } bool is_lock_free() const noexcept { return this->m_p.is_lock_free(); } private: static bool post_compare_exchange(bool exchange_result, T * saved_expected, value_type & expected, value_type & desired) noexcept { if (exchange_result) { //success: we are desired and expected is unchanged desired.m_p = nullptr; //saved_expected is equal to our original value which we need to sub_ref value_type::do_sub_ref(saved_expected); } else { //failure: expected is us and desired is unchanged. value_type::do_add_ref(expected.m_p); //our value going out value_type::do_sub_ref(saved_expected); //old expected } return exchange_result; } private: std::atomic m_p = nullptr; }; } #undef ISPTR_TRIVIAL_ABI #undef ISPTR_CONSTEXPR_SINCE_CPP20 #undef ISPTR_USE_SPACESHIP_OPERATOR #endif intrusive_shared_ptr-1.4/inc/intrusive_shared_ptr/python_ptr.h000066400000000000000000000020421445713275300252250ustar00rootroot00000000000000/* Copyright 2023 Eugene Gershnik Use of this source code is governed by a BSD-style license that can be found in the LICENSE file or at https://github.com/gershnik/intrusive_shared_ptr/blob/master/LICENSE.txt */ #ifndef HEADER_PYTHON_PTR_H_INCLUDED #define HEADER_PYTHON_PTR_H_INCLUDED #include #include namespace isptr { struct py_traits { static void add_ref(PyObject * ptr) noexcept { Py_INCREF(ptr); } static void sub_ref(PyObject * ptr) noexcept { Py_DECREF(ptr); } static void add_ref(PyTypeObject * ptr) noexcept { Py_INCREF(ptr); } static void sub_ref(PyTypeObject * ptr) noexcept { Py_DECREF(ptr); } }; template using py_ptr = intrusive_shared_ptr; template py_ptr py_retain(T * ptr) { return py_ptr::ref(ptr); } template py_ptr py_attach(T * ptr) { return py_ptr::noref(ptr); } } #endif intrusive_shared_ptr-1.4/inc/intrusive_shared_ptr/ref_counted.h000066400000000000000000000574431445713275300253330ustar00rootroot00000000000000/* Copyright 2004 Eugene Gershnik Use of this source code is governed by a BSD-style license that can be found in the LICENSE file or at https://github.com/gershnik/intrusive_shared_ptr/blob/master/LICENSE.txt */ #ifndef HEADER_REF_COUNTED_H_INCLUDED #define HEADER_REF_COUNTED_H_INCLUDED #include #include #include #include namespace isptr { //MARK:- ref_counted_flags enum class ref_counted_flags : unsigned { none = 0, provide_weak_references = 1, single_threaded = 2 }; constexpr ref_counted_flags operator|(ref_counted_flags lhs, ref_counted_flags rhs) noexcept { return ref_counted_flags(unsigned(lhs) | unsigned(rhs)); } constexpr ref_counted_flags operator&(ref_counted_flags lhs, ref_counted_flags rhs) noexcept { return ref_counted_flags(unsigned(lhs) & unsigned(rhs)); } constexpr ref_counted_flags operator^(ref_counted_flags lhs, ref_counted_flags rhs) noexcept { return ref_counted_flags(unsigned(lhs) ^ unsigned(rhs)); } constexpr ref_counted_flags operator~(ref_counted_flags arg) noexcept { return ref_counted_flags(~unsigned(arg)); } constexpr bool contains(ref_counted_flags val, ref_counted_flags flag) noexcept { return (val & flag) == flag; } //MARK:- Forward Declarations template using default_count_type = std::conditional_t; template> class ref_counted; template> class ref_counted_adapter; template> class ref_counted_wrapper; template using weak_ref_counted = ref_counted; template using weak_ref_counted_adapter = ref_counted_adapter; template using weak_ref_counted_wrapper = ref_counted_wrapper; template> using ref_counted_st = ref_counted; template> using ref_counted_adapter_st = ref_counted_adapter; template> using ref_counted_wrapper_st = ref_counted_wrapper; template using weak_ref_counted_st = ref_counted; template using weak_ref_counted_adapter_st = ref_counted_adapter; template using weak_ref_counted_wrapper_st = ref_counted_wrapper; template class weak_reference; //MARK:- struct ref_counted_traits { template static void add_ref(const T * obj) noexcept { obj->call_add_ref(); } template static void sub_ref(const T * obj) noexcept { obj->call_sub_ref(); } }; //MARK:- template class ref_counted { template friend class weak_reference; friend ref_counted_traits; public: using refcnt_ptr_traits = ref_counted_traits; using ref_counted_base = ref_counted; static constexpr bool provides_weak_references = contains(Flags, ref_counted_flags::provide_weak_references); static constexpr bool single_threaded = contains(Flags, ref_counted_flags::single_threaded); public: using weak_value_type = std::conditional_t, void>; using weak_ptr = std::conditional_t, void>; using const_weak_ptr = std::conditional_t, void>; private: static_assert(!ref_counted::provides_weak_references || (ref_counted::provides_weak_references && std::is_same_v), "CountType must be intptr_t (the default) when providing weak references"); static_assert(std::is_integral_v, "CountType must be an integral type"); static_assert(ref_counted::single_threaded || std::atomic::is_always_lock_free, "CountType must be such that std::atomic is alwayd lock free"); using count_type = std::conditional_t>; public: ref_counted(const ref_counted &) noexcept = delete; ref_counted & operator=(const ref_counted &) noexcept = delete; ref_counted(ref_counted &&) noexcept = delete; ref_counted & operator=(ref_counted &&) noexcept = delete; void add_ref() const noexcept; void sub_ref() const noexcept; template> > weak_ptr get_weak_ptr() { return weak_ptr::noref(const_cast *>(const_cast(this)->call_get_weak_value())); } template> > const_weak_ptr get_weak_ptr() const { return const_weak_ptr::noref(this->call_get_weak_value()); } protected: ref_counted() noexcept = default; ~ref_counted() noexcept; void destroy() const noexcept { delete static_cast(this); } const weak_value_type * get_weak_value() const; weak_value_type * make_weak_reference(intptr_t count) const { auto non_const_derived = static_cast(const_cast(this)); return new weak_value_type(count, non_const_derived); } private: //CRTP access void call_add_ref() const noexcept { static_cast(this)->add_ref(); } void call_sub_ref() const noexcept { static_cast(this)->sub_ref(); } void call_destroy() const noexcept { static_cast(this)->destroy(); } auto call_make_weak_reference(intptr_t count) const { if constexpr (ref_counted::provides_weak_references) return static_cast(this)->make_weak_reference(count); } auto call_get_weak_value() const { return static_cast(this)->get_weak_value(); } //Weak reference pointer decoding and encoding template static X * decode_pointer(intptr_t count) noexcept { return (X *)(uintptr_t(count) << 1); } template static intptr_t encode_pointer(X * ptr) noexcept { return (uintptr_t(ptr) >> 1) | uintptr_t(std::numeric_limits::min()); } static bool is_encoded_pointer(intptr_t count) noexcept { return count < 0; } private: mutable count_type m_count = 1; }; template class weak_reference { template friend class ref_counted; friend ref_counted_traits; public: using refcnt_ptr_traits = ref_counted_traits; using strong_value_type = Owner; using strong_ptr = intrusive_shared_ptr; using const_strong_ptr = intrusive_shared_ptr; static const bool single_threaded = Owner::single_threaded; private: using count_type = std::conditional_t>; public: weak_reference(const weak_reference &) noexcept = delete; weak_reference & operator=(const weak_reference &) noexcept = delete; weak_reference(weak_reference &&) noexcept = delete; weak_reference & operator=(weak_reference &&) noexcept = delete; void add_ref() const noexcept; void sub_ref() const noexcept; template const_strong_ptr lock() const noexcept { return const_strong_ptr::noref(this->call_lock_owner()); } template strong_ptr lock() noexcept { return strong_ptr::noref(this->call_lock_owner()); } protected: constexpr weak_reference(intptr_t initial_strong, Owner * owner) noexcept: m_strong(initial_strong), m_owner(owner) {} ~weak_reference() noexcept = default; void destroy() const { delete static_cast *>(this); } void add_owner_ref() noexcept; void sub_owner_ref() noexcept; strong_value_type * lock_owner() const noexcept; void on_owner_destruction() const noexcept {} private: template using derived_type = std::remove_pointer_t().call_make_weak_reference(0))>; void call_add_ref() const noexcept { static_cast *>(this)->add_ref(); } void call_sub_ref() const noexcept { static_cast *>(this)->sub_ref(); } void call_add_owner_ref() noexcept { static_cast *>(this)->add_owner_ref(); } void call_sub_owner_ref() noexcept { static_cast *>(this)->sub_owner_ref(); } void call_destroy() const { static_cast *>(this)->destroy(); } strong_value_type * call_lock_owner() const noexcept { return static_cast *>(this)->lock_owner(); } void call_on_owner_destruction() const noexcept { static_cast *>(this)->on_owner_destruction(); } private: mutable count_type m_count = 2; mutable count_type m_strong = 0; Owner * m_owner = nullptr; }; template class ref_counted_adapter : public ref_counted, Flags, CountType>, public T { friend ref_counted, Flags, CountType>; public: template>> ref_counted_adapter(Args&&... args) noexcept(noexcept(T(std::forward(args)...))): T(std::forward(args)...) {} protected: ~ref_counted_adapter() noexcept = default; }; template class ref_counted_wrapper : public ref_counted, Flags, CountType> { friend ref_counted, Flags, CountType>; public: template>> ref_counted_wrapper(Args&&... args) noexcept(noexcept(T(std::forward(args)...))): wrapped(std::forward(args)...) {} T wrapped; protected: ~ref_counted_wrapper() noexcept = default; }; //MARK:- Implementation template inline void weak_reference::add_ref() const noexcept { if constexpr (!weak_reference::single_threaded) { [[maybe_unused]] auto oldcount = this->m_count.fetch_add(1, std::memory_order_relaxed); assert(oldcount > 0); assert(oldcount < std::numeric_limits::max()); } else { assert(this->m_count > 0); assert(this->m_count < std::numeric_limitsm_count)>::max()); ++this->m_count; } } template inline void weak_reference::sub_ref() const noexcept { if constexpr (!weak_reference::single_threaded) { auto oldcount = this->m_count.fetch_sub(1, std::memory_order_release); assert(oldcount > 0); if (oldcount == 1) { std::atomic_thread_fence(std::memory_order_acquire); this->call_destroy(); } } else { assert(this->m_count > 0); if (--this->m_count == 0) this->call_destroy(); } } template inline void weak_reference::add_owner_ref() noexcept { if constexpr (!weak_reference::single_threaded) { [[maybe_unused]] auto oldcount = this->m_strong.fetch_add(1, std::memory_order_relaxed); assert(oldcount > 0); assert(oldcount < std::numeric_limits::max()); } else { assert(this->m_strong > 0); assert(this->m_strong < std::numeric_limitsm_count)>::max()); ++this->m_strong; } } template inline void weak_reference::sub_owner_ref() noexcept { if constexpr (!weak_reference::single_threaded) { auto oldcount = this->m_strong.fetch_sub(1, std::memory_order_release); assert(oldcount > 0); if (oldcount == 1) { std::atomic_thread_fence(std::memory_order_acquire); auto owner = this->m_owner; this->m_owner = nullptr; owner->call_destroy(); //this can cascade to deleting ourselves so must be the last thing } } else { assert(this->m_strong > 0); if (--this->m_strong == 0) { auto owner = this->m_owner; this->m_owner = nullptr; owner->call_destroy(); //this can cascade to deleting ourselves so must be the last thing } } } template inline auto weak_reference::lock_owner() const noexcept -> strong_value_type * { if constexpr (!weak_reference::single_threaded) { for (intptr_t value = this->m_strong.load(std::memory_order_relaxed); ; ) { assert(value >= 0); if (value == 0) return nullptr; if (this->m_strong.compare_exchange_strong(value, value + 1, std::memory_order_release, std::memory_order_relaxed)) return this->m_owner; } } else { if (this->m_strong == 0) return nullptr; ++this->m_strong; return this->m_owner; } } template inline void ref_counted::add_ref() const noexcept { if constexpr(!ref_counted::provides_weak_references) { if constexpr(!ref_counted::single_threaded) { [[maybe_unused]] auto oldcount = this->m_count.fetch_add(1, std::memory_order_relaxed); assert(oldcount > 0); assert(oldcount < std::numeric_limits::max()); } else { assert(this->m_count > 0); assert(this->m_count < std::numeric_limitsm_count)>::max()); ++this->m_count; } } else { if constexpr(!ref_counted::single_threaded) { for(intptr_t value = this->m_count.load(std::memory_order_relaxed); ; ) { assert(value != 0); if (!ref_counted::is_encoded_pointer(value)) { assert(value < std::numeric_limits::max()); if (this->m_count.compare_exchange_strong(value, value + 1, std::memory_order_release, std::memory_order_relaxed)) return; } else { auto ptr = ref_counted::decode_pointer(value); ptr->call_add_owner_ref(); return; } } } else { assert(this->m_count != 0); if (!ref_counted::is_encoded_pointer(this->m_count)) { assert(this->m_count < std::numeric_limitsm_count)>::max()); ++this->m_count; } else { auto ptr = ref_counted::decode_pointer(this->m_count); ptr->call_add_owner_ref(); } } } } template inline void ref_counted::sub_ref() const noexcept { if constexpr(!ref_counted::provides_weak_references) { if constexpr(!ref_counted::single_threaded) { auto oldcount = this->m_count.fetch_sub(1, std::memory_order_release); assert(oldcount > 0); if (oldcount == 1) { std::atomic_thread_fence(std::memory_order_acquire); this->call_destroy(); } } else { assert(this->m_count > 0); if (--this->m_count == 0) this->call_destroy(); } } else { if constexpr(!ref_counted::single_threaded) { for (intptr_t value = this->m_count.load(std::memory_order_relaxed); ; ) { assert(value != 0); if (!ref_counted::is_encoded_pointer(value)) { if (this->m_count.compare_exchange_strong(value, value - 1, std::memory_order_release, std::memory_order_relaxed)) { if (value == 1) { std::atomic_thread_fence(std::memory_order_acquire); this->call_destroy(); } return; } } else { auto ptr = ref_counted::decode_pointer(value); ptr->call_sub_owner_ref(); return; } } } else { assert(this->m_count != 0); if (!ref_counted::is_encoded_pointer(this->m_count)) { if (--this->m_count == 0) this->call_destroy(); } else { auto ptr = ref_counted::decode_pointer(this->m_count); ptr->call_sub_owner_ref(); } } } } template inline auto ref_counted::get_weak_value() const -> const weak_value_type * { static_assert(ref_counted::provides_weak_references, "class doesn't provide weak references"); if constexpr(!ref_counted::single_threaded) { for (intptr_t value = this->m_count.load(std::memory_order_acquire); ; ) { if (!ref_counted::is_encoded_pointer(value)) { weak_reference * ret = this->call_make_weak_reference(value); uintptr_t desired = ref_counted::encode_pointer(ret); if (this->m_count.compare_exchange_strong(value, desired, std::memory_order_release, std::memory_order_relaxed)) return ret; ret->call_destroy(); } else { auto ptr = ref_counted::decode_pointer(value); ptr->call_add_ref(); return ptr; } } } else { if (!ref_counted::is_encoded_pointer(this->m_count)) { weak_reference * ret = this->call_make_weak_reference(this->m_count); this->m_count = ref_counted::encode_pointer(ret); return ret; } else { auto ptr = ref_counted::decode_pointer(this->m_count); ptr->call_add_ref(); return ptr; } } } template inline ref_counted::~ref_counted() noexcept { [[maybe_unused]] auto valid_count = [](auto val) { return val == 0 || val == 1;}; if constexpr (ref_counted::provides_weak_references) { if constexpr(!ref_counted::single_threaded) { intptr_t value = this->m_count.load(std::memory_order_relaxed); if (ref_counted::is_encoded_pointer(value)) { auto ptr = ref_counted::decode_pointer(value); assert(valid_count(ptr->m_strong.load(std::memory_order_relaxed))); ptr->call_on_owner_destruction(); ptr->call_sub_ref(); } else { assert(valid_count(value)); } } else { if (ref_counted::is_encoded_pointer(this->m_count)) { auto ptr = ref_counted::decode_pointer(this->m_count); assert(valid_count(ptr->m_strong)); ptr->call_on_owner_destruction(); ptr->call_sub_ref(); } else { assert(valid_count(this->m_count)); } } } else { if constexpr(!ref_counted::single_threaded) assert(valid_count(this->m_count.load(std::memory_order_relaxed))); else assert(valid_count(this->m_count)); } } } #endif intrusive_shared_ptr-1.4/inc/intrusive_shared_ptr/refcnt_ptr.h000066400000000000000000000031111445713275300251630ustar00rootroot00000000000000/* Copyright 2004 Eugene Gershnik Use of this source code is governed by a BSD-style license that can be found in the LICENSE file or at https://github.com/gershnik/intrusive_shared_ptr/blob/master/LICENSE.txt */ #ifndef HEADER_REFCNT_PTR_H_INCLUDED #define HEADER_REFCNT_PTR_H_INCLUDED #include namespace isptr { template using refcnt_ptr = intrusive_shared_ptr; template constexpr refcnt_ptr refcnt_retain(T * ptr) noexcept { return refcnt_ptr::ref(ptr); } template constexpr refcnt_ptr refcnt_attach(T * ptr) noexcept { return refcnt_ptr::noref(ptr); } template inline refcnt_ptr make_refcnt(Args &&... args) { return refcnt_ptr::noref(new T( std::forward(args)... )); } template inline refcnt_ptr weak_cast(const refcnt_ptr & src) { return src->get_weak_ptr(); } template inline refcnt_ptr weak_cast(const refcnt_ptr & src) { return src->get_weak_ptr(); } template inline refcnt_ptr strong_cast(const refcnt_ptr & src) noexcept { return src->lock(); } template inline refcnt_ptr strong_cast(const refcnt_ptr & src) noexcept { return src->lock(); } } #endif intrusive_shared_ptr-1.4/test/000077500000000000000000000000001445713275300166135ustar00rootroot00000000000000intrusive_shared_ptr-1.4/test/CMakeLists.txt000066400000000000000000000041561445713275300213610ustar00rootroot00000000000000#cmake_policy(SET CMP0092 NEW) if (WIN32) set(CMAKE_SYSTEM_VERSION "10.0.19041.0" CACHE STRING "Windows SDK Version" FORCE) endif() find_package (Python3 COMPONENTS Interpreter Development) if(${Python3_Development_FOUND}) include_directories( SYSTEM ${Python3_INCLUDE_DIRS} ) link_libraries( ${Python3_LIBRARIES} ) endif() set (TEST_SUFFIXES 17 20 ) set(CXX_STANDARD_17 17) set(CXX_STANDARD_20 20) set(TEST_COMMAND "") foreach(TEST_SUFFIX ${TEST_SUFFIXES}) add_executable(test-${TEST_SUFFIX}) set_target_properties(test-${TEST_SUFFIX} PROPERTIES CXX_STANDARD ${CXX_STANDARD_${TEST_SUFFIX}} CXX_STANDARD_REQUIRED OFF CXX_EXTENSIONS OFF CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN ON ) target_link_libraries(test-${TEST_SUFFIX} PRIVATE isptr::isptr ) target_compile_options(test-${TEST_SUFFIX} PRIVATE $<$:/W4;/WX> $<$:-Wall;-Wextra;-pedantic;-Wno-self-assign-overloaded;-Wno-self-move> $<$:-Wall;-Wextra;-pedantic;-Wno-self-assign-overloaded;-Wno-self-move> $<$:-Wall;-Wextra;-pedantic> ) target_link_libraries(test-${TEST_SUFFIX} PRIVATE "$<$:-framework CoreFoundation>" ) target_compile_definitions(test-${TEST_SUFFIX} PRIVATE $<$:ISPTR_USE_PYTHON=1> ) target_sources(test-${TEST_SUFFIX} PRIVATE test_apple_cf_ptr.cpp test_atomic.cpp test_com_ptr.cpp test_python_ptr.cpp test_general.cpp test_main.cpp test_ref_counted.cpp test_ref_counted_st.cpp test_weak_ref_counted.cpp test_weak_ref_counted_st.cpp test_abstract_ref_counted.cpp test_abstract_ref_counted_st.cpp test_delegating_traits.cpp mocks.h catch.hpp ) list(APPEND TEST_COMMAND COMMAND) list(APPEND TEST_COMMAND test-${TEST_SUFFIX}) endforeach() add_custom_target(run-test ${TEST_COMMAND} ) intrusive_shared_ptr-1.4/test/catch.hpp000066400000000000000000024035741445713275300204250ustar00rootroot00000000000000/* * Catch v2.13.9 * Generated: 2022-04-12 22:37:23.260201 * ---------------------------------------------------------- * 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 9 #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