From 0e18026fba8eab1b646488770c02001fad7b08b7 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sat, 23 Sep 2023 15:47:31 -0400 Subject: [PATCH 01/60] initial commit --- README.md | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..0835e7b9 --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +# intrusive reference counting + +Refcnt is a small shared library supplying intrusive reference counting. + +## Features + +- base class `ref::Refcounted`. + Application classes opt-in to reference counting by inheriting this class. +- common base simplifies connecting to common-base-object applications such as python, java etc. + +## Getting Started + +### build + install `indentlog` dependency + +see [github/rconybea/indentlog](https://github.com/Rconybea/indentlog) + +### copy `refcnt` repository locally +``` +$ git clone git@github.com:rconybea/refcnt.git +$ ls -d refcnt +refcnt +``` + +### build + install +``` +$ cd refcnt +$ mkdir build +$ cd build +$ cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} .. +$ make +$ make install +``` + +alternatively, if you're a nix user: +``` +$ git clone git@github.com:rconybea/xo-nix.git +$ ls -d xo-nix +xo-nix +$ cd xo-nix +$ nix-build -A refcnt +``` + +## Examples + +### 1 +``` +#include "refcnt/Refcounted.hpp" + +using xo::ref::Refcounted; + +struct MyObject : public Refcounted { + static rp make() { return new MyObject(); } + +private: + // intrusively-reference-counted objects should only be heap-allocated + MyObject() { ... } +}; + +int main() { + // create reference-counted instance + auto x = MyObject::make(); + auto y = x; + // x,y refer to the same instance. + x = nullptr; + // y holds last reference + y = nullptr; + // MyObject has been deleted +} +``` + +### 2 + +To log reference-counting activity + +``` +xo::ref::intrusive_ptr_set_debug(true); +``` From 87b4bfa7953f254e716611f8a30e4679a39bfd54 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sat, 23 Sep 2023 15:48:36 -0400 Subject: [PATCH 02/60] + implementation --- CMakeLists.txt | 52 +++ cmake/code-coverage.cmake | 678 +++++++++++++++++++++++++++++++++ cmake/cxx.cmake | 86 +++++ include/cxxutil/demangle.hpp | 92 +++++ include/refcnt/Displayable.hpp | 29 ++ include/refcnt/Refcounted.hpp | 294 ++++++++++++++ include/refcnt/Unowned.hpp | 28 ++ src/CMakeLists.txt | 14 + src/Displayable.cpp | 16 + src/Refcounted.cpp | 157 ++++++++ 10 files changed, 1446 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 cmake/code-coverage.cmake create mode 100644 cmake/cxx.cmake create mode 100644 include/cxxutil/demangle.hpp create mode 100644 include/refcnt/Displayable.hpp create mode 100644 include/refcnt/Refcounted.hpp create mode 100644 include/refcnt/Unowned.hpp create mode 100644 src/CMakeLists.txt create mode 100644 src/Displayable.cpp create mode 100644 src/Refcounted.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..1e8053f9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,52 @@ +# refcnt/CMakeLists.txt + +cmake_minimum_required(VERSION 3.10) + +project(xo-refcnt VERSION 0.1) +enable_language(CXX) + +include(cmake/cxx.cmake) +include(cmake/code-coverage.cmake) + +# ---------------------------------------------------------------- +# unit test setup + +enable_testing() + + +# activate code coverage for all executables + libraries (when configured with -DCODE_COVERAGE=ON) +add_code_coverage() +# 1. assuming that /nix/store/ prefixes .hpp files belonging to gcc, catch2 etc. +# we're not interested in code coverage for these sources. +# 2. exclude the utest/ subdir, we don't need coverage on the unit tests themselves; +# rather, want coverage on the code that the unit tests exercise. +# +add_code_coverage_all_targets(EXCLUDE /nix/store/* utest/*) + +# ---------------------------------------------------------------- +# c++ settings + +set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") + +add_definitions(${PROJECT_CXX_FLAGS}) + +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 20) +endif() + +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# always write compile_commands.json +set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") + +# ---------------------------------------------------------------- +# sources + +add_subdirectory(src) + +# ---------------------------------------------------------------- +# install .hpp + +install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ DESTINATION include) + +# end CMakeLists.txt diff --git a/cmake/code-coverage.cmake b/cmake/code-coverage.cmake new file mode 100644 index 00000000..b6b36064 --- /dev/null +++ b/cmake/code-coverage.cmake @@ -0,0 +1,678 @@ +# +# Copyright (C) 2018-2020 by George Cave - gcave@stablecoder.ca +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +# USAGE: To enable any code coverage instrumentation/targets, the single CMake +# option of `CODE_COVERAGE` needs to be set to 'ON', either by GUI, ccmake, or +# on the command line. +# +# From this point, there are two primary methods for adding instrumentation to +# targets: +# +# 1 - A blanket instrumentation by calling `add_code_coverage()`, where +# all targets in that directory and all subdirectories are automatically +# instrumented. +# +# 2 - Per-target instrumentation by calling +# `target_code_coverage()`, where the target is given and thus only +# that target is instrumented. This applies to both libraries and executables. +# +# To add coverage targets, such as calling `make ccov` to generate the actual +# coverage information for perusal or consumption, call +# `target_code_coverage()` on an *executable* target. +# +# Example 1: All targets instrumented +# +# In this case, the coverage information reported will will be that of the +# `theLib` library target and `theExe` executable. +# +# 1a: Via global command +# +# ~~~ +# add_code_coverage() # Adds instrumentation to all targets +# +# add_library(theLib lib.cpp) +# +# add_executable(theExe main.cpp) +# target_link_libraries(theExe PRIVATE theLib) +# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target +# # (instrumentation already added via global anyways) +# # for generating code coverage reports. +# ~~~ +# +# 1b: Via target commands +# +# ~~~ +# add_library(theLib lib.cpp) +# target_code_coverage(theLib) # As a library target, adds coverage instrumentation but no targets. +# +# add_executable(theExe main.cpp) +# target_link_libraries(theExe PRIVATE theLib) +# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target and instrumentation for generating code coverage reports. +# ~~~ +# +# Example 2: Target instrumented, but with regex pattern of files to be excluded +# from report +# +# ~~~ +# add_executable(theExe main.cpp non_covered.cpp) +# target_code_coverage(theExe EXCLUDE non_covered.cpp test/*) # As an executable target, the reports will exclude the non-covered.cpp file, and any files in a test/ folder. +# ~~~ +# +# Example 3: Target added to the 'ccov' and 'ccov-all' targets +# +# ~~~ +# add_code_coverage_all_targets(EXCLUDE test/*) # Adds the 'ccov-all' target set and sets it to exclude all files in test/ folders. +# +# add_executable(theExe main.cpp non_covered.cpp) +# target_code_coverage(theExe AUTO ALL EXCLUDE non_covered.cpp test/*) # As an executable target, adds to the 'ccov' and ccov-all' targets, and the reports will exclude the non-covered.cpp file, and any files in a test/ folder. +# ~~~ + +# Options +option( + CODE_COVERAGE + "Builds targets with code coverage instrumentation. (Requires GCC or Clang)" + OFF) + +# Programs +find_program(LLVM_COV_PATH llvm-cov) +find_program(LLVM_PROFDATA_PATH llvm-profdata) +find_program(LCOV_PATH lcov) +find_program(GENHTML_PATH genhtml) +# Hide behind the 'advanced' mode flag for GUI/ccmake +mark_as_advanced(FORCE LLVM_COV_PATH LLVM_PROFDATA_PATH LCOV_PATH GENHTML_PATH) + +# Variables +set(CMAKE_COVERAGE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/ccov) +set_property(GLOBAL PROPERTY JOB_POOLS ccov_serial_pool=1) + +# Common initialization/checks +if(CODE_COVERAGE AND NOT CODE_COVERAGE_ADDED) + set(CODE_COVERAGE_ADDED ON) + + # Common Targets + add_custom_target( + ccov-preprocessing + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY} + DEPENDS ccov-clean) + + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + # Messages + message(STATUS "Building with llvm Code Coverage Tools") + + if(NOT LLVM_COV_PATH) + message(FATAL_ERROR "llvm-cov not found! Aborting.") + else() + # Version number checking for 'EXCLUDE' compatibility + execute_process(COMMAND ${LLVM_COV_PATH} --version + OUTPUT_VARIABLE LLVM_COV_VERSION_CALL_OUTPUT) + string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" LLVM_COV_VERSION + ${LLVM_COV_VERSION_CALL_OUTPUT}) + + if(LLVM_COV_VERSION VERSION_LESS "7.0.0") + message( + WARNING + "target_code_coverage()/add_code_coverage_all_targets() 'EXCLUDE' option only available on llvm-cov >= 7.0.0" + ) + endif() + endif() + + # Targets + if(${CMAKE_VERSION} VERSION_LESS "3.17.0") + add_custom_target( + ccov-clean + COMMAND ${CMAKE_COMMAND} -E remove -f + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list + COMMAND ${CMAKE_COMMAND} -E remove -f + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list) + else() + add_custom_target( + ccov-clean + COMMAND ${CMAKE_COMMAND} -E rm -f + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list + COMMAND ${CMAKE_COMMAND} -E rm -f + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list) + endif() + + # Used to get the shared object file list before doing the main all- + # processing + add_custom_target( + ccov-libs + COMMAND ; + COMMENT "libs ready for coverage report.") + + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES + "GNU") + # Messages + message(STATUS "Building with lcov Code Coverage Tools") + + if(CMAKE_BUILD_TYPE) + string(TOUPPER ${CMAKE_BUILD_TYPE} upper_build_type) + if(NOT ${upper_build_type} STREQUAL "DEBUG") + message( + WARNING + "Code coverage results with an optimized (non-Debug) build may be misleading" + ) + endif() + else() + message( + WARNING + "Code coverage results with an optimized (non-Debug) build may be misleading" + ) + endif() + if(NOT LCOV_PATH) + message(FATAL_ERROR "lcov not found! Aborting...") + endif() + if(NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif() + + # Targets + add_custom_target(ccov-clean COMMAND ${LCOV_PATH} --directory + ${CMAKE_BINARY_DIR} --zerocounters) + + else() + message(FATAL_ERROR "Code coverage requires Clang or GCC. Aborting.") + endif() +endif() + +# Adds code coverage instrumentation to a library, or instrumentation/targets +# for an executable target. +# ~~~ +# EXECUTABLE ADDED TARGETS: +# GCOV/LCOV: +# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter. +# ccov-${TARGET_NAME} : Generates HTML code coverage report for the associated named target. +# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report. +# +# LLVM-COV: +# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter. +# ccov-report : Generates HTML code coverage report for every target added with 'AUTO' parameter. +# ccov-${TARGET_NAME} : Generates HTML code coverage report. +# ccov-report-${TARGET_NAME} : Prints to command line summary per-file coverage information. +# ccov-export-${TARGET_NAME} : Exports the coverage report to a JSON file. +# ccov-show-${TARGET_NAME} : Prints to command line detailed per-line coverage information. +# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report. +# ccov-all-report : Prints summary per-file coverage information for every target added with ALL' parameter to the command line. +# ccov-all-export : Exports the coverage report to a JSON file. +# +# Required: +# TARGET_NAME - Name of the target to generate code coverage for. +# Optional: +# PUBLIC - Sets the visibility for added compile options to targets to PUBLIC instead of the default of PRIVATE. +# INTERFACE - Sets the visibility for added compile options to targets to INTERFACE instead of the default of PRIVATE. +# PLAIN - Do not set any target visibility (backward compatibility with old cmake projects) +# AUTO - Adds the target to the 'ccov' target so that it can be run in a batch with others easily. Effective on executable targets. +# ALL - Adds the target to the 'ccov-all' and 'ccov-all-report' targets, which merge several executable targets coverage data to a single report. Effective on executable targets. +# EXTERNAL - For GCC's lcov, allows the profiling of 'external' files from the processing directory +# COVERAGE_TARGET_NAME - For executables ONLY, changes the outgoing target name so instead of `ccov-${TARGET_NAME}` it becomes `ccov-${COVERAGE_TARGET_NAME}`. +# EXCLUDE - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex! **These do not copy to the 'all' targets.** +# OBJECTS - For executables ONLY, if the provided targets are shared libraries, adds coverage information to the output +# ARGS - For executables ONLY, appends the given arguments to the associated ccov-* executable call +# ~~~ +function(target_code_coverage TARGET_NAME) + # Argument parsing + set(options AUTO ALL EXTERNAL PUBLIC INTERFACE PLAIN) + set(single_value_keywords COVERAGE_TARGET_NAME) + set(multi_value_keywords EXCLUDE OBJECTS ARGS) + cmake_parse_arguments( + target_code_coverage "${options}" "${single_value_keywords}" + "${multi_value_keywords}" ${ARGN}) + + # Set the visibility of target functions to PUBLIC, INTERFACE or default to + # PRIVATE. + if(target_code_coverage_PUBLIC) + set(TARGET_VISIBILITY PUBLIC) + set(TARGET_LINK_VISIBILITY PUBLIC) + elseif(target_code_coverage_INTERFACE) + set(TARGET_VISIBILITY INTERFACE) + set(TARGET_LINK_VISIBILITY INTERFACE) + elseif(target_code_coverage_PLAIN) + set(TARGET_VISIBILITY PUBLIC) + set(TARGET_LINK_VISIBILITY) + else() + set(TARGET_VISIBILITY PRIVATE) + set(TARGET_LINK_VISIBILITY PRIVATE) + endif() + + if(NOT target_code_coverage_COVERAGE_TARGET_NAME) + # If a specific name was given, use that instead. + set(target_code_coverage_COVERAGE_TARGET_NAME ${TARGET_NAME}) + endif() + + if(CODE_COVERAGE) + + # Add code coverage instrumentation to the target's linker command + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} + -fprofile-instr-generate -fcoverage-mapping) + target_link_options(${TARGET_NAME} ${TARGET_VISIBILITY} + -fprofile-instr-generate -fcoverage-mapping) + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES + "GNU") + target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} -fprofile-arcs + -ftest-coverage) + target_link_libraries(${TARGET_NAME} ${TARGET_LINK_VISIBILITY} gcov) + endif() + + # Targets + get_target_property(target_type ${TARGET_NAME} TYPE) + + # Add shared library to processing for 'all' targets + if(target_type STREQUAL "SHARED_LIBRARY" AND target_code_coverage_ALL) + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + add_custom_target( + ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${CMAKE_COMMAND} -E echo "-object=$" >> + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list + DEPENDS ccov-preprocessing ${TARGET_NAME}) + + if(NOT TARGET ccov-libs) + message( + FATAL_ERROR + "Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'." + ) + endif() + + add_dependencies(ccov-libs + ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) + endif() + endif() + + # For executables add targets to run and produce output + if(target_type STREQUAL "EXECUTABLE") + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + + # If there are shared objects to also work with, generate the string to + # add them here + foreach(SO_TARGET ${target_code_coverage_OBJECTS}) + # Check to see if the target is a shared object + if(TARGET ${SO_TARGET}) + get_target_property(SO_TARGET_TYPE ${SO_TARGET} TYPE) + if(${SO_TARGET_TYPE} STREQUAL "SHARED_LIBRARY") + set(SO_OBJECTS ${SO_OBJECTS} -object=$) + endif() + endif() + endforeach() + + # Run the executable, generating raw profile data Make the run data + # available for further processing. Separated to allow Windows to run + # this target serially. + add_custom_target( + ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${CMAKE_COMMAND} -E env + LLVM_PROFILE_FILE=${target_code_coverage_COVERAGE_TARGET_NAME}.profraw + $ ${target_code_coverage_ARGS} + COMMAND + ${CMAKE_COMMAND} -E echo "-object=$" + ${SO_OBJECTS} >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list + COMMAND + ${CMAKE_COMMAND} -E echo + "${CMAKE_CURRENT_BINARY_DIR}/${target_code_coverage_COVERAGE_TARGET_NAME}.profraw" + >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list + JOB_POOL ccov_serial_pool + DEPENDS ccov-preprocessing ccov-libs ${TARGET_NAME}) + + # Merge the generated profile data so llvm-cov can process it + add_custom_target( + ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${LLVM_PROFDATA_PATH} merge -sparse + ${target_code_coverage_COVERAGE_TARGET_NAME}.profraw -o + ${target_code_coverage_COVERAGE_TARGET_NAME}.profdata + DEPENDS ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) + + # Ignore regex only works on LLVM >= 7 + if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0") + foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE}) + set(EXCLUDE_REGEX ${EXCLUDE_REGEX} + -ignore-filename-regex='${EXCLUDE_ITEM}') + endforeach() + endif() + + # Print out details of the coverage information to the command line + add_custom_target( + ccov-show-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${LLVM_COV_PATH} show $ ${SO_OBJECTS} + -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata + -show-line-counts-or-regions ${EXCLUDE_REGEX} + DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) + + # Print out a summary of the coverage information to the command line + add_custom_target( + ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${LLVM_COV_PATH} report $ ${SO_OBJECTS} + -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata + ${EXCLUDE_REGEX} + DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) + + # Export coverage information so continuous integration tools (e.g. + # Jenkins) can consume it + add_custom_target( + ccov-export-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${LLVM_COV_PATH} export $ ${SO_OBJECTS} + -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata + -format="text" ${EXCLUDE_REGEX} > + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.json + DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) + + # Generates HTML output of the coverage information for perusal + add_custom_target( + ccov-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${LLVM_COV_PATH} show $ ${SO_OBJECTS} + -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata + -show-line-counts-or-regions + -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME} + -format="html" ${EXCLUDE_REGEX} + DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) + + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES + "GNU") + set(COVERAGE_INFO + "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.info" + ) + + # Run the executable, generating coverage information + add_custom_target( + ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND $ ${target_code_coverage_ARGS} + DEPENDS ccov-preprocessing ${TARGET_NAME}) + + # Generate exclusion string for use + foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE}) + set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO} + '${EXCLUDE_ITEM}') + endforeach() + + if(EXCLUDE_REGEX) + set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file + ${COVERAGE_INFO}) + else() + set(EXCLUDE_COMMAND ;) + endif() + + if(NOT ${target_code_coverage_EXTERNAL}) + set(EXTERNAL_OPTION --no-external) + endif() + + # Capture coverage data + if(${CMAKE_VERSION} VERSION_LESS "3.17.0") + add_custom_target( + ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO} + COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters + COMMAND $ ${target_code_coverage_ARGS} + COMMAND + ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory + ${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file + ${COVERAGE_INFO} + COMMAND ${EXCLUDE_COMMAND} + DEPENDS ccov-preprocessing ${TARGET_NAME}) + else() + add_custom_target( + ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO} + COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters + COMMAND $ ${target_code_coverage_ARGS} + COMMAND + ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory + ${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file + ${COVERAGE_INFO} + COMMAND ${EXCLUDE_COMMAND} + DEPENDS ccov-preprocessing ${TARGET_NAME}) + endif() + + # Generates HTML output of the coverage information for perusal + add_custom_target( + ccov-${target_code_coverage_COVERAGE_TARGET_NAME} + COMMAND + ${GENHTML_PATH} -o + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME} + ${COVERAGE_INFO} + DEPENDS ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME}) + endif() + + add_custom_command( + TARGET ccov-${target_code_coverage_COVERAGE_TARGET_NAME} + POST_BUILD + COMMAND ; + COMMENT + "Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}/index.html in your browser to view the coverage report." + ) + + # AUTO + if(target_code_coverage_AUTO) + if(NOT TARGET ccov) + add_custom_target(ccov) + endif() + add_dependencies(ccov ccov-${target_code_coverage_COVERAGE_TARGET_NAME}) + + if(NOT CMAKE_C_COMPILER_ID MATCHES "GNU" AND NOT CMAKE_CXX_COMPILER_ID + MATCHES "GNU") + if(NOT TARGET ccov-report) + add_custom_target(ccov-report) + endif() + add_dependencies( + ccov-report + ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME}) + endif() + endif() + + # ALL + if(target_code_coverage_ALL) + if(NOT TARGET ccov-all-processing) + message( + FATAL_ERROR + "Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'." + ) + endif() + + add_dependencies(ccov-all-processing + ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) + endif() + endif() + endif() +endfunction() + +# Adds code coverage instrumentation to all targets in the current directory and +# any subdirectories. To add coverage instrumentation to only specific targets, +# use `target_code_coverage`. +function(add_code_coverage) + if(CODE_COVERAGE) + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + add_compile_options(-fprofile-instr-generate -fcoverage-mapping) + add_link_options(-fprofile-instr-generate -fcoverage-mapping) + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES + "GNU") + add_compile_options(-fprofile-arcs -ftest-coverage) + link_libraries(gcov) + endif() + endif() +endfunction() + +# Adds the 'ccov-all' type targets that calls all targets added via +# `target_code_coverage` with the `ALL` parameter, but merges all the coverage +# data from them into a single large report instead of the numerous smaller +# reports. Also adds the ccov-all-capture Generates an all-merged.info file, for +# use with coverage dashboards (e.g. codecov.io, coveralls). +# ~~~ +# Optional: +# EXCLUDE - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex! +# ~~~ +function(add_code_coverage_all_targets) + # Argument parsing + set(multi_value_keywords EXCLUDE) + cmake_parse_arguments(add_code_coverage_all_targets "" "" + "${multi_value_keywords}" ${ARGN}) + + if(CODE_COVERAGE) + if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" + OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") + + # Merge the profile data for all of the run executables + if(WIN32) + add_custom_target( + ccov-all-processing + COMMAND + powershell -Command $$FILELIST = Get-Content + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list\; llvm-profdata.exe + merge -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + -sparse $$FILELIST) + else() + add_custom_target( + ccov-all-processing + COMMAND + ${LLVM_PROFDATA_PATH} merge -o + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata -sparse `cat + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list`) + endif() + + # Regex exclude only available for LLVM >= 7 + if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0") + foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE}) + set(EXCLUDE_REGEX ${EXCLUDE_REGEX} + -ignore-filename-regex='${EXCLUDE_ITEM}') + endforeach() + endif() + + # Print summary of the code coverage information to the command line + if(WIN32) + add_custom_target( + ccov-all-report + COMMAND + powershell -Command $$FILELIST = Get-Content + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe + report $$FILELIST + -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + ${EXCLUDE_REGEX} + DEPENDS ccov-all-processing) + else() + add_custom_target( + ccov-all-report + COMMAND + ${LLVM_COV_PATH} report `cat + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` + -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + ${EXCLUDE_REGEX} + DEPENDS ccov-all-processing) + endif() + + # Export coverage information so continuous integration tools (e.g. + # Jenkins) can consume it + add_custom_target( + ccov-all-export + COMMAND + ${LLVM_COV_PATH} export `cat + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` + -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + -format="text" ${EXCLUDE_REGEX} > + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/coverage.json + DEPENDS ccov-all-processing) + + # Generate HTML output of all added targets for perusal + if(WIN32) + add_custom_target( + ccov-all + COMMAND + powershell -Command $$FILELIST = Get-Content + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe show + $$FILELIST + -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + -show-line-counts-or-regions + -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged + -format="html" ${EXCLUDE_REGEX} + DEPENDS ccov-all-processing) + else() + add_custom_target( + ccov-all + COMMAND + ${LLVM_COV_PATH} show `cat + ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` + -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata + -show-line-counts-or-regions + -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged + -format="html" ${EXCLUDE_REGEX} + DEPENDS ccov-all-processing) + endif() + + elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES + "GNU") + set(COVERAGE_INFO "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.info") + + # Nothing required for gcov + add_custom_target(ccov-all-processing COMMAND ;) + + # Exclusion regex string creation + set(EXCLUDE_REGEX) + foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE}) + set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO} + '${EXCLUDE_ITEM}') + endforeach() + + if(EXCLUDE_REGEX) + set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file + ${COVERAGE_INFO}) + else() + set(EXCLUDE_COMMAND ;) + endif() + + # Capture coverage data + if(${CMAKE_VERSION} VERSION_LESS "3.17.0") + add_custom_target( + ccov-all-capture + COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO} + COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture + --output-file ${COVERAGE_INFO} + COMMAND ${EXCLUDE_COMMAND} + DEPENDS ccov-preprocessing ccov-all-processing) + else() + add_custom_target( + ccov-all-capture + COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO} + COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture + --output-file ${COVERAGE_INFO} + COMMAND ${EXCLUDE_COMMAND} + DEPENDS ccov-preprocessing ccov-all-processing) + endif() + + # Generates HTML output of all targets for perusal + add_custom_target( + ccov-all + COMMAND ${GENHTML_PATH} -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged + ${COVERAGE_INFO} -p ${CMAKE_SOURCE_DIR} + DEPENDS ccov-all-capture) + + endif() + + add_custom_command( + TARGET ccov-all + POST_BUILD + COMMAND ; + COMMENT + "Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged/index.html in your browser to view the coverage report." + ) + endif() +endfunction() diff --git a/cmake/cxx.cmake b/cmake/cxx.cmake new file mode 100644 index 00000000..9fc84e60 --- /dev/null +++ b/cmake/cxx.cmake @@ -0,0 +1,86 @@ +# ---------------------------------------------------------------- +# use this in subdirs that compile c++ code +# +macro(xo_include_options target) + # ---------------------------------------------------------------- + # PROJECT_SOURCE_DIR: + # so we can for example write + # #include "ordinaltree/foo.hpp" + # from anywhere in the project + # PROJECT_BINARY_DIR: + # since generated version file will be in build directory, + # need that build directory to also appear in + # compiler's include path + # + target_include_directories( + ${target} PUBLIC + ${PROJECT_SOURCE_DIR}/include # e.g. for #include "indentlog/scope.hpp" + ${PROJECT_SOURCE_DIR}/include/${target} # e.g. for #include "Refcounted.hpp" in refcnt/src + ${PROJECT_BINARY_DIR} # e.g. for generated config.hpp file + ) + + # ---------------------------------------------------------------- + # make standard directories for std:: includes explicit + # so that + # (1) they appear in compile_commands.json. + # (2) clangd (run from emacs lsp-mode) can find them + # + if(CMAKE_EXPORT_COMPILE_COMMANDS) + set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) + endif() +endmacro() + +# ---------------------------------------------------------------- +# variable +# XO_ADDRESS_SANITIZE +# determines whether to enable address sanitizer for the XO project +# (see toplevel CMakeLists.txt) +# ---------------------------------------------------------------- +if(XO_ADDRESS_SANITIZE) + add_compile_options(-fsanitize=address) + add_link_options(-fsanitize=address) +endif() + +# XO_STANDARD_COMPILE_OPTIONS: use these when XO_ADDRESS_SANITIZE=OFF +set(XO_STANDARD_COMPILE_OPTIONS -Werror -Wall -Wextra) + +# XO_ADDRESS_SANITIZE_COMPILE_OPTIONS: use when XO_ADDRESS_SANITIZE=ON +# +# address sanitizer build complains about _FORTIFY_SOURCE redefines +# In file included from :460: +# :1:9: error: '_FORTIFY_SOURCE' macro redefined [-Werror,-Wmacro-redefined] +# #define _FORTIFY_SOURCE 2 +# +set(XO_ADDRESS_SANITIZE_COMPILE_OPTIONS -Werror -Wall -Wextra -Wno-macro-redefined) + +# XO_COMPILE_OPTIONS: use these with xo_compile_options() macro +if(XO_ADDRESS_SANITIZE) + set(XO_COMPILE_OPTIONS ${XO_ADDRESS_SANITIZE_COMPILE_OPTIONS}) +else() + set(XO_COMPILE_OPTIONS ${XO_STANDARD_COMPILE_OPTIONS}) +endif() + +# ---------------------------------------------------------------- +# generally want all the errors+warnings! +# however: address sanitizer generates error on _FORTIFY_SOURCE +# +macro(xo_compile_options target) + target_compile_options(${target} PRIVATE ${XO_COMPILE_OPTIONS}) +endmacro() + +# ---------------------------------------------------------------- +# use this for a subdir that builds a library +# +macro(xo_install_library target) + install(TARGETS ${target} DESTINATION lib) +endmacro() + +# ---------------------------------------------------------------- +# use this when relying on indentlog [[https://github.com/rconybea/indentlog]] headers +# +macro(xo_indentlog_dependency target) + find_package(indentlog REQUIRED) + #add_dependencies(${target} indentlog) + target_link_libraries(${target} PUBLIC indentlog) + #target_include_directories(${target} PUBLIC ${indentlog_DIR}/../../../include) +endmacro() diff --git a/include/cxxutil/demangle.hpp b/include/cxxutil/demangle.hpp new file mode 100644 index 00000000..8b8b8ba3 --- /dev/null +++ b/include/cxxutil/demangle.hpp @@ -0,0 +1,92 @@ +/* @file demangle.hpp */ + +#pragma once + +#include +#include +#include // std::array +#include // std::index_sequence + +namespace xo { + namespace reflect { + + template + constexpr auto + substring_as_array(std::string_view str, + std::index_sequence indexes) + { + //return std::array{str[Idxs]..., '\n'}; + return std::array{str[Idxs]...}; + } /*substring_as_array*/ + + template constexpr auto type_name_array() { +#if defined(__clang__) + constexpr auto prefix = std::string_view{"[T = "}; + constexpr auto suffix = std::string_view{"]"}; + constexpr auto function = std::string_view{__PRETTY_FUNCTION__}; +#elif defined(__GNUC__) + constexpr auto prefix = std::string_view{"with T = "}; + constexpr auto suffix = std::string_view{"]"}; + constexpr auto function = std::string_view{__PRETTY_FUNCTION__}; +#elif defined(_MSC_VER) + constexpr auto prefix = std::string_view{"type_name_array<"}; + constexpr auto suffix = std::string_view{">(void)"}; + constexpr auto function = std::string_view{__FUNCSIG__}; +#else +# error type_name_array: Unsupported compiler +#endif + + constexpr auto start = function.find(prefix) + prefix.size(); + constexpr auto end = function.rfind(suffix); + + //static_assert(start < end); + + constexpr auto name = function.substr(start, (end - start)); + + constexpr auto ixseq = std::make_index_sequence{}; + + return substring_as_array(name, ixseq); + } /*type_name_array*/ + + template + struct type_name_holder { + static inline constexpr auto value = type_name_array(); + }; + + template + constexpr auto type_name() -> std::string_view + { + constexpr auto& value = type_name_holder::value; + return std::string_view{value.data(), value.size()}; + } + +#ifdef NOT_IN_USE + template + struct join + { + // Join all strings into a single std::array of chars + static constexpr auto impl() noexcept + { + constexpr std::size_t len = (Strs.size() + ... + 0); + std::array arr{}; + auto append = [i = 0, &arr](auto const& s) mutable { + for (auto c : s) arr[i++] = c; + }; + (append(Strs), ...); + arr[len] = 0; + return arr; + } + // Give the joined string static storage + static constexpr auto arr = impl(); + // View as a std::string_view + static constexpr std::string_view value {arr.data(), arr.size() - 1}; + }; + + // Helper to get the value out + template + static constexpr auto join_v = join::value; +#endif + } /*namespace reflect*/ +} /*namespace xo*/ + +/* end demangle.hpp */ diff --git a/include/refcnt/Displayable.hpp b/include/refcnt/Displayable.hpp new file mode 100644 index 00000000..74708573 --- /dev/null +++ b/include/refcnt/Displayable.hpp @@ -0,0 +1,29 @@ +/* @file Displayable.hpp */ + +#pragma once + +#include "refcnt/Refcounted.hpp" + +namespace xo { + namespace ref { + class Displayable : public Refcount { + public: + /* write some kind of human-readable representation on stream */ + virtual void display(std::ostream & os) const = 0; + std::string display_string() const; + }; /*Displayable*/ + + /* see also + * operator<<(std::ostream &, intrusive_ptr const &) + * in [Refcounted.hpp] + */ + inline std::ostream & + operator<<(std::ostream &os, Displayable const & x) { + x.display(os); + return os; + } /*operator<<*/ + + } /*namespace ref*/ +} /*namespace xo*/ + +/* end Displayable.hpp */ diff --git a/include/refcnt/Refcounted.hpp b/include/refcnt/Refcounted.hpp new file mode 100644 index 00000000..17d778c7 --- /dev/null +++ b/include/refcnt/Refcounted.hpp @@ -0,0 +1,294 @@ +/* @file Refcounted.hpp */ + +#pragma once + +#include "indentlog/scope.hpp" +#include "cxxutil/demangle.hpp" + +//#include +#include +#include + +namespace xo { + namespace ref { + class Refcount; + + template + class Borrow; + + /* originally used boost::instrusive_ptr<>. + * ran into a bug. probably mine, but implemented + * refcounting inline for debugging + */ + template + class intrusive_ptr { + public: + using element_type = T; + + public: + intrusive_ptr() : ptr_(nullptr) {} + intrusive_ptr(T * x) : ptr_(x) { + intrusive_ptr_log_ctor(sc_self_type, this, x); + intrusive_ptr_add_ref(ptr_); + } /*ctor*/ + + /* NOTE: need exactly this form for copy-constructor + * clang11 will not recognize template form below as + * supplying copy ctor, and default version is broken for + * instrusive_ptr. + */ + intrusive_ptr(intrusive_ptr const & x) : ptr_(x.get()) { + intrusive_ptr_log_cctor(sc_self_type, this, x.get()); + intrusive_ptr_add_ref(ptr_); + } /*cctor*/ + + /* create from instrusive pointer to some related type S */ + template + intrusive_ptr(intrusive_ptr const & x) : ptr_(x.get()) { + intrusive_ptr_log_cctor(sc_self_type, this, x.get()); + intrusive_ptr_add_ref(ptr_); + } /*cctor*/ + + /* move ctor -- in this case don't need to update refcount */ + intrusive_ptr(intrusive_ptr && x) : ptr_{std::move(x.ptr_)} { + intrusive_ptr_log_mctor(sc_self_type, this, ptr_); + /* since we're moving from x, need to make sure x dtor + * doesn't decrement refcount + */ + x.ptr_ = nullptr; + } + + /* aliasing ctor. see ctor (8) here: + * [[https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr]] + * and this dicsussion: + * [[https://stackoverflow.com/questions/49178231/pybind11-multiple-inheritance-with-custom-holder-type-fails-to-cast-to-base-type/73131206#73131206]] + */ + template + intrusive_ptr(intrusive_ptr const & /*x*/, element_type * y) : ptr_{y} { + if (std::is_same()) { + intrusive_ptr_log_actor(sc_self_type, this, y); + intrusive_ptr_add_ref(ptr_); + ; /* trivial aliasing, proceed */ + } else { + using xo::xtag; + throw std::runtime_error(tostr("attempt to use aliasing ctor with", + xtag("Y", reflect::type_name()), + xtag("T", reflect::type_name()))); + } + } /*ctor*/ + + ~intrusive_ptr() { + T * x = this->ptr_; + + intrusive_ptr_log_dtor(sc_self_type, this, x); + + this->ptr_ = nullptr; + + intrusive_ptr_release(x); + } /*dtor*/ + + static bool compare(intrusive_ptr const & x, + intrusive_ptr const & y) { + return ptrdiff_t(x.get() - y.get()); + } + + Borrow borrow() const; + + T * get() const { return ptr_; } + + T * operator->() const { return ptr_; } + + operator bool() const { return ptr_ != nullptr; } + + intrusive_ptr & operator=(intrusive_ptr const & rhs) { + T * x = rhs.get(); + + intrusive_ptr_log_assign(sc_self_type, this, x); + + T * old = this->ptr_; + this->ptr_ = x; + + intrusive_ptr_add_ref(x); + intrusive_ptr_release(old); + + return *this; + } /*operator=*/ + + intrusive_ptr & operator=(intrusive_ptr && rhs) { + intrusive_ptr_log_massign(sc_self_type, this, rhs.get()); + + std::swap(this->ptr_, rhs.ptr_); + + /* dtor on rhs will decrement refcount on old value of this->ptr_ + * don't increment for new value, since refcount just transfers from rhs to *this + */ + + return *this; + } /*operator=*/ + + private: + static constexpr std::string_view sc_self_type = xo::reflect::type_name>(); + + private: + T * ptr_ = nullptr; + }; /*intrusive_ptr*/ + + template + inline bool operator==(intrusive_ptr const & x, intrusive_ptr const & y) { return intrusive_ptr::compare(x, y) == 0; } + + template + using rp = intrusive_ptr; + + class Refcount { + public: + Refcount() : reference_counter_(0) {} + /* WARNING: virtual dtor here is essential, + * since it's what allows us to invoke delete on a Refcount*, + * for an object of some derived class type T. Otherwise clang + * will use different addresses for Refcount-part and T-part of + * such instance, which means pointer given to delete will not be + * the same as pointer returned from new + */ + virtual ~Refcount() = default; + + uint32_t reference_counter() const { return reference_counter_.load(); } + + private: + friend uint32_t intrusive_ptr_refcount(Refcount *); + friend void intrusive_ptr_add_ref(Refcount *); + friend void intrusive_ptr_release(Refcount *); + + private: + std::atomic reference_counter_; + }; /*Refcount*/ + + inline uint32_t + intrusive_ptr_refcount(Refcount * x) { + /* reporting accurately for diagnostics */ + if (x) + return x->reference_counter_.load(); + else + return 0; + } /*intrusive_ptr_refcount*/ + + void intrusive_ptr_set_debug(bool x); + void intrusive_ptr_log_ctor(std::string_view const & self_type, + void * this_ptr, + Refcount * x); + /* here actor short for 'aliasing ctor' */ + void intrusive_ptr_log_actor(std::string_view const & self_type, + void * this_ptr, + Refcount * x); + void intrusive_ptr_log_cctor(std::string_view const & self_type, + void * this_ptr, + Refcount * x); + void intrusive_ptr_log_mctor(std::string_view const & self_type, + void *this_ptr, + Refcount * x); + void intrusive_ptr_log_dtor(std::string_view const & self_type, + void * this_ptr, + Refcount * x); + void intrusive_ptr_log_assign(std::string_view const & self_type, + void * this_ptr, + Refcount * x); + void intrusive_ptr_log_massign(std::string_view const & self_type, + void *this_ptr, + Refcount * x); + void intrusive_ptr_add_ref(Refcount * x); + void intrusive_ptr_release(Refcount * x); + + template + inline std::ostream & + operator<<(std::ostream & os, intrusive_ptr const & x) { + if(x.get()) { + os << *(x.get()); + } else { + os << "() << ">"; + } + return os; + } /*operator<<*/ + + /* borrow a reference-counted pointer to pass down the stack + * 1. borrowed pointer intended to replace: + * a. code like + * foo(rp x), + * passing rp by value requires increment/decrement pair, + * which is superfluous given that caller holds reference throughout + * b. code like + * foo(rp const & x) + * passing rp by reference requires double-indirection in called + * code + * 2. borrowed pointer does not check/maintain reference count. + * it should never be stored in a struct; intended strictly + * to be passed down stack + * 3. just the same, want to be able to copy the borrowed pointer, + * to avoid double-indirection + * 4. also can promote borrowed pointer to full reference-counted + * whenever desired + */ + template + class Borrow { + public: + template + Borrow(rp const & x) : ptr_(x.get()) {} + + Borrow(Borrow const & x) = default; + + /* convert from another borrow, if it has compatible pointer type */ + template + Borrow(Borrow const & x) : ptr_(x.get()) {} + + /* dynamic cast from a pointer to an object of some convertible type */ + template + static Borrow from(Borrow x) { + return Borrow(dynamic_cast(x.get())); + } /*from*/ + + /* promote from native pointer */ + static Borrow from_native(T * x) { + return Borrow(x); + } /*from_native*/ + + T * get() const { return ptr_; } + + rp promote() const { return rp(ptr_); } + + T & operator*() const { return *ptr_; } + T * operator->() const { return ptr_; } + + operator bool() const { return ptr_ != nullptr; } + + static int32_t compare(Borrow const & x, Borrow const & y) { + return ptrdiff_t(x.get() - y.get()); + } /*compare*/ + + static int32_t compare(rp const & x, Borrow const & y) { + return ptrdiff_t(x.get() - y.get()); + } /*compare*/ + + private: + Borrow(T * x) : ptr_(x) {} + + private: + T * ptr_ = nullptr; + }; /*Borrow*/ + + template + inline bool operator==(Borrow x, Borrow y) { return Borrow::compare(x, y) == 0; } + + template + inline bool operator==(rp const & x, Borrow y) { return Borrow::compare(x, y) == 0; } + + template + using brw = Borrow; + + template + Borrow + intrusive_ptr::borrow() const { + return Borrow(*this); + } /*borrow*/ + + } /*namespace ref*/ +} /*namespace xo*/ + +/* end Refcounted.hpp */ diff --git a/include/refcnt/Unowned.hpp b/include/refcnt/Unowned.hpp new file mode 100644 index 00000000..3f78f0d2 --- /dev/null +++ b/include/refcnt/Unowned.hpp @@ -0,0 +1,28 @@ +/* @file Unowned.hpp */ + +namespace xo { + namespace ref { + /* use this is a holder type for pointers that pybind11 should treat + * as "not-my-problem". in particular that pybind11 should never delete. + */ + template + class unowned_ptr { + public: + unowned_ptr(T * x) : ptr_{x} {} + unowned_ptr(unowned_ptr const & x) = default; + ~unowned_ptr() = default; + + T * get() const { return ptr_; } + T * operator->() const { return ptr_; } + + operator bool() const { return ptr_ != nullptr; } + + unowned_ptr & operator=(unowned_ptr const & rhs) = default; + + private: + T * ptr_ = nullptr; + }; /*unowned_ptr*/ + } /*namespace ref*/ +} /*namespace xo*/ + +/* end Unowned.hpp */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000..8083cee0 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,14 @@ +set(SELF_LIBRARY_NAME refcnt) +set(SELF_SOURCE_FILES Refcounted.cpp Displayable.cpp) +add_library(${SELF_LIBRARY_NAME} SHARED ${SELF_SOURCE_FILES}) + +set_target_properties(${SELF_LIBRARY_NAME} + PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION 1) + +xo_indentlog_dependency(${SELF_LIBRARY_NAME}) + +xo_include_options(${SELF_LIBRARY_NAME}) +xo_compile_options(${SELF_LIBRARY_NAME}) +xo_install_library(${SELF_LIBRARY_NAME}) diff --git a/src/Displayable.cpp b/src/Displayable.cpp new file mode 100644 index 00000000..b8793ad3 --- /dev/null +++ b/src/Displayable.cpp @@ -0,0 +1,16 @@ +/* @file Displayable.cpp */ + +#include "refcnt/Displayable.hpp" + +namespace xo { + using xo::tostr; + + namespace ref { + std::string + Displayable::display_string() const { + return tostr(*this); + } /*display_string*/ + } /*namespace ref*/ +} /*namespace xo*/ + +/* end Displayable.cpp */ diff --git a/src/Refcounted.cpp b/src/Refcounted.cpp new file mode 100644 index 00000000..9aeb579d --- /dev/null +++ b/src/Refcounted.cpp @@ -0,0 +1,157 @@ +/* @file Refcounted.cpp */ + +#include "Refcounted.hpp" + +namespace xo { + namespace ref { + namespace { + /* verbose logging for intrusive_ptr */ + static bool s_logging_enabled = false; + + void + intrusive_ptr_log_aux(std::string_view const & self_type, + std::string_view const & method_name, + void * this_ptr, + Refcount * x) + { + scope lscope(XO_LITERAL(verbose, self_type, method_name), + "enter", + xtag("this", this_ptr), + xtag("x", x), + xtag("n", intrusive_ptr_refcount(x))); + } /*intrusive_ptr_log_aux*/ + } /*namespace*/ + + bool + intrusive_ptr_set_debug(bool debug_flag) { + s_logging_enabled = debug_flag; + } /*intrusive_ptr_set_debug*/ + + void + intrusive_ptr_log_ctor(std::string_view const & self_type, + void * this_ptr, + Refcount * x) + { + if (s_logging_enabled) + intrusive_ptr_log_aux(self_type, "::ctor", this_ptr, x); + } /*intrusive_ptr_log_ctor*/ + + void + intrusive_ptr_log_actor(std::string_view const & self_type, + void * this_ptr, + Refcount * x) + { + if (s_logging_enabled) + intrusive_ptr_log_aux(self_type, "::actor", this_ptr, x); + } /*intrusive_ptr_log_actor*/ + + void + intrusive_ptr_log_cctor(std::string_view const & self_type, + void * this_ptr, + Refcount * x) + { + if (s_logging_enabled) + intrusive_ptr_log_aux(self_type, "::cctor", this_ptr, x); + } /*intrusive_ptr_log_cctor*/ + + void + intrusive_ptr_log_mctor(std::string_view const & self_type, + void * this_ptr, + Refcount * x) + { + if (s_logging_enabled) + intrusive_ptr_log_aux(self_type, "::mctor", this_ptr, x); + } /*intrusive_ptr_log_mctor*/ + + void + intrusive_ptr_log_dtor(std::string_view const & self_type, + void * this_ptr, + Refcount * x) + { + if (s_logging_enabled) + intrusive_ptr_log_aux(self_type, "::dtor", this_ptr, x); + } /*intrusive_ptr_log_dtor*/ + + void + intrusive_ptr_log_assign(std::string_view const & self_type, + void * this_ptr, + Refcount * x) + { + if (s_logging_enabled) + intrusive_ptr_log_aux(self_type, "::=", this_ptr, x); + } /*intrusive_ptr_log_assign*/ + + void + intrusive_ptr_log_massign(std::string_view const & self_type, + void * this_ptr, + Refcount * x) + { + if (s_logging_enabled) + intrusive_ptr_log_aux(self_type, "::m=", this_ptr, x); + } /*intrusive_ptr_log_massign*/ + + void + intrusive_ptr_add_ref(Refcount * x) + { + /* for adding reference -- can use relaxed order, + * since any reordering of a set of intrusive_ptr_add_ref() + * calls is ok, provided no intervening intrusive_ptr_release() + * calls. + */ + bool success = (x == nullptr); + + while(!success) { + uint32_t n = x->reference_counter_.load(std::memory_order_relaxed); + + success = x->reference_counter_.compare_exchange_strong(n, n+1, + std::memory_order_relaxed); + } + } /*intrusive_ptr_add_ref*/ + + void + intrusive_ptr_release(Refcount * x) + { + using xo::scope; + using xo::xtag; + + scope log(XO_ENTER0(verbose), + "enter", + xtag("x", x), + xtag("n", x ? x->reference_counter_.load() : 0)); + + /* for decrement, need acq_rel ordering */ + bool success = (x == nullptr); + uint32_t n = 0; + + while(!success) { + n = x->reference_counter_.load(std::memory_order_acquire); + + if(n == static_cast(-1)) { + log && log("detected double-delete attempt", + xtag("x", x), + xtag("n", n)); + assert(false); + } + + success = x->reference_counter_.compare_exchange_strong(n, n-1, + std::memory_order_acq_rel); + } + + if(n == 1) { + /* just deleted the last reference, so recover the object */ + + log && log("delete object with 0 refs"); + + /* for good measure: replace refcount with -1, + * in hope of detecting a double-delete attempt + */ + x->reference_counter_.store(static_cast(-1)); + + delete x; + } + } /*intrusive_ptr_release*/ + + } /*namespace ref*/ +} /*namespace xo*/ + +/* end Refcounted.cpp */ From b6723b921bbd925c4c267ea46705c42281f92018 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 12:27:19 -0400 Subject: [PATCH 03/60] refcnt: build + install fixes --- CMakeLists.txt | 40 ++++++++++++++++++++++++++++-- cmake/cxx.cmake | 18 +++++++++++--- cmake/refcntConfig.cmake.in | 4 +++ include/refcnt/Refcounted.hpp | 46 +++++++++++++++++------------------ src/Refcounted.cpp | 2 +- 5 files changed, 80 insertions(+), 30 deletions(-) create mode 100644 cmake/refcntConfig.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e8053f9..ed3c4486 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10) -project(xo-refcnt VERSION 0.1) +project(refcnt VERSION 0.1) enable_language(CXX) include(cmake/cxx.cmake) @@ -13,7 +13,6 @@ include(cmake/code-coverage.cmake) enable_testing() - # activate code coverage for all executables + libraries (when configured with -DCODE_COVERAGE=ON) add_code_coverage() # 1. assuming that /nix/store/ prefixes .hpp files belonging to gcc, catch2 etc. @@ -26,6 +25,7 @@ add_code_coverage_all_targets(EXCLUDE /nix/store/* utest/*) # ---------------------------------------------------------------- # c++ settings +set(XO_PROJECT_NAME refcnt) set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") add_definitions(${PROJECT_CXX_FLAGS}) @@ -43,6 +43,42 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") # sources add_subdirectory(src) +add_subdirectory(utest) + +# ---------------------------------------------------------------- +# cmake export + +set(XO_PROJECT_CONFIG_VERSION "${XO_PROJECT_NAME}ConfigVersion.cmake") +set(XO_PROJECT_CONFIG "${XO_PROJECT_NAME}Config.cmake") + +include(CMakePackageConfigHelpers) +write_basic_package_version_file("${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG_VERSION}" + VERSION 0.1 + COMPATIBILITY AnyNewerVersion +) + +#install( +# TARGETS ${XO_PROJECT_NAME} +# EXPORT ${XO_PROJECT_NAME}Targets +# LIBRARY DESTINATION lib COMPONENT Runtime +# ARCHIVE DESTINATION lib COMPONENT Development +# RUNTIME DESTINATION bin COMPONENT Runtime +# PUBLIC_HEADER DESTINATION include COMPONENT Development +# BUNDLE DESTINATION bin COMPONENT Runtime +# ) + +configure_package_config_file( + "${PROJECT_SOURCE_DIR}/cmake/${XO_PROJECT_NAME}Config.cmake.in" + "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG}" + INSTALL_DESTINATION lib/cmake/${XO_PROJECT_NAME} + ) + +install(EXPORT ${XO_PROJECT_NAME}Targets DESTINATION lib/cmake/${XO_PROJECT_NAME}) +install( + FILES + "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG_VERSION}" + "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG}" + DESTINATION lib/cmake/${XO_PROJECT_NAME}) # ---------------------------------------------------------------- # install .hpp diff --git a/cmake/cxx.cmake b/cmake/cxx.cmake index 9fc84e60..4f2dd2fa 100644 --- a/cmake/cxx.cmake +++ b/cmake/cxx.cmake @@ -14,9 +14,11 @@ macro(xo_include_options target) # target_include_directories( ${target} PUBLIC - ${PROJECT_SOURCE_DIR}/include # e.g. for #include "indentlog/scope.hpp" - ${PROJECT_SOURCE_DIR}/include/${target} # e.g. for #include "Refcounted.hpp" in refcnt/src - ${PROJECT_BINARY_DIR} # e.g. for generated config.hpp file + $ # e.g. for #include "indentlog/scope.hpp" + $ + $ # e.g. for #include "Refcounted.hpp" in refcnt/src + $ + $ # e.g. for generated config.hpp file ) # ---------------------------------------------------------------- @@ -72,7 +74,15 @@ endmacro() # use this for a subdir that builds a library # macro(xo_install_library target) - install(TARGETS ${target} DESTINATION lib) + install( + TARGETS ${target} + EXPORT ${target}Targets + LIBRARY DESTINATION lib COMPONENT Runtime + ARCHIVE DESTINATION lib COMPONENT Development + RUNTIME DESTINATION bin COMPONENT Runtime + PUBLIC_HEADER DESTINATION include COMPONENT Development + BUNDLE DESTINATION bin COMPONENT Runtime + ) endmacro() # ---------------------------------------------------------------- diff --git a/cmake/refcntConfig.cmake.in b/cmake/refcntConfig.cmake.in new file mode 100644 index 00000000..e13a2c54 --- /dev/null +++ b/cmake/refcntConfig.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@XO_PROJECT_NAME@Targets.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/include/refcnt/Refcounted.hpp b/include/refcnt/Refcounted.hpp index 17d778c7..dc069cf5 100644 --- a/include/refcnt/Refcounted.hpp +++ b/include/refcnt/Refcounted.hpp @@ -171,31 +171,31 @@ namespace xo { return 0; } /*intrusive_ptr_refcount*/ - void intrusive_ptr_set_debug(bool x); - void intrusive_ptr_log_ctor(std::string_view const & self_type, - void * this_ptr, - Refcount * x); + extern void intrusive_ptr_set_debug(bool x); + extern void intrusive_ptr_log_ctor(std::string_view const & self_type, + void * this_ptr, + Refcount * x); /* here actor short for 'aliasing ctor' */ - void intrusive_ptr_log_actor(std::string_view const & self_type, - void * this_ptr, - Refcount * x); - void intrusive_ptr_log_cctor(std::string_view const & self_type, - void * this_ptr, - Refcount * x); - void intrusive_ptr_log_mctor(std::string_view const & self_type, - void *this_ptr, - Refcount * x); - void intrusive_ptr_log_dtor(std::string_view const & self_type, - void * this_ptr, - Refcount * x); - void intrusive_ptr_log_assign(std::string_view const & self_type, - void * this_ptr, - Refcount * x); - void intrusive_ptr_log_massign(std::string_view const & self_type, - void *this_ptr, + extern void intrusive_ptr_log_actor(std::string_view const & self_type, + void * this_ptr, + Refcount * x); + extern void intrusive_ptr_log_cctor(std::string_view const & self_type, + void * this_ptr, + Refcount * x); + extern void intrusive_ptr_log_mctor(std::string_view const & self_type, + void *this_ptr, + Refcount * x); + extern void intrusive_ptr_log_dtor(std::string_view const & self_type, + void * this_ptr, + Refcount * x); + extern void intrusive_ptr_log_assign(std::string_view const & self_type, + void * this_ptr, + Refcount * x); + extern void intrusive_ptr_log_massign(std::string_view const & self_type, + void *this_ptr, Refcount * x); - void intrusive_ptr_add_ref(Refcount * x); - void intrusive_ptr_release(Refcount * x); + extern void intrusive_ptr_add_ref(Refcount * x); + extern void intrusive_ptr_release(Refcount * x); template inline std::ostream & diff --git a/src/Refcounted.cpp b/src/Refcounted.cpp index 9aeb579d..11c8cb62 100644 --- a/src/Refcounted.cpp +++ b/src/Refcounted.cpp @@ -22,7 +22,7 @@ namespace xo { } /*intrusive_ptr_log_aux*/ } /*namespace*/ - bool + void intrusive_ptr_set_debug(bool debug_flag) { s_logging_enabled = debug_flag; } /*intrusive_ptr_set_debug*/ From f8ca4dbe096b50c9628ba7e64ce46fde5d304d04 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 12:56:22 -0400 Subject: [PATCH 04/60] refcnt: + unit test --- utest/CMakeLists.txt | 60 +++++++ utest/README | 7 + utest/intrusive_ptr.test.cpp | 301 +++++++++++++++++++++++++++++++++++ utest/refcnt_utest_main.cpp | 6 + 4 files changed, 374 insertions(+) create mode 100644 utest/CMakeLists.txt create mode 100644 utest/README create mode 100644 utest/intrusive_ptr.test.cpp create mode 100644 utest/refcnt_utest_main.cpp diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt new file mode 100644 index 00000000..74706592 --- /dev/null +++ b/utest/CMakeLists.txt @@ -0,0 +1,60 @@ +# build unittest 'refcnt/utest/utest.refcnt + +set(SELF_EXECUTABLE_NAME utest.refcnt) + +# These tests can use the Catch2-provided main +set(SELF_SOURCE_FILES intrusive_ptr.test.cpp refcnt_utest_main.cpp) +add_executable(${SELF_EXECUTABLE_NAME} ${SELF_SOURCE_FILES}) +xo_include_options(${SELF_EXECUTABLE_NAME}) + +add_test(NAME ${SELF_EXECUTABLE_NAME} COMMAND ${SELF_EXECUTABLE_NAME}) +target_code_coverage(${SELF_EXECUTABLE_NAME} AUTO ALL) + +#target_link_libraries(${SELF_EXECUTABLE_NAME} PRIVATE Catch2::Catch2WithMain) + +# ---------------------------------------------------------------- +# generic project dependency + +# PROJECT_SOURCE_DIR: +# so we can for example write +# #include "indentlog/scope.hpp" +# from anywhere in the project +# PROJECT_BINARY_DIR: +# since version file will be in build directory, need that directory +# to also be included in compiler's include path +# +target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC + ${PROJECT_SOURCE_DIR} + ${PROJECT_BINARY_DIR}) + +# ---------------------------------------------------------------- +# internal dependencies: refcnt, ... + +target_link_libraries(${SELF_EXECUTABLE_NAME} PUBLIC refcnt) + +# ---------------------------------------------------------------- +# 3rd part dependency: catch2: + +find_package(Catch2 2 REQUIRED) + +# need this so that catch2/include appears in compile_commands.json, +# on which lsp integration relies. +# +# See also /nix/store/*-catch2-*/lib/cmake/Catch2/ParseAndAddCatchTests.cmake; +# commands here derived from ^ .cmake file +# +#find_path(CATCH_INCLUDE_DIR "catch2/catch.hpp") +#target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC ${CATCH_INCLUDE_DIR}) + +# ---------------------------------------------------------------- +# make standard directories for std:: includes explicit +# so that +# (1) they appear in compile_commands.json. +# (2) clangd (run from emacs lsp-mode) can find them +# +if(CMAKE_EXPORT_COMPILE_COMMANDS) + set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES + ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) +endif() + +# end CMakeLists.txt diff --git a/utest/README b/utest/README new file mode 100644 index 00000000..85cc27c2 --- /dev/null +++ b/utest/README @@ -0,0 +1,7 @@ +* to run unit tests for this directoyr + + $ cd path/to/kalman/build + $ ./refcnt/utest/utest.refcnt + + + \ No newline at end of file diff --git a/utest/intrusive_ptr.test.cpp b/utest/intrusive_ptr.test.cpp new file mode 100644 index 00000000..d8d756e2 --- /dev/null +++ b/utest/intrusive_ptr.test.cpp @@ -0,0 +1,301 @@ +/* @file intrusive_ptr.test.cpp */ + +#include "refcnt/Refcounted.hpp" +#include "indentlog/scope.hpp" +#include "catch2/catch.hpp" +#include +#include + +namespace xo { + using xo::ref::Refcount; + using xo::ref::Borrow; + using xo::ref::rp; + using xo::ref::brw; + using xo::ref::intrusive_ptr_refcount; + using xo::ref::intrusive_ptr_add_ref; + using xo::ref::intrusive_ptr_release; + + namespace ut { + namespace { + static uint32_t ctor_count = 0; + static uint32_t dtor_count = 0; + + /* empty object, except for refcount */ + class JustRefcount : public ref::Refcount { + public: + JustRefcount() { ++ctor_count; } + ~JustRefcount() { ++dtor_count; } + }; /*JustRefcount*/ + + inline std::ostream & operator<<(std::ostream & os, JustRefcount & x) { + os << "JustRefcount"; + return os; + } /*operator<<*/ + } /*namespace*/ + + TEST_CASE("refcount", "[refcnt][trivial]") { + REQUIRE(std::is_default_constructible() == true); + REQUIRE(std::has_virtual_destructor() == true); + + /* refcount object self-initializes to 0 */ + Refcount x; + REQUIRE(x.reference_counter() == 0); + } /*TEST_CASE(refcount)*/ + + TEST_CASE("null-intrusive-ptr", "[refcnt][trivial]") { + //constexpr std::string_view c_self = "TEST_CASE:null-intrusive-ptr"; + + REQUIRE(std::has_virtual_destructor() == true); + + rp p1; + rp p2; + + REQUIRE(sizeof(p1) == sizeof(JustRefcount*)); + + REQUIRE(p1.get() == nullptr); + REQUIRE(p1.operator->() == nullptr); + + REQUIRE(p2.get() == nullptr); + REQUIRE(p2.operator->() == nullptr); + + /* can assign a nullptr */ + rp p3; + + REQUIRE(p3.get() == nullptr); + p3 = p1; + REQUIRE(p3.get() == nullptr); + + /* can use aux functions on null pointers */ + REQUIRE(intrusive_ptr_refcount(p1.get()) == 0); + + intrusive_ptr_add_ref(nullptr); + intrusive_ptr_release(nullptr); + + /* can borrow a null intrusive_ptr */ + brw p1_brw = p1.borrow(); + brw p2_brw = p2.borrow(); + + REQUIRE(p1_brw.get() == nullptr); + REQUIRE(p1_brw.operator->() == nullptr); + /* null borrow is false-y */ + REQUIRE(p1_brw == false); + + /* can promote a borrowed pointer */ + rp pp = p1_brw.promote(); + + REQUIRE(p1.get() == pp.get()); + + /* comparisons */ + REQUIRE(Borrow::compare(p1_brw, p2_brw) == 0); + REQUIRE(p1_brw == p2_brw); + REQUIRE((p1_brw != p2_brw) == false); + REQUIRE(p1 == p1_brw); + REQUIRE((p1 != p1_brw) == false); + REQUIRE(p1_brw == p1); + REQUIRE((p1_brw != p1) == false); + } /*TEST_CASE(null-intrusive_ptr)*/ + + TEST_CASE("intrusive-ptr-identity", "[refcnt][identity]") + { + uint32_t cc = ctor_count; + uint32_t dc = dtor_count; + + rp p1(new JustRefcount()); + + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + REQUIRE(p1.get() != nullptr); + REQUIRE(p1.get() == p1.operator->()); + REQUIRE(intrusive_ptr_refcount(p1.get()) == 1); + REQUIRE(p1->reference_counter() == 1); + + intrusive_ptr_add_ref(p1.get()); + + REQUIRE(intrusive_ptr_refcount(p1.get()) == 2); + + intrusive_ptr_release(p1.get()); + + REQUIRE(intrusive_ptr_refcount(p1.get()) == 1); + + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + + rp p2(new JustRefcount()); + + REQUIRE(ctor_count == cc + 2); + REQUIRE(dtor_count == dc); + + REQUIRE(p2.get() != nullptr); + REQUIRE(p2.get() != p1.get()); + REQUIRE(p2.get() == p2.operator->()); + REQUIRE(p2->reference_counter() == 1); + + /* can borrow a non-null intrusive-ptr */ + brw p1_brw = p1.borrow(); + + REQUIRE(p1_brw.get() == p1.get()); + + /* borrowing does not change refcount, borrow not tracked */ + REQUIRE(ctor_count == cc + 2); + REQUIRE(dtor_count == dc); + REQUIRE(p1.get()->reference_counter() == 1); + + /* copying borrowed pointer does not touch refcount */ + brw p1_brw2 = p1_brw; + + REQUIRE(ctor_count == cc + 2); + REQUIRE(dtor_count == dc); + REQUIRE(p1_brw2.get() == p1.get()); + + REQUIRE(p1.get()->reference_counter() == 1); + } /*TEST_CASE(identity-intrusive-ptr)*/ + + TEST_CASE("intrusive-ptr-release", "[refcnt][release]") + { + uint32_t cc = ctor_count; + uint32_t dc = dtor_count; + + rp p1(new JustRefcount()); + + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + REQUIRE(p1.get() != nullptr); + REQUIRE(p1->reference_counter() == 1); + + /* reference count going to 0 -> delete object */ + p1 = nullptr; + + REQUIRE(p1.get() == nullptr); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc + 1); + } /*TEST_CASE(intrusive-ptr-release)*/ + + TEST_CASE("intrusive-ptr-copy", "[refcnt][copy]") + { + uint32_t cc = ctor_count; + uint32_t dc = dtor_count; + + rp p1(new JustRefcount()); + JustRefcount * p1_native = p1.get(); + + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + REQUIRE(p1.get() != nullptr); + REQUIRE(p1->reference_counter() == 1); + + /* copy ctor ran to make copy of p1, did not allocate */ + rp p2(p1); + + REQUIRE(p1->reference_counter() == 2); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + + } /*TEST_CASE(intrusive-ptr-copy)*/ + + TEST_CASE("intrusive-ptr-move", "[refcnt][move]") + { + uint32_t cc = ctor_count; + uint32_t dc = dtor_count; + + rp p1(new JustRefcount()); + JustRefcount * p1_native = p1.get(); + + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + REQUIRE(p1.get() != nullptr); + REQUIRE(p1->reference_counter() == 1); + + rp p2{std::move(p1)}; + + REQUIRE(p2->reference_counter() == 1); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + + p2 = nullptr; + + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc + 1); + } /*TEST_CASE(intrusive-ptr-move)*/ + + TEST_CASE("instrusive-ptr-assign", "[refcnt][assign]") + { + uint32_t cc = ctor_count; + uint32_t dc = dtor_count; + + rp p1(new JustRefcount()); + JustRefcount * p1_native = p1.get(); + + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + REQUIRE(p1.get() != nullptr); + REQUIRE(p1->reference_counter() == 1); + + rp p2; + + REQUIRE(p2.get() == nullptr); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + + p2 = p1; + + REQUIRE(p2.get() == p1.get()); + REQUIRE(p2->reference_counter() == 2); + + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + + p1 = nullptr; + + REQUIRE(p2->reference_counter() == 1); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + + p2 = nullptr; + + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc + 1); + } /*TEST_CASE(intrusive-ptr-assign)*/ + + TEST_CASE("intrusive-ptr-move-assign", "[refcnt][move-assign]") + { + uint32_t cc = ctor_count; + uint32_t dc = dtor_count; + + rp p1(new JustRefcount()); + JustRefcount * p1_native = p1.get(); + + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + REQUIRE(p1.get() != nullptr); + REQUIRE(p1->reference_counter() == 1); + + rp p2; + + REQUIRE(p2.get() == nullptr); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + + p2 = std::move(p1); + + REQUIRE(p1.get() == nullptr); + REQUIRE(p2.get() == p1_native); + REQUIRE(p2->reference_counter() == 1); + + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + + p1 = nullptr; /*no-op*/ + + REQUIRE(p2->reference_counter() == 1); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + + p2 = nullptr; + + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc + 1); + } /*TEST_CASE(intrusive-ptr-move-assign)*/ + } /*namespace ut*/ +} /*namespace xo*/ + +/* end intrusive_ptr.test.cpp */ diff --git a/utest/refcnt_utest_main.cpp b/utest/refcnt_utest_main.cpp new file mode 100644 index 00000000..327713b7 --- /dev/null +++ b/utest/refcnt_utest_main.cpp @@ -0,0 +1,6 @@ +/* @file refcnt_utest_main.cpp */ + +#define CATCH_CONFIG_MAIN +#include "catch2/catch.hpp" + +/* end refcnt_utest_main.cpp */ From 1bdb8bb459f6aecfd6c7eccd731ac3aeda294021 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 13:06:25 -0400 Subject: [PATCH 05/60] github actions attempt --- .github/cmake-single-platform.yml | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/cmake-single-platform.yml diff --git a/.github/cmake-single-platform.yml b/.github/cmake-single-platform.yml new file mode 100644 index 00000000..542d220d --- /dev/null +++ b/.github/cmake-single-platform.yml @@ -0,0 +1,42 @@ +# This starter workflow is for a CMake project running on a single platform. There is a different starter workflow if you need cross-platform coverage. +# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-multi-platform.yml +name: CMake on a single platform + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. + # You can convert this to a matrix build if you need cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Install catch2 + # install catch2. see [[https://stackoverflow.com/questions/57982945/how-to-apt-get-install-in-a-github-actions-workflow]] + run: sudo apt-get install -y catch2 + +# - name: Configure CMake +# # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. +# # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type +# run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + +# - name: Build +# # Build your program with the given configuration +# run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + +# - name: Test +# working-directory: ${{github.workspace}}/build +# # Execute tests defined by the CMake configuration. +# # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail +# run: ctest -C ${{env.BUILD_TYPE}} From 9161210d5237659680d7d5f8fd86debcd366329d Mon Sep 17 00:00:00 2001 From: Roland Date: Sun, 24 Sep 2023 13:09:31 -0400 Subject: [PATCH 06/60] Create main.yml --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1 @@ + From 572572c7bb1fb3f7c5b689519ae175fd456aeae1 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 13:10:35 -0400 Subject: [PATCH 07/60] github actions, take 2 --- .github/{ => workflows}/cmake-single-platform.yml | 0 .github/workflows/main.yml | 1 - 2 files changed, 1 deletion(-) rename .github/{ => workflows}/cmake-single-platform.yml (100%) delete mode 100644 .github/workflows/main.yml diff --git a/.github/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml similarity index 100% rename from .github/cmake-single-platform.yml rename to .github/workflows/cmake-single-platform.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 8b137891..00000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1 +0,0 @@ - From 8340c0c6159f8cfda4779d7a33837789c3c08f99 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 13:13:55 -0400 Subject: [PATCH 08/60] github actions, take 3 --- .github/workflows/cmake-single-platform.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 542d220d..749f36c1 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -26,6 +26,10 @@ jobs: # install catch2. see [[https://stackoverflow.com/questions/57982945/how-to-apt-get-install-in-a-github-actions-workflow]] run: sudo apt-get install -y catch2 + - name: Fetch indentlog + # fetch source tree for indentlog dependency + run: git clone git@github.com:rconybea/indentlog.git + # - name: Configure CMake # # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type From 464449df065ea3857ad25b122f38c14c7c3c0036 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 13:23:09 -0400 Subject: [PATCH 09/60] github actions, take 4 --- .github/workflows/cmake-single-platform.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 749f36c1..fb4ec30d 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -28,7 +28,7 @@ jobs: - name: Fetch indentlog # fetch source tree for indentlog dependency - run: git clone git@github.com:rconybea/indentlog.git + run: git clone https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com:rconybea/indentlog.git # - name: Configure CMake # # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. From 8f596d8f3d973287eaab31b390d4e8a77aa50ab5 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 13:28:31 -0400 Subject: [PATCH 10/60] github actions, take 5 --- .github/workflows/cmake-single-platform.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index fb4ec30d..505cdc1c 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -26,7 +26,11 @@ jobs: # install catch2. see [[https://stackoverflow.com/questions/57982945/how-to-apt-get-install-in-a-github-actions-workflow]] run: sudo apt-get install -y catch2 - - name: Fetch indentlog + - name: Clone indentlog + with: + repository: Rconybea/indentlog + path: repo/indentlog + # fetch source tree for indentlog dependency run: git clone https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com:rconybea/indentlog.git From 264da8e122ff49104aa6aa7deda3fac062e7bd4f Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 13:29:34 -0400 Subject: [PATCH 11/60] bugfix: typo in .tm --- .github/workflows/cmake-single-platform.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 505cdc1c..92e5ac73 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -31,8 +31,8 @@ jobs: repository: Rconybea/indentlog path: repo/indentlog - # fetch source tree for indentlog dependency - run: git clone https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com:rconybea/indentlog.git +# # fetch source tree for indentlog dependency +# run: git clone https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com:rconybea/indentlog.git # - name: Configure CMake # # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. From a605654fb9aa64dfbf57b915fb4136fe0ff9cb25 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 13:30:41 -0400 Subject: [PATCH 12/60] bugfix: missing 'uses: ..' in .yml --- .github/workflows/cmake-single-platform.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 92e5ac73..cd9aca1a 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -27,6 +27,7 @@ jobs: run: sudo apt-get install -y catch2 - name: Clone indentlog + uses: actions/checkout@v3 with: repository: Rconybea/indentlog path: repo/indentlog From 2fc39afdcc8979e7288ee450a317d04467117b05 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 13:36:16 -0400 Subject: [PATCH 13/60] github actions, take 6 --- .github/workflows/cmake-single-platform.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index cd9aca1a..26c36193 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -32,6 +32,10 @@ jobs: repository: Rconybea/indentlog path: repo/indentlog + - name: Configure indentlog + # configure cmake for indentlog in dedicated build directory. + run: cmake -B ${{github.workspace}}/build_indentlog -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/indentlog + # # fetch source tree for indentlog dependency # run: git clone https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com:rconybea/indentlog.git From be3880779f1a078e879b68e8b4bbce143445642c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 13:37:59 -0400 Subject: [PATCH 14/60] github: + indentlog build action --- .github/workflows/cmake-single-platform.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 26c36193..d943af45 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -36,6 +36,9 @@ jobs: # configure cmake for indentlog in dedicated build directory. run: cmake -B ${{github.workspace}}/build_indentlog -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/indentlog + - name: Build indentlog + run: cmake --build ${{github.workspace}}/build_indentlog --config ${{env.BUILD_TYPE}} + # # fetch source tree for indentlog dependency # run: git clone https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com:rconybea/indentlog.git From 7cc51fc13de69c09e07358aef3072e36d421aec1 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 13:40:07 -0400 Subject: [PATCH 15/60] github: install indentlog --- .github/workflows/cmake-single-platform.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index d943af45..9fceb907 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -39,6 +39,10 @@ jobs: - name: Build indentlog run: cmake --build ${{github.workspace}}/build_indentlog --config ${{env.BUILD_TYPE}} + - name: Install indentlog + # install into ${{github.workspace}}/local + run: cmake --build ${{github.workspace}}/build_indentlog install + # # fetch source tree for indentlog dependency # run: git clone https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com:rconybea/indentlog.git From 9a3b104ec01084424e35b30c9b2b6c2ed02e01d9 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 13:42:04 -0400 Subject: [PATCH 16/60] bugfix: cmake --install syntax --- .github/workflows/cmake-single-platform.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 9fceb907..4f435149 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -41,7 +41,7 @@ jobs: - name: Install indentlog # install into ${{github.workspace}}/local - run: cmake --build ${{github.workspace}}/build_indentlog install + run: cmake --install ${{github.workspace}}/build_indentlog # # fetch source tree for indentlog dependency # run: git clone https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com:rconybea/indentlog.git From f305581cdf43a456257e794f909bc39aa2de477a Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 13:47:08 -0400 Subject: [PATCH 17/60] github: try building refcnt, now with indentlog dep --- .github/workflows/cmake-single-platform.yml | 27 +++++++++------------ 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 4f435149..ed2bd910 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -43,20 +43,17 @@ jobs: # install into ${{github.workspace}}/local run: cmake --install ${{github.workspace}}/build_indentlog -# # fetch source tree for indentlog dependency -# run: git clone https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com:rconybea/indentlog.git + - name: Configure refcnt + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: cmake -B ${{github.workspace}}/build_refcnt -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -# - name: Configure CMake -# # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. -# # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type -# run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + - name: Build refcnt + # Build your program with the given configuration + run: cmake --build ${{github.workspace}}/build_refcnt --config ${{env.BUILD_TYPE}} -# - name: Build -# # Build your program with the given configuration -# run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} - -# - name: Test -# working-directory: ${{github.workspace}}/build -# # Execute tests defined by the CMake configuration. -# # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail -# run: ctest -C ${{env.BUILD_TYPE}} + - name: Test refcnt + working-directory: ${{github.workspace}}/build_recnt + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C ${{env.BUILD_TYPE}} From df4420c6a5e7b8f6cd18fc4174e14c27a1cd4fb5 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 13:49:09 -0400 Subject: [PATCH 18/60] github: bugfix: typo in .yml --- .github/workflows/cmake-single-platform.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index ed2bd910..5d74447d 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -53,7 +53,7 @@ jobs: run: cmake --build ${{github.workspace}}/build_refcnt --config ${{env.BUILD_TYPE}} - name: Test refcnt - working-directory: ${{github.workspace}}/build_recnt + working-directory: ${{github.workspace}}/build_refcnt # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest -C ${{env.BUILD_TYPE}} From 34e0a8acea5ea2cfa35a35f6630d68ce1c4c4ace Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 13:53:38 -0400 Subject: [PATCH 19/60] github: streamline attempt in .yml --- .github/workflows/cmake-single-platform.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 5d74447d..29cb9534 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -34,7 +34,7 @@ jobs: - name: Configure indentlog # configure cmake for indentlog in dedicated build directory. - run: cmake -B ${{github.workspace}}/build_indentlog -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/indentlog + run: cmake -B ${{github.workspace}}/build_indentlog -DCMAKE_INSTALL_PREFIX=${{env.LOCAL_INSTALL_PREFIX}} repo/indentlog - name: Build indentlog run: cmake --build ${{github.workspace}}/build_indentlog --config ${{env.BUILD_TYPE}} From 4b3c854fc7872ba404ce4e0af25ecfdc47822ae1 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 24 Sep 2023 13:56:34 -0400 Subject: [PATCH 20/60] github: abandon streamline experiment, not sucessful --- .github/workflows/cmake-single-platform.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 29cb9534..5d74447d 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -34,7 +34,7 @@ jobs: - name: Configure indentlog # configure cmake for indentlog in dedicated build directory. - run: cmake -B ${{github.workspace}}/build_indentlog -DCMAKE_INSTALL_PREFIX=${{env.LOCAL_INSTALL_PREFIX}} repo/indentlog + run: cmake -B ${{github.workspace}}/build_indentlog -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/indentlog - name: Build indentlog run: cmake --build ${{github.workspace}}/build_indentlog --config ${{env.BUILD_TYPE}} From 5b4d9331c8c60a69e850617e23970d635aa19537 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 12:32:12 -0400 Subject: [PATCH 21/60] build: provide typical default value for CMAKE_INSTALL_RPATH --- CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ed3c4486..a0cdb52c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,6 @@ include(cmake/code-coverage.cmake) # unit test setup enable_testing() - # activate code coverage for all executables + libraries (when configured with -DCODE_COVERAGE=ON) add_code_coverage() # 1. assuming that /nix/store/ prefixes .hpp files belonging to gcc, catch2 etc. @@ -39,6 +38,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) # always write compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") +if(NOT CMAKE_INSTALL_RPATH) + set(CMAKE_INSTALL_RPATH $(CMAKE_INSTALL_PREFIX)/lib CACHE STRING "runpath in installed libraries/executables") +endif() + # ---------------------------------------------------------------- # sources From 8e1b51c96f2e825731e28cc7a48ebbaa6f5b732b Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 15:21:02 -0400 Subject: [PATCH 22/60] github: experiment with --debug-find --- .github/workflows/cmake-single-platform.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index 5d74447d..b8f26927 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -32,6 +32,8 @@ jobs: repository: Rconybea/indentlog path: repo/indentlog + + - name: Configure indentlog # configure cmake for indentlog in dedicated build directory. run: cmake -B ${{github.workspace}}/build_indentlog -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/indentlog @@ -43,10 +45,12 @@ jobs: # install into ${{github.workspace}}/local run: cmake --install ${{github.workspace}}/build_indentlog + + - name: Configure refcnt # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: cmake -B ${{github.workspace}}/build_refcnt -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + run: cmake -B ${{github.workspace}}/build_refcnt -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} --debug-find - name: Build refcnt # Build your program with the given configuration From 8c9b541e7d6530f162cddcff6a382d6a115c9a17 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 26 Sep 2023 15:48:17 -0400 Subject: [PATCH 23/60] build: specify CONFIG for find_package(indentlog) --- cmake/cxx.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/cxx.cmake b/cmake/cxx.cmake index 4f2dd2fa..fd9aed25 100644 --- a/cmake/cxx.cmake +++ b/cmake/cxx.cmake @@ -72,6 +72,7 @@ endmacro() # ---------------------------------------------------------------- # use this for a subdir that builds a library +# and supports find_package() # macro(xo_install_library target) install( @@ -89,7 +90,7 @@ endmacro() # use this when relying on indentlog [[https://github.com/rconybea/indentlog]] headers # macro(xo_indentlog_dependency target) - find_package(indentlog REQUIRED) + find_package(indentlog CONFIG REQUIRED) #add_dependencies(${target} indentlog) target_link_libraries(${target} PUBLIC indentlog) #target_include_directories(${target} PUBLIC ${indentlog_DIR}/../../../include) From 870eb57aa79c9ad0369a0471308d0f93df598716 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 09:29:47 -0400 Subject: [PATCH 24/60] build: adopt xo-cmake dependency --- .github/workflows/cmake-single-platform.yml | 29 ++++++++++++++++----- CMakeLists.txt | 2 ++ README.md | 14 ++++++++-- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml index b8f26927..527d5655 100644 --- a/.github/workflows/cmake-single-platform.yml +++ b/.github/workflows/cmake-single-platform.yml @@ -26,17 +26,34 @@ jobs: # install catch2. see [[https://stackoverflow.com/questions/57982945/how-to-apt-get-install-in-a-github-actions-workflow]] run: sudo apt-get install -y catch2 + # ---------------------------------------------------------------- + + - name: Clone xo-cmake + uses: actions/checkout@v3 + with: + repository: Rconybea/xo-cmake + path: repo/xo-cmake + + - name: Configure xo-cmake + run: cmake -B ${{github.workspace}}/build_xo-cmake -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/xo-cmake + + - name: Build xo-cmake (trivial) + run: cmake --build ${{github.workspace}}/build_xo-cmake --config ${{env.BUILD_TYPE}} + + - name: Install xo-cmake + run: cmake --install ${{github.workspace}}/build_xo-cmake + + # ---------------------------------------------------------------- + - name: Clone indentlog uses: actions/checkout@v3 with: repository: Rconybea/indentlog path: repo/indentlog - - - name: Configure indentlog # configure cmake for indentlog in dedicated build directory. - run: cmake -B ${{github.workspace}}/build_indentlog -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/indentlog + run: cmake -B ${{github.workspace}}/build_indentlog -DCMAKE_MODULE_PATH=${{github.workspace}}/local/share/cmake -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local repo/indentlog - name: Build indentlog run: cmake --build ${{github.workspace}}/build_indentlog --config ${{env.BUILD_TYPE}} @@ -45,12 +62,12 @@ jobs: # install into ${{github.workspace}}/local run: cmake --install ${{github.workspace}}/build_indentlog + # ---------------------------------------------------------------- - - - name: Configure refcnt + - name: Configure self (refcnt) # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: cmake -B ${{github.workspace}}/build_refcnt -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} --debug-find + run: cmake -B ${{github.workspace}}/build_refcnt -DCMAKE_MODULE_PATH=${{github.workspace}}/local/share/cmake -DCMAKE_PREFIX_PATH=${{github.workspace}}/local -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/local -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} - name: Build refcnt # Build your program with the given configuration diff --git a/CMakeLists.txt b/CMakeLists.txt index a0cdb52c..c360483c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,8 @@ cmake_minimum_required(VERSION 3.10) project(refcnt VERSION 0.1) enable_language(CXX) +# common XO cmake macros (see proj/xo-cmake) +include(xo_macros/xo_cxx) include(cmake/cxx.cmake) include(cmake/code-coverage.cmake) diff --git a/README.md b/README.md index 0835e7b9..bae1fb2c 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Refcnt is a small shared library supplying intrusive reference counting. ### build + install `indentlog` dependency -see [github/rconybea/indentlog](https://github.com/Rconybea/indentlog) +see [github/Rconybea/indentlog](https://github.com/Rconybea/indentlog) ### copy `refcnt` repository locally ``` @@ -26,11 +26,13 @@ refcnt $ cd refcnt $ mkdir build $ cd build -$ cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} .. +$ cmake -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} .. $ make $ make install ``` +`CMAKE_PREFIX_PATH` should point to prefix where `indentlog` is installed + alternatively, if you're a nix user: ``` $ git clone git@github.com:rconybea/xo-nix.git @@ -40,6 +42,14 @@ $ cd xo-nix $ nix-build -A refcnt ``` +### build for unit test coverage +``` +$ cd refcnt +$ mkdir ccov +$ cd ccov +$ cmake -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCODE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug .. +``` + ## Examples ### 1 From 6c56ed4aecbf0972d22d2ecdc19c37626e3fc2fc Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 13:21:27 -0400 Subject: [PATCH 25/60] refcnt: build: use new xo-make macros --- CMakeLists.txt | 64 ++++++++++++++++++++----------------- cmake/refcntConfig.cmake.in | 2 +- src/CMakeLists.txt | 5 +-- 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c360483c..7f7971f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,41 +53,45 @@ add_subdirectory(utest) # ---------------------------------------------------------------- # cmake export -set(XO_PROJECT_CONFIG_VERSION "${XO_PROJECT_NAME}ConfigVersion.cmake") -set(XO_PROJECT_CONFIG "${XO_PROJECT_NAME}Config.cmake") +xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) -include(CMakePackageConfigHelpers) -write_basic_package_version_file("${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG_VERSION}" - VERSION 0.1 - COMPATIBILITY AnyNewerVersion -) - -#install( -# TARGETS ${XO_PROJECT_NAME} -# EXPORT ${XO_PROJECT_NAME}Targets -# LIBRARY DESTINATION lib COMPONENT Runtime -# ARCHIVE DESTINATION lib COMPONENT Development -# RUNTIME DESTINATION bin COMPONENT Runtime -# PUBLIC_HEADER DESTINATION include COMPONENT Development -# BUNDLE DESTINATION bin COMPONENT Runtime +#set(XO_PROJECT_CONFIG_VERSION "${XO_PROJECT_NAME}ConfigVersion.cmake") +#set(XO_PROJECT_CONFIG "${XO_PROJECT_NAME}Config.cmake") +# +#include(CMakePackageConfigHelpers) +#write_basic_package_version_file("${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG_VERSION}" +# VERSION 0.1 +# COMPATIBILITY AnyNewerVersion +#) +# +##install( +## TARGETS ${XO_PROJECT_NAME} +## EXPORT ${XO_PROJECT_NAME}Targets +## LIBRARY DESTINATION lib COMPONENT Runtime +## ARCHIVE DESTINATION lib COMPONENT Development +## RUNTIME DESTINATION bin COMPONENT Runtime +## PUBLIC_HEADER DESTINATION include COMPONENT Development +## BUNDLE DESTINATION bin COMPONENT Runtime +## ) +# +#configure_package_config_file( +# "${PROJECT_SOURCE_DIR}/cmake/${XO_PROJECT_NAME}Config.cmake.in" +# "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG}" +# INSTALL_DESTINATION lib/cmake/${XO_PROJECT_NAME} # ) - -configure_package_config_file( - "${PROJECT_SOURCE_DIR}/cmake/${XO_PROJECT_NAME}Config.cmake.in" - "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG}" - INSTALL_DESTINATION lib/cmake/${XO_PROJECT_NAME} - ) - -install(EXPORT ${XO_PROJECT_NAME}Targets DESTINATION lib/cmake/${XO_PROJECT_NAME}) -install( - FILES - "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG_VERSION}" - "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG}" - DESTINATION lib/cmake/${XO_PROJECT_NAME}) +# +#install(EXPORT ${XO_PROJECT_NAME}Targets DESTINATION lib/cmake/${XO_PROJECT_NAME}) +#install( +# FILES +# "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG_VERSION}" +# "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG}" +# DESTINATION lib/cmake/${XO_PROJECT_NAME}) # ---------------------------------------------------------------- # install .hpp -install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ DESTINATION include) +xo_install_include_tree() + +#install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ DESTINATION include) # end CMakeLists.txt diff --git a/cmake/refcntConfig.cmake.in b/cmake/refcntConfig.cmake.in index e13a2c54..9c15f36a 100644 --- a/cmake/refcntConfig.cmake.in +++ b/cmake/refcntConfig.cmake.in @@ -1,4 +1,4 @@ @PACKAGE_INIT@ -include("${CMAKE_CURRENT_LIST_DIR}/@XO_PROJECT_NAME@Targets.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") check_required_components("@PROJECT_NAME@") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8083cee0..f58170e8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,8 +7,9 @@ set_target_properties(${SELF_LIBRARY_NAME} VERSION ${PROJECT_VERSION} SOVERSION 1) -xo_indentlog_dependency(${SELF_LIBRARY_NAME}) +xo_internal_dependency(${SELF_LIBRARY_NAME} indentlog) +#xo_indentlog_dependency(${SELF_LIBRARY_NAME}) -xo_include_options(${SELF_LIBRARY_NAME}) +xo_include_options2(${SELF_LIBRARY_NAME}) xo_compile_options(${SELF_LIBRARY_NAME}) xo_install_library(${SELF_LIBRARY_NAME}) From a35770dc78d033659230449b823d743c36d7d9f2 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 17:07:49 -0400 Subject: [PATCH 26/60] refcnt: build: streamline, using xo-cmake macros --- CMakeLists.txt | 38 +-- cmake/code-coverage.cmake | 678 -------------------------------------- src/CMakeLists.txt | 3 +- utest/CMakeLists.txt | 5 +- 4 files changed, 6 insertions(+), 718 deletions(-) delete mode 100644 cmake/code-coverage.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f7971f6..54ec054d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,8 @@ enable_language(CXX) # common XO cmake macros (see proj/xo-cmake) include(xo_macros/xo_cxx) -include(cmake/cxx.cmake) -include(cmake/code-coverage.cmake) +#include(cmake/cxx.cmake) +include(xo_macros/code-coverage) # ---------------------------------------------------------------- # unit test setup @@ -55,43 +55,9 @@ add_subdirectory(utest) xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) -#set(XO_PROJECT_CONFIG_VERSION "${XO_PROJECT_NAME}ConfigVersion.cmake") -#set(XO_PROJECT_CONFIG "${XO_PROJECT_NAME}Config.cmake") -# -#include(CMakePackageConfigHelpers) -#write_basic_package_version_file("${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG_VERSION}" -# VERSION 0.1 -# COMPATIBILITY AnyNewerVersion -#) -# -##install( -## TARGETS ${XO_PROJECT_NAME} -## EXPORT ${XO_PROJECT_NAME}Targets -## LIBRARY DESTINATION lib COMPONENT Runtime -## ARCHIVE DESTINATION lib COMPONENT Development -## RUNTIME DESTINATION bin COMPONENT Runtime -## PUBLIC_HEADER DESTINATION include COMPONENT Development -## BUNDLE DESTINATION bin COMPONENT Runtime -## ) -# -#configure_package_config_file( -# "${PROJECT_SOURCE_DIR}/cmake/${XO_PROJECT_NAME}Config.cmake.in" -# "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG}" -# INSTALL_DESTINATION lib/cmake/${XO_PROJECT_NAME} -# ) -# -#install(EXPORT ${XO_PROJECT_NAME}Targets DESTINATION lib/cmake/${XO_PROJECT_NAME}) -#install( -# FILES -# "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG_VERSION}" -# "${PROJECT_BINARY_DIR}/${XO_PROJECT_CONFIG}" -# DESTINATION lib/cmake/${XO_PROJECT_NAME}) - # ---------------------------------------------------------------- # install .hpp xo_install_include_tree() -#install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ DESTINATION include) - # end CMakeLists.txt diff --git a/cmake/code-coverage.cmake b/cmake/code-coverage.cmake deleted file mode 100644 index b6b36064..00000000 --- a/cmake/code-coverage.cmake +++ /dev/null @@ -1,678 +0,0 @@ -# -# Copyright (C) 2018-2020 by George Cave - gcave@stablecoder.ca -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may not -# use this file except in compliance with the License. You may obtain a copy of -# the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. - -# USAGE: To enable any code coverage instrumentation/targets, the single CMake -# option of `CODE_COVERAGE` needs to be set to 'ON', either by GUI, ccmake, or -# on the command line. -# -# From this point, there are two primary methods for adding instrumentation to -# targets: -# -# 1 - A blanket instrumentation by calling `add_code_coverage()`, where -# all targets in that directory and all subdirectories are automatically -# instrumented. -# -# 2 - Per-target instrumentation by calling -# `target_code_coverage()`, where the target is given and thus only -# that target is instrumented. This applies to both libraries and executables. -# -# To add coverage targets, such as calling `make ccov` to generate the actual -# coverage information for perusal or consumption, call -# `target_code_coverage()` on an *executable* target. -# -# Example 1: All targets instrumented -# -# In this case, the coverage information reported will will be that of the -# `theLib` library target and `theExe` executable. -# -# 1a: Via global command -# -# ~~~ -# add_code_coverage() # Adds instrumentation to all targets -# -# add_library(theLib lib.cpp) -# -# add_executable(theExe main.cpp) -# target_link_libraries(theExe PRIVATE theLib) -# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target -# # (instrumentation already added via global anyways) -# # for generating code coverage reports. -# ~~~ -# -# 1b: Via target commands -# -# ~~~ -# add_library(theLib lib.cpp) -# target_code_coverage(theLib) # As a library target, adds coverage instrumentation but no targets. -# -# add_executable(theExe main.cpp) -# target_link_libraries(theExe PRIVATE theLib) -# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target and instrumentation for generating code coverage reports. -# ~~~ -# -# Example 2: Target instrumented, but with regex pattern of files to be excluded -# from report -# -# ~~~ -# add_executable(theExe main.cpp non_covered.cpp) -# target_code_coverage(theExe EXCLUDE non_covered.cpp test/*) # As an executable target, the reports will exclude the non-covered.cpp file, and any files in a test/ folder. -# ~~~ -# -# Example 3: Target added to the 'ccov' and 'ccov-all' targets -# -# ~~~ -# add_code_coverage_all_targets(EXCLUDE test/*) # Adds the 'ccov-all' target set and sets it to exclude all files in test/ folders. -# -# add_executable(theExe main.cpp non_covered.cpp) -# target_code_coverage(theExe AUTO ALL EXCLUDE non_covered.cpp test/*) # As an executable target, adds to the 'ccov' and ccov-all' targets, and the reports will exclude the non-covered.cpp file, and any files in a test/ folder. -# ~~~ - -# Options -option( - CODE_COVERAGE - "Builds targets with code coverage instrumentation. (Requires GCC or Clang)" - OFF) - -# Programs -find_program(LLVM_COV_PATH llvm-cov) -find_program(LLVM_PROFDATA_PATH llvm-profdata) -find_program(LCOV_PATH lcov) -find_program(GENHTML_PATH genhtml) -# Hide behind the 'advanced' mode flag for GUI/ccmake -mark_as_advanced(FORCE LLVM_COV_PATH LLVM_PROFDATA_PATH LCOV_PATH GENHTML_PATH) - -# Variables -set(CMAKE_COVERAGE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/ccov) -set_property(GLOBAL PROPERTY JOB_POOLS ccov_serial_pool=1) - -# Common initialization/checks -if(CODE_COVERAGE AND NOT CODE_COVERAGE_ADDED) - set(CODE_COVERAGE_ADDED ON) - - # Common Targets - add_custom_target( - ccov-preprocessing - COMMAND ${CMAKE_COMMAND} -E make_directory - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY} - DEPENDS ccov-clean) - - if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" - OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") - # Messages - message(STATUS "Building with llvm Code Coverage Tools") - - if(NOT LLVM_COV_PATH) - message(FATAL_ERROR "llvm-cov not found! Aborting.") - else() - # Version number checking for 'EXCLUDE' compatibility - execute_process(COMMAND ${LLVM_COV_PATH} --version - OUTPUT_VARIABLE LLVM_COV_VERSION_CALL_OUTPUT) - string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" LLVM_COV_VERSION - ${LLVM_COV_VERSION_CALL_OUTPUT}) - - if(LLVM_COV_VERSION VERSION_LESS "7.0.0") - message( - WARNING - "target_code_coverage()/add_code_coverage_all_targets() 'EXCLUDE' option only available on llvm-cov >= 7.0.0" - ) - endif() - endif() - - # Targets - if(${CMAKE_VERSION} VERSION_LESS "3.17.0") - add_custom_target( - ccov-clean - COMMAND ${CMAKE_COMMAND} -E remove -f - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list - COMMAND ${CMAKE_COMMAND} -E remove -f - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list) - else() - add_custom_target( - ccov-clean - COMMAND ${CMAKE_COMMAND} -E rm -f - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list - COMMAND ${CMAKE_COMMAND} -E rm -f - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list) - endif() - - # Used to get the shared object file list before doing the main all- - # processing - add_custom_target( - ccov-libs - COMMAND ; - COMMENT "libs ready for coverage report.") - - elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES - "GNU") - # Messages - message(STATUS "Building with lcov Code Coverage Tools") - - if(CMAKE_BUILD_TYPE) - string(TOUPPER ${CMAKE_BUILD_TYPE} upper_build_type) - if(NOT ${upper_build_type} STREQUAL "DEBUG") - message( - WARNING - "Code coverage results with an optimized (non-Debug) build may be misleading" - ) - endif() - else() - message( - WARNING - "Code coverage results with an optimized (non-Debug) build may be misleading" - ) - endif() - if(NOT LCOV_PATH) - message(FATAL_ERROR "lcov not found! Aborting...") - endif() - if(NOT GENHTML_PATH) - message(FATAL_ERROR "genhtml not found! Aborting...") - endif() - - # Targets - add_custom_target(ccov-clean COMMAND ${LCOV_PATH} --directory - ${CMAKE_BINARY_DIR} --zerocounters) - - else() - message(FATAL_ERROR "Code coverage requires Clang or GCC. Aborting.") - endif() -endif() - -# Adds code coverage instrumentation to a library, or instrumentation/targets -# for an executable target. -# ~~~ -# EXECUTABLE ADDED TARGETS: -# GCOV/LCOV: -# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter. -# ccov-${TARGET_NAME} : Generates HTML code coverage report for the associated named target. -# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report. -# -# LLVM-COV: -# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter. -# ccov-report : Generates HTML code coverage report for every target added with 'AUTO' parameter. -# ccov-${TARGET_NAME} : Generates HTML code coverage report. -# ccov-report-${TARGET_NAME} : Prints to command line summary per-file coverage information. -# ccov-export-${TARGET_NAME} : Exports the coverage report to a JSON file. -# ccov-show-${TARGET_NAME} : Prints to command line detailed per-line coverage information. -# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report. -# ccov-all-report : Prints summary per-file coverage information for every target added with ALL' parameter to the command line. -# ccov-all-export : Exports the coverage report to a JSON file. -# -# Required: -# TARGET_NAME - Name of the target to generate code coverage for. -# Optional: -# PUBLIC - Sets the visibility for added compile options to targets to PUBLIC instead of the default of PRIVATE. -# INTERFACE - Sets the visibility for added compile options to targets to INTERFACE instead of the default of PRIVATE. -# PLAIN - Do not set any target visibility (backward compatibility with old cmake projects) -# AUTO - Adds the target to the 'ccov' target so that it can be run in a batch with others easily. Effective on executable targets. -# ALL - Adds the target to the 'ccov-all' and 'ccov-all-report' targets, which merge several executable targets coverage data to a single report. Effective on executable targets. -# EXTERNAL - For GCC's lcov, allows the profiling of 'external' files from the processing directory -# COVERAGE_TARGET_NAME - For executables ONLY, changes the outgoing target name so instead of `ccov-${TARGET_NAME}` it becomes `ccov-${COVERAGE_TARGET_NAME}`. -# EXCLUDE - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex! **These do not copy to the 'all' targets.** -# OBJECTS - For executables ONLY, if the provided targets are shared libraries, adds coverage information to the output -# ARGS - For executables ONLY, appends the given arguments to the associated ccov-* executable call -# ~~~ -function(target_code_coverage TARGET_NAME) - # Argument parsing - set(options AUTO ALL EXTERNAL PUBLIC INTERFACE PLAIN) - set(single_value_keywords COVERAGE_TARGET_NAME) - set(multi_value_keywords EXCLUDE OBJECTS ARGS) - cmake_parse_arguments( - target_code_coverage "${options}" "${single_value_keywords}" - "${multi_value_keywords}" ${ARGN}) - - # Set the visibility of target functions to PUBLIC, INTERFACE or default to - # PRIVATE. - if(target_code_coverage_PUBLIC) - set(TARGET_VISIBILITY PUBLIC) - set(TARGET_LINK_VISIBILITY PUBLIC) - elseif(target_code_coverage_INTERFACE) - set(TARGET_VISIBILITY INTERFACE) - set(TARGET_LINK_VISIBILITY INTERFACE) - elseif(target_code_coverage_PLAIN) - set(TARGET_VISIBILITY PUBLIC) - set(TARGET_LINK_VISIBILITY) - else() - set(TARGET_VISIBILITY PRIVATE) - set(TARGET_LINK_VISIBILITY PRIVATE) - endif() - - if(NOT target_code_coverage_COVERAGE_TARGET_NAME) - # If a specific name was given, use that instead. - set(target_code_coverage_COVERAGE_TARGET_NAME ${TARGET_NAME}) - endif() - - if(CODE_COVERAGE) - - # Add code coverage instrumentation to the target's linker command - if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" - OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") - target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} - -fprofile-instr-generate -fcoverage-mapping) - target_link_options(${TARGET_NAME} ${TARGET_VISIBILITY} - -fprofile-instr-generate -fcoverage-mapping) - elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES - "GNU") - target_compile_options(${TARGET_NAME} ${TARGET_VISIBILITY} -fprofile-arcs - -ftest-coverage) - target_link_libraries(${TARGET_NAME} ${TARGET_LINK_VISIBILITY} gcov) - endif() - - # Targets - get_target_property(target_type ${TARGET_NAME} TYPE) - - # Add shared library to processing for 'all' targets - if(target_type STREQUAL "SHARED_LIBRARY" AND target_code_coverage_ALL) - if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" - OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") - add_custom_target( - ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND - ${CMAKE_COMMAND} -E echo "-object=$" >> - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list - DEPENDS ccov-preprocessing ${TARGET_NAME}) - - if(NOT TARGET ccov-libs) - message( - FATAL_ERROR - "Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'." - ) - endif() - - add_dependencies(ccov-libs - ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) - endif() - endif() - - # For executables add targets to run and produce output - if(target_type STREQUAL "EXECUTABLE") - if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" - OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") - - # If there are shared objects to also work with, generate the string to - # add them here - foreach(SO_TARGET ${target_code_coverage_OBJECTS}) - # Check to see if the target is a shared object - if(TARGET ${SO_TARGET}) - get_target_property(SO_TARGET_TYPE ${SO_TARGET} TYPE) - if(${SO_TARGET_TYPE} STREQUAL "SHARED_LIBRARY") - set(SO_OBJECTS ${SO_OBJECTS} -object=$) - endif() - endif() - endforeach() - - # Run the executable, generating raw profile data Make the run data - # available for further processing. Separated to allow Windows to run - # this target serially. - add_custom_target( - ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND - ${CMAKE_COMMAND} -E env - LLVM_PROFILE_FILE=${target_code_coverage_COVERAGE_TARGET_NAME}.profraw - $ ${target_code_coverage_ARGS} - COMMAND - ${CMAKE_COMMAND} -E echo "-object=$" - ${SO_OBJECTS} >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list - COMMAND - ${CMAKE_COMMAND} -E echo - "${CMAKE_CURRENT_BINARY_DIR}/${target_code_coverage_COVERAGE_TARGET_NAME}.profraw" - >> ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list - JOB_POOL ccov_serial_pool - DEPENDS ccov-preprocessing ccov-libs ${TARGET_NAME}) - - # Merge the generated profile data so llvm-cov can process it - add_custom_target( - ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND - ${LLVM_PROFDATA_PATH} merge -sparse - ${target_code_coverage_COVERAGE_TARGET_NAME}.profraw -o - ${target_code_coverage_COVERAGE_TARGET_NAME}.profdata - DEPENDS ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) - - # Ignore regex only works on LLVM >= 7 - if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0") - foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE}) - set(EXCLUDE_REGEX ${EXCLUDE_REGEX} - -ignore-filename-regex='${EXCLUDE_ITEM}') - endforeach() - endif() - - # Print out details of the coverage information to the command line - add_custom_target( - ccov-show-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND - ${LLVM_COV_PATH} show $ ${SO_OBJECTS} - -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata - -show-line-counts-or-regions ${EXCLUDE_REGEX} - DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) - - # Print out a summary of the coverage information to the command line - add_custom_target( - ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND - ${LLVM_COV_PATH} report $ ${SO_OBJECTS} - -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata - ${EXCLUDE_REGEX} - DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) - - # Export coverage information so continuous integration tools (e.g. - # Jenkins) can consume it - add_custom_target( - ccov-export-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND - ${LLVM_COV_PATH} export $ ${SO_OBJECTS} - -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata - -format="text" ${EXCLUDE_REGEX} > - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.json - DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) - - # Generates HTML output of the coverage information for perusal - add_custom_target( - ccov-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND - ${LLVM_COV_PATH} show $ ${SO_OBJECTS} - -instr-profile=${target_code_coverage_COVERAGE_TARGET_NAME}.profdata - -show-line-counts-or-regions - -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME} - -format="html" ${EXCLUDE_REGEX} - DEPENDS ccov-processing-${target_code_coverage_COVERAGE_TARGET_NAME}) - - elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES - "GNU") - set(COVERAGE_INFO - "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}.info" - ) - - # Run the executable, generating coverage information - add_custom_target( - ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND $ ${target_code_coverage_ARGS} - DEPENDS ccov-preprocessing ${TARGET_NAME}) - - # Generate exclusion string for use - foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE}) - set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO} - '${EXCLUDE_ITEM}') - endforeach() - - if(EXCLUDE_REGEX) - set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file - ${COVERAGE_INFO}) - else() - set(EXCLUDE_COMMAND ;) - endif() - - if(NOT ${target_code_coverage_EXTERNAL}) - set(EXTERNAL_OPTION --no-external) - endif() - - # Capture coverage data - if(${CMAKE_VERSION} VERSION_LESS "3.17.0") - add_custom_target( - ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO} - COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters - COMMAND $ ${target_code_coverage_ARGS} - COMMAND - ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory - ${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file - ${COVERAGE_INFO} - COMMAND ${EXCLUDE_COMMAND} - DEPENDS ccov-preprocessing ${TARGET_NAME}) - else() - add_custom_target( - ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO} - COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --zerocounters - COMMAND $ ${target_code_coverage_ARGS} - COMMAND - ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --base-directory - ${CMAKE_SOURCE_DIR} --capture ${EXTERNAL_OPTION} --output-file - ${COVERAGE_INFO} - COMMAND ${EXCLUDE_COMMAND} - DEPENDS ccov-preprocessing ${TARGET_NAME}) - endif() - - # Generates HTML output of the coverage information for perusal - add_custom_target( - ccov-${target_code_coverage_COVERAGE_TARGET_NAME} - COMMAND - ${GENHTML_PATH} -o - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME} - ${COVERAGE_INFO} - DEPENDS ccov-capture-${target_code_coverage_COVERAGE_TARGET_NAME}) - endif() - - add_custom_command( - TARGET ccov-${target_code_coverage_COVERAGE_TARGET_NAME} - POST_BUILD - COMMAND ; - COMMENT - "Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${target_code_coverage_COVERAGE_TARGET_NAME}/index.html in your browser to view the coverage report." - ) - - # AUTO - if(target_code_coverage_AUTO) - if(NOT TARGET ccov) - add_custom_target(ccov) - endif() - add_dependencies(ccov ccov-${target_code_coverage_COVERAGE_TARGET_NAME}) - - if(NOT CMAKE_C_COMPILER_ID MATCHES "GNU" AND NOT CMAKE_CXX_COMPILER_ID - MATCHES "GNU") - if(NOT TARGET ccov-report) - add_custom_target(ccov-report) - endif() - add_dependencies( - ccov-report - ccov-report-${target_code_coverage_COVERAGE_TARGET_NAME}) - endif() - endif() - - # ALL - if(target_code_coverage_ALL) - if(NOT TARGET ccov-all-processing) - message( - FATAL_ERROR - "Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'." - ) - endif() - - add_dependencies(ccov-all-processing - ccov-run-${target_code_coverage_COVERAGE_TARGET_NAME}) - endif() - endif() - endif() -endfunction() - -# Adds code coverage instrumentation to all targets in the current directory and -# any subdirectories. To add coverage instrumentation to only specific targets, -# use `target_code_coverage`. -function(add_code_coverage) - if(CODE_COVERAGE) - if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" - OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") - add_compile_options(-fprofile-instr-generate -fcoverage-mapping) - add_link_options(-fprofile-instr-generate -fcoverage-mapping) - elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES - "GNU") - add_compile_options(-fprofile-arcs -ftest-coverage) - link_libraries(gcov) - endif() - endif() -endfunction() - -# Adds the 'ccov-all' type targets that calls all targets added via -# `target_code_coverage` with the `ALL` parameter, but merges all the coverage -# data from them into a single large report instead of the numerous smaller -# reports. Also adds the ccov-all-capture Generates an all-merged.info file, for -# use with coverage dashboards (e.g. codecov.io, coveralls). -# ~~~ -# Optional: -# EXCLUDE - Excludes files of the patterns provided from coverage. Note that GCC/lcov excludes by glob pattern, and clang/LLVM excludes via regex! -# ~~~ -function(add_code_coverage_all_targets) - # Argument parsing - set(multi_value_keywords EXCLUDE) - cmake_parse_arguments(add_code_coverage_all_targets "" "" - "${multi_value_keywords}" ${ARGN}) - - if(CODE_COVERAGE) - if(CMAKE_C_COMPILER_ID MATCHES "(Apple)?[Cc]lang" - OR CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?[Cc]lang") - - # Merge the profile data for all of the run executables - if(WIN32) - add_custom_target( - ccov-all-processing - COMMAND - powershell -Command $$FILELIST = Get-Content - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list\; llvm-profdata.exe - merge -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata - -sparse $$FILELIST) - else() - add_custom_target( - ccov-all-processing - COMMAND - ${LLVM_PROFDATA_PATH} merge -o - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata -sparse `cat - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list`) - endif() - - # Regex exclude only available for LLVM >= 7 - if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0") - foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE}) - set(EXCLUDE_REGEX ${EXCLUDE_REGEX} - -ignore-filename-regex='${EXCLUDE_ITEM}') - endforeach() - endif() - - # Print summary of the code coverage information to the command line - if(WIN32) - add_custom_target( - ccov-all-report - COMMAND - powershell -Command $$FILELIST = Get-Content - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe - report $$FILELIST - -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata - ${EXCLUDE_REGEX} - DEPENDS ccov-all-processing) - else() - add_custom_target( - ccov-all-report - COMMAND - ${LLVM_COV_PATH} report `cat - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` - -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata - ${EXCLUDE_REGEX} - DEPENDS ccov-all-processing) - endif() - - # Export coverage information so continuous integration tools (e.g. - # Jenkins) can consume it - add_custom_target( - ccov-all-export - COMMAND - ${LLVM_COV_PATH} export `cat - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` - -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata - -format="text" ${EXCLUDE_REGEX} > - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/coverage.json - DEPENDS ccov-all-processing) - - # Generate HTML output of all added targets for perusal - if(WIN32) - add_custom_target( - ccov-all - COMMAND - powershell -Command $$FILELIST = Get-Content - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list\; llvm-cov.exe show - $$FILELIST - -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata - -show-line-counts-or-regions - -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged - -format="html" ${EXCLUDE_REGEX} - DEPENDS ccov-all-processing) - else() - add_custom_target( - ccov-all - COMMAND - ${LLVM_COV_PATH} show `cat - ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list` - -instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata - -show-line-counts-or-regions - -output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged - -format="html" ${EXCLUDE_REGEX} - DEPENDS ccov-all-processing) - endif() - - elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES - "GNU") - set(COVERAGE_INFO "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.info") - - # Nothing required for gcov - add_custom_target(ccov-all-processing COMMAND ;) - - # Exclusion regex string creation - set(EXCLUDE_REGEX) - foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE}) - set(EXCLUDE_REGEX ${EXCLUDE_REGEX} --remove ${COVERAGE_INFO} - '${EXCLUDE_ITEM}') - endforeach() - - if(EXCLUDE_REGEX) - set(EXCLUDE_COMMAND ${LCOV_PATH} ${EXCLUDE_REGEX} --output-file - ${COVERAGE_INFO}) - else() - set(EXCLUDE_COMMAND ;) - endif() - - # Capture coverage data - if(${CMAKE_VERSION} VERSION_LESS "3.17.0") - add_custom_target( - ccov-all-capture - COMMAND ${CMAKE_COMMAND} -E remove -f ${COVERAGE_INFO} - COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture - --output-file ${COVERAGE_INFO} - COMMAND ${EXCLUDE_COMMAND} - DEPENDS ccov-preprocessing ccov-all-processing) - else() - add_custom_target( - ccov-all-capture - COMMAND ${CMAKE_COMMAND} -E rm -f ${COVERAGE_INFO} - COMMAND ${LCOV_PATH} --directory ${CMAKE_BINARY_DIR} --capture - --output-file ${COVERAGE_INFO} - COMMAND ${EXCLUDE_COMMAND} - DEPENDS ccov-preprocessing ccov-all-processing) - endif() - - # Generates HTML output of all targets for perusal - add_custom_target( - ccov-all - COMMAND ${GENHTML_PATH} -o ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged - ${COVERAGE_INFO} -p ${CMAKE_SOURCE_DIR} - DEPENDS ccov-all-capture) - - endif() - - add_custom_command( - TARGET ccov-all - POST_BUILD - COMMAND ; - COMMENT - "Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged/index.html in your browser to view the coverage report." - ) - endif() -endfunction() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f58170e8..0a60fbea 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -8,8 +8,7 @@ set_target_properties(${SELF_LIBRARY_NAME} SOVERSION 1) xo_internal_dependency(${SELF_LIBRARY_NAME} indentlog) -#xo_indentlog_dependency(${SELF_LIBRARY_NAME}) xo_include_options2(${SELF_LIBRARY_NAME}) xo_compile_options(${SELF_LIBRARY_NAME}) -xo_install_library(${SELF_LIBRARY_NAME}) +xo_install_library2(${SELF_LIBRARY_NAME}) diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt index 74706592..1f5ced17 100644 --- a/utest/CMakeLists.txt +++ b/utest/CMakeLists.txt @@ -5,7 +5,7 @@ set(SELF_EXECUTABLE_NAME utest.refcnt) # These tests can use the Catch2-provided main set(SELF_SOURCE_FILES intrusive_ptr.test.cpp refcnt_utest_main.cpp) add_executable(${SELF_EXECUTABLE_NAME} ${SELF_SOURCE_FILES}) -xo_include_options(${SELF_EXECUTABLE_NAME}) +xo_include_options2(${SELF_EXECUTABLE_NAME}) add_test(NAME ${SELF_EXECUTABLE_NAME} COMMAND ${SELF_EXECUTABLE_NAME}) target_code_coverage(${SELF_EXECUTABLE_NAME} AUTO ALL) @@ -30,7 +30,8 @@ target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC # ---------------------------------------------------------------- # internal dependencies: refcnt, ... -target_link_libraries(${SELF_EXECUTABLE_NAME} PUBLIC refcnt) +xo_self_dependency(${SELF_EXECUTABLE_NAME} refcnt) +#target_link_libraries(${SELF_EXECUTABLE_NAME} PUBLIC refcnt) # ---------------------------------------------------------------- # 3rd part dependency: catch2: From a2e3bda4ff1549d8a826b2971fb62113c34c6946 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 17:21:51 -0400 Subject: [PATCH 27/60] refcnt: build: bugfix: + xo_toplevel_compile_options() --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 54ec054d..3c5a6fc6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) # always write compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") +xo_toplevel_compile_options() + if(NOT CMAKE_INSTALL_RPATH) set(CMAKE_INSTALL_RPATH $(CMAKE_INSTALL_PREFIX)/lib CACHE STRING "runpath in installed libraries/executables") endif() From 6d09431d407206ea82609dd7f37630219849db66 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 17:34:19 -0400 Subject: [PATCH 28/60] refcnt: build tidy --- CMakeLists.txt | 1 - cmake/cxx.cmake | 97 ------------------------------------------------- 2 files changed, 98 deletions(-) delete mode 100644 cmake/cxx.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c5a6fc6..bc8e2ef1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,6 @@ enable_language(CXX) # common XO cmake macros (see proj/xo-cmake) include(xo_macros/xo_cxx) -#include(cmake/cxx.cmake) include(xo_macros/code-coverage) # ---------------------------------------------------------------- diff --git a/cmake/cxx.cmake b/cmake/cxx.cmake deleted file mode 100644 index fd9aed25..00000000 --- a/cmake/cxx.cmake +++ /dev/null @@ -1,97 +0,0 @@ -# ---------------------------------------------------------------- -# use this in subdirs that compile c++ code -# -macro(xo_include_options target) - # ---------------------------------------------------------------- - # PROJECT_SOURCE_DIR: - # so we can for example write - # #include "ordinaltree/foo.hpp" - # from anywhere in the project - # PROJECT_BINARY_DIR: - # since generated version file will be in build directory, - # need that build directory to also appear in - # compiler's include path - # - target_include_directories( - ${target} PUBLIC - $ # e.g. for #include "indentlog/scope.hpp" - $ - $ # e.g. for #include "Refcounted.hpp" in refcnt/src - $ - $ # e.g. for generated config.hpp file - ) - - # ---------------------------------------------------------------- - # make standard directories for std:: includes explicit - # so that - # (1) they appear in compile_commands.json. - # (2) clangd (run from emacs lsp-mode) can find them - # - if(CMAKE_EXPORT_COMPILE_COMMANDS) - set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) - endif() -endmacro() - -# ---------------------------------------------------------------- -# variable -# XO_ADDRESS_SANITIZE -# determines whether to enable address sanitizer for the XO project -# (see toplevel CMakeLists.txt) -# ---------------------------------------------------------------- -if(XO_ADDRESS_SANITIZE) - add_compile_options(-fsanitize=address) - add_link_options(-fsanitize=address) -endif() - -# XO_STANDARD_COMPILE_OPTIONS: use these when XO_ADDRESS_SANITIZE=OFF -set(XO_STANDARD_COMPILE_OPTIONS -Werror -Wall -Wextra) - -# XO_ADDRESS_SANITIZE_COMPILE_OPTIONS: use when XO_ADDRESS_SANITIZE=ON -# -# address sanitizer build complains about _FORTIFY_SOURCE redefines -# In file included from :460: -# :1:9: error: '_FORTIFY_SOURCE' macro redefined [-Werror,-Wmacro-redefined] -# #define _FORTIFY_SOURCE 2 -# -set(XO_ADDRESS_SANITIZE_COMPILE_OPTIONS -Werror -Wall -Wextra -Wno-macro-redefined) - -# XO_COMPILE_OPTIONS: use these with xo_compile_options() macro -if(XO_ADDRESS_SANITIZE) - set(XO_COMPILE_OPTIONS ${XO_ADDRESS_SANITIZE_COMPILE_OPTIONS}) -else() - set(XO_COMPILE_OPTIONS ${XO_STANDARD_COMPILE_OPTIONS}) -endif() - -# ---------------------------------------------------------------- -# generally want all the errors+warnings! -# however: address sanitizer generates error on _FORTIFY_SOURCE -# -macro(xo_compile_options target) - target_compile_options(${target} PRIVATE ${XO_COMPILE_OPTIONS}) -endmacro() - -# ---------------------------------------------------------------- -# use this for a subdir that builds a library -# and supports find_package() -# -macro(xo_install_library target) - install( - TARGETS ${target} - EXPORT ${target}Targets - LIBRARY DESTINATION lib COMPONENT Runtime - ARCHIVE DESTINATION lib COMPONENT Development - RUNTIME DESTINATION bin COMPONENT Runtime - PUBLIC_HEADER DESTINATION include COMPONENT Development - BUNDLE DESTINATION bin COMPONENT Runtime - ) -endmacro() - -# ---------------------------------------------------------------- -# use this when relying on indentlog [[https://github.com/rconybea/indentlog]] headers -# -macro(xo_indentlog_dependency target) - find_package(indentlog CONFIG REQUIRED) - #add_dependencies(${target} indentlog) - target_link_libraries(${target} PUBLIC indentlog) - #target_include_directories(${target} PUBLIC ${indentlog_DIR}/../../../include) -endmacro() From 29d5d68319a2563ed35e6fdc673d0f1fa30c5502 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 17:59:47 -0400 Subject: [PATCH 29/60] refcnt: build: xo-macros defaults CMAKE_MODULE_PATH --- CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bc8e2ef1..1b0b2a73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,10 +41,6 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") xo_toplevel_compile_options() -if(NOT CMAKE_INSTALL_RPATH) - set(CMAKE_INSTALL_RPATH $(CMAKE_INSTALL_PREFIX)/lib CACHE STRING "runpath in installed libraries/executables") -endif() - # ---------------------------------------------------------------- # sources From c361c5d4ca988c8d67120a633c8ba4ddd29e0dab Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 18:13:15 -0400 Subject: [PATCH 30/60] refcnt: consolidate CMAKE_CXX_STANDARD setting --- CMakeLists.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b0b2a73..733ff9f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,12 +30,6 @@ set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") add_definitions(${PROJECT_CXX_FLAGS}) -if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 20) -endif() - -set(CMAKE_CXX_STANDARD_REQUIRED True) - # always write compile_commands.json set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") From 277935c25e202124a4973476dac62c33e3f1ff5c Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 18:23:40 -0400 Subject: [PATCH 31/60] randomgen: consolidate CMAKE_EXPORT_COMPILE_COMMANDS --- CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 733ff9f9..aef9d414 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,9 +30,6 @@ set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") add_definitions(${PROJECT_CXX_FLAGS}) -# always write compile_commands.json -set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE INTERNAL "") - xo_toplevel_compile_options() # ---------------------------------------------------------------- From 4e0c8e92f236ebfce2b345b686f8b45fc26ba8b7 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 27 Sep 2023 19:41:26 -0400 Subject: [PATCH 32/60] refcnt: use xo_add_shared_library() from xo-cmake --- src/CMakeLists.txt | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0a60fbea..9d006fd9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,14 +1,5 @@ set(SELF_LIBRARY_NAME refcnt) set(SELF_SOURCE_FILES Refcounted.cpp Displayable.cpp) -add_library(${SELF_LIBRARY_NAME} SHARED ${SELF_SOURCE_FILES}) - -set_target_properties(${SELF_LIBRARY_NAME} - PROPERTIES - VERSION ${PROJECT_VERSION} - SOVERSION 1) +xo_add_shared_library(${SELF_LIBRARY_NAME} ${PROJECT_VERSION} 1 ${SELF_SOURCE_FILES}) xo_internal_dependency(${SELF_LIBRARY_NAME} indentlog) - -xo_include_options2(${SELF_LIBRARY_NAME}) -xo_compile_options(${SELF_LIBRARY_NAME}) -xo_install_library2(${SELF_LIBRARY_NAME}) From ac265dc2b709eb97c22a5e8d004369e6928671af Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 3 Oct 2023 17:36:50 -0400 Subject: [PATCH 33/60] build: clenaup dependencies --- src/CMakeLists.txt | 3 ++- utest/CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8083cee0..43fe7a82 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,7 +7,8 @@ set_target_properties(${SELF_LIBRARY_NAME} VERSION ${PROJECT_VERSION} SOVERSION 1) -xo_indentlog_dependency(${SELF_LIBRARY_NAME}) +xo_internal_dependency(${SELF_LIBRARY_NAME} indentlog) +#xo_indentlog_dependency(${SELF_LIBRARY_NAME}) xo_include_options(${SELF_LIBRARY_NAME}) xo_compile_options(${SELF_LIBRARY_NAME}) diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt index 74706592..cfdf594a 100644 --- a/utest/CMakeLists.txt +++ b/utest/CMakeLists.txt @@ -35,7 +35,7 @@ target_link_libraries(${SELF_EXECUTABLE_NAME} PUBLIC refcnt) # ---------------------------------------------------------------- # 3rd part dependency: catch2: -find_package(Catch2 2 REQUIRED) +xo_external_target_dependency(${SELF_EXECUTABLE_NAME} Catch2 Catch2::Catch2) # need this so that catch2/include appears in compile_commands.json, # on which lsp integration relies. From 592447fa153ea2ccfc3bb65bb0ed9f6ba6db0789 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 3 Oct 2023 17:37:50 -0400 Subject: [PATCH 34/60] + .gitignore --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..86d062ab --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# symlink to ${mybuilddirectory}/compile_commands.json for LSP +compile_commands.json +# LSP keeps state here +.cache +# typical build directories +build +ccov From 4e88ca9820e2dc1c0bd610816af33fb5cd6bc7be Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 3 Oct 2023 21:55:52 -0400 Subject: [PATCH 35/60] build: refcnt: streamline utest build --- utest/CMakeLists.txt | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt index cfdf594a..ef9a4006 100644 --- a/utest/CMakeLists.txt +++ b/utest/CMakeLists.txt @@ -1,11 +1,11 @@ # build unittest 'refcnt/utest/utest.refcnt set(SELF_EXECUTABLE_NAME utest.refcnt) - # These tests can use the Catch2-provided main set(SELF_SOURCE_FILES intrusive_ptr.test.cpp refcnt_utest_main.cpp) + add_executable(${SELF_EXECUTABLE_NAME} ${SELF_SOURCE_FILES}) -xo_include_options(${SELF_EXECUTABLE_NAME}) +xo_include_options2(${SELF_EXECUTABLE_NAME}) add_test(NAME ${SELF_EXECUTABLE_NAME} COMMAND ${SELF_EXECUTABLE_NAME}) target_code_coverage(${SELF_EXECUTABLE_NAME} AUTO ALL) @@ -15,17 +15,17 @@ target_code_coverage(${SELF_EXECUTABLE_NAME} AUTO ALL) # ---------------------------------------------------------------- # generic project dependency -# PROJECT_SOURCE_DIR: -# so we can for example write -# #include "indentlog/scope.hpp" -# from anywhere in the project -# PROJECT_BINARY_DIR: -# since version file will be in build directory, need that directory -# to also be included in compiler's include path -# -target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC - ${PROJECT_SOURCE_DIR} - ${PROJECT_BINARY_DIR}) +## PROJECT_SOURCE_DIR: +## so we can for example write +## #include "indentlog/scope.hpp" +## from anywhere in the project +## PROJECT_BINARY_DIR: +## since version file will be in build directory, need that directory +## to also be included in compiler's include path +## +#target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC +# ${PROJECT_SOURCE_DIR} +# ${PROJECT_BINARY_DIR}) # ---------------------------------------------------------------- # internal dependencies: refcnt, ... @@ -33,7 +33,7 @@ target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC target_link_libraries(${SELF_EXECUTABLE_NAME} PUBLIC refcnt) # ---------------------------------------------------------------- -# 3rd part dependency: catch2: +# 3rd party dependency: catch2: xo_external_target_dependency(${SELF_EXECUTABLE_NAME} Catch2 Catch2::Catch2) From d480174107a7a2c89b761c9c7a7a483055dd9656 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 6 Oct 2023 18:03:21 -0400 Subject: [PATCH 36/60] refcnt: insert xo/ into include path --- include/{ => xo}/cxxutil/demangle.hpp | 0 include/{ => xo}/refcnt/Displayable.hpp | 2 +- include/{ => xo}/refcnt/Refcounted.hpp | 0 include/{ => xo}/refcnt/Unowned.hpp | 0 src/Displayable.cpp | 2 +- utest/intrusive_ptr.test.cpp | 2 +- 6 files changed, 3 insertions(+), 3 deletions(-) rename include/{ => xo}/cxxutil/demangle.hpp (100%) rename include/{ => xo}/refcnt/Displayable.hpp (95%) rename include/{ => xo}/refcnt/Refcounted.hpp (100%) rename include/{ => xo}/refcnt/Unowned.hpp (100%) diff --git a/include/cxxutil/demangle.hpp b/include/xo/cxxutil/demangle.hpp similarity index 100% rename from include/cxxutil/demangle.hpp rename to include/xo/cxxutil/demangle.hpp diff --git a/include/refcnt/Displayable.hpp b/include/xo/refcnt/Displayable.hpp similarity index 95% rename from include/refcnt/Displayable.hpp rename to include/xo/refcnt/Displayable.hpp index 74708573..5a184667 100644 --- a/include/refcnt/Displayable.hpp +++ b/include/xo/refcnt/Displayable.hpp @@ -2,7 +2,7 @@ #pragma once -#include "refcnt/Refcounted.hpp" +#include "Refcounted.hpp" namespace xo { namespace ref { diff --git a/include/refcnt/Refcounted.hpp b/include/xo/refcnt/Refcounted.hpp similarity index 100% rename from include/refcnt/Refcounted.hpp rename to include/xo/refcnt/Refcounted.hpp diff --git a/include/refcnt/Unowned.hpp b/include/xo/refcnt/Unowned.hpp similarity index 100% rename from include/refcnt/Unowned.hpp rename to include/xo/refcnt/Unowned.hpp diff --git a/src/Displayable.cpp b/src/Displayable.cpp index b8793ad3..ea839841 100644 --- a/src/Displayable.cpp +++ b/src/Displayable.cpp @@ -1,6 +1,6 @@ /* @file Displayable.cpp */ -#include "refcnt/Displayable.hpp" +#include "Displayable.hpp" namespace xo { using xo::tostr; diff --git a/utest/intrusive_ptr.test.cpp b/utest/intrusive_ptr.test.cpp index d8d756e2..f9d1f212 100644 --- a/utest/intrusive_ptr.test.cpp +++ b/utest/intrusive_ptr.test.cpp @@ -1,6 +1,6 @@ /* @file intrusive_ptr.test.cpp */ -#include "refcnt/Refcounted.hpp" +#include "Refcounted.hpp" #include "indentlog/scope.hpp" #include "catch2/catch.hpp" #include From 8ba04142eaec5de963c208979c42f05fa830d7b0 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 6 Oct 2023 18:43:00 -0400 Subject: [PATCH 37/60] build fix: account for xo/ insertion into include path --- README.md | 18 ++++++++++++------ include/xo/refcnt/Refcounted.hpp | 4 ++-- utest/intrusive_ptr.test.cpp | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index bae1fb2c..0b53dc81 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,13 @@ see [github/Rconybea/indentlog](https://github.com/Rconybea/indentlog) ### copy `refcnt` repository locally ``` $ git clone git@github.com:rconybea/refcnt.git -$ ls -d refcnt -refcnt +$ ls -d xo-refcnt +xo-refcnt ``` ### build + install ``` -$ cd refcnt +$ cd xo-refcnt $ mkdir build $ cd build $ cmake -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} .. @@ -39,22 +39,28 @@ $ git clone git@github.com:rconybea/xo-nix.git $ ls -d xo-nix xo-nix $ cd xo-nix -$ nix-build -A refcnt +$ nix-build -A xo-refcnt ``` ### build for unit test coverage ``` -$ cd refcnt +$ cd xo-refcnt $ mkdir ccov $ cd ccov $ cmake -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCODE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug .. ``` +### LSP support +``` +$ cd xo-refcnt +$ ln -s build/compile_commands.json # lsp will look for compile_commands.json in the root of the source tree +``` + ## Examples ### 1 ``` -#include "refcnt/Refcounted.hpp" +#include "xo/refcnt/Refcounted.hpp" using xo::ref::Refcounted; diff --git a/include/xo/refcnt/Refcounted.hpp b/include/xo/refcnt/Refcounted.hpp index dc069cf5..3db621a1 100644 --- a/include/xo/refcnt/Refcounted.hpp +++ b/include/xo/refcnt/Refcounted.hpp @@ -2,8 +2,8 @@ #pragma once -#include "indentlog/scope.hpp" -#include "cxxutil/demangle.hpp" +#include "xo/indentlog/scope.hpp" +#include "xo/cxxutil/demangle.hpp" //#include #include diff --git a/utest/intrusive_ptr.test.cpp b/utest/intrusive_ptr.test.cpp index f9d1f212..bc45d38c 100644 --- a/utest/intrusive_ptr.test.cpp +++ b/utest/intrusive_ptr.test.cpp @@ -1,7 +1,7 @@ /* @file intrusive_ptr.test.cpp */ #include "Refcounted.hpp" -#include "indentlog/scope.hpp" +#include "xo/indentlog/scope.hpp" #include "catch2/catch.hpp" #include #include From 5f22bca0dc92a1abe3754eadaaa47f50afe2e9e4 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 6 Oct 2023 23:56:16 -0400 Subject: [PATCH 38/60] refcnt: build tweaks --- src/CMakeLists.txt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f8b65942..8a05a143 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,5 @@ -set(SELF_LIBRARY_NAME refcnt) -set(SELF_SOURCE_FILES Refcounted.cpp Displayable.cpp) - -xo_add_shared_library(${SELF_LIBRARY_NAME} ${PROJECT_VERSION} 1 ${SELF_SOURCE_FILES}) -xo_internal_dependency(${SELF_LIBRARY_NAME} indentlog) +set(SELF_LIB refcnt) +set(SELF_SRCS Refcounted.cpp Displayable.cpp) +xo_add_shared_library(${SELF_LIB} ${PROJECT_VERSION} 1 ${SELF_SRCS}) +xo_dependency_headeronly(${SELF_LIB} indentlog) From c494a44f990310bfe6034791c12cc9c5edef8cfe Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 8 Oct 2023 13:55:45 -0400 Subject: [PATCH 39/60] build: xo_dependency_headeronly() -> xo_dependency() --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8a05a143..9d35b21d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,4 +2,4 @@ set(SELF_LIB refcnt) set(SELF_SRCS Refcounted.cpp Displayable.cpp) xo_add_shared_library(${SELF_LIB} ${PROJECT_VERSION} 1 ${SELF_SRCS}) -xo_dependency_headeronly(${SELF_LIB} indentlog) +xo_dependency(${SELF_LIB} indentlog) From 4d2cc526585116aed8a9a90cf948cad0085d6490 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 10 Oct 2023 13:03:45 -0400 Subject: [PATCH 40/60] refcnt: + indentlog dep in generated cmake config --- cmake/refcntConfig.cmake.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/refcntConfig.cmake.in b/cmake/refcntConfig.cmake.in index 9c15f36a..5b38fa74 100644 --- a/cmake/refcntConfig.cmake.in +++ b/cmake/refcntConfig.cmake.in @@ -1,4 +1,6 @@ @PACKAGE_INIT@ +include(CMakeFindDependencyMacro) +find_dependency(indentlog) include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") check_required_components("@PROJECT_NAME@") From 9227ed0ecfa5c785e023afa9227a3c9c77b3f269 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 17 Oct 2023 00:19:13 -0400 Subject: [PATCH 41/60] build: drop default gcc-only concept diagnostic --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aef9d414..655a14a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,8 @@ add_code_coverage_all_targets(EXCLUDE /nix/store/* utest/*) # c++ settings set(XO_PROJECT_NAME refcnt) -set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") +set(PROJECT_CXX_FLAGS "") +#set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") # gcc-only! add_definitions(${PROJECT_CXX_FLAGS}) From fb36eee3d0906331e85b4de00b334d3a911b9388 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 17 Oct 2023 14:42:33 -0400 Subject: [PATCH 42/60] track XO_LITERAL api change --- src/Refcounted.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Refcounted.cpp b/src/Refcounted.cpp index 11c8cb62..eade7a50 100644 --- a/src/Refcounted.cpp +++ b/src/Refcounted.cpp @@ -14,7 +14,7 @@ namespace xo { void * this_ptr, Refcount * x) { - scope lscope(XO_LITERAL(verbose, self_type, method_name), + scope lscope(XO_LITERAL(log_level::verbose, self_type, method_name), "enter", xtag("this", this_ptr), xtag("x", x), From 6bb448708be7e616bff3676a7fa2100b84449df4 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 19 Oct 2023 15:46:37 -0400 Subject: [PATCH 43/60] build: symlink-aware install --- CMakeLists.txt | 3 ++- src/CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 655a14a4..bcb080d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,7 @@ xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets # ---------------------------------------------------------------- # install .hpp -xo_install_include_tree() +#xo_install_include_tree() +xo_install_include_tree3(include/xo/cxxutil) # end CMakeLists.txt diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9d35b21d..4622ad44 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,5 @@ set(SELF_LIB refcnt) set(SELF_SRCS Refcounted.cpp Displayable.cpp) -xo_add_shared_library(${SELF_LIB} ${PROJECT_VERSION} 1 ${SELF_SRCS}) +xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) xo_dependency(${SELF_LIB} indentlog) From 24a145b48936688a4493a09b6e29d1ba0abde249 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Sun, 22 Oct 2023 17:05:37 -0400 Subject: [PATCH 44/60] cosmetic: warning comment --- src/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4622ad44..1b90ed07 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,4 +2,8 @@ set(SELF_LIB refcnt) set(SELF_SRCS Refcounted.cpp Displayable.cpp) xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) + +# NOTE: +# dependency set here must be kept consistent with refcnt/cmake/refcntConfig.cmake.in +# xo_dependency(${SELF_LIB} indentlog) From c40f5f3ccf2a5b05be83f56272675876b1b0079a Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 15 Mar 2024 19:24:55 -0400 Subject: [PATCH 45/60] build: streamline CMAKE_MODULE_PATH interaction --- .gitignore | 3 +- CMakeLists.txt | 3 +- cmake/xo-bootstrap-macros.cmake | 12 + utest/intrusive_ptr.test.cpp | 420 ++++++++++++++++---------------- 4 files changed, 224 insertions(+), 214 deletions(-) create mode 100644 cmake/xo-bootstrap-macros.cmake diff --git a/.gitignore b/.gitignore index 86d062ab..9648517d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,4 @@ compile_commands.json # LSP keeps state here .cache # typical build directories -build -ccov +.build* diff --git a/CMakeLists.txt b/CMakeLists.txt index bcb080d4..e9176eee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,7 @@ project(refcnt VERSION 0.1) enable_language(CXX) # common XO cmake macros (see proj/xo-cmake) -include(xo_macros/xo_cxx) -include(xo_macros/code-coverage) +include(cmake/xo-bootstrap-macros.cmake) # ---------------------------------------------------------------- # unit test setup diff --git a/cmake/xo-bootstrap-macros.cmake b/cmake/xo-bootstrap-macros.cmake new file mode 100644 index 00000000..16644435 --- /dev/null +++ b/cmake/xo-bootstrap-macros.cmake @@ -0,0 +1,12 @@ +if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL "prefix")) + # default to typical install location for xo-project-macros + set(CMAKE_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/share/cmake) +endif() + +message("-- CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") +message("-- CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") + +# needs to have been installed somewhere on CMAKE_MODULE_PATH, +# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) +# +include(xo_macros/xo-project-macros) diff --git a/utest/intrusive_ptr.test.cpp b/utest/intrusive_ptr.test.cpp index bc45d38c..890832b7 100644 --- a/utest/intrusive_ptr.test.cpp +++ b/utest/intrusive_ptr.test.cpp @@ -7,295 +7,295 @@ #include namespace xo { - using xo::ref::Refcount; - using xo::ref::Borrow; - using xo::ref::rp; - using xo::ref::brw; - using xo::ref::intrusive_ptr_refcount; - using xo::ref::intrusive_ptr_add_ref; - using xo::ref::intrusive_ptr_release; + using xo::ref::Refcount; + using xo::ref::Borrow; + using xo::ref::rp; + using xo::ref::brw; + using xo::ref::intrusive_ptr_refcount; + using xo::ref::intrusive_ptr_add_ref; + using xo::ref::intrusive_ptr_release; - namespace ut { - namespace { - static uint32_t ctor_count = 0; - static uint32_t dtor_count = 0; + namespace ut { + namespace { + static uint32_t ctor_count = 0; + static uint32_t dtor_count = 0; - /* empty object, except for refcount */ - class JustRefcount : public ref::Refcount { - public: - JustRefcount() { ++ctor_count; } - ~JustRefcount() { ++dtor_count; } - }; /*JustRefcount*/ + /* empty object, except for refcount */ + class JustRefcount : public ref::Refcount { + public: + JustRefcount() { ++ctor_count; } + ~JustRefcount() { ++dtor_count; } + }; /*JustRefcount*/ - inline std::ostream & operator<<(std::ostream & os, JustRefcount & x) { - os << "JustRefcount"; - return os; - } /*operator<<*/ - } /*namespace*/ + inline std::ostream & operator<<(std::ostream & os, JustRefcount & x) { + os << "JustRefcount"; + return os; + } /*operator<<*/ + } /*namespace*/ - TEST_CASE("refcount", "[refcnt][trivial]") { - REQUIRE(std::is_default_constructible() == true); - REQUIRE(std::has_virtual_destructor() == true); + TEST_CASE("refcount", "[refcnt][trivial]") { + REQUIRE(std::is_default_constructible() == true); + REQUIRE(std::has_virtual_destructor() == true); - /* refcount object self-initializes to 0 */ - Refcount x; - REQUIRE(x.reference_counter() == 0); - } /*TEST_CASE(refcount)*/ + /* refcount object self-initializes to 0 */ + Refcount x; + REQUIRE(x.reference_counter() == 0); + } /*TEST_CASE(refcount)*/ - TEST_CASE("null-intrusive-ptr", "[refcnt][trivial]") { - //constexpr std::string_view c_self = "TEST_CASE:null-intrusive-ptr"; + TEST_CASE("null-intrusive-ptr", "[refcnt][trivial]") { + //constexpr std::string_view c_self = "TEST_CASE:null-intrusive-ptr"; - REQUIRE(std::has_virtual_destructor() == true); + REQUIRE(std::has_virtual_destructor() == true); - rp p1; - rp p2; + rp p1; + rp p2; - REQUIRE(sizeof(p1) == sizeof(JustRefcount*)); + REQUIRE(sizeof(p1) == sizeof(JustRefcount*)); - REQUIRE(p1.get() == nullptr); - REQUIRE(p1.operator->() == nullptr); + REQUIRE(p1.get() == nullptr); + REQUIRE(p1.operator->() == nullptr); - REQUIRE(p2.get() == nullptr); - REQUIRE(p2.operator->() == nullptr); + REQUIRE(p2.get() == nullptr); + REQUIRE(p2.operator->() == nullptr); - /* can assign a nullptr */ - rp p3; + /* can assign a nullptr */ + rp p3; - REQUIRE(p3.get() == nullptr); - p3 = p1; - REQUIRE(p3.get() == nullptr); + REQUIRE(p3.get() == nullptr); + p3 = p1; + REQUIRE(p3.get() == nullptr); - /* can use aux functions on null pointers */ - REQUIRE(intrusive_ptr_refcount(p1.get()) == 0); + /* can use aux functions on null pointers */ + REQUIRE(intrusive_ptr_refcount(p1.get()) == 0); - intrusive_ptr_add_ref(nullptr); - intrusive_ptr_release(nullptr); + intrusive_ptr_add_ref(nullptr); + intrusive_ptr_release(nullptr); - /* can borrow a null intrusive_ptr */ - brw p1_brw = p1.borrow(); - brw p2_brw = p2.borrow(); + /* can borrow a null intrusive_ptr */ + brw p1_brw = p1.borrow(); + brw p2_brw = p2.borrow(); - REQUIRE(p1_brw.get() == nullptr); - REQUIRE(p1_brw.operator->() == nullptr); - /* null borrow is false-y */ - REQUIRE(p1_brw == false); + REQUIRE(p1_brw.get() == nullptr); + REQUIRE(p1_brw.operator->() == nullptr); + /* null borrow is false-y */ + REQUIRE(p1_brw == false); - /* can promote a borrowed pointer */ - rp pp = p1_brw.promote(); + /* can promote a borrowed pointer */ + rp pp = p1_brw.promote(); - REQUIRE(p1.get() == pp.get()); + REQUIRE(p1.get() == pp.get()); - /* comparisons */ - REQUIRE(Borrow::compare(p1_brw, p2_brw) == 0); - REQUIRE(p1_brw == p2_brw); - REQUIRE((p1_brw != p2_brw) == false); - REQUIRE(p1 == p1_brw); - REQUIRE((p1 != p1_brw) == false); - REQUIRE(p1_brw == p1); - REQUIRE((p1_brw != p1) == false); - } /*TEST_CASE(null-intrusive_ptr)*/ + /* comparisons */ + REQUIRE(Borrow::compare(p1_brw, p2_brw) == 0); + REQUIRE(p1_brw == p2_brw); + REQUIRE((p1_brw != p2_brw) == false); + REQUIRE(p1 == p1_brw); + REQUIRE((p1 != p1_brw) == false); + REQUIRE(p1_brw == p1); + REQUIRE((p1_brw != p1) == false); + } /*TEST_CASE(null-intrusive_ptr)*/ - TEST_CASE("intrusive-ptr-identity", "[refcnt][identity]") - { - uint32_t cc = ctor_count; - uint32_t dc = dtor_count; + TEST_CASE("intrusive-ptr-identity", "[refcnt][identity]") + { + uint32_t cc = ctor_count; + uint32_t dc = dtor_count; - rp p1(new JustRefcount()); + rp p1(new JustRefcount()); - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc); - REQUIRE(p1.get() != nullptr); - REQUIRE(p1.get() == p1.operator->()); - REQUIRE(intrusive_ptr_refcount(p1.get()) == 1); - REQUIRE(p1->reference_counter() == 1); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + REQUIRE(p1.get() != nullptr); + REQUIRE(p1.get() == p1.operator->()); + REQUIRE(intrusive_ptr_refcount(p1.get()) == 1); + REQUIRE(p1->reference_counter() == 1); - intrusive_ptr_add_ref(p1.get()); + intrusive_ptr_add_ref(p1.get()); - REQUIRE(intrusive_ptr_refcount(p1.get()) == 2); + REQUIRE(intrusive_ptr_refcount(p1.get()) == 2); - intrusive_ptr_release(p1.get()); + intrusive_ptr_release(p1.get()); - REQUIRE(intrusive_ptr_refcount(p1.get()) == 1); + REQUIRE(intrusive_ptr_refcount(p1.get()) == 1); - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); - rp p2(new JustRefcount()); + rp p2(new JustRefcount()); - REQUIRE(ctor_count == cc + 2); - REQUIRE(dtor_count == dc); + REQUIRE(ctor_count == cc + 2); + REQUIRE(dtor_count == dc); - REQUIRE(p2.get() != nullptr); - REQUIRE(p2.get() != p1.get()); - REQUIRE(p2.get() == p2.operator->()); - REQUIRE(p2->reference_counter() == 1); + REQUIRE(p2.get() != nullptr); + REQUIRE(p2.get() != p1.get()); + REQUIRE(p2.get() == p2.operator->()); + REQUIRE(p2->reference_counter() == 1); - /* can borrow a non-null intrusive-ptr */ - brw p1_brw = p1.borrow(); + /* can borrow a non-null intrusive-ptr */ + brw p1_brw = p1.borrow(); - REQUIRE(p1_brw.get() == p1.get()); + REQUIRE(p1_brw.get() == p1.get()); - /* borrowing does not change refcount, borrow not tracked */ - REQUIRE(ctor_count == cc + 2); - REQUIRE(dtor_count == dc); - REQUIRE(p1.get()->reference_counter() == 1); + /* borrowing does not change refcount, borrow not tracked */ + REQUIRE(ctor_count == cc + 2); + REQUIRE(dtor_count == dc); + REQUIRE(p1.get()->reference_counter() == 1); - /* copying borrowed pointer does not touch refcount */ - brw p1_brw2 = p1_brw; + /* copying borrowed pointer does not touch refcount */ + brw p1_brw2 = p1_brw; - REQUIRE(ctor_count == cc + 2); - REQUIRE(dtor_count == dc); - REQUIRE(p1_brw2.get() == p1.get()); + REQUIRE(ctor_count == cc + 2); + REQUIRE(dtor_count == dc); + REQUIRE(p1_brw2.get() == p1.get()); - REQUIRE(p1.get()->reference_counter() == 1); - } /*TEST_CASE(identity-intrusive-ptr)*/ + REQUIRE(p1.get()->reference_counter() == 1); + } /*TEST_CASE(identity-intrusive-ptr)*/ - TEST_CASE("intrusive-ptr-release", "[refcnt][release]") - { - uint32_t cc = ctor_count; - uint32_t dc = dtor_count; + TEST_CASE("intrusive-ptr-release", "[refcnt][release]") + { + uint32_t cc = ctor_count; + uint32_t dc = dtor_count; - rp p1(new JustRefcount()); + rp p1(new JustRefcount()); - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc); - REQUIRE(p1.get() != nullptr); - REQUIRE(p1->reference_counter() == 1); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + REQUIRE(p1.get() != nullptr); + REQUIRE(p1->reference_counter() == 1); - /* reference count going to 0 -> delete object */ - p1 = nullptr; + /* reference count going to 0 -> delete object */ + p1 = nullptr; - REQUIRE(p1.get() == nullptr); - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc + 1); - } /*TEST_CASE(intrusive-ptr-release)*/ + REQUIRE(p1.get() == nullptr); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc + 1); + } /*TEST_CASE(intrusive-ptr-release)*/ - TEST_CASE("intrusive-ptr-copy", "[refcnt][copy]") - { - uint32_t cc = ctor_count; - uint32_t dc = dtor_count; + TEST_CASE("intrusive-ptr-copy", "[refcnt][copy]") + { + uint32_t cc = ctor_count; + uint32_t dc = dtor_count; - rp p1(new JustRefcount()); - JustRefcount * p1_native = p1.get(); + rp p1(new JustRefcount()); + JustRefcount * p1_native = p1.get(); - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc); - REQUIRE(p1.get() != nullptr); - REQUIRE(p1->reference_counter() == 1); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + REQUIRE(p1.get() != nullptr); + REQUIRE(p1->reference_counter() == 1); - /* copy ctor ran to make copy of p1, did not allocate */ - rp p2(p1); + /* copy ctor ran to make copy of p1, did not allocate */ + rp p2(p1); - REQUIRE(p1->reference_counter() == 2); - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc); + REQUIRE(p1->reference_counter() == 2); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); - } /*TEST_CASE(intrusive-ptr-copy)*/ + } /*TEST_CASE(intrusive-ptr-copy)*/ - TEST_CASE("intrusive-ptr-move", "[refcnt][move]") - { - uint32_t cc = ctor_count; - uint32_t dc = dtor_count; + TEST_CASE("intrusive-ptr-move", "[refcnt][move]") + { + uint32_t cc = ctor_count; + uint32_t dc = dtor_count; - rp p1(new JustRefcount()); - JustRefcount * p1_native = p1.get(); + rp p1(new JustRefcount()); + JustRefcount * p1_native = p1.get(); - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc); - REQUIRE(p1.get() != nullptr); - REQUIRE(p1->reference_counter() == 1); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + REQUIRE(p1.get() != nullptr); + REQUIRE(p1->reference_counter() == 1); - rp p2{std::move(p1)}; + rp p2{std::move(p1)}; - REQUIRE(p2->reference_counter() == 1); - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc); + REQUIRE(p2->reference_counter() == 1); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); - p2 = nullptr; + p2 = nullptr; - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc + 1); - } /*TEST_CASE(intrusive-ptr-move)*/ + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc + 1); + } /*TEST_CASE(intrusive-ptr-move)*/ - TEST_CASE("instrusive-ptr-assign", "[refcnt][assign]") - { - uint32_t cc = ctor_count; - uint32_t dc = dtor_count; + TEST_CASE("instrusive-ptr-assign", "[refcnt][assign]") + { + uint32_t cc = ctor_count; + uint32_t dc = dtor_count; - rp p1(new JustRefcount()); - JustRefcount * p1_native = p1.get(); + rp p1(new JustRefcount()); + JustRefcount * p1_native = p1.get(); - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc); - REQUIRE(p1.get() != nullptr); - REQUIRE(p1->reference_counter() == 1); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + REQUIRE(p1.get() != nullptr); + REQUIRE(p1->reference_counter() == 1); - rp p2; + rp p2; - REQUIRE(p2.get() == nullptr); - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc); + REQUIRE(p2.get() == nullptr); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); - p2 = p1; + p2 = p1; - REQUIRE(p2.get() == p1.get()); - REQUIRE(p2->reference_counter() == 2); + REQUIRE(p2.get() == p1.get()); + REQUIRE(p2->reference_counter() == 2); - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); - p1 = nullptr; + p1 = nullptr; - REQUIRE(p2->reference_counter() == 1); - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc); + REQUIRE(p2->reference_counter() == 1); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); - p2 = nullptr; + p2 = nullptr; - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc + 1); - } /*TEST_CASE(intrusive-ptr-assign)*/ + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc + 1); + } /*TEST_CASE(intrusive-ptr-assign)*/ - TEST_CASE("intrusive-ptr-move-assign", "[refcnt][move-assign]") - { - uint32_t cc = ctor_count; - uint32_t dc = dtor_count; + TEST_CASE("intrusive-ptr-move-assign", "[refcnt][move-assign]") + { + uint32_t cc = ctor_count; + uint32_t dc = dtor_count; - rp p1(new JustRefcount()); - JustRefcount * p1_native = p1.get(); + rp p1(new JustRefcount()); + JustRefcount * p1_native = p1.get(); - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc); - REQUIRE(p1.get() != nullptr); - REQUIRE(p1->reference_counter() == 1); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); + REQUIRE(p1.get() != nullptr); + REQUIRE(p1->reference_counter() == 1); - rp p2; + rp p2; - REQUIRE(p2.get() == nullptr); - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc); + REQUIRE(p2.get() == nullptr); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); - p2 = std::move(p1); + p2 = std::move(p1); - REQUIRE(p1.get() == nullptr); - REQUIRE(p2.get() == p1_native); - REQUIRE(p2->reference_counter() == 1); + REQUIRE(p1.get() == nullptr); + REQUIRE(p2.get() == p1_native); + REQUIRE(p2->reference_counter() == 1); - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); - p1 = nullptr; /*no-op*/ + p1 = nullptr; /*no-op*/ - REQUIRE(p2->reference_counter() == 1); - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc); + REQUIRE(p2->reference_counter() == 1); + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc); - p2 = nullptr; + p2 = nullptr; - REQUIRE(ctor_count == cc + 1); - REQUIRE(dtor_count == dc + 1); - } /*TEST_CASE(intrusive-ptr-move-assign)*/ - } /*namespace ut*/ + REQUIRE(ctor_count == cc + 1); + REQUIRE(dtor_count == dc + 1); + } /*TEST_CASE(intrusive-ptr-move-assign)*/ + } /*namespace ut*/ } /*namespace xo*/ /* end intrusive_ptr.test.cpp */ From ddc15c0416a863658d68e19c881052544f9caff2 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 29 Mar 2024 14:33:41 -0400 Subject: [PATCH 46/60] build: suppress bootstrap message in submodule build --- cmake/xo-bootstrap-macros.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/xo-bootstrap-macros.cmake b/cmake/xo-bootstrap-macros.cmake index 16644435..96592216 100644 --- a/cmake/xo-bootstrap-macros.cmake +++ b/cmake/xo-bootstrap-macros.cmake @@ -3,8 +3,10 @@ if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL "pr set(CMAKE_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/share/cmake) endif() -message("-- CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") -message("-- CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") +if (NOT XO_SUBMODULE_BUILD) + message("-- CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}") + message("-- CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") +endif() # needs to have been installed somewhere on CMAKE_MODULE_PATH, # (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) From cffffdae7c15273dfad8e04acf64193e644bda81 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 29 Mar 2024 14:43:12 -0400 Subject: [PATCH 47/60] comsetic: whitespace --- include/xo/cxxutil/demangle.hpp | 120 ++++++++++++++++---------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/include/xo/cxxutil/demangle.hpp b/include/xo/cxxutil/demangle.hpp index 8b8b8ba3..e2184f5d 100644 --- a/include/xo/cxxutil/demangle.hpp +++ b/include/xo/cxxutil/demangle.hpp @@ -8,85 +8,85 @@ #include // std::index_sequence namespace xo { - namespace reflect { + namespace reflect { - template - constexpr auto - substring_as_array(std::string_view str, - std::index_sequence indexes) - { - //return std::array{str[Idxs]..., '\n'}; - return std::array{str[Idxs]...}; - } /*substring_as_array*/ + template + constexpr auto + substring_as_array(std::string_view str, + std::index_sequence indexes) + { + //return std::array{str[Idxs]..., '\n'}; + return std::array{str[Idxs]...}; + } /*substring_as_array*/ - template constexpr auto type_name_array() { + template constexpr auto type_name_array() { #if defined(__clang__) - constexpr auto prefix = std::string_view{"[T = "}; - constexpr auto suffix = std::string_view{"]"}; - constexpr auto function = std::string_view{__PRETTY_FUNCTION__}; + constexpr auto prefix = std::string_view{"[T = "}; + constexpr auto suffix = std::string_view{"]"}; + constexpr auto function = std::string_view{__PRETTY_FUNCTION__}; #elif defined(__GNUC__) - constexpr auto prefix = std::string_view{"with T = "}; - constexpr auto suffix = std::string_view{"]"}; - constexpr auto function = std::string_view{__PRETTY_FUNCTION__}; + constexpr auto prefix = std::string_view{"with T = "}; + constexpr auto suffix = std::string_view{"]"}; + constexpr auto function = std::string_view{__PRETTY_FUNCTION__}; #elif defined(_MSC_VER) - constexpr auto prefix = std::string_view{"type_name_array<"}; - constexpr auto suffix = std::string_view{">(void)"}; - constexpr auto function = std::string_view{__FUNCSIG__}; + constexpr auto prefix = std::string_view{"type_name_array<"}; + constexpr auto suffix = std::string_view{">(void)"}; + constexpr auto function = std::string_view{__FUNCSIG__}; #else # error type_name_array: Unsupported compiler #endif - constexpr auto start = function.find(prefix) + prefix.size(); - constexpr auto end = function.rfind(suffix); + constexpr auto start = function.find(prefix) + prefix.size(); + constexpr auto end = function.rfind(suffix); - //static_assert(start < end); + //static_assert(start < end); - constexpr auto name = function.substr(start, (end - start)); + constexpr auto name = function.substr(start, (end - start)); - constexpr auto ixseq = std::make_index_sequence{}; + constexpr auto ixseq = std::make_index_sequence{}; - return substring_as_array(name, ixseq); - } /*type_name_array*/ + return substring_as_array(name, ixseq); + } /*type_name_array*/ - template - struct type_name_holder { - static inline constexpr auto value = type_name_array(); - }; - - template - constexpr auto type_name() -> std::string_view - { - constexpr auto& value = type_name_holder::value; - return std::string_view{value.data(), value.size()}; - } + template + struct type_name_holder { + static inline constexpr auto value = type_name_array(); + }; + + template + constexpr auto type_name() -> std::string_view + { + constexpr auto& value = type_name_holder::value; + return std::string_view{value.data(), value.size()}; + } #ifdef NOT_IN_USE - template - struct join - { - // Join all strings into a single std::array of chars - static constexpr auto impl() noexcept - { - constexpr std::size_t len = (Strs.size() + ... + 0); - std::array arr{}; - auto append = [i = 0, &arr](auto const& s) mutable { - for (auto c : s) arr[i++] = c; + template + struct join + { + // Join all strings into a single std::array of chars + static constexpr auto impl() noexcept + { + constexpr std::size_t len = (Strs.size() + ... + 0); + std::array arr{}; + auto append = [i = 0, &arr](auto const& s) mutable { + for (auto c : s) arr[i++] = c; + }; + (append(Strs), ...); + arr[len] = 0; + return arr; + } + // Give the joined string static storage + static constexpr auto arr = impl(); + // View as a std::string_view + static constexpr std::string_view value {arr.data(), arr.size() - 1}; }; - (append(Strs), ...); - arr[len] = 0; - return arr; - } - // Give the joined string static storage - static constexpr auto arr = impl(); - // View as a std::string_view - static constexpr std::string_view value {arr.data(), arr.size() - 1}; - }; - // Helper to get the value out - template - static constexpr auto join_v = join::value; + // Helper to get the value out + template + static constexpr auto join_v = join::value; #endif - } /*namespace reflect*/ + } /*namespace reflect*/ } /*namespace xo*/ /* end demangle.hpp */ From b4052c647fc8d28f2c8b7daeb6faeb9a320efea6 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 30 Apr 2024 10:16:51 -0500 Subject: [PATCH 48/60] xo-refcnt: build: streamline using recent xo-camke macros --- CMakeLists.txt | 33 +++++++--------------- cmake/xo-bootstrap-macros.cmake | 35 +++++++++++++++++++++++ src/CMakeLists.txt | 2 +- utest/CMakeLists.txt | 50 ++------------------------------- utest/intrusive_ptr.test.cpp | 2 +- 5 files changed, 49 insertions(+), 73 deletions(-) create mode 100644 cmake/xo-bootstrap-macros.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 655a14a4..336947d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,38 +3,30 @@ cmake_minimum_required(VERSION 3.10) project(refcnt VERSION 0.1) -enable_language(CXX) -# common XO cmake macros (see proj/xo-cmake) -include(xo_macros/xo_cxx) -include(xo_macros/code-coverage) +include(GNUInstallDirs) +include(cmake/xo-bootstrap-macros.cmake) + +xo_cxx_toplevel_options2() # ---------------------------------------------------------------- -# unit test setup +# cmake -DCMAKE_BUILD_TYPE=debug +xo_toplevel_debug_config2() -enable_testing() -# activate code coverage for all executables + libraries (when configured with -DCODE_COVERAGE=ON) -add_code_coverage() -# 1. assuming that /nix/store/ prefixes .hpp files belonging to gcc, catch2 etc. -# we're not interested in code coverage for these sources. -# 2. exclude the utest/ subdir, we don't need coverage on the unit tests themselves; -# rather, want coverage on the code that the unit tests exercise. -# -add_code_coverage_all_targets(EXCLUDE /nix/store/* utest/*) +# ---------------------------------------------------------------- +# cmake -DCMAKE_BUILD_TYPE=coverage +xo_toplevel_coverage_config2() # ---------------------------------------------------------------- # c++ settings -set(XO_PROJECT_NAME refcnt) +set(XO_PROJECT_NAME refcnt) # is this used? set(PROJECT_CXX_FLAGS "") #set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") # gcc-only! add_definitions(${PROJECT_CXX_FLAGS}) -xo_toplevel_compile_options() - # ---------------------------------------------------------------- -# sources add_subdirectory(src) add_subdirectory(utest) @@ -44,9 +36,4 @@ add_subdirectory(utest) xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) -# ---------------------------------------------------------------- -# install .hpp - -xo_install_include_tree() - # end CMakeLists.txt diff --git a/cmake/xo-bootstrap-macros.cmake b/cmake/xo-bootstrap-macros.cmake new file mode 100644 index 00000000..aba31169 --- /dev/null +++ b/cmake/xo-bootstrap-macros.cmake @@ -0,0 +1,35 @@ +# ---------------------------------------------------------------- +# for example: +# $ PREFIX=/usr/local # for example +# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build +# +# will get +# CMAKE_MODULE_PATH +# from xo-cmake-config --cmake-module-path +# +# and expect .cmake macros in +# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake +# ---------------------------------------------------------------- + +find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED) + +if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND") + message(FATAL "could not find xo-cmake-config executable") +endif() + +message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}") + +if (NOT XO_SUBMODULE_BUILD) + if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix)) + # default to typical install location for xo-project-macros + execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH) + message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") + endif() +endif() + +# needs to have been installed somewhere on CMAKE_MODULE_PATH, +# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) +# +include(xo_macros/xo_cxx) + +xo_cxx_bootstrap_message() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9d35b21d..4622ad44 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,5 @@ set(SELF_LIB refcnt) set(SELF_SRCS Refcounted.cpp Displayable.cpp) -xo_add_shared_library(${SELF_LIB} ${PROJECT_VERSION} 1 ${SELF_SRCS}) +xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) xo_dependency(${SELF_LIB} indentlog) diff --git a/utest/CMakeLists.txt b/utest/CMakeLists.txt index c19d0070..2e65b14b 100644 --- a/utest/CMakeLists.txt +++ b/utest/CMakeLists.txt @@ -4,58 +4,12 @@ set(SELF_EXECUTABLE_NAME utest.refcnt) # These tests can use the Catch2-provided main set(SELF_SOURCE_FILES intrusive_ptr.test.cpp refcnt_utest_main.cpp) -add_executable(${SELF_EXECUTABLE_NAME} ${SELF_SOURCE_FILES}) -xo_include_options2(${SELF_EXECUTABLE_NAME}) - -add_test(NAME ${SELF_EXECUTABLE_NAME} COMMAND ${SELF_EXECUTABLE_NAME}) -target_code_coverage(${SELF_EXECUTABLE_NAME} AUTO ALL) - -#target_link_libraries(${SELF_EXECUTABLE_NAME} PRIVATE Catch2::Catch2WithMain) - -# ---------------------------------------------------------------- -# generic project dependency - -## PROJECT_SOURCE_DIR: -## so we can for example write -## #include "indentlog/scope.hpp" -## from anywhere in the project -## PROJECT_BINARY_DIR: -## since version file will be in build directory, need that directory -## to also be included in compiler's include path -## -#target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC -# ${PROJECT_SOURCE_DIR} -# ${PROJECT_BINARY_DIR}) - -# ---------------------------------------------------------------- -# internal dependencies: refcnt, ... - -xo_self_dependency(${SELF_EXECUTABLE_NAME} refcnt) -#target_link_libraries(${SELF_EXECUTABLE_NAME} PUBLIC refcnt) +xo_add_utest_executable(${SELF_EXECUTABLE_NAME} ${SELF_SOURCE_FILES}) # ---------------------------------------------------------------- # 3rd party dependency: catch2: +xo_self_dependency(${SELF_EXECUTABLE_NAME} refcnt) xo_external_target_dependency(${SELF_EXECUTABLE_NAME} Catch2 Catch2::Catch2) -# need this so that catch2/include appears in compile_commands.json, -# on which lsp integration relies. -# -# See also /nix/store/*-catch2-*/lib/cmake/Catch2/ParseAndAddCatchTests.cmake; -# commands here derived from ^ .cmake file -# -#find_path(CATCH_INCLUDE_DIR "catch2/catch.hpp") -#target_include_directories(${SELF_EXECUTABLE_NAME} PUBLIC ${CATCH_INCLUDE_DIR}) - -# ---------------------------------------------------------------- -# make standard directories for std:: includes explicit -# so that -# (1) they appear in compile_commands.json. -# (2) clangd (run from emacs lsp-mode) can find them -# -if(CMAKE_EXPORT_COMPILE_COMMANDS) - set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES - ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) -endif() - # end CMakeLists.txt diff --git a/utest/intrusive_ptr.test.cpp b/utest/intrusive_ptr.test.cpp index bc45d38c..2b87f1d2 100644 --- a/utest/intrusive_ptr.test.cpp +++ b/utest/intrusive_ptr.test.cpp @@ -2,7 +2,7 @@ #include "Refcounted.hpp" #include "xo/indentlog/scope.hpp" -#include "catch2/catch.hpp" +#include #include #include From 9e0ae8b193e2f94ed4d4699b152a0ff3de8d6f58 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 30 Apr 2024 10:17:36 -0500 Subject: [PATCH 49/60] xo-refcnt: gitignore: ignore .build* dirs --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 86d062ab..cc75cfd3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,5 @@ compile_commands.json # LSP keeps state here .cache # typical build directories -build +.build* ccov From 847bf85360aa59493fe8d005787e8ea56f0e9768 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 1 May 2024 14:33:10 -0400 Subject: [PATCH 50/60] xo-recnt: build: bugfix to install cxxutil dir --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1b90ed07..9cbc92ff 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,7 @@ set(SELF_LIB refcnt) set(SELF_SRCS Refcounted.cpp Displayable.cpp) xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) +xo_install_include_tree3(include/xo/cxxutil) # NOTE: # dependency set here must be kept consistent with refcnt/cmake/refcntConfig.cmake.in From 3ec39aa29508fad3b0ff141d4b788ea3e19d0dce Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 14 Jun 2024 15:21:49 -0400 Subject: [PATCH 51/60] xo-refcnt: exclude logging unless XO_INTRUSIVE_PTR_ENABLE_LOGGING --- include/xo/refcnt/Refcounted.hpp | 32 ++++++++++++++++++++++++++++++-- src/Refcounted.cpp | 2 ++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/include/xo/refcnt/Refcounted.hpp b/include/xo/refcnt/Refcounted.hpp index 3db621a1..b54d5b5e 100644 --- a/include/xo/refcnt/Refcounted.hpp +++ b/include/xo/refcnt/Refcounted.hpp @@ -28,7 +28,9 @@ namespace xo { public: intrusive_ptr() : ptr_(nullptr) {} intrusive_ptr(T * x) : ptr_(x) { +#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING intrusive_ptr_log_ctor(sc_self_type, this, x); +#endif intrusive_ptr_add_ref(ptr_); } /*ctor*/ @@ -38,20 +40,26 @@ namespace xo { * instrusive_ptr. */ intrusive_ptr(intrusive_ptr const & x) : ptr_(x.get()) { +#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING intrusive_ptr_log_cctor(sc_self_type, this, x.get()); +#endif intrusive_ptr_add_ref(ptr_); } /*cctor*/ /* create from instrusive pointer to some related type S */ template intrusive_ptr(intrusive_ptr const & x) : ptr_(x.get()) { +#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING intrusive_ptr_log_cctor(sc_self_type, this, x.get()); +#endif intrusive_ptr_add_ref(ptr_); } /*cctor*/ /* move ctor -- in this case don't need to update refcount */ intrusive_ptr(intrusive_ptr && x) : ptr_{std::move(x.ptr_)} { +#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING intrusive_ptr_log_mctor(sc_self_type, this, ptr_); +#endif /* since we're moving from x, need to make sure x dtor * doesn't decrement refcount */ @@ -66,7 +74,9 @@ namespace xo { template intrusive_ptr(intrusive_ptr const & /*x*/, element_type * y) : ptr_{y} { if (std::is_same()) { +#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING intrusive_ptr_log_actor(sc_self_type, this, y); +#endif intrusive_ptr_add_ref(ptr_); ; /* trivial aliasing, proceed */ } else { @@ -80,7 +90,9 @@ namespace xo { ~intrusive_ptr() { T * x = this->ptr_; +#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING intrusive_ptr_log_dtor(sc_self_type, this, x); +#endif this->ptr_ = nullptr; @@ -103,7 +115,9 @@ namespace xo { intrusive_ptr & operator=(intrusive_ptr const & rhs) { T * x = rhs.get(); +#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING intrusive_ptr_log_assign(sc_self_type, this, x); +#endif T * old = this->ptr_; this->ptr_ = x; @@ -115,7 +129,9 @@ namespace xo { } /*operator=*/ intrusive_ptr & operator=(intrusive_ptr && rhs) { +#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING intrusive_ptr_log_massign(sc_self_type, this, rhs.get()); +#endif std::swap(this->ptr_, rhs.ptr_); @@ -134,7 +150,8 @@ namespace xo { }; /*intrusive_ptr*/ template - inline bool operator==(intrusive_ptr const & x, intrusive_ptr const & y) { return intrusive_ptr::compare(x, y) == 0; } + inline bool operator==(intrusive_ptr const & x, + intrusive_ptr const & y) { return intrusive_ptr::compare(x, y) == 0; } template using rp = intrusive_ptr; @@ -171,6 +188,7 @@ namespace xo { return 0; } /*intrusive_ptr_refcount*/ +#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING extern void intrusive_ptr_set_debug(bool x); extern void intrusive_ptr_log_ctor(std::string_view const & self_type, void * this_ptr, @@ -193,7 +211,8 @@ namespace xo { Refcount * x); extern void intrusive_ptr_log_massign(std::string_view const & self_type, void *this_ptr, - Refcount * x); + Refcount * x); +#endif extern void intrusive_ptr_add_ref(Refcount * x); extern void intrusive_ptr_release(Refcount * x); @@ -208,6 +227,15 @@ namespace xo { return os; } /*operator<<*/ + /** Wrap a (presumably non-reference-counted) class T so that it has a refcount. + **/ + template + class RefcountWrapper : public Refcount, public T { + public: + template + RefcountWrapper(Args... args) : Refcount(), T(std::forward(args...)) {} + }; + /* borrow a reference-counted pointer to pass down the stack * 1. borrowed pointer intended to replace: * a. code like diff --git a/src/Refcounted.cpp b/src/Refcounted.cpp index eade7a50..38accc85 100644 --- a/src/Refcounted.cpp +++ b/src/Refcounted.cpp @@ -4,6 +4,7 @@ namespace xo { namespace ref { +#ifdef XO_INTRUSIVE_PTR_ENABLE_LOGGING namespace { /* verbose logging for intrusive_ptr */ static bool s_logging_enabled = false; @@ -89,6 +90,7 @@ namespace xo { if (s_logging_enabled) intrusive_ptr_log_aux(self_type, "::m=", this_ptr, x); } /*intrusive_ptr_log_massign*/ +#endif void intrusive_ptr_add_ref(Refcount * x) From ae9acf59b333e74f63b71ccb194a97dd1c0c9e80 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 14 Jun 2024 15:22:15 -0400 Subject: [PATCH 52/60] xo-refcnt: README: mention xo-build assistant --- README.md | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 0b53dc81..19a14a1d 100644 --- a/README.md +++ b/README.md @@ -10,28 +10,41 @@ Refcnt is a small shared library supplying intrusive reference counting. ## Getting Started -### build + install `indentlog` dependency +### build + install `xo-cmake` dependency (cmake macros) -see [github/Rconybea/indentlog](https://github.com/Rconybea/indentlog) +see [github/Rconybea/xo-cmake](https://github.com/Rconybea/xo-cmake) + +Installs a few cmake ingredients, along with a build assistant for XO projects such as this one. + +### build + install XO deps +``` +$ xo-build --clone --configure --build --install xo-indentlog +``` ### copy `refcnt` repository locally ``` -$ git clone git@github.com:rconybea/refcnt.git -$ ls -d xo-refcnt -xo-refcnt +$ xo-build --clone xo-refcnt +``` + +or equivalently +``` +$ git clone git@github.com:rconybea/refcnt.git xo-refcnt ``` ### build + install ``` -$ cd xo-refcnt -$ mkdir build -$ cd build -$ cmake -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} .. -$ make -$ make install +$ xo-build --configure --build --install xo-refcnt ``` -`CMAKE_PREFIX_PATH` should point to prefix where `indentlog` is installed +or equivalently: +``` +$ mkdir xo-refcnt/.build +$ cmake -DCMAKE_INSTALL_PREFIX=${PREFIX} -S xo-refcnt -B xo-refcnt/.build +$ cmake --build xo-refcnt/.build +$ cmake --install xo-refcnt/.build +``` + +`CMAKE_PREFIX_PATH` should point to the prefix where `xo-indentlog` is installed alternatively, if you're a nix user: ``` @@ -44,10 +57,8 @@ $ nix-build -A xo-refcnt ### build for unit test coverage ``` -$ cd xo-refcnt -$ mkdir ccov -$ cd ccov -$ cmake -DCMAKE_MODULE_PATH=${INSTALL_PREFIX}/share/cmake -DCMAKE_PREFIX_PATH=${INSTALL_PREFIX} -DCODE_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug .. +$ cmake -DCMAKE_BUILD_TYPE=coverage -DCMAKE_PREFIX_PATH=${PREFIX} -S xo-refcnt -B xo-refcnt/.build-ccov +$ cmake --build xo-refcnt/.build-ccov ``` ### LSP support From 6451b126171cdeb633f66f0ce1fc9ea0a033c513 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 14 Jun 2024 15:22:30 -0400 Subject: [PATCH 53/60] xo-refcnt: build: streamline --- CMakeLists.txt | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 336947d3..4ef7c6c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,15 +7,7 @@ project(refcnt VERSION 0.1) include(GNUInstallDirs) include(cmake/xo-bootstrap-macros.cmake) -xo_cxx_toplevel_options2() - -# ---------------------------------------------------------------- -# cmake -DCMAKE_BUILD_TYPE=debug -xo_toplevel_debug_config2() - -# ---------------------------------------------------------------- -# cmake -DCMAKE_BUILD_TYPE=coverage -xo_toplevel_coverage_config2() +xo_cxx_toplevel_options3() # ---------------------------------------------------------------- # c++ settings From 8c3c3a7ac4230b648aef39debb576c5788daae30 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 14 Jun 2024 15:23:01 -0400 Subject: [PATCH 54/60] .gitignore: + .projectile --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index cc75cfd3..39d62bcb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +# emacs workspace config +.projectile # symlink to ${mybuilddirectory}/compile_commands.json for LSP compile_commands.json # LSP keeps state here From c7b37d35bb8e5a97131d3dcf908fbee0161201f9 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 24 Jun 2024 23:57:57 -0400 Subject: [PATCH 55/60] xo-refcnt: naked pointer -> Borrow ctor --- README.md | 2 +- include/xo/refcnt/Refcounted.hpp | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 19a14a1d..e17529d9 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ $ xo-build --clone xo-refcnt or equivalently ``` -$ git clone git@github.com:rconybea/refcnt.git xo-refcnt +$ git clone git@github.com:Rconybea/refcnt.git xo-refcnt ``` ### build + install diff --git a/include/xo/refcnt/Refcounted.hpp b/include/xo/refcnt/Refcounted.hpp index b54d5b5e..da4ba539 100644 --- a/include/xo/refcnt/Refcounted.hpp +++ b/include/xo/refcnt/Refcounted.hpp @@ -257,9 +257,12 @@ namespace xo { template class Borrow { public: - template + template Borrow(rp const & x) : ptr_(x.get()) {} + template + Borrow(S * x) : ptr_(x) {} + Borrow(Borrow const & x) = default; /* convert from another borrow, if it has compatible pointer type */ From c7a030aa3755f9021381dab036804989f5be1a16 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 3 Jul 2024 15:43:43 -0400 Subject: [PATCH 56/60] xo-reflect: + Borrow::operator=() --- include/xo/refcnt/Refcounted.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/xo/refcnt/Refcounted.hpp b/include/xo/refcnt/Refcounted.hpp index da4ba539..0b62e557 100644 --- a/include/xo/refcnt/Refcounted.hpp +++ b/include/xo/refcnt/Refcounted.hpp @@ -297,8 +297,11 @@ namespace xo { return ptrdiff_t(x.get() - y.get()); } /*compare*/ - private: - Borrow(T * x) : ptr_(x) {} + template + Borrow & operator=(const Borrow & x) { + ptr_ = x.get(); + return *this; + } private: T * ptr_ = nullptr; From b6ba7615513b0913c725e5690a4317676ad6ee03 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 3 Jul 2024 15:45:37 -0400 Subject: [PATCH 57/60] xo-expression: Borrow ctor --- include/xo/refcnt/Refcounted.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/xo/refcnt/Refcounted.hpp b/include/xo/refcnt/Refcounted.hpp index 0b62e557..33c5b377 100644 --- a/include/xo/refcnt/Refcounted.hpp +++ b/include/xo/refcnt/Refcounted.hpp @@ -257,6 +257,8 @@ namespace xo { template class Borrow { public: + Borrow() = default; + template Borrow(rp const & x) : ptr_(x.get()) {} From 0c45d2b883ab27b960c90c48e6701f1f136c1df1 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 5 Aug 2024 14:28:14 -0400 Subject: [PATCH 58/60] xo-refcnt: ns: xo::ref::rp -> xo::rp --- include/xo/refcnt/Refcounted.hpp | 11 ++++++++--- utest/intrusive_ptr.test.cpp | 1 - 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/include/xo/refcnt/Refcounted.hpp b/include/xo/refcnt/Refcounted.hpp index 33c5b377..a9d4386b 100644 --- a/include/xo/refcnt/Refcounted.hpp +++ b/include/xo/refcnt/Refcounted.hpp @@ -10,6 +10,14 @@ #include namespace xo { + namespace ref { + template + class intrusive_ptr; + } + + template + using rp = ref::intrusive_ptr; + namespace ref { class Refcount; @@ -153,9 +161,6 @@ namespace xo { inline bool operator==(intrusive_ptr const & x, intrusive_ptr const & y) { return intrusive_ptr::compare(x, y) == 0; } - template - using rp = intrusive_ptr; - class Refcount { public: Refcount() : reference_counter_(0) {} diff --git a/utest/intrusive_ptr.test.cpp b/utest/intrusive_ptr.test.cpp index 112268a9..78e3181a 100644 --- a/utest/intrusive_ptr.test.cpp +++ b/utest/intrusive_ptr.test.cpp @@ -9,7 +9,6 @@ namespace xo { using xo::ref::Refcount; using xo::ref::Borrow; - using xo::ref::rp; using xo::ref::brw; using xo::ref::intrusive_ptr_refcount; using xo::ref::intrusive_ptr_add_ref; From 7c593816045729c8d854f4db54f3f4dd9ac8c5d1 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 6 Aug 2024 13:01:43 -0400 Subject: [PATCH 59/60] xo-refcnt: feat: printing for brw --- include/xo/refcnt/Refcounted.hpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/xo/refcnt/Refcounted.hpp b/include/xo/refcnt/Refcounted.hpp index a9d4386b..41abf938 100644 --- a/include/xo/refcnt/Refcounted.hpp +++ b/include/xo/refcnt/Refcounted.hpp @@ -329,6 +329,17 @@ namespace xo { return Borrow(*this); } /*borrow*/ + template + inline std::ostream & + operator<<(std::ostream & os, Borrow x) { + if (x) { + os << *x; + } else { + os << "() << ">"; + } + return os; + } /*operator<<*/ + } /*namespace ref*/ } /*namespace xo*/ From bcd86e532442eb8e9379b59c23c89e879c4b24ed Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Thu, 8 May 2025 23:40:30 -0500 Subject: [PATCH 60/60] (clang 15) compiler nit --- include/xo/refcnt/Refcounted.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/xo/refcnt/Refcounted.hpp b/include/xo/refcnt/Refcounted.hpp index 41abf938..bab9f1ba 100644 --- a/include/xo/refcnt/Refcounted.hpp +++ b/include/xo/refcnt/Refcounted.hpp @@ -310,6 +310,11 @@ namespace xo { return *this; } + Borrow& operator=(const Borrow& x) { + ptr_ = x.get(); + return *this; + } + private: T * ptr_ = nullptr; }; /*Borrow*/