[
  {
    "path": ".clang-format",
    "content": "---\nLanguage:        Cpp\n# BasedOnStyle:  Microsoft\nAccessModifierOffset: -2\nAlignAfterOpenBracket: Align\nAlignArrayOfStructures: None\nAlignConsecutiveMacros: None\nAlignConsecutiveAssignments: None\nAlignConsecutiveBitFields: None\nAlignConsecutiveDeclarations: None\nAlignEscapedNewlines: Right\nAlignOperands:   Align\nAlignTrailingComments: true\nAllowAllArgumentsOnNextLine: true\nAllowAllConstructorInitializersOnNextLine: true\nAllowAllParametersOfDeclarationOnNextLine: true\nAllowShortEnumsOnASingleLine: false\nAllowShortBlocksOnASingleLine: Never\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: None\nAllowShortLambdasOnASingleLine: All\nAllowShortIfStatementsOnASingleLine: Never\nAllowShortLoopsOnASingleLine: false\nAlwaysBreakAfterDefinitionReturnType: None\nAlwaysBreakAfterReturnType: None\nAlwaysBreakBeforeMultilineStrings: false\nAlwaysBreakTemplateDeclarations: MultiLine\nAttributeMacros:\n  - __capability\nBinPackArguments: true\nBinPackParameters: true\nBraceWrapping:\n  AfterCaseLabel:  false\n  AfterClass:      true\n  AfterControlStatement: Always\n  AfterEnum:       true\n  AfterFunction:   true\n  AfterNamespace:  true\n  AfterObjCDeclaration: true\n  AfterStruct:     true\n  AfterUnion:      false\n  AfterExternBlock: true\n  BeforeCatch:     true\n  BeforeElse:      true\n  BeforeLambdaBody: false\n  BeforeWhile:     false\n  IndentBraces:    false\n  SplitEmptyFunction: true\n  SplitEmptyRecord: true\n  SplitEmptyNamespace: true\nBreakBeforeBinaryOperators: None\nBreakBeforeConceptDeclarations: true\nBreakBeforeBraces: Custom\nBreakBeforeInheritanceComma: false\nBreakInheritanceList: BeforeColon\nBreakBeforeTernaryOperators: true\nBreakConstructorInitializersBeforeComma: false\nBreakConstructorInitializers: BeforeColon\nBreakAfterJavaFieldAnnotations: false\nBreakStringLiterals: true\nColumnLimit:     120\nCommentPragmas:  '^ IWYU pragma:'\nCompactNamespaces: false\nConstructorInitializerAllOnOneLineOrOnePerLine: false\nConstructorInitializerIndentWidth: 4\nContinuationIndentWidth: 4\nCpp11BracedListStyle: true\nDeriveLineEnding: true\nDerivePointerAlignment: false\nDisableFormat:   false\nEmptyLineAfterAccessModifier: Never\nEmptyLineBeforeAccessModifier: LogicalBlock\nExperimentalAutoDetectBinPacking: false\nFixNamespaceComments: true\nForEachMacros:\n  - foreach\n  - Q_FOREACH\n  - BOOST_FOREACH\nIfMacros:\n  - KJ_IF_MAYBE\nIncludeBlocks:   Preserve\nIncludeCategories:\n  - Regex:           '^\"(llvm|llvm-c|clang|clang-c)/'\n    Priority:        2\n    SortPriority:    0\n    CaseSensitive:   false\n  - Regex:           '^(<|\"(gtest|gmock|isl|json)/)'\n    Priority:        3\n    SortPriority:    0\n    CaseSensitive:   false\n  - Regex:           '.*'\n    Priority:        1\n    SortPriority:    0\n    CaseSensitive:   false\nIncludeIsMainRegex: '(Test)?$'\nIncludeIsMainSourceRegex: ''\nIndentAccessModifiers: false\nIndentCaseLabels: false\nIndentCaseBlocks: false\nIndentGotoLabels: true\nIndentPPDirectives: None\nIndentExternBlock: AfterExternBlock\nIndentRequires:  false\nIndentWidth:     4\nIndentWrappedFunctionNames: false\nInsertTrailingCommas: None\nJavaScriptQuotes: Leave\nJavaScriptWrapImports: true\nKeepEmptyLinesAtTheStartOfBlocks: true\nLambdaBodyIndentation: Signature\nMacroBlockBegin: ''\nMacroBlockEnd:   ''\nMaxEmptyLinesToKeep: 1\nNamespaceIndentation: None\nObjCBinPackProtocolList: Auto\nObjCBlockIndentWidth: 2\nObjCBreakBeforeNestedBlockParam: true\nObjCSpaceAfterProperty: false\nObjCSpaceBeforeProtocolList: true\nPenaltyBreakAssignment: 2\nPenaltyBreakBeforeFirstCallParameter: 19\nPenaltyBreakComment: 300\nPenaltyBreakFirstLessLess: 120\nPenaltyBreakString: 1000\nPenaltyBreakTemplateDeclaration: 10\nPenaltyExcessCharacter: 1000000\nPenaltyReturnTypeOnItsOwnLine: 1000\nPenaltyIndentedWhitespace: 0\nPointerAlignment: Right\nPPIndentWidth:   -1\nReferenceAlignment: Pointer\nReflowComments:  true\nShortNamespaceLines: 1\nSortIncludes:    CaseSensitive\nSortJavaStaticImport: Before\nSortUsingDeclarations: true\nSpaceAfterCStyleCast: false\nSpaceAfterLogicalNot: false\nSpaceAfterTemplateKeyword: true\nSpaceBeforeAssignmentOperators: true\nSpaceBeforeCaseColon: false\nSpaceBeforeCpp11BracedList: false\nSpaceBeforeCtorInitializerColon: true\nSpaceBeforeInheritanceColon: true\nSpaceBeforeParens: ControlStatements\nSpaceAroundPointerQualifiers: Default\nSpaceBeforeRangeBasedForLoopColon: true\nSpaceInEmptyBlock: false\nSpaceInEmptyParentheses: false\nSpacesBeforeTrailingComments: 1\nSpacesInAngles:  Never\nSpacesInConditionalStatement: false\nSpacesInContainerLiterals: true\nSpacesInCStyleCastParentheses: false\nSpacesInLineCommentPrefix:\n  Minimum:         1\n  Maximum:         -1\nSpacesInParentheses: false\nSpacesInSquareBrackets: false\nSpaceBeforeSquareBrackets: false\nBitFieldColonSpacing: Both\nStandard:        Latest\nStatementAttributeLikeMacros:\n  - Q_EMIT\nStatementMacros:\n  - Q_UNUSED\n  - QT_REQUIRE_VERSION\nTabWidth:        4\nUseCRLF:         false\nUseTab:          Never\nWhitespaceSensitiveMacros:\n  - STRINGIZE\n  - PP_STRINGIZE\n  - BOOST_PP_STRINGIZE\n  - NS_SWIFT_NAME\n  - CF_SWIFT_NAME\n...\n\n"
  },
  {
    "path": ".github/workflows/all_platforms.yml",
    "content": "name: All Platforms\n\non: [push]\n\njobs:\n  build:\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [ubuntu-latest, ubuntu-20.04, macos-latest, macos-13, windows-latest, windows-2019]\n        build_type: ['Release', 'Debug']\n        cxx_std: [11, 20]\n\n    steps:\n    - uses: actions/checkout@v4\n\n    - name: Build with cxx standard specified\n      run: |\n        cmake -B ${{github.workspace}}/build/${{matrix.build_type}} -DCMAKE_CXX_STANDARD=${{matrix.cxx_std}} -DCMAKE_INSTALL_PREFIX=\"${{github.workspace}}/install/${{matrix.build_type}}\" -DDEPS=REMOTE\n        cmake --build ${{github.workspace}}/build/${{matrix.build_type}} --config ${{matrix.build_type}} --target install\n\n    - name: Build tests with install space\n      run: |\n        cmake -S ${{github.workspace}}/tests -B ${{github.workspace}}/build-tests-standalone/${{matrix.build_type}} -DCMAKE_CXX_STANDARD=${{matrix.cxx_std}} -DCMAKE_PREFIX_PATH=\"${{github.workspace}}/install/${{matrix.build_type}}\" -DDEPS=REMOTE\n        cmake --build ${{github.workspace}}/build-tests-standalone/${{matrix.build_type}} --config ${{matrix.build_type}}\n\n    - name: Test\n      env:\n          CTEST_OUTPUT_ON_FAILURE: 1\n      run: ctest -C ${{matrix.build_type}} --test-dir ${{github.workspace}}/build/${{matrix.build_type}}\n"
  },
  {
    "path": ".gitignore",
    "content": "build*/\ninstall*/\ncompile_commands.json\n.vscode/\n.cache/\nTesting/\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.9.2)\n\ncmake_policy(SET CMP0048 NEW)\nproject(safe VERSION 1.1.0 LANGUAGES CXX)\noption(BUILD_TESTING \"Build tests\" ON)\n\nset(DEPS \"AUTO\" CACHE STRING \"Fetch git repos or use local packages (AUTO/REMOTE/LOCAL)\")\nset(DEPS_LIST AUTO REMOTE LOCAL)\nset_property(CACHE DEPS PROPERTY STRINGS ${DEPS_LIST})\nif(NOT DEPS IN_LIST DEPS_LIST)\n    message(FATAL_ERROR \"DEPS must be one of ${DEPS_LIST}\")\nendif()\n\ninclude(GNUInstallDirs)\nlist(APPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/cmake\")\n\nadd_library(${PROJECT_NAME} INTERFACE)\ntarget_include_directories(${PROJECT_NAME} INTERFACE\n\t$<BUILD_INTERFACE:${${PROJECT_NAME}_SOURCE_DIR}/include>\n\t$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>\n)\ntarget_compile_features(${PROJECT_NAME} INTERFACE cxx_std_11)\nadd_library(safe::safe ALIAS safe)\n\nif(BUILD_TESTING)\n\tenable_testing()\n\tadd_subdirectory(tests)\nendif()\n\ninclude(InstallTarget)\ninstall(DIRECTORY ${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019-2023 Louis-Charles Caron\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# *Every variable protected by a mutex should be wrapped with safe.*\n[![build](https://github.com/LouisCharlesC/safe/actions/workflows/all_platforms.yml/badge.svg)](https://github.com/LouisCharlesC/safe/actions/workflows/all_platforms.yml)\n## Contents\n*safe* is a header-only library that makes code with mutexes safer and easier to understand.  \nThis readme will walk you through the important features of the library using several code examples. Read on, and enjoy safe mutexes!\n- [Overview](#overview)\n- [Motivation](#motivation)\n- [Main features](#main-features)\n- [Installation](#installation)\n- [Advanced usage](#advanced-usage)\n## Overview\nTwo class templates are at the core of *safe*: Safe and Access. Safe objects pack a mutex and a value object together. Access objects act as a lock (e.g. std::lock_guard) for the mutex and provide pointer-like access to the value object.\n\nHere is why you want to use *safe*:\n### Mutex code without *safe*\n```c++\nstd::string foo; // do I need to lock a mutex to safely access this variable ?\nstd::string bar;\nstd::string baz; // what about this one ?\nstd::mutex fooMutex; // don't forget to change the name of this variable if foo's name changes!\nstd::mutex barMutex;\n\n{\n\tstd::lock_guard<std::mutex> lock(fooMutex); // is this the right mutex for what I am about to do ?\n\tbar = \"Hello, World!\"; // Hmm, did I just to something wrong ?\n}\n\nstd::cout << bar << std::endl; // unprotected access, is this intended ?\nstd::cout << baz << std::endl; // what about this access ?\n```\n### Mutex code with *safe*\n```c++\n#include \"safe/safe.h\"\nsafe::Safe<std::string> safeFoo; // std::string and mutex packaged together!\nsafe::Safe<std::string> safeBar;\nstd::string baz; // now you can see that this variable has no mutex\n\n{\n\tsafe::WriteAccess<safe::Safe<std::string>> foo(safeFoo); // this locks the mutex and gives you access to foo\n\t*foo = \"Hello, World!\"; // access the value using pointer dereference: * and ->\n}\n\nstd::cout << safeBar.unsafe() << std::endl; // unprotected access: clearly expressed!\nstd::cout << baz << std::endl; // all good this is just a string!\n```\n## Motivation\nSince C++11, the standard library provides mutexes, like std::mutex, along with tools to facilitate their usage, like std::lock_guard and std::unique_lock. These are sufficient to write safe multithreaded code, but it is all too easy to write code you think is safe but actually is not. Typical mistakes are: locking the wrong mutex and accessing the value object before locking (or after unlocking) the mutex. Other minor mistakes like unnecessary locking or keeping a mutex locked for too long can also be avoided.  \n\n*safe* prevents common mutex usage mistakes by providing tools that complement the C++ standard library. Using *safe*, you will find it much easier to protect a variable using a mutex, and your code will be easier to understand. No more locking the wrong mutex, no more mistaken access outside the safety of a locked mutex. No more naked shared variables, no more plain mutexes lying around and no more *mutable* keyword (ever locked a member mutex variable within a const-qualified member function ?).\n## Main features\n### Flexibility\n#### Choose the mutex and lock that fit your need\nThe Safe class template has a template parameter for the mutex: \n- use std::mutex, std::shared_mutex (C++17), name it!\n\nThe Access class template has a template parameter for the lock object: \n- use std::lock_guard, boost::shared_lock_guard, anything you want!\n- you can use the lock you need for every Access object you construct.\n#### Store the value object and mutex inside the Safe object, or refer to existing objects\nYou can use any combination of reference and non-reference types for your Safe objects:\n```c++\nsafe::Safe<int, std::mutex>;\nsafe::Safe<int>; // equivalent to the above, as the second template parameter defaults to std::mutex\nsafe::Safe<int&, std::mutex>;\nsafe::Safe<int, std::mutex&>;\nsafe::Safe<int&, std::mutex&>;\n```\nSee [this section](#with-legacy-code) for an example of using reference types to deal with legacy code.\n#### Flexibly construct the value object and mutex\nThe Safe constructor accepts the arguments needed to construct the value and the mutex object. The last argument is forwarded to the mutex constructor and the rest to the value's.  \nIf the last argument cannot be used to construct the mutex, *safe* detects it and forwards everything to the value constructor.  \nIf you explicitely do not want to use the last argument to construct the mutex object, use the safe::default_construct_mutex tag as last argument.  \nExamples:\n```c++\nstd::mutex mutex;\nsafe::Safe<std::pair<int,int>, std::mutex> bothDefault;            // mutex and value are default constructed\nsafe::Safe<std::pair<int,int>, std::mutex &> noDefault(42, 24, mutex); // mutex and value are initialized\nsafe::Safe<std::pair<int,int>, std::mutex &> valueDefault(mutex);  // mutex is initialized, and value is default constructed\nsafe::Safe<std::pair<int,int>, std::mutex> mutexDefault(42, 24); // mutex is default constructed, and value is initialized\nsafe::Safe<std::pair<int,int>, std::mutex> mutexDefaultTag(42, 24, safe::default_construct_mutex); // mutex is default constructed, and value is initialized\n```\n#### Flexibly construct the Lock objects\nThe Access constructors have a variadic parameter pack that is forwarded to the Lock object's constructor. This can be used to pass in standard lock tags such as std::adopt_lock, but also to construct your custom locks that may require additionnal arguments than just the mutex.   \nThere are many equivalent ways to get an Access object from a Safe object. Choose the syntax you prefer:\n```c++\nsafe::Safe<int> safeValue;\nconst safe::ReadAccess<safe::Safe<int>> value(safeValue);\nconst safe::Safe<int>::ReadAccess<> value(safeValue); // the empty <> are unfortunately necessary.\nconst auto value = safeValue.readLock(); // nicer, but only with C++17 and later\nconst auto value = safeValue.readLock<std::unique_lock>(); // ok even pre-C++17, using a std::unique_lock rather than the default std::lock_guard\nconst safe::Safe<int>::ReadAccess<std::unique_lock> value(safeValue); // equivalent to the above\n// All of the above exist in read-write versions, simply replace \"read\" by \"write\" (and remove the const).\n```\nSee [this section](#with-stdlock_guard) to understand why the nicer version doesn't work pre-C++17.\n\nExamples passing arguments to the Lock object:\n```c++\nstd::mutex aLockedMutex;\naLockedMutex.lock();\nsafe::Safe<int, std::mutex&> safeValue(aLockedMutex); // given a Safe object with an already locked mutex.\n\n// Because the mutex is already locked, you need to pass the std::adopt_lock tag to std::lock_guard when you construct your Access object.\n// No matter how you get your Access objects, you can pass arguments to the lock's constructor.\n// Again, all the lines below are equivalent but here we need to specify the mutex type for the Safe type, because it is not the default (notice the &):\nsafe::WriteAccess<safe::Safe<int, std::mutex&>> value(safeValue, std::adopt_lock);\nsafe::Safe<int, std::mutex&>::WriteAccess<> value(safeValue, std::adopt_lock);\nauto value = safeValue.writeLock(std::adopt_lock); // again, only in C++17\nauto value = safeValue.writeLock<std::unique_lock>(std::adopt_lock); // using a std::unique_lock still works\n```\n```c++\n// Here's a safe int using a timed_mutex\nsafe::Safe<int, std::timed_mutex> safeValue;\n\n// Pass in a time-out when you lock the mutex, and it will try to lock for that duration!\n// safe does not do anything here, std::unique_lock does it all. safe simply forwards any argument\n// it gets, and when std::unique_lock receives a std::duration, it try to lock before giving up.\nauto value = safeValue.writeLock<std::unique_lock>(std::chrono::seconds(1));\n// You can reach the lock through the Access object and check whether or not the locking was successful:\nassert(value.lock.owns_lock());\n```\n### Even more safety!\n#### Choose the access mode that suits each access\nYou will instatiate one Safe object for every value object you want to protect. But, you will create an Access object in every scope where you want to operate on the value object. For each of these accesses, you can choose whether the access is read-write or read-only.\n#### Force read-only access with shared_locks\nShared mutexes and shared locks allow multiple reading threads to access the value object simultaneously. Unfortunately, using only mutexes and locks, the read-only restriction is not guaranteed to be applied. That is, it is possible to lock a mutex in shared mode and write to the shared value. With *safe*, you can enforce read-only access when using shared locking by using ReadAccess objects. See [this section](#enforcing-read-only-access) for details.\n### Compatibility\n#### With legacy code\nYou can use *safe* with old-style unsafe code that uses the soon-to-be out-of-fashion separate-mutex-and-value idiom. Imagine you are provided with the typical mutex and int. *safe* allows you to wrap these variables, without having to modify the existing code. Enjoy the safety and avoid the headaches:\n```c++\nstd::mutex lousyMutex;\nint unsafeValue;\n\n// Wrap the existing variables\nsafe::Safe<int&, std::mutex&> safeValue(lousyMutex, unsafeValue);\n// do not use lousyMutex and unsafeValue directly from here on!\n```\n#### With code from the future\n*safe* is written in C++11, but it is fully compatible with mutexes and locks from different sources like C++14's std::shared_lock and C++17's std::shared_mutex, thanks to template parameters. Of course, you can also use boost::shared_lock_guard and your own custom mutexes and locks.\n#### With standard uses of mutexes and locks\nThe mutex is accessible from the Safe object through an accessor functions, and the lock object is a public member of the Access class. Anything you can do with your typical mutexes and locks you can do with *safe*. \n\nFor example, *safe* can seamlessly be used with std::condition_variable:\n```c++\nstd::condition_variable cv;\nsafe::Safe<int> safeValue;\nsafe::Safe<int>::WriteAccess<std::unique_lock> value(safeValue);\ncv.wait(value.lock, [](){return true;});\n// use `value` as you usually would.\n```\n#### With std::lock_guard\nSome code shown in this readme does not compile with C++ versions prior C++17. This is because of the new rules on temporaries introduced in C++17, and because *safe* uses std::lock_guard by default. std::lock_guard is non-copiable, non-moveable so it cannot be initialized as nicely as other locks prior to C++17. If you don't like using std::lock_guard, check out the [Advanced Usage](#advanced-usage) section.\n## Installation\n### Method 1: Copy the source files in your project\n*safe* is a header-only library. Using the library can simply mean copy the contents of the include/ folder to some place of your convenience. This is the most straightforward installation method.\n\n### Method 2: Via CMake FetchContent (CMake > 3.14)\n```CMake\ncmake_minimum_required(VERSION 3.14)\nproject(my_project)\n\nFetchContent_Declare(\n  safe\n  GIT_REPOSITORY https://github.com/LouisCharlesC/safe.git\n  GIT_TAG        v1.1.0\n)\nFetchContent_MakeAvailable(safe)\n\nadd_executable(my_project my_project.cc)\ntarget_link_library(my_project safe::safe)\n```\nNOTE: `find_package(safe CONFIG REQUIRED)` is not needed with this method.\n### Method 3: Install locally via CMake\n```bash\ngit clone https://github.com/louischarlescaron/safe\n\ncd safe\ncmake -B safe-build -DCMAKE_INSTALL_PREFIX=\"$(pwd)/safe-install\"\ncmake --build safe-build --config Release --target install\n```\nThen you can use `find_package` in your project:\n```cmake\ncmake_minimum_required(VERSION 3.11)\nproject(my_project)\n\nfind_package(safe CONFIG REQUIRED)\n\nadd_executable(my_project my_project.cc)\ntarget_link_library(my_project safe::safe)\n```\nAnd build with:\n```bash\ncd my_project\ncmake -B build -DCMAKE_PREFIX_PATH=\"path/to/safe-install\"\ncmake --build build --config Release\n```\nNOTE: `CMAKE_PREFIX_PATH` is used to tell `find_package()` where to look for libraries. `path/to/safe-install` is not a standard path but it's easier to remove when needed.\n### Method 4: Install system-wide via CMake (not recommended)\n```bash\ngit clone https://github.com/LouisCharlesC/safe\n\ncd safe\ncmake -B build\nsudo cmake --build build --config Release --target install\n```\n*safe* will be installed into your OS's standard intallation path. Be aware that system-wide installation make it hard to deal with multilpe library versions, and can cause collisions if you happen to install another library called safe!\n\nWhen you build your own project, you **won't** need to append `-DCMAKE_PREFIX_PATH=\"path/to/safe-install\"`.\n## Advanced usage\n### Enforcing read-only access\nYou can inform the *safe* library that some locks that you use are read-only (e.g. std::shared_lock, boost::shared_lock_guard). If you do so, trying to instantiate a WriteAccess object with these locks will trigger a compilation error. Use the trait class safe::AccessTraits to customize this behavior.\n\nHere is how the trait works:\n- If no specialization of the type trait exists for a lock type, the lock can be used with read-write and read-only Access objects.\n- If a specialization exists, it must declare the IsReadOnly boolean variable.\n  - If IsReadOnly is true, the lock is read-only: constructinfg an Access object with this lock using Mode = AccessMode::ReadWrite will fail to compile.\n  - If IsReadOnly is false, the result is the same as if no specialization exists.\n\nAs an example, here is how to specialize the trait for std::shared_lock (you will find this exact code snippet in safe/access_mode.h):\n```c++\ntemplate<typename MutexType>\nstruct safe::AccessTraits<std::shared_lock<MutexType>>\n{\n\tstatic constexpr bool IsReadOnly = true;\n};\n```\n### Avoid some typing by defining your own default lock types\n*safe* uses std::lock_guard by default everywhere. If you know you will always use a certain lock type given some mutex type (for instance, std::unique_lock with std::timed_mutex), you can inform *safe* and it will use this lock by default with this mutex type.  \nTo do so, you must specialize the safe::DefaultLocks class template. Have a look at the tests/test_default_locks.cpp files. As you can see, you can specify a different lock type for read and write accesses:\n```c++\ntemplate<>\nstruct safe::impl::DefaultLocks<std::timed_mutex>\n{\n    using ReadOnly = std::unique_lock<std::timed_mutex>;\n    using ReadWrite = std::lock_guard<std::timed_mutex>;\n};\n```\n# Acknowledgment\nThanks to all contributors, issue raisers and stargazers!\nThe cmake is inspired from https://github.com/bsamseth/cpp-project and Craig Scott's CppCon 2019 talk: Deep CMake for Library Authors. Many thanks to the authors!\n"
  },
  {
    "path": "cmake/InstallTarget.cmake",
    "content": "include(CMakePackageConfigHelpers)\nwrite_basic_package_version_file(\n\t\"${PROJECT_NAME}ConfigVersion.cmake\"\n\tVERSION ${PROJECT_VERSION}\n\tCOMPATIBILITY SameMajorVersion\n)\nconfigure_package_config_file(\n\t\"${CMAKE_CURRENT_LIST_DIR}/${PROJECT_NAME}Config.cmake.in\"\n\t\"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake\"\n\tINSTALL_DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake\n)\ninstall(FILES\n\t${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake\n\t${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake\n\tDESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake\n)\n\ninstall(TARGETS ${PROJECT_NAME}\n\tEXPORT ${PROJECT_NAME}Targets\n\tINCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}\n\tRUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}\n\t\t\t\t\tCOMPONENT ${PROJECT_NAME}_RunTime\n\tLIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}\n\t\t\t\t\tCOMPONENT ${PROJECT_NAME}_RunTime\n# \t\t\t\t\tNAMELINK_COMPONENT ${PROJECT_NAME}_Development\n\tARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}\n\t\t\t\t\tCOMPONENT NAME}_Development\n)\n\ninstall(EXPORT ${PROJECT_NAME}Targets\n\tDESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake\n\tNAMESPACE ${PROJECT_NAME}::\n\tFILE ${PROJECT_NAME}Targets.cmake\n\tCOMPONENT ${PROJECT_NAME}_Development\n)\n"
  },
  {
    "path": "cmake/safeConfig.cmake.in",
    "content": "@PACKAGE_INIT@\n\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake\")\ncheck_required_components(\"@PROJECT_NAME@\")"
  },
  {
    "path": "include/safe/access_mode.h",
    "content": "// Copyright (c) 2019-2023 Louis-Charles Caron\n\n// This file is part of the safe library (https://github.com/LouisCharlesC/safe).\n\n// Use of this source code is governed by an MIT-style license that can be\n// found in the LICENSE file or at https://opensource.org/licenses/MIT.\n\n#pragma once\n\n#include <mutex>\n#if __cplusplus >= 201402L\n#include <shared_mutex>\n#endif // __cplusplus >= 201402L\n\nnamespace safe\n{\nenum class AccessMode\n{\n    ReadOnly,\n    ReadWrite\n};\n\n// Base template: most LockTypes are not read only.\n// Specialize this template if you want to force a LockType to be read only.\n// If you specialize this template, consider using the pattern described in the \n// test_default_locks.cpp file to avoid ill-formed programs!\ntemplate <typename LockType> struct AccessTraits\n{\n    static constexpr bool IsReadOnly = false;\n};\n// Partial specialization for lock_guard: not read only.\ntemplate <typename MutexType> struct AccessTraits<std::lock_guard<MutexType>>\n{\n    static constexpr bool IsReadOnly = false;\n};\n// Partial specialization for unique_lock: not read only.\ntemplate <typename MutexType> struct AccessTraits<std::unique_lock<MutexType>>\n{\n    static constexpr bool IsReadOnly = false;\n};\n// Partial specialization for shared_lock: read only!\n#if __cplusplus >= 201402L\ntemplate <typename MutexType> struct AccessTraits<std::shared_lock<MutexType>>\n{\n    static constexpr bool IsReadOnly = true;\n};\n#endif // __cplusplus >= 201402L\n} // namespace safe"
  },
  {
    "path": "include/safe/default_locks.h",
    "content": "// Copyright (c) 2019-2022 Louis-Charles Caron\n\n// This file is part of the safe library (https://github.com/LouisCharlesC/safe).\n\n// Use of this source code is governed by an MIT-style license that can be\n// found in the LICENSE file or at https://opensource.org/licenses/MIT.\n\n#pragma once\n\n#include <mutex>\n\nnamespace safe\n{\nnamespace impl\n{\n// Base template defining default lock types for all mutex types.\n// Specialize this template as shown in the ReadMe and tests to define your own default locks.\n// This struct only uses the first template parameter (MutexType), the parameter pack is there\n// only to allow one to partially specialize the template using a single template parameter. Doing\n// so overrides the default types for any MutexType. See the test_default_locks.cpp file for examples.\ntemplate<typename MutexType, typename...>\nstruct DefaultLocks\n{\n    using ReadOnly = std::lock_guard<MutexType>;\n    using ReadWrite = std::lock_guard<MutexType>;\n};\n} // namespace impl\n\ntemplate<typename MutexType>\nusing DefaultReadOnlyLockType = typename impl::DefaultLocks<MutexType>::ReadOnly;\ntemplate<typename MutexType>\nusing DefaultReadWriteLockType = typename impl::DefaultLocks<MutexType>::ReadWrite;\n} // namespace safe"
  },
  {
    "path": "include/safe/meta.h",
    "content": "// Copyright (c) 2023 Louis-Charles Caron\n\n// This file is part of the safe library (https://github.com/LouisCharlesC/safe).\n\n// Use of this source code is governed by an MIT-style license that can be\n// found in the LICENSE file or at https://opensource.org/licenses/MIT.\n\n#pragma once\n\n#include <cstddef>\n\nnamespace safe\n{\nnamespace impl\n{\n// This set of template and specializations is used to extract the type of the last argument of a paramter pack.\ntemplate <typename... Ts> struct Last; // Base template, specializations cover all uses.\ntemplate <typename First, typename Second, typename... Others> struct Last<First, Second, Others...>\n{\n    using type = typename Last<Second, Others...>::type;\n};\ntemplate <typename T> struct Last<T>\n{\n    using type = T;\n};\ntemplate <> struct Last<>\n{\n    using type = void;\n};\n\ntemplate<std::size_t... Is>\nstruct index_sequence {};\ntemplate<std::size_t N, std::size_t... Is>\nstruct index_sequence<N, Is...>\n{\n    using type = typename index_sequence<N-1, N-1, Is...>::type;\n};\ntemplate<std::size_t... Is>\nstruct index_sequence<0, Is...>\n{\n    using type = index_sequence<Is...>;\n};\n\ntemplate<std::size_t N>\nconstexpr typename index_sequence<N>::type make_index_sequence()\n{\n    return {};\n}\n} // namespace impl\n\ntemplate <typename... Ts> using Last = typename impl::Last<Ts...>::type;\n} // namespace safe"
  },
  {
    "path": "include/safe/mutable_ref.h",
    "content": "// Copyright (c) 2019-2022 Louis-Charles Caron\n\n// This file is part of the safe library (https://github.com/LouisCharlesC/safe).\n\n// Use of this source code is governed by an MIT-style license that can be\n// found in the LICENSE file or at https://opensource.org/licenses/MIT.\n\n#pragma once\n\n#include <utility>\n\nnamespace safe\n{\nnamespace impl\n{\n/**\n * @brief A helper class that defines a member variable of type Type. The variable is defined \"mutable Type\" if Type is\n * not a reference, the variable is \"Type&\" if Type is a reference.\n *\n * @tparam Type The type of the variable to define.\n */\ntemplate <typename Type> struct MutableIfNotReference\n{\n    /// Mutable Type object.\n    mutable Type get;\n};\n/**\n * @brief Specialization of MutableIfNotReference for references.\n *\n * @tparam Type The type of the reference to define.\n */\ntemplate <typename Type> struct MutableIfNotReference<Type &>\n{\n    /// Reference to a Type object.\n    Type &get;\n};\n} // namespace impl\n} // namespace safe"
  },
  {
    "path": "include/safe/safe.h",
    "content": "// Copyright (c) 2019-2023 Louis-Charles Caron\n\n// This file is part of the safe library (https://github.com/LouisCharlesC/safe).\n\n// Use of this source code is governed by an MIT-style license that can be\n// found in the LICENSE file or at https://opensource.org/licenses/MIT.\n\n#pragma once\n\n#include \"access_mode.h\"\n#include \"default_locks.h\"\n#include \"meta.h\"\n#include \"mutable_ref.h\"\n\n#include <type_traits>\n#include <utility>\n\n#if __cplusplus >= 201703L\n#define EXPLICIT_IF_CPP17 explicit\n#define EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17 ReturnType\n#else\n#define EXPLICIT_IF_CPP17\n#define EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17\n#endif\n\nnamespace safe\n{\nnamespace impl\n{\nstruct DefaultConstructMutex\n{\n};\n} // namespace impl\n\n/**\n * @brief Use this tag to default construct the mutex when constructing a Safe object.\n */\nconstexpr impl::DefaultConstructMutex default_construct_mutex;\n\n/**\n * @brief Wraps a value together with a mutex.\n *\n * @tparam ValueType The type of the value to protect.\n * @tparam MutexType The type of the mutex.\n */\ntemplate <typename ValueType, typename MutexType = std::mutex> class Safe\n{\n  private:\n    /// Type ValueType with reference removed, if present\n    using RemoveRefValueType = typename std::remove_reference<ValueType>::type;\n    /// Type MutexType with reference removed, if present\n    using RemoveRefMutexType = typename std::remove_reference<MutexType>::type;\n\n    /**\n     * @brief Manages a mutex and gives pointer-like access to a value object.\n     *\n     * @tparam LockType The type of the lock object that manages the mutex, example: std::lock_guard.\n     * @tparam Mode Determines the access mode of the Access object. Can be either AccessMode::ReadOnly or\n     * AccessMode::ReadWrite.\n     */\n    template <template <typename> class LockType, AccessMode Mode> class Access\n    {\n        // Make sure AccessMode is ReadOnly if a read-only lock is used\n        static_assert(!(AccessTraits<LockType<RemoveRefMutexType>>::IsReadOnly && Mode == AccessMode::ReadWrite),\n                      \"Cannot have ReadWrite access mode with ReadOnly lock. \"\n                      \"Check the value of \"\n                      \"AccessTraits<LockType>::IsReadOnly if it exists.\");\n\n        /// ValueType with const qualifier if AccessMode is ReadOnly.\n        using ConstIfReadOnlyValueType =\n            typename std::conditional<Mode == AccessMode::ReadOnly, const RemoveRefValueType, RemoveRefValueType>::type;\n\n      public:\n        /// Pointer-to-const ValueType\n        using ConstPointerType = const ConstIfReadOnlyValueType *;\n        /// Pointer-to-const ValueType if Mode is ReadOnly, pointer to ValueType otherwise.\n        using PointerType = ConstIfReadOnlyValueType *;\n        /// Reference-to-const ValueType\n        using ConstReferenceType = const ConstIfReadOnlyValueType &;\n        /// Reference-to-const ValueType if Mode is ReadOnly, reference to ValueType otherwise.\n        using ReferenceType = ConstIfReadOnlyValueType &;\n\n        /**\n         * @brief Construct an Access object from a possibly const reference to the value object and any additionnal\n         * argument needed to construct the Lock object.\n         *\n         * @tparam LockArgs Deduced from lockArgs.\n         * @param value Reference to the value.\n         * @param lockArgs Arguments needed to construct the lock object.\n         */\n        template <typename... OtherLockArgs>\n        EXPLICIT_IF_CPP17 Access(ReferenceType value, MutexType &mutex, OtherLockArgs &&...otherLockArgs)\n            : lock(mutex, std::forward<OtherLockArgs>(otherLockArgs)...), m_value(value)\n        {\n        }\n\n        /**\n         * @brief Construct a read-only Access object from a const safe::Safe object and any additionnal argument needed\n         * to construct the Lock object.\n         *\n         * If needed, you can provide additionnal arguments to construct the lock object (such as std::adopt_lock). The\n         * mutex from the safe::Locakble object is already passed to the lock object's constructor though, you must not\n         * provide it.\n         *\n         * @tparam OtherLockArgs Deduced from otherLockArgs.\n         * @param safe The const Safe object to give protected access to.\n         * @param otherLockArgs Other arguments needed to construct the lock object.\n         */\n        template <typename... OtherLockArgs>\n        EXPLICIT_IF_CPP17 Access(const Safe &safe, OtherLockArgs &&...otherLockArgs)\n            : Access(safe.m_value, safe.m_mutex.get, std::forward<OtherLockArgs>(otherLockArgs)...)\n        {\n        }\n\n        /**\n         * @brief Construct a read-write Access object from a safe::Safe object and any additionnal argument needed to\n         * construct the Lock object.\n         *\n         * If needed, you can provide additionnal arguments to construct the lock object (such as std::adopt_lock). The\n         * mutex from the safe object is already passed to the lock object's constructor though, you must not provide\n         * it.\n         *\n         * @tparam OtherLockArgs Deduced from otherLockArgs.\n         * @param safe The Safe object to give protected access to.\n         * @param otherLockArgs Other arguments needed to construct the lock object.\n         */\n        template <typename... OtherLockArgs>\n        EXPLICIT_IF_CPP17 Access(Safe &safe, OtherLockArgs &&...otherLockArgs)\n            : Access(safe.m_value, safe.m_mutex.get, std::forward<OtherLockArgs>(otherLockArgs)...)\n        {\n        }\n\n        /**\n         * @brief Const accessor to the value.\n         * @return ConstPointerType Const pointer to the protected value.\n         */\n        ConstPointerType operator->() const noexcept\n        {\n            return &m_value;\n        }\n\n        /**\n         * @brief Accessor to the value.\n         * @return ValuePointerType Pointer to the protected value.\n         */\n        PointerType operator->() noexcept\n        {\n            return &m_value;\n        }\n\n        /**\n         * @brief Const accessor to the value.\n         * @return ConstValueReferenceType Const reference to the protected value.\n         */\n        ConstReferenceType operator*() const noexcept\n        {\n            return m_value;\n        }\n\n        /**\n         * @brief Accessor to the value.\n         * @return ValueReferenceType Reference to the protected.\n         */\n        ReferenceType operator*() noexcept\n        {\n            return m_value;\n        }\n\n        /// The lock that manages the mutex.\n        mutable LockType<RemoveRefMutexType> lock;\n\n      private:\n        /// The protected value.\n        ReferenceType m_value;\n    };\n\n    /// Reference-to-const ValueType.\n    using ConstValueReferenceType = const RemoveRefValueType &;\n    /// Reference to ValueType.\n    using ValueReferenceType = RemoveRefValueType &;\n    /// Reference to MutexType.\n    using MutexReferenceType = RemoveRefMutexType &;\n\n    struct UseLastArgumentForMutex\n    {\n    };\n    struct LastArgumentIsATag\n    {\n    };\n\n  public:\n    /// Aliases to ReadAccess and WriteAccess classes for this Safe class.\n    template <template <typename> class LockType = DefaultReadOnlyLockType>\n    using ReadAccess = Access<LockType, AccessMode::ReadOnly>;\n    template <template <typename> class LockType = DefaultReadWriteLockType>\n    using WriteAccess = Access<LockType, AccessMode::ReadWrite>;\n\n    /**\n     * @brief Construct a Safe object\n     */\n    Safe() = default;\n    /**\n     * @brief Construct a Safe object, forwarding the last argument to construct the mutex and the other arguments to\n     * construct the value object. This constructor will be selected if the mutex can be constructed from the last\n     * argument of the parameter pack. To avoid using the last argument to construct the mutex, add the\n     * default_construct_mutex tag as last argument.\n     *\n     * @tparam Args Deduced from args.\n     * @tparam SFINAE constraint.\n     * @param args Perfect forwarding arguments to split between the value and mutex.\n     */\n    template <typename... Args,\n              typename std::enable_if<std::is_constructible<MutexType, Last<Args...>>::value, bool>::type = true>\n    explicit Safe(Args &&...args)\n        : Safe(UseLastArgumentForMutex(), std::forward_as_tuple(std::forward<Args>(args)...),\n               safe::impl::make_index_sequence<sizeof...(args) - 1>()) // delegate to a private constructor to split the\n                                                                       // parameter pack\n    {\n    }\n    /**\n     * @brief Construct a Safe object, forwarding all arguments to construct the value object. This constructor will be\n     * selected if the mutex cannot be constructed from the last argument of the parameter pack.\n     *\n     * @tparam Args Deduced from args.\n     * @param args Perfect forwarding arguments to construct the value object.\n     */\n    template <typename... Args,\n              typename std::enable_if<!std::is_same<const impl::DefaultConstructMutex &, Last<Args...>>::value &&\n                                          !std::is_constructible<MutexType, Last<Args...>>::value,\n                                      bool>::type = true>\n    explicit Safe(Args &&...args) : m_mutex{}, m_value(std::forward<Args>(args)...)\n    {\n    }\n    /**\n     * @brief Construct a Safe object, forwarding all arguments but the last (the default_construct_mutex tag) to\n     * construct the value object.\n     *\n     * @tparam Args Deduced from args.\n     * @param default_construct_mutex tag.\n     * @param args Perfect forwarding arguments to construct the value object.\n     */\n    template <typename... Args,\n              typename std::enable_if<std::is_same<const impl::DefaultConstructMutex &, Last<Args...>>::value,\n                                      bool>::type = true>\n    explicit Safe(Args &&...args)\n        : Safe(LastArgumentIsATag(), std::forward_as_tuple(std::forward<Args>(args)...),\n               safe::impl::make_index_sequence<sizeof...(args) - 1>()) // delegate to a private constructor to split the\n                                                                       // parameter pack\n    {\n    }\n\n    /// Delete all copy/move construction/assignment, as these operations require locking the mutex under the covers.\n    Safe(const Safe &) = delete;\n    Safe(Safe &&) = delete;\n    Safe &operator=(const Safe &) = delete;\n    Safe &operator=(Safe &&) = delete;\n\n    /**\n     * @brief Lock the Safe object to get a ReadAccess object.\n     *\n     * @tparam Args Deduced from args.\n     * @param args Perfect forwarding arguments to construct the lock object.\n     */\n    template <template <typename> class LockType = DefaultReadOnlyLockType, typename... LockArgs>\n    ReadAccess<LockType> readLock(LockArgs &&...lockArgs) const\n    {\n        using ReturnType = ReadAccess<LockType>;\n        return EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17{*this, std::forward<LockArgs>(lockArgs)...};\n    }\n\n    /**\n     * @brief Lock the Safe object to get a WriteAccess object.\n     *\n     * @tparam Args Deduced from args.\n     * @param args Perfect forwarding arguments to construct the lock object.\n     */\n    template <template <typename> class LockType = DefaultReadWriteLockType, typename... LockArgs>\n    WriteAccess<LockType> writeLock(LockArgs &&...lockArgs)\n    {\n        using ReturnType = WriteAccess<LockType>;\n        return EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17{*this, std::forward<LockArgs>(lockArgs)...};\n    }\n\n    /**\n     * @brief Unsafe const accessor to the value. If you use this function, you exit the realm of safe!\n     *\n     * @return ConstValueReferenceType Const reference to the value object.\n     */\n    ConstValueReferenceType unsafe() const noexcept\n    {\n        return m_value;\n    }\n    /**\n     * @brief Unsafe accessor to the value. If you use this function, you exit the realm of safe!\n     *\n     * @return ValueReferenceType Reference to the value object.\n     */\n    ValueReferenceType unsafe() noexcept\n    {\n        return m_value;\n    }\n\n    /**\n     * @brief Accessor to the mutex.\n     *\n     * @return MutexReferenceType Reference to the mutex.\n     */\n    MutexReferenceType mutex() const noexcept\n    {\n        return m_mutex.get;\n    }\n\n  private:\n    // The next two constructors are helper constructors to split the input arguments between the value and mutex\n    // constructors\n    template <typename ArgsTuple, size_t... AllButLast>\n    explicit Safe(const UseLastArgumentForMutex, ArgsTuple &&args, safe::impl::index_sequence<AllButLast...>)\n        : m_mutex{std::get<sizeof...(AllButLast)>(\n              std::forward<ArgsTuple>(args))},                            // use the last argument to constuct the mutex\n          m_value(std::get<AllButLast>(std::forward<ArgsTuple>(args))...) // and the rest to construct the value\n    {\n    }\n    template <typename ArgsTuple, size_t... AllButLast>\n    explicit Safe(const LastArgumentIsATag, ArgsTuple &&args, safe::impl::index_sequence<AllButLast...>)\n        : m_mutex{}, // default construct the mutex since the last argument is a tag\n          m_value(std::get<AllButLast>(std::forward<ArgsTuple>(args))...) // use the rest to construct the value\n    {\n    }\n\n    /// The helper object that holds the mutable mutex, or a reference to a mutex.\n    impl::MutableIfNotReference<MutexType> m_mutex;\n    /// The value to protect.\n    ValueType m_value;\n};\n\n/**\n * @brief Type alias for read-only Access.\n *\n * @tparam SafeType The type of Safe object to give read-only access to.\n * @tparam LockType The type of lock.\n */\ntemplate <typename SafeType, template <typename> class LockType = DefaultReadOnlyLockType>\nusing ReadAccess = typename SafeType::template ReadAccess<LockType>;\n\n/**\n * @brief Type alias for read-write Access.\n *\n * @tparam SafeType The type of Safe object to give read-write access to.\n * @tparam LockType The type of lock.\n */\ntemplate <typename SafeType, template <typename> class LockType = DefaultReadWriteLockType>\nusing WriteAccess = typename SafeType::template WriteAccess<LockType>;\n} // namespace safe\n\n#undef EXPLICIT_IF_CPP17\n#undef EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17"
  },
  {
    "path": "tests/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.9.2)\n\nproject(safe_tests LANGUAGES CXX)\n\n# Detect if used in add_subdirectory() or install space\nif(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)\n\tfind_package(safe CONFIG REQUIRED)\nendif()\n\nlist(APPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/cmake\")\ninclude(Warnings)\n\ninclude(add_package)\nadd_package(\n\tdoctest\n\tdoctest\n\thttps://github.com/onqtam/doctest\n\tv2.4.9\n\tscripts/cmake\n)\n\nadd_executable(safe_tests test_main.cpp test_readme.cpp test_safe.cpp test_default_locks.cpp)\ntarget_link_libraries(safe_tests PRIVATE safe::safe doctest::doctest)\ntarget_set_warnings(safe_tests ENABLE ALL AS_ERROR ALL DISABLE Annoying)\ntarget_compile_features(safe_tests INTERFACE cxx_std_17)\n\ninclude(CTest)\ninclude(doctest)\ndoctest_discover_tests(safe_tests)\n"
  },
  {
    "path": "tests/cmake/Warnings.cmake",
    "content": "# MIT License\n\n# Copyright (c) 2017 Lectem\n\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n\n# The above copyright notice and this permission notice shall be included in all\n# copies or substantial portions of the Software.\n\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n# SOFTWARE.\n\n\nfunction(target_set_warnings)\n    if(NOT ENABLE_WARNINGS_SETTINGS)\n        return()\n    endif()\n    if (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"MSVC\")\n      set(WMSVC TRUE)\n    elseif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"GNU\")\n      set(WGCC TRUE)\n    elseif (\"${CMAKE_CXX_COMPILER_ID}\" MATCHES \"Clang\")\n      set(WCLANG TRUE)\n    endif()\n    set(multiValueArgs ENABLE DISABLE AS_ERROR)\n    cmake_parse_arguments(this \"\" \"\" \"${multiValueArgs}\" ${ARGN})\n    list(FIND this_ENABLE \"ALL\" enable_all)\n    list(FIND this_DISABLE \"ALL\" disable_all)\n    list(FIND this_AS_ERROR \"ALL\" as_error_all)\n    if(NOT ${enable_all} EQUAL -1)\n      if(WMSVC)\n        # Not all the warnings, but WAll is unusable when using libraries\n        # Unless you'd like to support MSVC in the code with pragmas, this is probably the best option\n        list(APPEND WarningFlags \"/W4\")\n      elseif(WGCC)\n        list(APPEND WarningFlags \"-Wall\" \"-Wextra\" \"-Wpedantic\")\n      elseif(WCLANG)\n        list(APPEND WarningFlags \"-Wall\" \"-Weverything\" \"-Wpedantic\")\n      endif()\n    elseif(NOT ${disable_all} EQUAL -1)\n      set(SystemIncludes TRUE) # Treat includes as if coming from system\n      if(WMSVC)\n        list(APPEND WarningFlags \"/w\" \"/W0\")\n      elseif(WGCC OR WCLANG)\n        list(APPEND WarningFlags \"-w\")\n      endif()\n    endif()\n\n    list(FIND this_DISABLE \"Annoying\" disable_annoying)\n    if(NOT ${disable_annoying} EQUAL -1)\n      if(WMSVC)\n        # bounds-checked functions require to set __STDC_WANT_LIB_EXT1__ which we usually don't need/want\n        list(APPEND WarningDefinitions -D_CRT_SECURE_NO_WARNINGS)\n        # disable C4514 C4710 C4711... Those are useless to add most of the time\n        #list(APPEND WarningFlags \"/wd4514\" \"/wd4710\" \"/wd4711\")\n        #list(APPEND WarningFlags \"/wd4365\") #signed/unsigned mismatch\n        #list(APPEND WarningFlags \"/wd4668\") # is not defined as a preprocessor macro, replacing with '0' for\n      elseif(WGCC OR WCLANG)\n        list(APPEND WarningFlags -Wno-switch-enum)\n        if(WCLANG)\n          list(APPEND WarningFlags -Wno-unknown-warning-option -Wno-padded -Wno-undef -Wno-reserved-id-macro -fcomment-block-commands=test,retval)\n          if(NOT CMAKE_CXX_STANDARD EQUAL 98)\n              list(APPEND WarningFlags -Wno-c++98-compat -Wno-c++98-compat-pedantic)\n          endif()\n          if (\"${CMAKE_CXX_SIMULATE_ID}\" STREQUAL \"MSVC\") # clang-cl has some VCC flags by default that it will not recognize...\n              list(APPEND WarningFlags -Wno-unused-command-line-argument)\n          endif()\n        endif(WCLANG)\n      endif()\n    endif()\n\n    if(NOT ${as_error_all} EQUAL -1)\n      if(WMSVC)\n        list(APPEND WarningFlags \"/WX\")\n      elseif(WGCC OR WCLANG)\n        list(APPEND WarningFlags \"-Werror\")\n      endif()\n    endif()\n    foreach(target IN LISTS this_UNPARSED_ARGUMENTS)\n      if(WarningFlags)\n        target_compile_options(${target} PRIVATE ${WarningFlags})\n      endif()\n      if(WarningDefinitions)\n        target_compile_definitions(${target} PRIVATE ${WarningDefinitions})\n      endif()\n      if(SystemIncludes)\n        set_target_properties(${target} PROPERTIES\n            INTERFACE_SYSTEM_INCLUDE_DIRECTORIES $<TARGET_PROPERTY:${target},INTERFACE_INCLUDE_DIRECTORIES>)\n      endif()\n    endforeach()\nendfunction(target_set_warnings)\n"
  },
  {
    "path": "tests/cmake/add_package.cmake",
    "content": "macro(add_package name depname git_repo git_rev mod_dir)\n    unset(${name}_FOUND CACHE) # needed for correct behaviour on rebuilds\n\n    if (DEPS STREQUAL \"AUTO\")\n        find_package (${name} CONFIG QUIET)\n    elseif (DEPS STREQUAL \"LOCAL\")\n        find_package (${name} REQUIRED CONFIG)\n    endif()\n\n    if (NOT ${name}_FOUND)\n        include(FetchContent)\n        FetchContent_Declare (${depname}\n            GIT_REPOSITORY ${git_repo}\n            GIT_TAG ${git_rev})\n        FetchContent_MakeAvailable (${depname})\n        FetchContent_GetProperties(${depname})\n        if(NOT ${depname}_POPULATED)\n            FetchContent_Populate(${depname})\n            add_subdirectory(${${depname}_SOURCE_DIR} ${${depname}_BINARY_DIR} EXCLUDE_FROM_ALL) # prevent installing of dependencies\n        endif()\n        set (${name}_POPULATED TRUE) # this is supposed to be done in MakeAvailable but it seems not to?!?\n        list(APPEND CMAKE_MODULE_PATH \"${${depname}_SOURCE_DIR}/${mod_dir}\")\n    endif()\nendmacro()\n"
  },
  {
    "path": "tests/safe_with_custom_defaults.h",
    "content": "#pragma once\n\n#include \"safe/safe.h\"\n\n// Specialize DefaultLocks for any mutex types. This effectively specifies new default locks.\ntemplate<typename MutexType>\nstruct safe::impl::DefaultLocks<MutexType>\n{\n    using ReadOnly = std::lock_guard<MutexType>;\n    using ReadWrite = std::unique_lock<MutexType>;\n};\n\n// Specialize DefaultLocks for a specific mutex type (here: std::timed_mutex).\ntemplate<>\nstruct safe::impl::DefaultLocks<std::timed_mutex>\n{\n    using ReadOnly = std::unique_lock<std::timed_mutex>;\n    using ReadWrite = std::lock_guard<std::timed_mutex>;\n};\n"
  },
  {
    "path": "tests/test_default_locks.cpp",
    "content": "// Copyright (c) 2023 Louis-Charles Caron\n\n// This file is part of the safe library (https://github.com/LouisCharlesC/safe).\n\n// Use of this source code is governed by an MIT-style license that can be\n// found in the LICENSE file or at https://opensource.org/licenses/MIT.\n\n// To avoid any problem with specialized templates not being visible from every compilation unit, you should use the\n// pattern shown here: create your own header file in which you include safe and define your template specializations.\n// Only ever include your own header in your project.\n#include \"safe_with_custom_defaults.h\"\n\n#include <doctest/doctest.h>\n#include <type_traits>\n\nTEST_CASE(\"First specialization works for all mutexes\")\n{\n    static_assert(std::is_same<safe::DefaultReadOnlyLockType<std::mutex>, std::lock_guard<std::mutex>>::value, \"Specialization did not work!\");\n    static_assert(std::is_same<safe::DefaultReadWriteLockType<std::mutex>, std::unique_lock<std::mutex>>::value, \"Specialization did not work!\");\n\n    static_assert(std::is_same<safe::DefaultReadOnlyLockType<std::recursive_mutex>, std::lock_guard<std::recursive_mutex>>::value, \"Specialization did not work!\");\n    static_assert(std::is_same<safe::DefaultReadWriteLockType<std::recursive_mutex>, std::unique_lock<std::recursive_mutex>>::value, \"Specialization did not work!\");\n}\n\nTEST_CASE(\"Second specialization overrides even the first one\")\n{\n    static_assert(std::is_same<safe::DefaultReadOnlyLockType<std::timed_mutex>, std::unique_lock<std::timed_mutex>>::value, \"Specialization did not work!\");\n    static_assert(std::is_same<safe::DefaultReadWriteLockType<std::timed_mutex>, std::lock_guard<std::timed_mutex>>::value, \"Specialization did not work!\");\n}\n"
  },
  {
    "path": "tests/test_main.cpp",
    "content": "#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN\n#include \"doctest/doctest.h\""
  },
  {
    "path": "tests/test_readme.cpp",
    "content": "// Copyright (c) 2019-2023 Louis-Charles Caron\n\n// This file is part of the safe library (https://github.com/LouisCharlesC/safe).\n\n// Use of this source code is governed by an MIT-style license that can be\n// found in the LICENSE file or at https://opensource.org/licenses/MIT.\n\n#include \"safe/safe.h\"\n\n#include <doctest/doctest.h>\n\n#include <cassert>\n#include <condition_variable>\n#include <iostream>\n#include <mutex>\n#include <string>\n\nTEST_CASE(\"Readme without safe example\")\n{\nstd::string foo; // do I need to lock a mutex to safely access this variable ?\nstd::string bar;\nstd::string baz; // what about this one ?\nstd::mutex fooMutex; // don't forget to change the name of this variable if foo's name changes!\nstd::mutex barMutex;\n\n{\n\tstd::lock_guard<std::mutex> lock(fooMutex); // is this the right mutex for what I am about to do ?\n\tbar = \"Hello, World!\"; // Hmm, did I just to something wrong ?\n}\n\nstd::cout << bar << std::endl; // unprotected access, is this intended ?\nstd::cout << baz << std::endl; // what about this access ?\n}\n\nTEST_CASE(\"Readme with safe example\")\n{\n#include \"safe/safe.h\"\nsafe::Safe<std::string> safeFoo; // std::string and mutex packaged together!\nsafe::Safe<std::string> safeBar;\nstd::string baz; // now you can see that this variable has no mutex\n\n{\n\tsafe::WriteAccess<safe::Safe<std::string>> foo(safeFoo); // this locks the mutex and gives you access to foo\n\t*foo = \"Hello, World!\"; // access the value using pointer dereference: * and ->\n}\n\nstd::cout << safeBar.unsafe() << std::endl; // unprotected access: clearly expressed!\nstd::cout << baz << std::endl; // all good this is just a string!\n}\n\nTEST_CASE(\"Readme ref and non ref\")\n{\nstd::mutex mutex;\nint value;\nsafe::Safe<int, std::mutex> valmut;\nsafe::Safe<int> valdef; // equivalent to the above, as the second template parameter defaults to std::mutex\nsafe::Safe<int &, std::mutex> refmut(value, safe::default_construct_mutex);\nsafe::Safe<int, std::mutex &> valref(42, mutex);\nsafe::Safe<int &, std::mutex &> refref(value, mutex);\nCHECK_EQ(&refmut.unsafe(), &value);\nCHECK_EQ(valref.unsafe(), 42);\nCHECK_EQ(&valref.mutex(), &mutex);\nCHECK_EQ(&refref.unsafe(), &value);\nCHECK_EQ(&refref.mutex(), &mutex);\n}\n\nTEST_CASE(\"Readme default construct mutex tag\")\n{\nstd::mutex mutex;\nsafe::Safe<std::pair<int,int>, std::mutex> bothDefault;            // mutex and value are default constructed\nsafe::Safe<std::pair<int,int>, std::mutex &> noDefault(42, 24, mutex); // mutex and value are initialized\nsafe::Safe<std::pair<int,int>, std::mutex &> valueDefault(mutex);  // mutex is initialized, and value is default constructed\nsafe::Safe<std::pair<int,int>, std::mutex> mutexDefault(42, 24); // mutex is default constructed, and value is initialized\nsafe::Safe<std::pair<int,int>, std::mutex> mutexDefaultTag(42, 24, safe::default_construct_mutex); // mutex is default constructed, and value is initialized\nCHECK_EQ(noDefault.unsafe(), std::pair<int,int>(42, 24));\nCHECK_EQ(&noDefault.mutex(), &mutex);\nCHECK_EQ(&valueDefault.mutex(), &mutex);\nCHECK_EQ(mutexDefaultTag.unsafe(), std::pair<int,int>(42, 24));\n}\n\nTEST_CASE(\"Readme equivalent ways to construct lock\")\n{\nsafe::Safe<int> safeValue;\n{\nconst safe::ReadAccess<safe::Safe<int>> value(safeValue);\n}\n{\nconst safe::Safe<int>::ReadAccess<> value(safeValue); // the empty <> are unfortunately necessary.\n}\n#if __cplusplus >= 201703L\n{\nconst auto value = safeValue.readLock(); // nicer, but only with C++17 and later\n}\n#endif\n{\n    const auto value = safeValue.readLock<std::unique_lock>(); // ok even pre-C++17, using a std::unique_lock rather than the default std::lock_guard\n}\n{\n    const safe::Safe<int>::ReadAccess<std::unique_lock> value(safeValue); // equivalent to the above\n}\n// All of the above exist in read-write versions, simply replace \"read\" by \"write\" (and remove the const).\n}\n\nTEST_CASE(\"Readme already locked mutex\")\n{\nstd::mutex aLockedMutex;\naLockedMutex.lock();\nsafe::Safe<int, std::mutex&> safeValue(aLockedMutex); // given a Safe object with an already locked mutex.\n\n// Because the mutex is already locked, you need to pass the std::adopt_lock tag to std::lock_guard when you construct your Access object.\n// No matter how you get your Access objects, you can pass arguments to the lock's constructor.\n// Again, all the lines below are equivalent:\n{\nsafe::WriteAccess<safe::Safe<int, std::mutex&>> value(safeValue, std::adopt_lock);\n}\naLockedMutex.lock();\n{\nsafe::Safe<int, std::mutex&>::WriteAccess<> value(safeValue, std::adopt_lock);\n}\n#if __cplusplus >= 201703L\naLockedMutex.lock();\n{\nauto value = safeValue.writeLock(std::adopt_lock); // again, only in C++17\n}\n#endif\naLockedMutex.lock();\n{\nauto value = safeValue.writeLock<std::unique_lock>(std::adopt_lock); // using a std::unique_lock still works\n}\n}\n\nTEST_CASE(\"Readme timed mutex\")\n{\n// Here's a safe int using a timed_mutex\nsafe::Safe<int, std::timed_mutex> safeValue;\n\n// Pass in a time-out when you lock the mutex, and it will try to lock for that duration!\n// safe does not do anything here, std::unique_lock does it all. safe simply forwards any argument\n// it gets, and when std::unique_lock receives a std::duration, it try to lock before giving up.\nauto value = safeValue.writeLock<std::unique_lock>(std::chrono::seconds(1));\n// You can reach the lock through the Access object and check whether or not the locking was successful:\nassert(value.lock.owns_lock());\n}\n\nTEST_CASE(\"Readme legacy\")\n{\n    std::mutex lousyMutex;\n    int unsafeValue;\n\n    // Wrap the existing variables\n    safe::Safe<int &, std::mutex &> safeValue(unsafeValue, lousyMutex);\n    // do not use lousyMutex and unsafeValue directly from here on!\n}\n\nTEST_CASE(\"Readme condition variable\")\n{\n    std::condition_variable cv;\n    safe::Safe<int> safeValue;\n    safe::Safe<int>::WriteAccess<std::unique_lock> value(safeValue);\n    cv.wait(value.lock, []() { return true; });\n    CHECK_EQ(&*value, &safeValue.unsafe());\n    CHECK_EQ(value.lock.mutex(), &safeValue.mutex());\n}\n"
  },
  {
    "path": "tests/test_safe.cpp",
    "content": "// Copyright (c) 2018-2023 Louis-Charles Caron\n\n// This file is part of the safe library (https://github.com/LouisCharlesC/safe).\n\n// Use of this source code is governed by an MIT-style license that can be\n// found in the LICENSE file or at https://opensource.org/licenses/MIT.\n\n#include \"safe/safe.h\"\n\n#include <doctest/doctest.h>\n\n#include <mutex>\n\nTEST_CASE(\"If mutex is constructible, construct with the last argument even it if could construct the value\")\n{\n    safe::Safe<int, int> weirdSafeInt(42);\n    CHECK_EQ(weirdSafeInt.unsafe(), 0);\n    CHECK_EQ(weirdSafeInt.mutex(), 42);\n}\n\nTEST_CASE(\"If tag is passed, do not use the argument to construct the mutex\")\n{\n    safe::Safe<int, int> weirdSafeInt(42, safe::default_construct_mutex);\n    CHECK_EQ(weirdSafeInt.unsafe(), 42);\n    CHECK_EQ(weirdSafeInt.mutex(), 0);\n}\n\nTEST_CASE(\"If tag is passed, do not use the argument to construct the mutex\")\n{\n    safe::Safe<int, std::mutex> weirdSafeInt(42);\n    CHECK_EQ(weirdSafeInt.unsafe(), 42);\n}\n"
  }
]