pax_global_header00006660000000000000000000000064147526210620014517gustar00rootroot0000000000000052 comment=1cb58063c7d0bf912b6a61e075228d981cb3b872 intrusive_shared_ptr-1.6/000077500000000000000000000000001475262106200156305ustar00rootroot00000000000000intrusive_shared_ptr-1.6/.gitattributes000066400000000000000000000000351475262106200205210ustar00rootroot00000000000000doc/* linguist-documentation intrusive_shared_ptr-1.6/.github/000077500000000000000000000000001475262106200171705ustar00rootroot00000000000000intrusive_shared_ptr-1.6/.github/workflows/000077500000000000000000000000001475262106200212255ustar00rootroot00000000000000intrusive_shared_ptr-1.6/.github/workflows/publish.yml000066400000000000000000000021221475262106200234130ustar00rootroot00000000000000name: Publish on: push: tags: - 'v*' jobs: main: runs-on: ubuntu-latest permissions: write-all steps: - name: Checkout uses: actions/checkout@v4 - name: Get release Name shell: python run: | import os ref = os.environ['GITHUB_REF'] name = ref[ref.rfind('/') + 2:] with open(os.environ['GITHUB_ENV'], 'w') as env: print('RELEASE_NAME=' + name, file=env) - name: Zip Headers shell: bash run: | tar -czf intrusive_shared_ptr-${{ env.RELEASE_NAME }}.tar.gz \ --transform 's/inc/intrusive_shared_ptr\/inc/' \ --transform 's/modules/intrusive_shared_ptr\/modules/' \ inc modules - name: Make release uses: softprops/action-gh-release@v2 id: create_release with: draft: true prerelease: false name: ${{ env.RELEASE_NAME }} body: ...edit me... files: | intrusive_shared_ptr-${{ env.RELEASE_NAME }}.tar.gz intrusive_shared_ptr-1.6/.github/workflows/test.yml000066400000000000000000000057231475262106200227360ustar00rootroot00000000000000name: Test on: push: paths-ignore: - 'README.md' - '.gitignore' - 'LICENSE' - 'CHANGELOG.md' - 'doc/**' - 'tools/**' jobs: build: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - os: macos-latest - os: windows-latest module: true - os: ubuntu-latest compiler: gcc version: 11 - os: ubuntu-latest compiler: gcc version: 12 - os: ubuntu-latest compiler: gcc version: 13 - os: ubuntu-24.04 compiler: gcc version: 14 #module: true GCC 14 is a piece of shit - os: ubuntu-latest compiler: clang version: 13 - os: ubuntu-latest compiler: clang version: 14 - os: ubuntu-latest compiler: clang version: 15 - os: ubuntu-latest compiler: clang version: 16 module: true - os: ubuntu-latest compiler: clang version: 17 module: true - os: ubuntu-latest compiler: clang version: 18 module: true steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: python-version: "3.12" - name: System Setup shell: bash run: | if [[ '${{ matrix.os }}' == ubuntu-* ]]; then if [[ '${{ matrix.compiler }}' == 'clang' ]]; then wget https://apt.llvm.org/llvm.sh chmod u+x llvm.sh sudo ./llvm.sh ${{ matrix.version }} sudo apt-get install -y clang-tools-${{ matrix.version }} echo "CC=clang-${{ matrix.version }}" >> $GITHUB_ENV echo "CXX=clang++-${{ matrix.version }}" >> $GITHUB_ENV fi if [[ '${{ matrix.compiler }}' == 'gcc' ]]; then sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install -y gcc-${{ matrix.version }} g++-${{ matrix.version }} echo "CC=gcc-${{ matrix.version }}" >> $GITHUB_ENV echo "CXX=g++-${{ matrix.version }}" >> $GITHUB_ENV fi mkdir bin wget -qO- https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-linux.zip | \ gunzip > bin/ninja chmod a+x bin/ninja echo PATH=`pwd`/bin:$PATH >> $GITHUB_ENV echo "CMAKE_GENERATOR=-GNinja" >> $GITHUB_ENV fi if [[ '${{ matrix.module}}' == 'true' ]]; then echo "CMAKE_ARGS=-DISPTR_PROVIDE_MODULE=ON" >> $GITHUB_ENV fi - name: Configure shell: bash run: | cmake $CMAKE_GENERATOR -S . -B build $CMAKE_ARGS -DISPTR_ENABLE_PYTHON=ON -DCMAKE_BUILD_TYPE=Release - name: Build and Test shell: bash run: | cmake --build build --config Release --target run-test intrusive_shared_ptr-1.6/.gitignore000066400000000000000000000001011475262106200176100ustar00rootroot00000000000000.DS_Store .vscode .vs /build/ /**/out/ *.pdb CMakeSettings.json intrusive_shared_ptr-1.6/CHANGELOG.md000066400000000000000000000036141475262106200174450ustar00rootroot00000000000000# 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.6] - 2025-02-11 ### Changed - Test targets are now excluded from default build - Doctest is now included via `` to make it possible to build tests against an external Doctest not brought via FetchContent. ### Fixed - `common.h` header is now correctly installed by CMake install ## [1.5] - 2024-08-16 ### Added - C++ module support (experimental). The library can now be used as C++ module. See [README](https://github.com/gershnik/intrusive_shared_ptr/) for details. ### Fixed - `weak_reference::single_threaded` flag is made `constexpr` rather than `static const` ## [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 [1.5]: https://github.com/gershnik/intrusive_shared_ptr/releases/v1.5 [1.6]: https://github.com/gershnik/intrusive_shared_ptr/releases/v1.6 intrusive_shared_ptr-1.6/CMakeLists.txt000066400000000000000000000056441475262106200204010ustar00rootroot00000000000000# # 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) option(ISPTR_PROVIDE_MODULE "whether C++ module target should be provided" OFF) option(ISPTR_ENABLE_PYTHON "whether to enable Python support in C++ module" OFF) 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/common.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 ${LIBNAME}) add_library(${LIBNAME}::${LIBNAME} ALIAS ${LIBNAME}) if(ISPTR_PROVIDE_MODULE) if(ISPTR_ENABLE_PYTHON AND NOT Python3_Development_FOUND) find_package (Python3 COMPONENTS Development REQUIRED) endif() add_library(${LIBNAME}m_helper INTERFACE) add_library(${LIBNAME}m::helper ALIAS ${LIBNAME}m_helper) if (ISPTR_ENABLE_PYTHON) target_include_directories(${LIBNAME}m_helper SYSTEM INTERFACE ${Python3_INCLUDE_DIRS} ) target_link_libraries(${LIBNAME}m_helper INTERFACE ${Python3_LIBRARIES} ) target_compile_definitions(${LIBNAME}m_helper INTERFACE ISPTR_ENABLE_PYTHON=1 ) endif() add_library(${LIBNAME}m OBJECT) target_compile_features(${LIBNAME}m PUBLIC cxx_std_20 ) set_target_properties(${LIBNAME}m PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN ON ) target_link_libraries(${LIBNAME}m PRIVATE $ ) target_sources(${LIBNAME}m PUBLIC FILE_SET CXX_MODULES BASE_DIRS ${SRCDIR}/modules FILES ${SRCDIR}/modules/isptr.cppm ) add_library(${LIBNAME}m::${LIBNAME}m ALIAS ${LIBNAME}m) endif() if (PROJECT_IS_TOP_LEVEL) include(cmake/install.cmake) add_subdirectory(test) endif()intrusive_shared_ptr-1.6/LICENSE.txt000066400000000000000000000020601475262106200174510ustar00rootroot00000000000000MIT 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.6/README.md000066400000000000000000000524401475262106200171140ustar00rootroot00000000000000# 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
Clang 8 or above
GCC 7.4.0 or above
It can be used either as a classical header-only library or as C++ module (experimental). 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) - [Module support](#module-support) - [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 the standard C++ library, 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 opinion, this is an extremely bad idea. 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 the behavior invisible and hard to predict at the **call site**. It guarantees that someone, somewhere will make a wrong assumption. In my experience, almost all reference counting bugs happen on the **boundary** between code that uses smart and raw pointers 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 object into a smart pointer). A better way to define 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 the base classes 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 base class allows user to enable a decent implementation of weak pointers via policy based design. ## Integration ### CMake via FetchContent ```cmake include(FetchContent) ... #Uncomment the next line to enable use of C++ module #set(ISPTR_PROVIDE_MODULE ON) #Uncomment the next line to enable Python pointers in C++ module #set(ISPTR_ENABLE_PYTHON ON) FetchContent_Declare(isptr GIT_REPOSITORY https://github.com/gershnik/intrusive_shared_ptr.git GIT_TAG v1.5 #use the tag, branch or sha you need GIT_SHALLOW TRUE ) ... FetchContent_MakeAvailable(isptr) ... target_link_libraries(mytarget PRIVATE #To use header files: isptr::isptr #To use C++ module use the following instead (note the *m*): #isptrm::isptrm ) ``` > ℹ️ _[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 # If you want to enable use of C++ modules use the following # cmake -S . -B build -DISPTR_PROVIDE_MODULE=ON 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 To use the header files set the include directory to `/include` where `` is the install prefix from above. To use C++ module (if enabled during the build) include `/lib/isptr/isptr.cppm` in your build. To have the module expose Python smart pointers make sure you `-DISPTR_ENABLE_PYTHON=1` for **module file compilation**. #### CMake package ```cmake #Uncomment the next line to enable use of C++ module #set(ISPTR_PROVIDE_MODULE ON) #Uncomment the next line to enable Python pointers in C++ module #set(ISPTR_ENABLE_PYTHON ON) find_package(isptr) target_link_libraries(mytarget PRIVATE #To use header files: isptr::isptr #To use C++ module use the following instead (note the *m*): #isptrm::isptrm ) ``` #### 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 this repository from [Releases](https://github.com/gershnik/intrusive_shared_ptr/releases) page (named `intrusive_shared_ptr-X.Y.tar.gz`) and unpack it somewhere in your source tree. To use header files add the `inc` sub-directory to your include path. To use the module add `modules/isptr.cppm` to your build. To have the module expose Python smart pointers make sure you `-DISPTR_ENABLE_PYTHON=1` for **module file compilation**. ## 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 ``/module `isptr` 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 //Or, if using modules: //import isptr; using namespace isptr; 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 //Or, if using modules: //import isptr; using namespace isptr; 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 //Or, if using modules: //import isptr; using namespace isptr; 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 //Or, if using modules: //import isptr; using namespace isptr; //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 //Or, if using modules: //import isptr; using namespace isptr; com_shared_ptr pStream; CreateStreamOnHGlobal(nullptr, true, pStream.get_output_param()); pStream->Write(....); ``` ### Using with Python objects ```cpp #include //Or, if using modules: //import isptr; using namespace isptr; auto str = py_attach(PyUnicode_FromString("Hello")); std::cout << PyUnicode_GetLength(str.get()); ``` Note that to use Python smart pointers with C++ module you need ensure `-DISPTR_ENABLE_PYTHON=1` is used for **module file compilation**. When using CMake this is accomplished by `set(ISPTR_ENABLE_PYTHON ON)` in CMake code or via `-DISPTR_ENABLE_PYTHON=ON` during configuration. ### 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 //Or, if using modules: //import isptr; 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 //Or, if using modules: //import isptr; 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 ## Module support Since version 1.5 this library support being used as a C++ module. This mode is currently **experimental**. Please report bugs if you encounter any issues. In order to use C++ modules you need a compiler that supports them. Currently CLang >= 16 and MSVC toolset >= 14.34 are definitely known to work. Other compilers/versions may or may not work. If using CMake follow the requirements at [cmake-cxxmodules](https://cmake.org/cmake/help/latest/manual/cmake-cxxmodules.7.html). In order to enable module support for this library you need to set `ISPTR_PROVIDE_MODULE` CMake variable to `ON` before referencing it. The library consists of a single module file at [modules/isptr.cppm](modules/isptr.cppm). This file is auto-generated from all the library headers. One notable difference between headers and module use concerns Python pointers. With header files you can simply control whether to use them by including or not including ``. With module, which is compiled separately, you need to tell the module file whether to enable Python pointers (and use `` header) ahead of time. If you compile module yourself you can control whether Python pointers are enabled by setting `-DISPTR_ENABLE_PYTHON=1` for its compilation and make sure the include path contains ``. If you use CMake then you need to set CMake option `ISPTR_ENABLE_PYTHON` to `ON` either from command line or in CMake code before referencing this library. With this variable set to `ON` the CMake script will - Perform `find_package (Python3 COMPONENTS Development REQUIRED)` if Python development component hasn't been already found. - Add `Python3_INCLUDE_DIRS` to module include path and define `ISPTR_ENABLE_PYTHON=1` - Add `Python3_LIBRARIES` to library dependencies. You can control which Python installation to use by controlling `find_package (Python3)` (or calling it yourself ahead of time) as described in [FindPython3](https://cmake.org/cmake/help/latest/module/FindPython3.html) ## 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.6/VERSION000066400000000000000000000000041475262106200166720ustar00rootroot000000000000001.6 intrusive_shared_ptr-1.6/cmake/000077500000000000000000000000001475262106200167105ustar00rootroot00000000000000intrusive_shared_ptr-1.6/cmake/install.cmake000066400000000000000000000032721475262106200213640ustar00rootroot00000000000000# # 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) if (ISPTR_PROVIDE_MODULE) install(TARGETS isptrm EXPORT isptrm FILE_SET CXX_MODULES DESTINATION ${CMAKE_INSTALL_LIBDIR}/isptr) install(EXPORT isptrm NAMESPACE isptrm:: FILE isptrm-exports.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/isptr) endif() 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.6/cmake/isptr-config.cmake.in000066400000000000000000000024371475262106200227310ustar00rootroot00000000000000# # 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() if(ISPTR_PROVIDE_MODULE) if(NOT TARGET isptrm::isptrm) include(${CMAKE_CURRENT_LIST_DIR}/isptrm-exports.cmake) if(ISPTR_ENABLE_PYTHON AND NOT Python3_Development_FOUND) find_package (Python3 COMPONENTS Development REQUIRED) endif() add_library(isptrm_helper INTERFACE) add_library(isptrm::helper ALIAS isptrm_helper) if (ISPTR_ENABLE_PYTHON) target_include_directories(isptrm_helper SYSTEM INTERFACE ${Python3_INCLUDE_DIRS} ) target_link_libraries(isptrm_helper INTERFACE ${Python3_LIBRARIES} ) target_compile_definitions(isptrm_helper INTERFACE ISPTR_ENABLE_PYTHON=1 ) endif() target_link_libraries(isptrm::isptrm INTERFACE isptrm::helper ) endif() endif() intrusive_shared_ptr-1.6/cmake/isptr.pc.in000066400000000000000000000004311475262106200210000ustar00rootroot00000000000000prefix=${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.6/doc/000077500000000000000000000000001475262106200163755ustar00rootroot00000000000000intrusive_shared_ptr-1.6/doc/intrusive_shared_ptr.md000066400000000000000000000151671475262106200231740ustar00rootroot00000000000000# 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<isptr::intrusive_shared_ptr> 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.6/doc/ref_counted.md000066400000000000000000000400471475262106200212210ustar00rootroot00000000000000# 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.6/doc/refcnt_ptr.md000066400000000000000000000042461475262106200210730ustar00rootroot00000000000000# 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.6/doc/reference_counting.md000066400000000000000000000200061475262106200225610ustar00rootroot00000000000000# 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.6/doc/trivial_abi.md000066400000000000000000000065271475262106200212160ustar00rootroot00000000000000When 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.6/inc/000077500000000000000000000000001475262106200164015ustar00rootroot00000000000000intrusive_shared_ptr-1.6/inc/intrusive_shared_ptr/000077500000000000000000000000001475262106200226445ustar00rootroot00000000000000intrusive_shared_ptr-1.6/inc/intrusive_shared_ptr/apple_cf_ptr.h000066400000000000000000000020311475262106200254470ustar00rootroot00000000000000/* 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); } }; ISPTR_EXPORTED template using cf_ptr = intrusive_shared_ptr, cf_traits>; ISPTR_EXPORTED template cf_ptr cf_retain(T * ptr) { return cf_ptr::ref(ptr); } ISPTR_EXPORTED template cf_ptr cf_attach(T * ptr) { return cf_ptr::noref(ptr); } } #endif #endif intrusive_shared_ptr-1.6/inc/intrusive_shared_ptr/com_ptr.h000066400000000000000000000021671475262106200244660ustar00rootroot00000000000000/* 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(); } }; ISPTR_EXPORTED template using com_shared_ptr = intrusive_shared_ptr; ISPTR_EXPORTED template com_shared_ptr com_retain(T * ptr) { return com_shared_ptr::ref(ptr); } ISPTR_EXPORTED template com_shared_ptr com_attach(T * ptr) { return com_shared_ptr::noref(ptr); } } #endif #endif intrusive_shared_ptr-1.6/inc/intrusive_shared_ptr/common.h000066400000000000000000000022061475262106200243050ustar00rootroot00000000000000/* 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 #ifndef ISPTR_EXPORTED #define ISPTR_EXPORTED #endif namespace isptr::internal { template constexpr bool dependent_bool = Val; } #endif intrusive_shared_ptr-1.6/inc/intrusive_shared_ptr/intrusive_shared_ptr.h000066400000000000000000000447151475262106200272730ustar00rootroot00000000000000/* 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; ISPTR_EXPORTED 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; ISPTR_EXPORTED template inline constexpr std::enable_if_t, Dest> intrusive_const_cast(intrusive_shared_ptr p) noexcept { return Dest::noref(const_cast(p.release())); } ISPTR_EXPORTED 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(); } ISPTR_EXPORTED 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 { ISPTR_EXPORTED 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.6/inc/intrusive_shared_ptr/python_ptr.h000066400000000000000000000021071475262106200252230ustar00rootroot00000000000000/* 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; ISPTR_EXPORTED template py_ptr py_retain(T * ptr) { return py_ptr::ref(ptr); } ISPTR_EXPORTED template py_ptr py_attach(T * ptr) { return py_ptr::noref(ptr); } } #endif intrusive_shared_ptr-1.6/inc/intrusive_shared_ptr/ref_counted.h000066400000000000000000000602171475262106200253200ustar00rootroot00000000000000/* 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 ISPTR_EXPORTED enum class ref_counted_flags : unsigned { none = 0, provide_weak_references = 1, single_threaded = 2 }; ISPTR_EXPORTED constexpr ref_counted_flags operator|(ref_counted_flags lhs, ref_counted_flags rhs) noexcept { return ref_counted_flags(unsigned(lhs) | unsigned(rhs)); } ISPTR_EXPORTED constexpr ref_counted_flags operator&(ref_counted_flags lhs, ref_counted_flags rhs) noexcept { return ref_counted_flags(unsigned(lhs) & unsigned(rhs)); } ISPTR_EXPORTED constexpr ref_counted_flags operator^(ref_counted_flags lhs, ref_counted_flags rhs) noexcept { return ref_counted_flags(unsigned(lhs) ^ unsigned(rhs)); } ISPTR_EXPORTED constexpr ref_counted_flags operator~(ref_counted_flags arg) noexcept { return ref_counted_flags(~unsigned(arg)); } ISPTR_EXPORTED constexpr bool contains(ref_counted_flags val, ref_counted_flags flag) noexcept { return (val & flag) == flag; } //MARK:- Forward Declarations ISPTR_EXPORTED template using default_count_type = std::conditional_t; ISPTR_EXPORTED template> class ref_counted; ISPTR_EXPORTED template> class ref_counted_adapter; ISPTR_EXPORTED template> class ref_counted_wrapper; ISPTR_EXPORTED template using weak_ref_counted = ref_counted; ISPTR_EXPORTED template using weak_ref_counted_adapter = ref_counted_adapter; ISPTR_EXPORTED template using weak_ref_counted_wrapper = ref_counted_wrapper; ISPTR_EXPORTED template> using ref_counted_st = ref_counted; ISPTR_EXPORTED template> using ref_counted_adapter_st = ref_counted_adapter; ISPTR_EXPORTED template> using ref_counted_wrapper_st = ref_counted_wrapper; ISPTR_EXPORTED template using weak_ref_counted_st = ref_counted; ISPTR_EXPORTED template using weak_ref_counted_adapter_st = ref_counted_adapter; ISPTR_EXPORTED template using weak_ref_counted_wrapper_st = ref_counted_wrapper; ISPTR_EXPORTED 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 constexpr 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.6/inc/intrusive_shared_ptr/refcnt_ptr.h000066400000000000000000000033151475262106200251650ustar00rootroot00000000000000/* 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 { ISPTR_EXPORTED template using refcnt_ptr = intrusive_shared_ptr; ISPTR_EXPORTED template constexpr refcnt_ptr refcnt_retain(T * ptr) noexcept { return refcnt_ptr::ref(ptr); } ISPTR_EXPORTED template constexpr refcnt_ptr refcnt_attach(T * ptr) noexcept { return refcnt_ptr::noref(ptr); } ISPTR_EXPORTED template inline refcnt_ptr make_refcnt(Args &&... args) { return refcnt_ptr::noref(new T( std::forward(args)... )); } ISPTR_EXPORTED template inline refcnt_ptr weak_cast(const refcnt_ptr & src) { return src->get_weak_ptr(); } ISPTR_EXPORTED template inline refcnt_ptr weak_cast(const refcnt_ptr & src) { return src->get_weak_ptr(); } ISPTR_EXPORTED template inline refcnt_ptr strong_cast(const refcnt_ptr & src) noexcept { return src->lock(); } ISPTR_EXPORTED template inline refcnt_ptr strong_cast(const refcnt_ptr & src) noexcept { return src->lock(); } } #endif intrusive_shared_ptr-1.6/modules/000077500000000000000000000000001475262106200173005ustar00rootroot00000000000000intrusive_shared_ptr-1.6/modules/isptr.cppm000066400000000000000000001364141475262106200213330ustar00rootroot00000000000000/* Copyright 2024 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 */ module; #if (defined(__APPLE__) && defined(__MACH__)) #include #endif #if ISPTR_ENABLE_PYTHON #include #endif #if defined(_WIN32) #define NOMINMAX #include #endif #include #include #include #include #include #include export module isptr; #define ISPTR_EXPORTED export #ifndef HEADER_INTRUSIVE_SHARED_PTR_H_INCLUDED #define HEADER_INTRUSIVE_SHARED_PTR_H_INCLUDED #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 #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 #ifndef ISPTR_EXPORTED #define ISPTR_EXPORTED #endif namespace isptr::internal { template constexpr bool dependent_bool = Val; } #endif 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; ISPTR_EXPORTED 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; ISPTR_EXPORTED template inline constexpr std::enable_if_t, Dest> intrusive_const_cast(intrusive_shared_ptr p) noexcept { return Dest::noref(const_cast(p.release())); } ISPTR_EXPORTED 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(); } ISPTR_EXPORTED 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 { ISPTR_EXPORTED 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 #ifndef HEADER_REF_COUNTED_H_INCLUDED #define HEADER_REF_COUNTED_H_INCLUDED namespace isptr { //MARK:- ref_counted_flags ISPTR_EXPORTED enum class ref_counted_flags : unsigned { none = 0, provide_weak_references = 1, single_threaded = 2 }; ISPTR_EXPORTED constexpr ref_counted_flags operator|(ref_counted_flags lhs, ref_counted_flags rhs) noexcept { return ref_counted_flags(unsigned(lhs) | unsigned(rhs)); } ISPTR_EXPORTED constexpr ref_counted_flags operator&(ref_counted_flags lhs, ref_counted_flags rhs) noexcept { return ref_counted_flags(unsigned(lhs) & unsigned(rhs)); } ISPTR_EXPORTED constexpr ref_counted_flags operator^(ref_counted_flags lhs, ref_counted_flags rhs) noexcept { return ref_counted_flags(unsigned(lhs) ^ unsigned(rhs)); } ISPTR_EXPORTED constexpr ref_counted_flags operator~(ref_counted_flags arg) noexcept { return ref_counted_flags(~unsigned(arg)); } ISPTR_EXPORTED constexpr bool contains(ref_counted_flags val, ref_counted_flags flag) noexcept { return (val & flag) == flag; } //MARK:- Forward Declarations ISPTR_EXPORTED template using default_count_type = std::conditional_t; ISPTR_EXPORTED template> class ref_counted; ISPTR_EXPORTED template> class ref_counted_adapter; ISPTR_EXPORTED template> class ref_counted_wrapper; ISPTR_EXPORTED template using weak_ref_counted = ref_counted; ISPTR_EXPORTED template using weak_ref_counted_adapter = ref_counted_adapter; ISPTR_EXPORTED template using weak_ref_counted_wrapper = ref_counted_wrapper; ISPTR_EXPORTED template> using ref_counted_st = ref_counted; ISPTR_EXPORTED template> using ref_counted_adapter_st = ref_counted_adapter; ISPTR_EXPORTED template> using ref_counted_wrapper_st = ref_counted_wrapper; ISPTR_EXPORTED template using weak_ref_counted_st = ref_counted; ISPTR_EXPORTED template using weak_ref_counted_adapter_st = ref_counted_adapter; ISPTR_EXPORTED template using weak_ref_counted_wrapper_st = ref_counted_wrapper; ISPTR_EXPORTED 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 constexpr 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 #ifndef HEADER_APPLE_CF_PTR_H_INCLUDED #define HEADER_APPLE_CF_PTR_H_INCLUDED #if (defined(__APPLE__) && defined(__MACH__)) namespace isptr { struct cf_traits { static void add_ref(CFTypeRef ptr) noexcept { CFRetain(ptr); } static void sub_ref(CFTypeRef ptr) noexcept { CFRelease(ptr); } }; ISPTR_EXPORTED template using cf_ptr = intrusive_shared_ptr, cf_traits>; ISPTR_EXPORTED template cf_ptr cf_retain(T * ptr) { return cf_ptr::ref(ptr); } ISPTR_EXPORTED template cf_ptr cf_attach(T * ptr) { return cf_ptr::noref(ptr); } } #endif #endif #ifndef HEADER_COM_PTR_H_INCLUDED #define HEADER_COM_PTR_H_INCLUDED #if defined(_WIN32) 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(); } }; ISPTR_EXPORTED template using com_shared_ptr = intrusive_shared_ptr; ISPTR_EXPORTED template com_shared_ptr com_retain(T * ptr) { return com_shared_ptr::ref(ptr); } ISPTR_EXPORTED template com_shared_ptr com_attach(T * ptr) { return com_shared_ptr::noref(ptr); } } #endif #endif #if ISPTR_ENABLE_PYTHON #ifndef HEADER_PYTHON_PTR_H_INCLUDED #define HEADER_PYTHON_PTR_H_INCLUDED 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; ISPTR_EXPORTED template py_ptr py_retain(T * ptr) { return py_ptr::ref(ptr); } ISPTR_EXPORTED template py_ptr py_attach(T * ptr) { return py_ptr::noref(ptr); } } #endif #endif #ifndef HEADER_REFCNT_PTR_H_INCLUDED #define HEADER_REFCNT_PTR_H_INCLUDED namespace isptr { ISPTR_EXPORTED template using refcnt_ptr = intrusive_shared_ptr; ISPTR_EXPORTED template constexpr refcnt_ptr refcnt_retain(T * ptr) noexcept { return refcnt_ptr::ref(ptr); } ISPTR_EXPORTED template constexpr refcnt_ptr refcnt_attach(T * ptr) noexcept { return refcnt_ptr::noref(ptr); } ISPTR_EXPORTED template inline refcnt_ptr make_refcnt(Args &&... args) { return refcnt_ptr::noref(new T( std::forward(args)... )); } ISPTR_EXPORTED template inline refcnt_ptr weak_cast(const refcnt_ptr & src) { return src->get_weak_ptr(); } ISPTR_EXPORTED template inline refcnt_ptr weak_cast(const refcnt_ptr & src) { return src->get_weak_ptr(); } ISPTR_EXPORTED template inline refcnt_ptr strong_cast(const refcnt_ptr & src) noexcept { return src->lock(); } ISPTR_EXPORTED template inline refcnt_ptr strong_cast(const refcnt_ptr & src) noexcept { return src->lock(); } } #endif intrusive_shared_ptr-1.6/test/000077500000000000000000000000001475262106200166075ustar00rootroot00000000000000intrusive_shared_ptr-1.6/test/CMakeLists.txt000066400000000000000000000070501475262106200213510ustar00rootroot00000000000000#cmake_policy(SET CMP0092 NEW) if (WIN32) set(CMAKE_SYSTEM_VERSION "10.0.19041.0" CACHE STRING "Windows SDK Version" FORCE) endif() include(FetchContent) FetchContent_Declare(doctest URL https://raw.githubusercontent.com/doctest/doctest/v2.4.11/doctest/doctest.h DOWNLOAD_NO_EXTRACT TRUE SOURCE_DIR downloaded/doctest ) FetchContent_MakeAvailable(doctest) if(ISPTR_ENABLE_PYTHON AND NOT Python3_Development_FOUND) find_package (Python3 COMPONENTS Development REQUIRED) endif() set (TEST_SUFFIXES 17 20 ) set(CXX_STANDARD_17 17) set(CXX_STANDARD_20 20) set(TEST_VARIANTS_17 "headers") if (${ISPTR_PROVIDE_MODULE}) set(TEST_VARIANTS_20 "headers;module") else() set(TEST_VARIANTS_20 "headers") endif() set(TEST_COMMAND "") foreach(TEST_SUFFIX ${TEST_SUFFIXES}) foreach(TEST_VARIANT ${TEST_VARIANTS_${TEST_SUFFIX}}) set(TEST_TARGET_NAME test-${TEST_SUFFIX}-${TEST_VARIANT}) add_executable(${TEST_TARGET_NAME} EXCLUDE_FROM_ALL) set_target_properties(${TEST_TARGET_NAME} PROPERTIES CXX_STANDARD ${CXX_STANDARD_${TEST_SUFFIX}} CXX_STANDARD_REQUIRED OFF CXX_EXTENSIONS OFF CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN ON ) if("${TEST_VARIANT}" STREQUAL "module") set_target_properties(${TEST_TARGET_NAME} PROPERTIES CXX_SCAN_FOR_MODULES ON ) endif() target_include_directories(${TEST_TARGET_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/downloaded ) if (ISPTR_ENABLE_PYTHON) target_link_libraries(${TEST_TARGET_NAME} PRIVATE ${Python3_LIBRARIES} ) target_include_directories(${TEST_TARGET_NAME} SYSTEM PRIVATE ${Python3_INCLUDE_DIRS} ) endif() target_link_libraries(${TEST_TARGET_NAME} PRIVATE "$<$:-framework CoreFoundation>" $,isptrm::isptrm,isptr::isptr> ) target_compile_options(${TEST_TARGET_NAME} 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;-Wno-self-move> ) target_compile_definitions(${TEST_TARGET_NAME} PRIVATE $<$:ISPTR_USE_PYTHON=1> $<$:ISPTR_USE_MODULES=1> _FILE_OFFSET_BITS=64 # prevents weird issues with modules and clang on Ubuntu ) target_sources(${TEST_TARGET_NAME} 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 ) list(APPEND TEST_COMMAND COMMAND) list(APPEND TEST_COMMAND echo Running: ${TEST_TARGET_NAME}) list(APPEND TEST_COMMAND COMMAND) list(APPEND TEST_COMMAND ${TEST_TARGET_NAME}) endforeach() endforeach() add_custom_target(run-test ${TEST_COMMAND} ) intrusive_shared_ptr-1.6/test/mocks.h000066400000000000000000000024621475262106200201000ustar00rootroot00000000000000 template struct instrumented_counted { mutable int count = 1; virtual ~instrumented_counted() { CHECK(this->count == -1); } }; template struct derived_instrumented_counted : instrumented_counted {}; struct non_counted {}; template struct mock_traits { template static void add_ref(const instrumented_counted * c) noexcept { REQUIRE(c->count > 0); ++c->count; } template static void sub_ref(const instrumented_counted * c) noexcept { CHECK(c->count > 0); if (--c->count == 0) c->count = -1; } }; template using mock_ptr = isptr::intrusive_shared_ptr>; template using mock_ptr_different_traits = isptr::intrusive_shared_ptr>; template mock_ptr mock_ref(T * ptr) { return mock_ptr::ref(ptr); } template mock_ptr mock_noref(T * ptr) { return mock_ptr::noref(ptr); } template mock_ptr_different_traits mock_ref_different_traits(T * ptr) { return mock_ptr_different_traits::ref(ptr); } template mock_ptr_different_traits mock_noref_different_traits(T * ptr) { return mock_ptr_different_traits::noref(ptr); } intrusive_shared_ptr-1.6/test/test_abstract_ref_counted.cpp000066400000000000000000000115641475262106200245410ustar00rootroot00000000000000#if ISPTR_USE_MODULES import isptr; #else #include #include #endif #include #include #include using namespace isptr; namespace { bool counted_add_ref_called = false; bool counted_sub_ref_called = false; bool counted_destroy_called = false; bool make_weak_reference_called = false; bool get_weak_value_called = false; class abstract_weak_reference; class abstract_ref_counted : public ref_counted { friend ref_counted; public: virtual void add_ref() const noexcept final { counted_add_ref_called = true; ref_counted::add_ref(); } virtual void sub_ref() const noexcept final { counted_sub_ref_called = true; ref_counted::sub_ref(); } protected: virtual ~abstract_ref_counted() noexcept {} virtual void destroy() const noexcept { counted_destroy_called = true; ref_counted::destroy(); } virtual abstract_weak_reference * make_weak_reference(intptr_t count) const; virtual const weak_reference * get_weak_value() const { get_weak_value_called = true; return ref_counted::get_weak_value(); } }; bool weak_add_ref_called = false; bool weak_sub_ref_called = false; bool weak_destroy_called = false; bool add_owner_ref_called = false; bool sub_owner_ref_called = false; bool lock_owner_called = false; bool on_owner_destruction_called = false; class abstract_weak_reference : public weak_reference { friend weak_reference; friend abstract_ref_counted; public: virtual void add_ref() const noexcept final { weak_add_ref_called = true; weak_reference::add_ref(); } virtual void sub_ref() const noexcept final { weak_sub_ref_called = true; weak_reference::sub_ref(); } protected: virtual ~abstract_weak_reference() noexcept {} virtual void destroy() const noexcept { weak_destroy_called = true; weak_reference::destroy(); } private: abstract_weak_reference(intptr_t count, abstract_ref_counted * owner): weak_reference(count, owner) {} virtual void add_owner_ref() noexcept { add_owner_ref_called = true; weak_reference::add_owner_ref(); } virtual void sub_owner_ref() noexcept { sub_owner_ref_called = true; weak_reference::sub_owner_ref(); } virtual abstract_ref_counted * lock_owner() const noexcept { lock_owner_called = true; return static_cast(weak_reference::lock_owner()); } virtual void on_owner_destruction() const noexcept { on_owner_destruction_called = true; } }; inline auto abstract_ref_counted::make_weak_reference(intptr_t count) const -> abstract_weak_reference * { make_weak_reference_called = true; return new abstract_weak_reference(count, const_cast(this)); } class simple : public abstract_ref_counted { }; } TEST_SUITE("abstract_ref_counted") { TEST_CASE( "Abstract ref counted works" ) { SUBCASE( "Simple" ) { auto p = refcnt_attach(new simple()); decltype(p) p1 = p; p1.reset(); auto w = weak_cast(p); static_assert(std::is_same_v>, "invalid weak reference type"); auto w1 = p->get_weak_ptr(); static_assert(std::is_same_v>, "invalid weak reference type"); auto p2 = strong_cast(w); auto p3 = p2; static_assert(std::is_same_v, "invalid weak reference type"); p.reset(); p2.reset(); p3.reset(); w.reset(); w1.reset(); CHECK(counted_add_ref_called); CHECK(counted_sub_ref_called); CHECK(counted_destroy_called); CHECK(make_weak_reference_called); CHECK(get_weak_value_called); CHECK(weak_add_ref_called); CHECK(weak_sub_ref_called); CHECK(weak_destroy_called); CHECK(add_owner_ref_called); CHECK(sub_owner_ref_called); CHECK(lock_owner_called); CHECK(on_owner_destruction_called); } } } intrusive_shared_ptr-1.6/test/test_abstract_ref_counted_st.cpp000066400000000000000000000120211475262106200252340ustar00rootroot00000000000000#if ISPTR_USE_MODULES import isptr; #else #include #include #endif #include #include #include using namespace isptr; namespace { bool counted_add_ref_called = false; bool counted_sub_ref_called = false; bool counted_destroy_called = false; bool make_weak_reference_called = false; bool get_weak_value_called = false; class abstract_weak_reference; class abstract_ref_counted : public ref_counted { friend ref_counted; public: virtual void add_ref() const noexcept final { counted_add_ref_called = true; ref_counted::add_ref(); } virtual void sub_ref() const noexcept final { counted_sub_ref_called = true; ref_counted::sub_ref(); } protected: virtual ~abstract_ref_counted() noexcept {} virtual void destroy() const noexcept { counted_destroy_called = true; ref_counted::destroy(); } virtual abstract_weak_reference * make_weak_reference(intptr_t count) const; virtual const weak_reference * get_weak_value() const { get_weak_value_called = true; return ref_counted::get_weak_value(); } }; bool weak_add_ref_called = false; bool weak_sub_ref_called = false; bool weak_destroy_called = false; bool add_owner_ref_called = false; bool sub_owner_ref_called = false; bool lock_owner_called = false; bool on_owner_destruction_called = false; class abstract_weak_reference : public weak_reference { friend weak_reference; friend abstract_ref_counted; public: virtual void add_ref() const noexcept final { weak_add_ref_called = true; weak_reference::add_ref(); } virtual void sub_ref() const noexcept final { weak_sub_ref_called = true; weak_reference::sub_ref(); } protected: virtual ~abstract_weak_reference() noexcept {} virtual void destroy() const noexcept { weak_destroy_called = true; weak_reference::destroy(); } private: abstract_weak_reference(intptr_t count, abstract_ref_counted * owner): weak_reference(count, owner) {} virtual void add_owner_ref() noexcept { add_owner_ref_called = true; weak_reference::add_owner_ref(); } virtual void sub_owner_ref() noexcept { sub_owner_ref_called = true; weak_reference::sub_owner_ref(); } virtual abstract_ref_counted * lock_owner() const noexcept { lock_owner_called = true; return static_cast(weak_reference::lock_owner()); } virtual void on_owner_destruction() const noexcept { on_owner_destruction_called = true; } }; inline auto abstract_ref_counted::make_weak_reference(intptr_t count) const -> abstract_weak_reference * { make_weak_reference_called = true; return new abstract_weak_reference(count, const_cast(this)); } class simple : public abstract_ref_counted { }; } TEST_SUITE("abstract_ref_counted_st") { TEST_CASE( "Abstract ref counted st works" ) { SUBCASE( "Simple" ) { auto p = refcnt_attach(new simple()); decltype(p) p1 = p; p1.reset(); auto w = weak_cast(p); static_assert(std::is_same_v>, "invalid weak reference type"); auto w1 = p->get_weak_ptr(); static_assert(std::is_same_v>, "invalid weak reference type"); auto p2 = strong_cast(w); auto p3 = p2; static_assert(std::is_same_v, "invalid weak reference type"); p.reset(); p2.reset(); p3.reset(); w.reset(); w1.reset(); CHECK(counted_add_ref_called); CHECK(counted_sub_ref_called); CHECK(counted_destroy_called); CHECK(make_weak_reference_called); CHECK(get_weak_value_called); CHECK(weak_add_ref_called); CHECK(weak_sub_ref_called); CHECK(weak_destroy_called); CHECK(add_owner_ref_called); CHECK(sub_owner_ref_called); CHECK(lock_owner_called); CHECK(on_owner_destruction_called); } } } intrusive_shared_ptr-1.6/test/test_apple_cf_ptr.cpp000066400000000000000000000006421475262106200230120ustar00rootroot00000000000000#if ISPTR_USE_MODULES import isptr; #else #include #endif #include #if (defined(__APPLE__) && defined(__MACH__)) using namespace isptr; TEST_SUITE("apple") { TEST_CASE( "Apple Ptr" ) { auto str = cf_attach(CFStringCreateWithCString(nullptr, "Hello", kCFStringEncodingUTF8)); CHECK( CFStringGetLength(str.get()) == 5 ); } } #endif intrusive_shared_ptr-1.6/test/test_atomic.cpp000066400000000000000000000173211475262106200216320ustar00rootroot00000000000000#if ISPTR_USE_MODULES import isptr; #else #include #endif #include #include "mocks.h" #include #include using namespace isptr; TEST_SUITE("traits") { TEST_CASE( "Atomic type traits are correct" ) { using ptr = std::atomic>>; SUBCASE("Construction, destruction and assignment") { CHECK( sizeof(ptr) == sizeof(std::atomic *>) ); CHECK( std::alignment_of_v == std::alignment_of_v *>> ); CHECK( std::is_default_constructible_v ); CHECK( std::is_nothrow_default_constructible_v ); CHECK( !std::is_trivially_default_constructible_v ); CHECK( !std::is_copy_constructible_v ); CHECK( !std::is_move_constructible_v ); CHECK( !std::is_copy_assignable_v ); CHECK( !std::is_move_assignable_v ); CHECK( !std::is_swappable_v ); CHECK( std::is_destructible_v ); CHECK( !std::is_trivially_destructible_v ); CHECK( std::is_nothrow_destructible_v ); CHECK( !std::is_assignable_v ); CHECK( std::is_constructible_v>> ); CHECK( std::is_nothrow_constructible_v>> ); CHECK( !std::is_trivially_constructible_v>> ); CHECK( std::is_assignable_v>> ); CHECK( std::is_nothrow_assignable_v>> ); CHECK( !std::is_trivially_assignable_v>> ); CHECK( std::is_convertible_v>> ); //CHECK( std::is_nothrow_convertible_v>> ); } } TEST_CASE( "Atomic load" ) { SUBCASE( "Explicit" ) { instrumented_counted<> object; std::atomic>> ptr = mock_noref(&object); auto ptr1 = ptr.load(); CHECK( ptr1.get() == &object ); CHECK( object.count == 2 ); } SUBCASE( "Implicit" ) { instrumented_counted<> object; std::atomic>> ptr = mock_noref(&object); mock_ptr> ptr1 = ptr; CHECK( ptr1.get() == &object ); CHECK( object.count == 2 ); } SUBCASE( "Free function" ) { instrumented_counted<> object; std::atomic>> ptr = mock_noref(&object); mock_ptr> ptr1 = std::atomic_load(&ptr); CHECK( ptr1.get() == &object ); CHECK( object.count == 2 ); } } TEST_CASE( "Atomic store" ) { SUBCASE( "Explicit" ) { instrumented_counted<> object1, object2; std::atomic>> ptr = mock_noref(&object1); auto ptr1 = mock_noref(&object2); ptr.store(ptr1); CHECK( ptr1.get() == &object2 ); CHECK( ptr.load().get() == &object2 ); CHECK( object1.count == -1 ); CHECK( object2.count == 2 ); } SUBCASE( "Implicit" ) { instrumented_counted<> object1, object2; std::atomic>> ptr = mock_noref(&object1); auto ptr1 = mock_noref(&object2); ptr = ptr1; CHECK( ptr1.get() == &object2 ); CHECK( ptr.load().get() == &object2 ); CHECK( object1.count == -1 ); CHECK( object2.count == 2 ); } SUBCASE( "Free function" ) { instrumented_counted<> object1, object2; std::atomic>> ptr = mock_noref(&object1); auto ptr1 = mock_noref(&object2); std::atomic_store(&ptr, ptr1); CHECK( ptr1.get() == &object2 ); CHECK( ptr.load().get() == &object2 ); CHECK( object1.count == -1 ); CHECK( object2.count == 2 ); } } TEST_CASE( "Atomic comapre and exchange" ) { SUBCASE( "Strong" ) { instrumented_counted<> object1, object2, object3; std::atomic>> ptr1 = mock_noref(&object1); auto ptr2 = mock_noref(&object2); auto ptr3 = mock_noref(&object3); auto res = ptr1.compare_exchange_strong(ptr2, ptr3); CHECK( !res ); CHECK( ptr1.load().get() == &object1 ); CHECK( ptr2.get() == &object1 ); CHECK( ptr3.get() == &object3 ); CHECK( object1.count == 2 ); CHECK( object2.count == -1 ); CHECK( object3.count == 1 ); res = ptr1.compare_exchange_strong(ptr2, ptr3); CHECK( res ); CHECK( ptr1.load().get() == &object3 ); CHECK( ptr2.get() == &object1 ); CHECK( ptr3.get() == &object3 ); CHECK( object1.count == 1 ); CHECK( object3.count == 2 ); } SUBCASE( "Strong 2 arg" ) { instrumented_counted<> object1, object2, object3; std::atomic>> ptr1 = mock_noref(&object1); auto ptr2 = mock_noref(&object2); auto ptr3 = mock_noref(&object3); auto res = ptr1.compare_exchange_strong(ptr2, ptr3, std::memory_order_seq_cst, std::memory_order_seq_cst); CHECK( !res ); CHECK( ptr1.load().get() == &object1 ); CHECK( ptr2.get() == &object1 ); CHECK( ptr3.get() == &object3 ); CHECK( object1.count == 2 ); CHECK( object2.count == -1 ); CHECK( object3.count == 1 ); res = ptr1.compare_exchange_strong(ptr2, ptr3, std::memory_order_seq_cst, std::memory_order_seq_cst); CHECK( res ); CHECK( ptr1.load().get() == &object3 ); CHECK( ptr2.get() == &object1 ); CHECK( ptr3.get() == &object3 ); CHECK( object1.count == 1 ); CHECK( object3.count == 2 ); } SUBCASE( "Weak" ) { instrumented_counted<> object1, object2, object3; std::atomic>> ptr1 = mock_noref(&object1); auto ptr2 = mock_noref(&object2); auto ptr3 = mock_noref(&object3); auto res = ptr1.compare_exchange_weak(ptr2, ptr3); CHECK( !res ); CHECK( ptr1.load().get() == &object1 ); CHECK( ptr2.get() == &object1 ); CHECK( ptr3.get() == &object3 ); CHECK( object1.count == 2 ); CHECK( object2.count == -1 ); CHECK( object3.count == 1 ); res = ptr1.compare_exchange_weak(ptr2, ptr3); CHECK( res ); CHECK( ptr1.load().get() == &object3 ); CHECK( ptr2.get() == &object1 ); CHECK( ptr3.get() == &object3 ); CHECK( object1.count == 1 ); CHECK( object3.count == 2 ); } SUBCASE( "Weak 2 arg" ) { instrumented_counted<> object1, object2, object3; std::atomic>> ptr1 = mock_noref(&object1); auto ptr2 = mock_noref(&object2); auto ptr3 = mock_noref(&object3); auto res = ptr1.compare_exchange_weak(ptr2, ptr3, std::memory_order_seq_cst, std::memory_order_seq_cst); CHECK( !res ); CHECK( ptr1.load().get() == &object1 ); CHECK( ptr2.get() == &object1 ); CHECK( ptr3.get() == &object3 ); CHECK( object1.count == 2 ); CHECK( object2.count == -1 ); CHECK( object3.count == 1 ); res = ptr1.compare_exchange_weak(ptr2, ptr3, std::memory_order_seq_cst, std::memory_order_seq_cst); CHECK( res ); CHECK( ptr1.load().get() == &object3 ); CHECK( ptr2.get() == &object1 ); CHECK( ptr3.get() == &object3 ); CHECK( object1.count == 1 ); CHECK( object3.count == 2 ); } } } intrusive_shared_ptr-1.6/test/test_com_ptr.cpp000066400000000000000000000007131475262106200220160ustar00rootroot00000000000000 #if ISPTR_USE_MODULES import isptr; #ifdef _WIN32 #include #endif #else #include #endif #include #ifdef _WIN32 using namespace isptr; TEST_SUITE("com") { TEST_CASE( "COM Ptr") { com_shared_ptr pStream; HRESULT res = CreateStreamOnHGlobal(nullptr, true, pStream.get_output_param()); CHECK( SUCCEEDED(res) ); CHECK( pStream ); } } #endif intrusive_shared_ptr-1.6/test/test_delegating_traits.cpp000066400000000000000000000241171475262106200240500ustar00rootroot00000000000000#if ISPTR_USE_MODULES import isptr; #else #include #include #endif #include #include #include #include using namespace isptr; namespace { template class ref_counted_delegating_traits; template struct ref_counted_weak_delegating_traits { using strong_value_type = Inner; using strong_ptr_traits = ref_counted_delegating_traits; static void add_ref(const void * ref) noexcept { real_weak_from_delegating_weak(ref)->add_ref(); } static void sub_ref(const void * ref) noexcept { real_weak_from_delegating_weak(ref)->sub_ref(); } static const Inner * lock(const void * ref) noexcept { auto outer_strong = real_weak_from_delegating_weak(ref)->lock_owner(); return outer_strong ? delegating_from_real(outer_strong) : nullptr; } static Inner * lock(void * ref) noexcept { auto outer_strong = real_weak_from_delegating_weak(ref)->lock_owner(); return outer_strong ? delegating_from_real(outer_strong) : nullptr; } private: static T * real_from_delegating(Inner * pinner) { return const_cast(Converter::real_from_delegating(const_cast(pinner))); } static const T * real_from_delegating(const Inner * pinner) { return const_cast(Converter::real_from_delegating(pinner)); } static Inner * delegating_from_real(T * pouter) { return const_cast(Converter::delegating_from_real(const_cast(pouter))); } static const Inner * delegating_from_real(const T * pouter) { return const_cast(Converter::delegating_from_real(pouter)); } static typename T::weak_value_type * real_weak_from_delegating_weak(void * ref) { return static_cast(ref); } static const typename T::weak_value_type * real_weak_from_delegating_weak(const void * ref) { return static_cast(ref); } }; template class ref_counted_delegating_traits { public: using weak_value_type = void; public: using weak_ptr_traits = std::conditional_t, void>; static void add_ref(const Inner * pinner) noexcept { real_from_delegating(pinner)->add_ref(); } static void sub_ref(const Inner * pinner) noexcept { real_from_delegating(pinner)->sub_ref(); } static const void * get_weak_value(const Inner * pinner) { return real_from_delegating(pinner)->get_weak_value(); } static void * get_weak_value(Inner * pinner) { return const_cast(ref_counted_delegating_traits::get_weak_value(const_cast(pinner))); } private: static T * real_from_delegating(Inner * pinner) { return const_cast(Converter::real_from_delegating(const_cast(pinner))); } static const T * real_from_delegating(const Inner * pinner) { return const_cast(Converter::real_from_delegating(pinner)); } }; template inline intrusive_shared_ptr> weak_cast(const intrusive_shared_ptr> & src) { using dst_type = intrusive_shared_ptr>; return dst_type::noref(ref_counted_delegating_traits::get_weak_value(src.get())); } template inline intrusive_shared_ptr> weak_cast(const intrusive_shared_ptr> & src) { using dst_type = intrusive_shared_ptr>; return dst_type::noref(ref_counted_delegating_traits::get_weak_value(src.get())); } template inline intrusive_shared_ptr> strong_cast(const intrusive_shared_ptr> & src) noexcept { using dst_type = intrusive_shared_ptr>; return dst_type::noref(ref_counted_weak_delegating_traits::lock(src.get())); } template inline intrusive_shared_ptr> strong_cast(const intrusive_shared_ptr> & src) noexcept { using dst_type = intrusive_shared_ptr>; return dst_type::noref(ref_counted_weak_delegating_traits::lock(src.get())); } class outer : public ref_counted { friend ref_counted; private: struct inner_converter { static const outer * real_from_delegating(const int * pinner) noexcept { outer * dummy = nullptr; size_t distance = (uintptr_t)&(dummy->_inner) - (uintptr_t)dummy; return (const outer *)((std::byte *)pinner - distance); //return offsetof(outer, _inner); } }; using inner_traits = ref_counted_delegating_traits; friend ref_counted_delegating_traits; public: using inner_ptr = intrusive_shared_ptr; using const_inner_ptr = intrusive_shared_ptr; inner_ptr get_inner_ptr() noexcept { return inner_ptr::ref(&_inner); } const_inner_ptr get_inner_ptr() const noexcept { return const_inner_ptr::ref(&_inner); } int & inner() { return _inner; } private: int _inner = 0; }; class weak_outer : public ref_counted { friend ref_counted; private: struct inner_converter { static const weak_outer * real_from_delegating(const int * pinner) noexcept { weak_outer * dummy = nullptr; size_t distance = (uintptr_t)&(dummy->_inner) - (uintptr_t)dummy; return (const weak_outer *)((std::byte *)pinner - distance); //return offsetof(outer, _inner); } static const int * delegating_from_real(const weak_outer * pouter) noexcept { return &pouter->_inner; } }; using inner_traits = ref_counted_delegating_traits; friend ref_counted_delegating_traits; public: using inner_ptr = intrusive_shared_ptr; using const_inner_ptr = intrusive_shared_ptr; using weak_inner_ptr = intrusive_shared_ptr; using const_weak_inner_ptr = intrusive_shared_ptr; class weak_value_type : public weak_reference { friend weak_outer; friend ref_counted_weak_delegating_traits; private: weak_value_type(intptr_t count, weak_outer * owner): weak_reference(count, owner) {}; }; inner_ptr get_inner_ptr() noexcept { return inner_ptr::ref(&_inner); } const_inner_ptr get_inner_ptr() const noexcept { return const_inner_ptr::ref(&_inner); } weak_inner_ptr get_weak_inner_ptr() { return weak_inner_ptr::noref(inner_traits::get_weak_value(&_inner)); } const_weak_inner_ptr get_weak_inner_ptr() const { return const_weak_inner_ptr::noref(inner_traits::get_weak_value(&_inner)); } weak_value_type * make_weak_reference(intptr_t count) const { return new weak_value_type(count, const_cast(this)); } int & inner() { return _inner; } private: int _inner = 0; }; } TEST_SUITE("inner_ref_counted") { TEST_CASE( "Inner counting" ) { auto pouter = refcnt_attach(new outer()); auto pinner = pouter->get_inner_ptr(); CHECK(pinner); *pinner = 3; CHECK(pouter->inner() == 3); } TEST_CASE( "Weak inner counting" ) { SUBCASE( "Non const" ) { auto pouter = refcnt_attach(new weak_outer()); auto pinner1 = pouter->get_inner_ptr(); CHECK(pinner1); auto weak1 = pouter->get_weak_inner_ptr(); CHECK(weak1); auto weak2 = weak_cast(pinner1); CHECK(weak1 == weak2); auto pinner2 = strong_cast(weak2); CHECK(pinner2 == pinner1); } SUBCASE( "Const" ) { auto pouter = refcnt_attach(const_cast(new weak_outer())); auto pinner1 = pouter->get_inner_ptr(); CHECK(pinner1); auto weak1 = pouter->get_weak_inner_ptr(); CHECK(weak1); auto weak2 = weak_cast(pinner1); CHECK(weak1 == weak2); auto pinner2 = strong_cast(weak2); CHECK(pinner2 == pinner1); } } } intrusive_shared_ptr-1.6/test/test_general.cpp000066400000000000000000000360261475262106200217760ustar00rootroot00000000000000#if ISPTR_USE_MODULES import isptr; #else #include #endif #include #include "mocks.h" #include #include #include using namespace isptr; TEST_SUITE("traits") { TEST_CASE( "Type traits are correct" ) { using ptr = mock_ptr>; SUBCASE("Construction, destruction and assignment") { CHECK( sizeof(ptr) == sizeof(instrumented_counted<1> *) ); CHECK( std::alignment_of_v == std::alignment_of_v *> ); CHECK( std::is_default_constructible_v ); CHECK( std::is_nothrow_default_constructible_v ); CHECK( !std::is_trivially_default_constructible_v ); CHECK( std::is_copy_constructible_v ); CHECK( !std::is_trivially_copy_constructible_v ); CHECK( std::is_nothrow_copy_constructible_v ); CHECK( std::is_move_constructible_v ); CHECK( !std::is_trivially_move_constructible_v ); CHECK( std::is_nothrow_move_constructible_v ); CHECK( std::is_copy_assignable_v ); CHECK( !std::is_trivially_copy_assignable_v ); CHECK( std::is_nothrow_copy_assignable_v ); CHECK( std::is_move_assignable_v ); CHECK( !std::is_trivially_move_assignable_v ); CHECK( std::is_nothrow_move_assignable_v ); CHECK( std::is_swappable_v ); CHECK( std::is_nothrow_swappable_v ); CHECK( std::is_destructible_v ); CHECK( !std::is_trivially_destructible_v ); CHECK( std::is_nothrow_destructible_v ); CHECK( std::is_constructible_v ); CHECK( std::is_nothrow_constructible_v ); CHECK( !std::is_trivially_constructible_v ); } using another_ptr = mock_ptr>; SUBCASE( "Conversions to/from other ptr types" ) { CHECK( std::is_convertible_v ); CHECK( std::is_constructible_v ); CHECK( !std::is_trivially_constructible_v ); CHECK( std::is_nothrow_constructible_v ); CHECK( std::is_assignable_v ); CHECK( !std::is_trivially_assignable_v ); CHECK( std::is_nothrow_assignable_v ); CHECK( !std::is_convertible_v ); CHECK( !std::is_constructible_v ); CHECK( !std::is_assignable_v ); } using ptr_different_traits = mock_ptr_different_traits>; SUBCASE( "Conversions to/from other traits" ) { CHECK( std::is_convertible_v ); CHECK( std::is_convertible_v ); CHECK( std::is_constructible_v ); CHECK( !std::is_trivially_constructible_v ); CHECK( std::is_nothrow_constructible_v ); CHECK( std::is_assignable_v ); CHECK( !std::is_trivially_assignable_v ); CHECK( std::is_nothrow_assignable_v ); } } using IncompatibleTypes = std::tuple *, mock_ptr> >; TEST_CASE_TEMPLATE_DEFINE( "Conversion and assignment from other types", TestType, conv_assign_incompat) { using ptr = mock_ptr>; CHECK( !std::is_constructible_v ); CHECK( !std::is_assignable_v ); } TEST_CASE_TEMPLATE_APPLY(conv_assign_incompat, IncompatibleTypes); } TEST_SUITE("empty") { TEST_CASE( "Default constructed and constructed from nullptr ptr behaves like nullptr " ) { mock_ptr> empty; SUBCASE("Default constructed") { CHECK(empty.get() == nullptr); CHECK(!bool(empty)); CHECK(empty.operator->() == nullptr); } SUBCASE("Construsted from nullptr") { mock_ptr> empty1(nullptr); CHECK(empty1.get() == nullptr); CHECK(!bool(empty1)); CHECK(empty1.operator->() == nullptr); } SUBCASE( "Comparing to another empty ptr" ) { mock_ptr> empty1; CHECK(empty == empty1); CHECK(!(empty != empty1)); CHECK(!(empty < empty1)); CHECK(empty <= empty1); CHECK(!(empty > empty1)); CHECK(empty >= empty1); } SUBCASE( "Comparing to a ptr created from nullptr" ) { mock_ptr> empty1(nullptr); CHECK(empty == empty1); CHECK(!(empty != empty1)); CHECK(!(empty < empty1)); CHECK(empty <= empty1); CHECK(!(empty > empty1)); CHECK(empty >= empty1); } SUBCASE( "Comparing to nullptr" ) { CHECK(empty == nullptr); CHECK(nullptr == empty ); CHECK(!(empty != nullptr)); CHECK(!(nullptr != empty)); } SUBCASE( "Comparing to raw pointer" ) { instrumented_counted<> * raw = nullptr; CHECK(empty == raw); CHECK(raw == empty ); CHECK(!(empty != raw)); CHECK(!(raw != empty)); CHECK(!(empty < raw)); CHECK(!(raw < empty)); CHECK(empty <= raw); CHECK(raw <= empty); CHECK(!(empty > raw)); CHECK(!(raw > empty)); CHECK(empty >= raw); CHECK(raw >= empty); } SUBCASE( "Comparing to void pointer" ) { void * raw = nullptr; CHECK(empty == raw); CHECK(raw == empty ); CHECK(!(empty != raw)); CHECK(!(raw != empty)); CHECK(!(empty < raw)); CHECK(!(raw < empty)); CHECK(empty <= raw); CHECK(raw <= empty); CHECK(!(empty > raw)); CHECK(!(raw > empty)); CHECK(empty >= raw); CHECK(raw >= empty); } SUBCASE( "Comparing to different traits" ) { mock_ptr_different_traits> empty1; CHECK(empty == empty1); CHECK(!(empty != empty1)); CHECK(!(empty < empty1)); CHECK(empty <= empty1); CHECK(!(empty > empty1)); CHECK(empty >= empty1); } } } TEST_SUITE("counting") { TEST_CASE( "Basic counting " ) { SUBCASE( "Attach" ) { instrumented_counted<> object; auto ptr = mock_noref(&object); REQUIRE( &object == ptr.get() ); REQUIRE( object.count == 1 ); CHECK(bool(ptr)); CHECK(ptr.operator->() == &object); } SUBCASE( "Ref" ) { instrumented_counted<> object; auto ptr = mock_ref(&object); REQUIRE( &object == ptr.get() ); REQUIRE( object.count == 2 ); CHECK(bool(ptr)); CHECK(ptr.operator->() == &object); mock_traits<>::sub_ref(&object); } SUBCASE( "Release" ) { instrumented_counted<> object; auto ptr = mock_noref(&object); REQUIRE( object.count == 1 ); auto p = ptr.release(); CHECK( p == &object ); CHECK(ptr.get() == nullptr); CHECK(!bool(ptr)); CHECK(ptr.operator->() == nullptr); mock_traits<>::sub_ref(&object); } SUBCASE( "Reset" ) { instrumented_counted<> object; auto ptr = mock_noref(&object); REQUIRE( object.count == 1 ); ptr.reset(); CHECK(ptr.get() == nullptr); CHECK(!bool(ptr)); CHECK(ptr.operator->() == nullptr); } SUBCASE( "Copy and assignment" ) { instrumented_counted<> object; auto ptr = mock_noref(&object); REQUIRE( object.count == 1 ); auto ptr2(ptr); REQUIRE( object.count == 2 ); auto ptr3(mock_ref(&object)); REQUIRE( object.count == 3 ); mock_ptr> ptr4; ptr4 = ptr; REQUIRE( object.count == 4 ); mock_ptr> ptr5; ptr5 = mock_ref(&object); REQUIRE( object.count == 5 ); } SUBCASE( "Self assignment" ) { instrumented_counted<> object; auto ptr = mock_noref(&object); ptr = ptr; REQUIRE( object.count == 1 ); ptr = std::move(ptr); REQUIRE( ptr.get() == &object ); REQUIRE( object.count == 1 ); } SUBCASE( " Swap ") { instrumented_counted<> object1, object2; auto ptr1 = mock_noref(&object1); auto ptr2 = mock_noref(&object2); swap(ptr1, ptr2); CHECK( ptr1.get() == &object2 ); CHECK( ptr2.get() == &object1 ); REQUIRE( object1.count == 1 ); REQUIRE( object2.count == 1 ); ptr1.swap(ptr2); CHECK( ptr1.get() == &object1 ); CHECK( ptr2.get() == &object2 ); REQUIRE( object1.count == 1 ); REQUIRE( object2.count == 1 ); ptr2.swap(ptr1); CHECK( ptr1.get() == &object2 ); CHECK( ptr2.get() == &object1 ); REQUIRE( object1.count == 1 ); REQUIRE( object2.count == 1 ); } } TEST_CASE( "Conversions " ) { SUBCASE( "Attach" ) { derived_instrumented_counted<> object; mock_ptr> ptr = mock_noref(&object); REQUIRE( &object == ptr.get() ); REQUIRE( object.count == 1 ); CHECK(bool(ptr)); CHECK(ptr.operator->() == &object); } SUBCASE( "Ref" ) { derived_instrumented_counted<> object; mock_ptr> ptr = mock_ref(&object); REQUIRE( &object == ptr.get() ); REQUIRE( object.count == 2 ); CHECK(bool(ptr)); CHECK(ptr.operator->() == &object); mock_traits<>::sub_ref(&object); } SUBCASE( "Release" ) { derived_instrumented_counted<> object; mock_ptr> ptr = mock_noref(&object); REQUIRE( object.count == 1 ); auto p = ptr.release(); CHECK( p == &object ); CHECK(ptr.get() == nullptr); CHECK(!bool(ptr)); CHECK(ptr.operator->() == nullptr); mock_traits<>::sub_ref(&object); } SUBCASE( "Reset" ) { derived_instrumented_counted<> object; mock_ptr> ptr = mock_noref(&object); REQUIRE( object.count == 1 ); ptr.reset(); CHECK(ptr.get() == nullptr); CHECK(!bool(ptr)); CHECK(ptr.operator->() == nullptr); } SUBCASE( "Copy and assignment" ) { derived_instrumented_counted<> object; auto ptr = mock_noref(&object); REQUIRE( object.count == 1 ); mock_ptr> ptr2(ptr); REQUIRE( object.count == 2 ); mock_ptr> ptr3(mock_ref(&object)); REQUIRE( object.count == 3 ); mock_ptr> ptr4; ptr4 = ptr; REQUIRE( object.count == 4 ); mock_ptr> ptr5; ptr5 = mock_ref(&object); REQUIRE( object.count == 5 ); } } TEST_CASE( "Different traits " ) { SUBCASE( "Copy" ) { instrumented_counted<> object; mock_ptr_different_traits> ptr = mock_noref_different_traits(&object); mock_ptr> ptr1(ptr); REQUIRE( &object == ptr1.get() ); REQUIRE( object.count == 2 ); } SUBCASE( "Move" ) { instrumented_counted<> object; mock_ptr_different_traits> ptr = mock_noref_different_traits(&object); mock_ptr> ptr1(std::move(ptr)); REQUIRE( &object == ptr1.get() ); REQUIRE( object.count == 1 ); } } TEST_CASE( "Casts" ) { derived_instrumented_counted<> object; const instrumented_counted<> const_object; derived_instrumented_counted<> derived_object; mock_ptr> ptr = mock_noref(&object); auto ptr_const = mock_noref(&const_object); auto ptr_derived = mock_noref(&derived_object); SUBCASE( "Const cast" ) { auto res = intrusive_const_cast>>(ptr_const); CHECK( res.get() == &const_object ); CHECK( const_object.count == 2 ); res = intrusive_const_cast>>(mock_ref(&const_object)); CHECK( res.get() == &const_object ); CHECK( const_object.count == 2 ); } SUBCASE( "Dynamic cast" ) { auto res = intrusive_dynamic_cast>>(ptr); CHECK( res.get() == &object ); CHECK( object.count == 2 ); res = intrusive_dynamic_cast>>(mock_ptr>(ptr)); CHECK( res.get() == &object ); CHECK( object.count == 2 ); auto res1 = intrusive_dynamic_cast>>(ptr_const); CHECK( !res1 ); res1 = intrusive_dynamic_cast>>(mock_ref(&const_object)); CHECK( !res1 ); } SUBCASE( "Static cast" ) { auto res = intrusive_static_cast>>(ptr); CHECK( res.get() == &object ); CHECK( object.count == 2 ); res = intrusive_static_cast>>(mock_ptr>(ptr)); CHECK( res.get() == &object ); CHECK( object.count == 2 ); } } } TEST_SUITE("output") { TEST_CASE( "Output" ) { instrumented_counted<> object; auto ptr = mock_noref(&object); std::ostringstream str1; auto & res = (str1 << ptr); CHECK( &res == &str1 ); std::ostringstream str2; str2 << &object; CHECK( str1.str() == str2.str() ); } } TEST_SUITE("output param") { TEST_CASE( "Output param" ) { instrumented_counted<> object, object1; auto ptr = mock_noref(&object); auto ptr1 = mock_noref(&object1); auto func = [&object1] (instrumented_counted<> ** out) { mock_traits<>::add_ref(&object1); *out = &object1; }; func(ptr.get_output_param()); CHECK( ptr.get() == &object1 ); CHECK( object.count == -1 ); CHECK( object1.count == 2 ); } TEST_CASE( "Member pointer" ) { instrumented_counted<> object; auto ptr = mock_noref(&object); int instrumented_counted<>::*pcount = &instrumented_counted<>::count; auto x = ptr->*pcount; CHECK(x == 1); } } intrusive_shared_ptr-1.6/test/test_main.cpp000066400000000000000000000005411475262106200212760ustar00rootroot00000000000000#define DOCTEST_CONFIG_IMPLEMENT #include #if ISPTR_USE_PYTHON #include #endif int main(int argc, char** argv) { #if ISPTR_USE_PYTHON Py_Initialize(); #endif int ret = doctest::Context(argc, argv).run(); #if ISPTR_USE_PYTHON if (Py_FinalizeEx() < 0) { return 120; } #endif return ret; } intrusive_shared_ptr-1.6/test/test_python_ptr.cpp000066400000000000000000000007141475262106200225620ustar00rootroot00000000000000 #if ISPTR_USE_PYTHON #if ISPTR_USE_MODULES import isptr; #define PY_SSIZE_T_CLEAN #include #else #define PY_SSIZE_T_CLEAN #include #endif #include using namespace isptr; TEST_SUITE("python") { TEST_CASE( "Python Ptr" ) { auto str = py_attach(PyUnicode_FromString("Hello")); REQUIRE(str); CHECK( PyUnicode_GetLength(str.get()) == 5 ); } } #endif intrusive_shared_ptr-1.6/test/test_ref_counted.cpp000066400000000000000000000131131475262106200226460ustar00rootroot00000000000000#if ISPTR_USE_MODULES import isptr; #else #include #include #endif #include #include #include using namespace isptr; namespace { struct minimal_counted : ref_counted { friend ref_counted; private: ~minimal_counted() noexcept {} }; static_assert( !std::is_default_constructible_v> ); static_assert( !std::is_copy_constructible_v> ); static_assert( !std::is_move_constructible_v> ); static_assert( !std::is_copy_assignable_v> ); static_assert( !std::is_move_assignable_v> ); static_assert( !std::is_swappable_v> ); static_assert( !std::is_destructible_v> ); static_assert( sizeof(minimal_counted) == sizeof(char) ); static_assert( !std::is_default_constructible_v ); static_assert( !std::is_copy_constructible_v ); static_assert( !std::is_move_constructible_v ); static_assert( !std::is_copy_assignable_v ); static_assert( !std::is_move_assignable_v ); static_assert( !std::is_swappable_v ); static_assert( !std::is_destructible_v ); struct adapded { char c; }; using minimal_adapded_counted = ref_counted_adapter; static_assert( sizeof(minimal_adapded_counted) == 2 * sizeof(char) ); static_assert( !std::is_default_constructible_v ); static_assert( !std::is_copy_constructible_v ); static_assert( !std::is_move_constructible_v ); static_assert( !std::is_copy_assignable_v ); static_assert( !std::is_move_assignable_v ); static_assert( !std::is_destructible_v ); using minimal_wrapped_counted = ref_counted_wrapper; static_assert( sizeof(minimal_wrapped_counted) == 2 * sizeof(char) ); static_assert( !std::is_default_constructible_v ); static_assert( !std::is_copy_constructible_v ); static_assert( !std::is_move_constructible_v ); static_assert( !std::is_copy_assignable_v ); static_assert( !std::is_move_assignable_v ); static_assert( !std::is_destructible_v ); struct simple_counted : ref_counted { friend ref_counted; static inline int instance_count = 0; simple_counted() noexcept { ++instance_count; } simple_counted(int) { throw std::runtime_error("x"); } private: ~simple_counted() noexcept { --instance_count; } }; static_assert( !std::is_default_constructible_v ); static_assert( !std::is_copy_constructible_v ); static_assert( !std::is_move_constructible_v ); static_assert( !std::is_copy_assignable_v ); static_assert( !std::is_move_assignable_v ); static_assert( !std::is_destructible_v ); } TEST_SUITE("ref_counted") { TEST_CASE( "Minimal ref counted works" ) { SUBCASE("derived") { auto p1 = refcnt_attach(new minimal_counted()); CHECK(p1); auto p2 = p1; CHECK(p2 == p1); } SUBCASE("adapted") { auto p1 = refcnt_attach(new minimal_adapded_counted(adapded{'a'})); CHECK(p1); CHECK(p1->c == 'a'); auto p2 = p1; CHECK(p2 == p1); } SUBCASE("wrapped") { auto p1 = refcnt_attach(new minimal_wrapped_counted('a')); CHECK(p1); CHECK(p1->wrapped == 'a'); auto p2 = p1; CHECK(p2 == p1); } } TEST_CASE( "Simple ref counted works" ) { auto p1 = refcnt_attach(new simple_counted()); CHECK(simple_counted::instance_count == 1); auto p2 = p1; CHECK(simple_counted::instance_count == 1); p1.reset(); CHECK(simple_counted::instance_count == 1); p2.reset(); CHECK(simple_counted::instance_count == 0); } TEST_CASE( "Ref counted with ctor exception" ) { try { auto p1 = refcnt_attach(new simple_counted(2)); } catch(std::exception &) { CHECK(simple_counted::instance_count == 0); } } TEST_CASE( "Custom destroy" ) { struct custom_destroy : ref_counted { friend ref_counted; custom_destroy(bool * d): destroyed(d) {} bool * destroyed; private: void destroy() const noexcept { *destroyed = true; free((void*)this); } }; bool destroyed = false; auto p1 = refcnt_attach(new (malloc(sizeof(custom_destroy))) custom_destroy{&destroyed}); p1.reset(); CHECK(destroyed); } TEST_CASE( "Ref counted wrapper" ) { SUBCASE("adapter") { auto p1 = refcnt_attach(new ref_counted_adapter>(5)); CHECK( p1->size() == 5 ); } SUBCASE("wrapper") { auto p1 = refcnt_attach(new ref_counted_wrapper>(5)); CHECK( p1->wrapped.size() == 5 ); } } } intrusive_shared_ptr-1.6/test/test_ref_counted_st.cpp000066400000000000000000000132131475262106200233550ustar00rootroot00000000000000#if ISPTR_USE_MODULES import isptr; #else #include #include #endif #include #include #include using namespace isptr; namespace { struct minimal_counted : ref_counted { friend ref_counted; private: ~minimal_counted() noexcept {} }; static_assert( !std::is_default_constructible_v> ); static_assert( !std::is_copy_constructible_v> ); static_assert( !std::is_move_constructible_v> ); static_assert( !std::is_copy_assignable_v> ); static_assert( !std::is_move_assignable_v> ); static_assert( !std::is_swappable_v> ); static_assert( !std::is_destructible_v> ); static_assert( sizeof(minimal_counted) == sizeof(char) ); static_assert( !std::is_default_constructible_v ); static_assert( !std::is_copy_constructible_v ); static_assert( !std::is_move_constructible_v ); static_assert( !std::is_copy_assignable_v ); static_assert( !std::is_move_assignable_v ); static_assert( !std::is_swappable_v ); static_assert( !std::is_destructible_v ); struct adapded { char c; }; using minimal_adapded_counted = ref_counted_adapter; static_assert( sizeof(minimal_adapded_counted) == 2 * sizeof(char) ); static_assert( !std::is_default_constructible_v ); static_assert( !std::is_copy_constructible_v ); static_assert( !std::is_move_constructible_v ); static_assert( !std::is_copy_assignable_v ); static_assert( !std::is_move_assignable_v ); static_assert( !std::is_destructible_v ); using minimal_wrapped_counted = ref_counted_wrapper; static_assert( sizeof(minimal_wrapped_counted) == 2 * sizeof(char) ); static_assert( !std::is_default_constructible_v ); static_assert( !std::is_copy_constructible_v ); static_assert( !std::is_move_constructible_v ); static_assert( !std::is_copy_assignable_v ); static_assert( !std::is_move_assignable_v ); static_assert( !std::is_destructible_v ); struct simple_counted : ref_counted_st { friend ref_counted; static inline int instance_count = 0; simple_counted() noexcept { ++instance_count; } simple_counted(int) { throw std::runtime_error("x"); } private: ~simple_counted() noexcept { --instance_count; } }; static_assert( !std::is_default_constructible_v ); static_assert( !std::is_copy_constructible_v ); static_assert( !std::is_move_constructible_v ); static_assert( !std::is_copy_assignable_v ); static_assert( !std::is_move_assignable_v ); static_assert( !std::is_destructible_v ); } TEST_SUITE("ref_counted_st") { TEST_CASE( "Minimal st ref counted works" ) { SUBCASE("derived") { auto p1 = refcnt_attach(new minimal_counted()); CHECK(p1); auto p2 = p1; CHECK(p2 == p1); } SUBCASE("adapted") { auto p1 = refcnt_attach(new minimal_adapded_counted(adapded{'a'})); CHECK(p1); CHECK(p1->c == 'a'); auto p2 = p1; CHECK(p2 == p1); } SUBCASE("wrapped") { auto p1 = refcnt_attach(new minimal_wrapped_counted('a')); CHECK(p1); CHECK(p1->wrapped == 'a'); auto p2 = p1; CHECK(p2 == p1); } } TEST_CASE( "Simple st ref counted works" ) { auto p1 = refcnt_attach(new simple_counted()); CHECK(simple_counted::instance_count == 1); auto p2 = p1; CHECK(simple_counted::instance_count == 1); p1.reset(); CHECK(simple_counted::instance_count == 1); p2.reset(); CHECK(simple_counted::instance_count == 0); } TEST_CASE( "St ref counted with ctor exception" ) { try { auto p1 = refcnt_attach(new simple_counted(2)); } catch(std::exception &) { CHECK(simple_counted::instance_count == 0); } } TEST_CASE( "Custom destroy st" ) { struct custom_destroy : ref_counted_st { friend ref_counted; custom_destroy(bool * d): destroyed(d) {} bool * destroyed; private: void destroy() const noexcept { *destroyed = true; free((void*)this); } }; bool destroyed = false; auto p1 = refcnt_attach(new (malloc(sizeof(custom_destroy))) custom_destroy{&destroyed}); p1.reset(); CHECK(destroyed); } TEST_CASE( "St ref counted wrapper" ) { SUBCASE("adapter") { auto p1 = refcnt_attach(new ref_counted_adapter_st>(5)); CHECK( p1->size() == 5 ); } SUBCASE("wrapper") { auto p1 = refcnt_attach(new ref_counted_wrapper_st>(5)); CHECK( p1->wrapped.size() == 5 ); } } } intrusive_shared_ptr-1.6/test/test_weak_ref_counted.cpp000066400000000000000000000160321475262106200236600ustar00rootroot00000000000000#if ISPTR_USE_MODULES import isptr; #else #include #include #endif #include #include #include using namespace isptr; namespace { int derived_count = 0; struct derived_counted : weak_ref_counted { friend ref_counted; public: derived_counted() noexcept { ++derived_count; } private: ~derived_counted() noexcept { auto weak = get_weak_ptr(); CHECK(weak); --derived_count; } }; int wrapped_count = 0; struct wrapped { wrapped() { ++wrapped_count; } ~wrapped() { --wrapped_count; } int value = 5; }; using wrapped_counted = weak_ref_counted_adapter; int with_custom_weak_reference_count = 0; struct custom_weak_reference; class with_custom_weak_reference : public ref_counted { friend ref_counted; public: with_custom_weak_reference() { ++with_custom_weak_reference_count; } private: ~with_custom_weak_reference() noexcept { auto weak = get_weak_ptr(); CHECK(weak); --with_custom_weak_reference_count; } custom_weak_reference * make_weak_reference(intptr_t count) const; }; struct custom_weak_reference : weak_reference { custom_weak_reference(intptr_t count, with_custom_weak_reference * obj): weak_reference(count, obj) {} ~custom_weak_reference() { CHECK(on_owner_destruction_called); CHECK(with_custom_weak_reference_count == 0); } void on_owner_destruction() const { CHECK(with_custom_weak_reference_count == 0); //owner is still alive but its refcount is 0, you cannot ressurrect! on_owner_destruction_called = true; } mutable bool on_owner_destruction_called = false; }; inline custom_weak_reference * with_custom_weak_reference::make_weak_reference(intptr_t count) const { return new custom_weak_reference(count, const_cast(this)); } } TEST_SUITE("traits") { TEST_CASE( "Weak ref counted type traits are correct" ) { SUBCASE( "Base" ) { CHECK( !std::is_default_constructible_v> ); CHECK( !std::is_copy_constructible_v> ); CHECK( !std::is_move_constructible_v> ); CHECK( !std::is_copy_assignable_v> ); CHECK( !std::is_move_assignable_v> ); CHECK( !std::is_swappable_v> ); CHECK( !std::is_destructible_v> ); } SUBCASE( "Derived" ) { CHECK( !std::is_default_constructible_v ); CHECK( !std::is_copy_constructible_v ); CHECK( !std::is_move_constructible_v ); CHECK( !std::is_copy_assignable_v ); CHECK( !std::is_move_assignable_v ); CHECK( !std::is_swappable_v ); CHECK( !std::is_destructible_v ); } SUBCASE( "Derived WeakRef" ) { CHECK( !std::is_default_constructible_v ); CHECK( !std::is_copy_constructible_v ); CHECK( !std::is_move_constructible_v ); CHECK( !std::is_copy_assignable_v ); CHECK( !std::is_move_assignable_v ); CHECK( !std::is_swappable_v ); CHECK( !std::is_destructible_v ); } SUBCASE( "Wrapped" ) { CHECK( !std::is_default_constructible_v ); CHECK( !std::is_copy_constructible_v ); CHECK( !std::is_move_constructible_v ); CHECK( !std::is_copy_assignable_v ); CHECK( !std::is_move_assignable_v ); CHECK( !std::is_destructible_v ); } SUBCASE( "Wrapped WeakRef" ) { CHECK( !std::is_default_constructible_v ); CHECK( !std::is_copy_constructible_v ); CHECK( !std::is_move_constructible_v ); CHECK( !std::is_copy_assignable_v ); CHECK( !std::is_move_assignable_v ); CHECK( !std::is_destructible_v ); } } } TEST_SUITE("ref_counted") { TEST_CASE( "Weak ref counted works" ) { SUBCASE( "Derived" ) { auto original = refcnt_attach(new derived_counted()); auto weak1 = original->get_weak_ptr(); CHECK(derived_count == 1); auto strong1 = weak1->lock(); CHECK(original == strong1); auto weak2 = strong1->get_weak_ptr(); CHECK(weak1 == weak2); auto weak3 = weak_cast(strong1); CHECK(weak1 == weak3); original.reset(); strong1.reset(); CHECK(derived_count == 0); strong1 = weak1->lock(); CHECK(!strong1); } SUBCASE( "Const Derived" ) { refcnt_ptr original = refcnt_attach(new derived_counted()); auto weak1 = original->get_weak_ptr(); CHECK(derived_count == 1); auto strong1 = weak1->lock(); CHECK(original == strong1); auto weak2 = strong1->get_weak_ptr(); CHECK(weak1 == weak2); original.reset(); strong1.reset(); CHECK(derived_count == 0); strong1 = weak1->lock(); CHECK(!strong1); strong1 = strong_cast(weak2); CHECK(!strong1); } SUBCASE( "Wrapped" ) { auto p = refcnt_attach(new wrapped_counted()); CHECK(wrapped_count == 1); CHECK(p->value == 5); p.reset(); CHECK( wrapped_count == 0 ); } SUBCASE( "Custom Weak Reference" ) { auto strong = refcnt_attach(new with_custom_weak_reference); CHECK(with_custom_weak_reference_count == 1); auto weak = strong->get_weak_ptr(); auto strong1 = weak->lock(); CHECK( strong1 == strong ); CHECK(with_custom_weak_reference_count == 1); strong.reset(); CHECK(with_custom_weak_reference_count == 1); strong1.reset(); CHECK(with_custom_weak_reference_count == 0); strong1 = weak->lock(); CHECK(!strong1); } } } intrusive_shared_ptr-1.6/test/test_weak_ref_counted_st.cpp000066400000000000000000000170151475262106200243700ustar00rootroot00000000000000#if ISPTR_USE_MODULES import isptr; #else #include #include #endif #include #include #include using namespace isptr; namespace { int derived_count = 0; struct derived_counted : weak_ref_counted_st { friend ref_counted; public: derived_counted() noexcept { ++derived_count; } private: ~derived_counted() noexcept { auto weak = get_weak_ptr(); CHECK(weak); --derived_count; } }; int wrapped_count = 0; struct wrapped { wrapped() { ++wrapped_count; } ~wrapped() { --wrapped_count; } int value = 5; }; using wrapped_counted = weak_ref_counted_adapter_st; int with_custom_weak_reference_count = 0; struct custom_weak_reference; class with_custom_weak_reference : public ref_counted { friend ref_counted; public: with_custom_weak_reference() { ++with_custom_weak_reference_count; } private: ~with_custom_weak_reference() noexcept { auto weak = get_weak_ptr(); CHECK(weak); --with_custom_weak_reference_count; } custom_weak_reference * make_weak_reference(intptr_t count) const; }; struct custom_weak_reference : weak_reference { custom_weak_reference(intptr_t count, with_custom_weak_reference * obj): weak_reference(count, obj) {} ~custom_weak_reference() { CHECK(on_owner_destruction_called); CHECK(with_custom_weak_reference_count == 0); } void on_owner_destruction() const { CHECK(with_custom_weak_reference_count == 0); //owner is still alive but its refcount is 0, you cannot ressurrect! on_owner_destruction_called = true; } mutable bool on_owner_destruction_called = false; }; inline custom_weak_reference * with_custom_weak_reference::make_weak_reference(intptr_t count) const { return new custom_weak_reference(count, const_cast(this)); } } TEST_SUITE("traits") { TEST_CASE( "Weak ref counted st type traits are correct" ) { SUBCASE( "Base" ) { CHECK( weak_ref_counted_st::single_threaded); CHECK( !std::is_default_constructible_v> ); CHECK( !std::is_copy_constructible_v> ); CHECK( !std::is_move_constructible_v> ); CHECK( !std::is_copy_assignable_v> ); CHECK( !std::is_move_assignable_v> ); CHECK( !std::is_swappable_v> ); CHECK( !std::is_destructible_v> ); } SUBCASE( "Derived" ) { CHECK( derived_counted::single_threaded); CHECK( !std::is_default_constructible_v ); CHECK( !std::is_copy_constructible_v ); CHECK( !std::is_move_constructible_v ); CHECK( !std::is_copy_assignable_v ); CHECK( !std::is_move_assignable_v ); CHECK( !std::is_swappable_v ); CHECK( !std::is_destructible_v ); } SUBCASE( "Derived WeakRef" ) { CHECK( derived_counted::weak_value_type::single_threaded); CHECK( !std::is_default_constructible_v ); CHECK( !std::is_copy_constructible_v ); CHECK( !std::is_move_constructible_v ); CHECK( !std::is_copy_assignable_v ); CHECK( !std::is_move_assignable_v ); CHECK( !std::is_swappable_v ); CHECK( !std::is_destructible_v ); } SUBCASE( "Wrapped" ) { CHECK( wrapped_counted::single_threaded); CHECK( !std::is_default_constructible_v ); CHECK( !std::is_copy_constructible_v ); CHECK( !std::is_move_constructible_v ); CHECK( !std::is_copy_assignable_v ); CHECK( !std::is_move_assignable_v ); CHECK( !std::is_destructible_v ); } SUBCASE( "Wrapped WeakRef" ) { CHECK( wrapped_counted::weak_value_type::single_threaded); CHECK( !std::is_default_constructible_v ); CHECK( !std::is_copy_constructible_v ); CHECK( !std::is_move_constructible_v ); CHECK( !std::is_copy_assignable_v ); CHECK( !std::is_move_assignable_v ); CHECK( !std::is_destructible_v ); } } } TEST_SUITE("ref_counted_st") { TEST_CASE( "Weak ref counted st works" ) { SUBCASE( "Derived" ) { auto original = refcnt_attach(new derived_counted()); auto weak1 = original->get_weak_ptr(); CHECK(derived_count == 1); auto strong1 = weak1->lock(); CHECK(original == strong1); auto weak2 = strong1->get_weak_ptr(); CHECK(weak1 == weak2); auto weak3 = weak_cast(strong1); CHECK(weak1 == weak3); original.reset(); strong1.reset(); CHECK(derived_count == 0); strong1 = weak1->lock(); CHECK(!strong1); } SUBCASE( "Const Derived" ) { refcnt_ptr original = refcnt_attach(new derived_counted()); auto weak1 = original->get_weak_ptr(); CHECK(derived_count == 1); auto strong1 = weak1->lock(); CHECK(original == strong1); auto weak2 = strong1->get_weak_ptr(); CHECK(weak1 == weak2); original.reset(); strong1.reset(); CHECK(derived_count == 0); strong1 = weak1->lock(); CHECK(!strong1); strong1 = strong_cast(weak2); CHECK(!strong1); } SUBCASE( "Wrapped" ) { auto p = refcnt_attach(new wrapped_counted()); CHECK(wrapped_count == 1); CHECK(p->value == 5); p.reset(); CHECK( wrapped_count == 0 ); } SUBCASE( "Custom Weak Reference" ) { auto strong = refcnt_attach(new with_custom_weak_reference); CHECK(with_custom_weak_reference_count == 1); auto weak = strong->get_weak_ptr(); auto strong1 = weak->lock(); CHECK( strong1 == strong ); CHECK(with_custom_weak_reference_count == 1); strong.reset(); CHECK(with_custom_weak_reference_count == 1); strong1.reset(); CHECK(with_custom_weak_reference_count == 0); strong1 = weak->lock(); CHECK(!strong1); } } } intrusive_shared_ptr-1.6/tools/000077500000000000000000000000001475262106200167705ustar00rootroot00000000000000intrusive_shared_ptr-1.6/tools/create-release000077500000000000000000000024241475262106200216010ustar00rootroot00000000000000#! /usr/bin/env -S python3 -u # pylint: disable=missing-module-docstring, missing-function-docstring, line-too-long import sys import re import subprocess from pathlib import Path from datetime import date MYPATH = Path(__file__).parent ROOT = MYPATH.parent NEW_VER = sys.argv[1] unreleased_link_pattern = re.compile(r"^\[Unreleased\]: (.*)$", re.DOTALL) lines = [] with open(ROOT / "CHANGELOG.md", "rt", encoding='utf-8') as change_log: for line in change_log.readlines(): # Move Unreleased section to new version if re.fullmatch(r"^## Unreleased.*$", line, re.DOTALL): lines.append(line) lines.append("\n") lines.append( f"## [{NEW_VER}] - {date.today().isoformat()}\n" ) else: lines.append(line) lines.append(f'[{NEW_VER}]: https://github.com/gershnik/intrusive_shared_ptr/releases/v{NEW_VER}\n') with open(ROOT / "CHANGELOG.md", "wt", encoding='utf-8') as change_log: change_log.writelines(lines) (ROOT / "VERSION").write_text(f'{NEW_VER}\n') subprocess.run(['git', 'add', ROOT / "CHANGELOG.md", ROOT / "VERSION"], check=True) subprocess.run(['git', 'commit', '-m', f'chore: creating version {NEW_VER}'], check=True) subprocess.run(['git', 'tag', f'v{NEW_VER}'], check=True) intrusive_shared_ptr-1.6/tools/make-module000077500000000000000000000067121475262106200211240ustar00rootroot00000000000000#! /usr/bin/env -S python3 -u # Copyright 2024 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 # # pylint: disable=missing-module-docstring, missing-function-docstring, line-too-long import sys import re from pathlib import Path from typing import Dict, List comment_start_re = re.compile(r'\s*/\*.*') comment_end_re = re.compile(r'.*\*/\s*') sys_include_re = re.compile(r'\s*#\s*include\s+<([^>]+)>\s*') lib_include_re = re.compile(r'\s*#\s*include\s+]+)>\s*') user_include_re = re.compile(r'\s*#\s*include\s+"([^"]+)"\s*') SPECIAL_HEADERS = { 'CoreFoundation/CoreFoundation.h': ''' #if (defined(__APPLE__) && defined(__MACH__)) ##INCLUDE## #endif '''.lstrip(), 'Unknwn.h': ''' #if defined(_WIN32) #define NOMINMAX ##INCLUDE## #endif '''.lstrip(), 'Python.h': ''' #if ISPTR_ENABLE_PYTHON ##INCLUDE## #endif '''.lstrip() } def process_header(folder: Path, path: Path, sys_includes: List[str], processed_headers:Dict[str, bool], strip_initial_comment: bool): ret = "" initial_comment_state = 0 with open(path, "rt", encoding='utf-8') as headerfile: for line in headerfile: if strip_initial_comment: if initial_comment_state == 0: m = comment_start_re.match(line) if m: initial_comment_state = 1 continue elif initial_comment_state == 1: m = comment_end_re.match(line) if m: initial_comment_state = 2 continue m = user_include_re.match(line) if not m: m = lib_include_re.match(line) if m: name = m.group(1) if not processed_headers.get(name): new_path = (folder / name).absolute() ret += process_header(new_path.parent, new_path, sys_includes, processed_headers, strip_initial_comment=True) processed_headers[name] = True continue m = sys_include_re.match(line) if m: sys_includes.append(m.group(1)) continue ret += line return ret def combine_headers(folder: Path, template: Path, output: Path): sys_includes = [] processed_headers = {} text = process_header(folder, template, sys_includes, processed_headers, strip_initial_comment=False) sys_includes = list(set(sys_includes)) sys_includes.sort() sys_includes_text = "" for sys_include in sys_includes: inc_line = f"#include <{sys_include}>" if (repl := SPECIAL_HEADERS.get(sys_include)) is not None: repl = repl.replace('##INCLUDE##', inc_line) else: repl = inc_line sys_includes_text += ("\n" + repl) text = text.replace("##SYS_INCLUDES##", sys_includes_text) output.parent.mkdir(parents=True, exist_ok=True) with open(output, "wt", encoding='utf-8') as outfile: print(text, file=outfile) def main(): mydir = Path(__file__).parent incdir = mydir.parent / 'inc/intrusive_shared_ptr' moddir = mydir.parent / 'modules' combine_headers(incdir, mydir / 'module-template.txt', moddir / 'isptr.cppm') if __name__ == "__main__": sys.exit(main()) intrusive_shared_ptr-1.6/tools/module-template.txt000066400000000000000000000007441475262106200226340ustar00rootroot00000000000000/* Copyright 2024 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 */ module; ##SYS_INCLUDES## export module isptr; #define ISPTR_EXPORTED export #include "intrusive_shared_ptr.h" #include "ref_counted.h" #include "apple_cf_ptr.h" #include "com_ptr.h" #if ISPTR_ENABLE_PYTHON #include "python_ptr.h" #endif #include "refcnt_ptr.h"