diff --git a/.clang-format b/.clang-format index b76a090..061c7da 100644 --- a/.clang-format +++ b/.clang-format @@ -1,6 +1,31 @@ +Standard: c++11 BasedOnStyle: Google IndentWidth: 4 Language: Cpp PointerAlignment: Left BreakBeforeBraces: Stroustrup ColumnLimit: 120 +IncludeCategories: + # Header file associated with cpp file (default) + + # gtest/gmock headers + - Regex: "^" + Priority: 1 + - Regex: "^" + Priority: 1 + + # Local/private headers included with "" + - Regex: '^"([^/]+/)*[^/]+\.hpp"$' + Priority: 2 + - Regex: '^"([^/]+/)*[^/]+\.h"$' + Priority: 2 + + # External headers included with <> + - Regex: '^<.*\.hpp>$' + Priority: 3 + - Regex: '^<.*\.h>$' + Priority: 3 + + # Standard library headers + - Regex: "^<.*>$" + Priority: 4 diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..5350c9b --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,37 @@ +FROM ubuntu:24.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y \ + bash-completion \ + build-essential \ + cmake \ + clang \ + clangd \ + clang-format \ + clang-tidy \ + curl \ + doxygen \ + gdb \ + git \ + graphviz \ + lcov \ + lldb \ + llvm \ + nano \ + software-properties-common \ + sudo \ + unzip \ + wget \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +ARG USERNAME=ubuntu +RUN passwd -d ${USERNAME} \ + && mkdir -p /home/${USERNAME}/.cache && chown -R ${USERNAME}:${USERNAME} /home/${USERNAME}/.cache \ + && SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/home/${USERNAME}/.cache/.bash_history" \ + && echo "$SNIPPET" >> "/home/${USERNAME}/.bashrc" + +USER $USERNAME + +WORKDIR /workspace diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..e6cfd58 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,25 @@ +{ + "name": "C++ Channel", + "dockerFile": "Dockerfile", + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.cmake-tools", + "streetsidesoftware.code-spell-checker", + "DavidAnson.vscode-markdownlint", + "xaver.clang-format", + "twxs.cmake", + "fredericbonnet.cmake-test-adapter", + "llvm-vs-code-extensions.vscode-clangd", + "vadimcn.vscode-lldb" + ] + } + }, + "mounts": [ + "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached", + "source=${env:HOME}/.ssh,target=/home/ubuntu/.ssh,type=bind,consistency=cached", + "source=cache,target=/home/ubuntu/.cache,type=volume" + ], + "workspaceFolder": "/workspace", + "remoteUser": "ubuntu" +} diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 501bb9f..1c7e4f5 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -5,13 +5,17 @@ on: branches: - master paths-ignore: + - '.devcontainer/**' - '.vscode/**' - 'LICENSE' + - 'Makefile' - '**/README.md' pull_request: paths-ignore: + - '.devcontainer/**' - '.vscode/**' - 'LICENSE' + - 'Makefile' - '**/README.md' workflow_dispatch: @@ -129,7 +133,7 @@ jobs: - uses: actions/checkout@v4 - name: Run Clang Format - run: clang-format --dry-run --Werror $(find include -type f) + run: clang-format --dry-run --Werror $(find include tests examples -type f -name *.*pp) clang-tidy: name: Clang Tidy diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 305208a..933c580 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,11 +1,5 @@ { "recommendations": [ - "ms-vscode.cpptools", - "ms-vscode.cpptools-extension-pack", - "xaver.clang-format", - "twxs.cmake", - "fredericbonnet.cmake-test-adapter", - "ms-vscode.cmake-tools", - "notskm.clang-tidy", + "ms-vscode-remote.remote-containers", ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index ef57131..b10c7f7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,27 +2,15 @@ "version": "0.2.0", "configurations": [ { - "name": "(gdb) Launch", - "type": "cppdbg", + "name": "Launch (LLDB)", + "type": "lldb", "request": "launch", "program": "${command:cmake.launchTargetPath}", - "args": [], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [ - { - "name": "PATH", - "value": "${env:PATH}:${command:cmake.getLaunchTargetDirectory}" - } + "args": [ + "--gtest_color=yes" ], - "MIMode": "gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] + "cwd": "${workspaceFolder}", + "terminal": "integrated" } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 507f7f5..38bae1b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,14 +6,20 @@ "-DCPP_CHANNEL_SANITIZERS=ON", "-DCPP_CHANNEL_SANITIZE_THREADS=OFF", "-DCMAKE_CXX_STANDARD=11", - "-DCMAKE_INSTALL_PREFIX=${workspaceFolder}/install", + "-DCMAKE_INSTALL_PREFIX=${workspaceFolder}/install" ], - "clang-tidy.fixOnSave": false, - "clang-tidy.lintOnSave": false, "editor.formatOnSave": true, "clang-format.executable": "clang-format", "clang-format.style": "file", "editor.defaultFormatter": "xaver.clang-format", "files.insertFinalNewline": true, - "files.trimFinalNewlines": true + "files.trimFinalNewlines": true, + "[jsonc]": { + "editor.defaultFormatter": "vscode.json-language-features" + }, + "clangd.arguments": [ + "--compile-commands-dir=${workspaceFolder}/build", + "--background-index", + "--clang-tidy" + ] } diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..aae49df --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +all: + +bench: + - sudo cpupower frequency-set --governor performance + + mkdir -p build/bench && cd build/bench \ + && cmake ../.. -DCMAKE_BUILD_TYPE=Release -DCPP_CHANNEL_BUILD_TESTS=ON \ + && cmake --build . --config Release --target channel_benchmark -j \ + && ./tests/channel_benchmark + + - sudo cpupower frequency-set --governor powersave + +coverage: + mkdir -p build/coverage && cd build/coverage \ + && cmake ../.. -DCMAKE_BUILD_TYPE=Debug -DCPP_CHANNEL_BUILD_TESTS=ON -DCPP_CHANNEL_COVERAGE=ON \ + && cmake --build . --config Debug --target channel_tests -j \ + && ctest -C Debug --verbose -L channel_tests --output-on-failure -j \ + && lcov --capture --directory . --output-file coverage.info --ignore-errors mismatch \ + && lcov --remove coverage.info "*/usr/*" -o coverage.info \ + && lcov --remove coverage.info "*/gtest/*" -o coverage.info \ + && genhtml coverage.info --output-directory coverage-report \ + && cd coverage-report \ + && python3 -m http.server 8000 + +doc: + doxygen + cd docs && python3 -m http.server 8000 diff --git a/README.md b/README.md index 50b5cf1..8a52d41 100644 --- a/README.md +++ b/README.md @@ -52,9 +52,9 @@ Exceptions: * Range-based for loop supported. * Close to prevent pushing and stop waiting to fetch. * Integrates with some of the STL algorithms. Eg: - * `std::move(ch.begin(), ch.end(), ...)` - * `std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan))`. - * `std::copy_if(chan.begin(), chan.end(), ...);` + * `std::move(ch.begin(), ch.end(), ...)` + * `std::transform(input_chan.begin(), input_chan.end(), msd::back_inserter(output_chan))`. + * `std::copy_if(chan.begin(), chan.end(), ...);` ## Installation @@ -63,6 +63,7 @@ Choose one of the methods: * Copy the [include](https://github.com/andreiavrammsd/cpp-channel/tree/master/include) directory into your project and add it to your include path. * [CMake FetchContent](https://github.com/andreiavrammsd/cpp-channel/tree/master/examples/cmake-project) * [CMake install](https://cmake.org/cmake/help/latest/command/install.html) - choose a [version](https://github.com/andreiavrammsd/cpp-channel/releases), then run: + ```shell VERSION=X.Y.Z \ && wget https://github.com/andreiavrammsd/cpp-channel/archive/refs/tags/v$VERSION.zip \ @@ -72,6 +73,7 @@ VERSION=X.Y.Z \ && cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local \ && sudo cmake --install . ``` + * [Bazel](https://github.com/andreiavrammsd/cpp-channel/tree/master/examples/bazel-project) ## Usage @@ -84,16 +86,17 @@ VERSION=X.Y.Z \ int main() { msd::channel chan; // Unbuffered - int in = 1; - int out = 0; - // Send to channel - chan << in; + chan << 1 << 2; // Read from channel - chan >> out; + int first{}; + int second{}; + + chan >> first >> second; - assert(out == 1); + assert(first == 1); + assert(second == 2); } ``` @@ -178,6 +181,6 @@ See [examples](https://github.com/andreiavrammsd/cpp-channel/tree/master/example * In some cases, the integration with some STL algorithms does not compile with MSVC. See the [Transform test](https://github.com/andreiavrammsd/cpp-channel/blob/master/tests/channel_test.cpp). -
+--- Developed with [CLion](https://www.jetbrains.com/?from=serializer) and [Visual Studio Code](https://code.visualstudio.com/). diff --git a/examples/.clang-tidy b/examples/.clang-tidy new file mode 100644 index 0000000..2b94fca --- /dev/null +++ b/examples/.clang-tidy @@ -0,0 +1,10 @@ +InheritParentConfig: true + +Checks: > + -cppcoreguidelines-avoid-magic-numbers, + -readability-magic-numbers, + -fuchsia-default-arguments-calls + +CheckOptions: + - { key: readability-identifier-naming.ClassCase, value: CamelCase } + - { key: readability-identifier-length.MinimumVariableNameLength, value: '1' } diff --git a/examples/basic.cpp b/examples/basic.cpp index f0fa8b9..873c664 100644 --- a/examples/basic.cpp +++ b/examples/basic.cpp @@ -1,26 +1,20 @@ -#include +#include -#include "msd/channel.hpp" +#include int main() { - msd::channel ch{10}; - - int in{}; - - in = 1; - ch << in; + constexpr std::size_t capacity = 10; + msd::channel channel{capacity}; - in = 2; - ch << in; + channel << 1; - in = 3; - ch << in; + channel << 2 << 3; - for (auto out : ch) { + for (auto out : channel) { std::cout << out << '\n'; - if (ch.empty()) { + if (channel.empty()) { break; } } diff --git a/examples/bazel-project/MODULE.bazel b/examples/bazel-project/MODULE.bazel index a3a4292..9277d97 100644 --- a/examples/bazel-project/MODULE.bazel +++ b/examples/bazel-project/MODULE.bazel @@ -20,7 +20,7 @@ archive_override( "echo '" + msd_channel_module + "' > MODULE.bazel", "echo '" + msd_channel_build + "' > BUILD.bazel", ], - urls = ["https://github.com/andreiavrammsd/cpp-channel/archive/refs/tags/v1.2.1.zip"], - strip_prefix = "cpp-channel-1.2.1", - integrity = "sha256-fGew8OKF/gWyjkupXFiu+nPxTHgOfI2Ixs8wSxv9+gA=", + urls = ["https://github.com/andreiavrammsd/cpp-channel/archive/refs/tags/v1.3.0.zip"], + strip_prefix = "cpp-channel-1.3.0", + integrity = "sha256-qHLwQP0jeLguWgUxuOHmZ6kXiRCuDYmIUBfl1R1bF2E=", ) diff --git a/examples/bazel-project/README.md b/examples/bazel-project/README.md index ecee583..2755e9c 100644 --- a/examples/bazel-project/README.md +++ b/examples/bazel-project/README.md @@ -1,8 +1,9 @@ # Bazel project -Example of using C++ Channel in a project with Bazel. +Example of using C++ Channel in a project with Bazel. ## Requirements + * C++11 compiler * Bazel diff --git a/examples/bazel-project/src/main.cpp b/examples/bazel-project/src/main.cpp index 336091b..0b97f01 100644 --- a/examples/bazel-project/src/main.cpp +++ b/examples/bazel-project/src/main.cpp @@ -1,12 +1,12 @@ +#include + #include #include -#include "msd/channel.hpp" - int main() { - constexpr std::size_t kChannelSize = 10; - msd::channel chan{kChannelSize}; + constexpr std::size_t channel_size = 10; + msd::channel chan{channel_size}; int input{}; diff --git a/examples/close.cpp b/examples/close.cpp index 967436a..04a6a03 100644 --- a/examples/close.cpp +++ b/examples/close.cpp @@ -1,52 +1,52 @@ +#include + #include #include #include -#include "msd/channel.hpp" - int main() { msd::channel channel{}; // Write data on the channel until it's closed - auto input = [](msd::channel& ch, int ms) { - static int i = 0; + const auto input = [](msd::channel& chan, int time_ms) { + static int inc = 0; while (true) { - if (ch.closed()) { + if (chan.closed()) { break; } - ch << ++i; - std::cout << "in: " << i << "\n"; + chan << ++inc; + std::cout << "in: " << inc << "\n"; - std::this_thread::sleep_for(std::chrono::milliseconds{ms}); + std::this_thread::sleep_for(std::chrono::milliseconds{time_ms}); } std::cout << "exit input\n"; }; - auto input_future = std::async(input, std::ref(channel), 10); + const auto input_future = std::async(input, std::ref(channel), 10); // Close the channel after some time - auto timeout = [](msd::channel& ch, int ms) { - std::this_thread::sleep_for(std::chrono::milliseconds{ms}); - ch.close(); + const auto timeout = [](msd::channel& chan, int time_ms) { + std::this_thread::sleep_for(std::chrono::milliseconds{time_ms}); + chan.close(); std::cout << "exit timeout\n"; }; auto timeout_future = std::async(timeout, std::ref(channel), 100); // Display all the data from the channel // When the channel is closed and empty, the iteration will end - auto write = [](msd::channel& ch, int ms) { - for (auto out : ch) { + const auto write = [](msd::channel& chan, int time_ms) { + for (auto out : chan) { std::cout << "out: " << out << "\n"; - std::this_thread::sleep_for(std::chrono::milliseconds{ms}); + std::this_thread::sleep_for(std::chrono::milliseconds{time_ms}); } std::cout << "exit write\n"; }; - auto write_future1 = std::async(write, std::ref(channel), 1); - auto write_future2 = std::async(write, std::ref(channel), 100); + const auto write_future1 = std::async(write, std::ref(channel), 1); + const auto write_future2 = std::async(write, std::ref(channel), 100); input_future.wait(); timeout_future.wait(); diff --git a/examples/cmake-project/CMakeLists.txt b/examples/cmake-project/CMakeLists.txt index 9cbad26..74b65b5 100644 --- a/examples/cmake-project/CMakeLists.txt +++ b/examples/cmake-project/CMakeLists.txt @@ -11,7 +11,7 @@ add_executable(cmake_project src/main.cpp) include(FetchContent) if (NOT channel_POPULATED) - FetchContent_Declare(channel URL https://github.com/andreiavrammsd/cpp-channel/archive/v1.2.1.zip DOWNLOAD_EXTRACT_TIMESTAMP TRUE) + FetchContent_Declare(channel URL https://github.com/andreiavrammsd/cpp-channel/archive/v1.3.0.zip DOWNLOAD_EXTRACT_TIMESTAMP TRUE) FetchContent_Populate(channel) include_directories(${channel_SOURCE_DIR}/include) # OR diff --git a/examples/cmake-project/README.md b/examples/cmake-project/README.md index b9152ec..2e58b27 100644 --- a/examples/cmake-project/README.md +++ b/examples/cmake-project/README.md @@ -1,8 +1,9 @@ # CMake project -Example of using C++ Channel in a project with CMake. +Example of using C++ Channel in a project with CMake. ## Requirements + * C++17 compiler * CMake 3.12+ diff --git a/examples/cmake-project/src/main.cpp b/examples/cmake-project/src/main.cpp index c75cb87..1ede10d 100644 --- a/examples/cmake-project/src/main.cpp +++ b/examples/cmake-project/src/main.cpp @@ -1,38 +1,37 @@ #include #include +#include #include -#include "msd/channel.hpp" - using chan = msd::channel; // Continuously write input data on the incoming channel -[[noreturn]] void GetIncoming(chan& incoming) +[[noreturn]] void get_incoming(chan& incoming) { while (true) { - static int i = 0; - incoming << ++i; + static int inc = 0; + incoming << ++inc; } } // Time-consuming operation for each input value -int Add(int in, int value) +int add(int input, int value) { std::this_thread::sleep_for(std::chrono::milliseconds{500}); - return in + value; + return input + value; } // Read data from the incoming channel, process it, then send it on the outgoing channel -void Transform(chan& incoming, chan& outgoing) +void transform(chan& incoming, chan& outgoing) { - for (auto in : incoming) { - auto result = Add(in, 2); + for (auto input : incoming) { + auto result = add(input, 2); outgoing << result; } } // Read result of processing from the outgoing channel and save it -void WriteOutgoing(chan& outgoing) +void write_outgoing(chan& outgoing) { for (auto out : outgoing) { std::cout << out << '\n'; @@ -51,15 +50,15 @@ int main() chan outgoing; // One thread for incoming data - auto incoming_thread = std::thread{GetIncoming, std::ref(incoming)}; + auto incoming_thread = std::thread{get_incoming, std::ref(incoming)}; // One thread for outgoing data - auto outgoing_thread = std::thread{WriteOutgoing, std::ref(outgoing)}; + auto outgoing_thread = std::thread{write_outgoing, std::ref(outgoing)}; // Multiple threads to process incoming data and send to outgoing std::vector process_threads; for (std::size_t i = 0U; i < threads; ++i) { - process_threads.emplace_back(Transform, std::ref(incoming), std::ref(outgoing)); + process_threads.emplace_back(transform, std::ref(incoming), std::ref(outgoing)); } // Join all threads @@ -67,7 +66,7 @@ int main() outgoing_thread.join(); - for (auto& th : process_threads) { - th.join(); + for (auto& thread : process_threads) { + thread.join(); } } diff --git a/examples/merge_channels.cpp b/examples/merge_channels.cpp index a6cf195..26a80ec 100644 --- a/examples/merge_channels.cpp +++ b/examples/merge_channels.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -5,8 +7,6 @@ #include #include -#include "msd/channel.hpp" - int main() { msd::channel input_chan{30}; diff --git a/examples/move.cpp b/examples/move.cpp index a47d8ea..4aa2f99 100644 --- a/examples/move.cpp +++ b/examples/move.cpp @@ -1,6 +1,6 @@ -#include +#include -#include "msd/channel.hpp" +#include class Data final { int i_{}; @@ -38,20 +38,20 @@ class Data final { int main() { - msd::channel ch{10}; + msd::channel chan{10}; auto in1 = Data{1}; - ch << in1; + chan << in1; - ch << Data{2}; + chan << Data{2}; auto in3 = Data{3}; - ch << std::move(in3); + chan << std::move(in3); - for (auto out : ch) { + for (const auto& out : chan) { std::cout << out.getI() << '\n'; - if (ch.empty()) { + if (chan.empty()) { break; } } diff --git a/examples/multithreading.cpp b/examples/multithreading.cpp index 49a6593..40324e3 100644 --- a/examples/multithreading.cpp +++ b/examples/multithreading.cpp @@ -1,11 +1,10 @@ +#include + #include #include #include #include #include -#include - -#include "msd/channel.hpp" int main() { @@ -14,10 +13,10 @@ int main() msd::channel channel{threads}; // Read - const auto out = [](msd::channel& ch, std::size_t i) { - for (auto number : ch) { + const auto out = [](msd::channel& chan, const std::size_t value) { + for (auto number : chan) { std::stringstream stream; - stream << number << " from thread: " << i << '\n'; + stream << number << " from thread: " << value << '\n'; std::cout << stream.str(); std::this_thread::sleep_for(std::chrono::milliseconds(500)); } @@ -29,14 +28,14 @@ int main() } // Write - const auto in = [](msd::channel& ch) { + const auto input = [](msd::channel& chan) { while (true) { - static std::int64_t i = 0; - ch << ++i; + static std::int64_t value = 0; + chan << ++value; } }; - auto write = std::thread{in, std::ref(channel)}; + auto write = std::thread{input, std::ref(channel)}; // Join all threads for (std::size_t i = 0U; i < threads; ++i) { diff --git a/examples/multithreading_static_channel.cpp b/examples/multithreading_static_channel.cpp index 9e93359..fb3b83b 100644 --- a/examples/multithreading_static_channel.cpp +++ b/examples/multithreading_static_channel.cpp @@ -1,11 +1,11 @@ +#include + #include #include #include #include #include -#include "msd/static_channel.hpp" - int main() { msd::static_channel chan{}; // always buffered diff --git a/examples/streaming.cpp b/examples/streaming.cpp index 49b565d..d7f689a 100644 --- a/examples/streaming.cpp +++ b/examples/streaming.cpp @@ -1,13 +1,12 @@ +#include + #include #include #include #include #include -#include #include -#include "msd/channel.hpp" - int main() { using messages = msd::channel; @@ -16,16 +15,16 @@ int main() messages channel{threads}; // Continuously get some data on multiple threads and send it all to a channel - const auto in = [](messages& ch, std::size_t thread, std::chrono::milliseconds pause) { - thread_local static std::size_t i = 0U; + const auto input = [](messages& chan, std::size_t thread, std::chrono::milliseconds pause) { + thread_local static std::size_t inc = 0U; while (true) { - if (ch.closed()) { + if (chan.closed()) { return; } - ++i; - ch << std::string{std::to_string(i) + " from: " + std::to_string(thread)}; + ++inc; + chan << std::string{std::to_string(inc) + " from: " + std::to_string(thread)}; std::this_thread::sleep_for(pause); } @@ -33,19 +32,19 @@ int main() std::vector> in_futures; for (std::size_t i = 0U; i < threads; ++i) { - in_futures.push_back(std::async(in, std::ref(channel), i, std::chrono::milliseconds{500U})); + in_futures.push_back(std::async(input, std::ref(channel), i, std::chrono::milliseconds{500})); } // Stream incoming data to a destination - const auto out = [](messages& ch, std::ostream& stream, const std::string& separator) { - std::move(ch.begin(), ch.end(), std::ostream_iterator(stream, separator.c_str())); + const auto out = [](messages& chan, std::ostream& stream, const std::string& separator) { + std::move(chan.begin(), chan.end(), std::ostream_iterator(stream, separator.c_str())); }; const auto out_future = std::async(out, std::ref(channel), std::ref(std::cout), "\n"); // Close the channel after some time - const auto timeout = [](messages& ch, std::chrono::milliseconds after) { + const auto timeout = [](messages& chan, std::chrono::milliseconds after) { std::this_thread::sleep_for(after); - ch.close(); + chan.close(); }; const auto timeout_future = std::async(timeout, std::ref(channel), std::chrono::milliseconds{3000U}); diff --git a/include/msd/channel.hpp b/include/msd/channel.hpp index 1c0a6e1..36f89a3 100644 --- a/include/msd/channel.hpp +++ b/include/msd/channel.hpp @@ -3,16 +3,16 @@ #ifndef MSD_CHANNEL_CHANNEL_HPP_ #define MSD_CHANNEL_CHANNEL_HPP_ +#include "blocking_iterator.hpp" +#include "nodiscard.hpp" +#include "storage.hpp" + #include #include #include #include #include -#include "blocking_iterator.hpp" -#include "nodiscard.hpp" -#include "storage.hpp" - /** @file */ namespace msd { diff --git a/include/msd/static_channel.hpp b/include/msd/static_channel.hpp index fefd46e..25e7f31 100644 --- a/include/msd/static_channel.hpp +++ b/include/msd/static_channel.hpp @@ -3,11 +3,11 @@ #ifndef MSD_CHANNEL_STATIC_CHANNEL_HPP_ #define MSD_CHANNEL_STATIC_CHANNEL_HPP_ -#include - #include "channel.hpp" #include "storage.hpp" +#include + /** @file */ namespace msd { diff --git a/include/msd/storage.hpp b/include/msd/storage.hpp index e95d277..d316081 100644 --- a/include/msd/storage.hpp +++ b/include/msd/storage.hpp @@ -3,13 +3,13 @@ #ifndef MSD_CHANNEL_STORAGE_HPP_ #define MSD_CHANNEL_STORAGE_HPP_ +#include "nodiscard.hpp" + #include #include #include #include -#include "nodiscard.hpp" - /** @file */ namespace msd { diff --git a/tests/.clang-tidy b/tests/.clang-tidy new file mode 100644 index 0000000..2b94fca --- /dev/null +++ b/tests/.clang-tidy @@ -0,0 +1,10 @@ +InheritParentConfig: true + +Checks: > + -cppcoreguidelines-avoid-magic-numbers, + -readability-magic-numbers, + -fuchsia-default-arguments-calls + +CheckOptions: + - { key: readability-identifier-naming.ClassCase, value: CamelCase } + - { key: readability-identifier-length.MinimumVariableNameLength, value: '1' } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5081d40..44281f7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -36,6 +36,12 @@ function(package_add_test TESTNAME) if (CPP_CHANNEL_SANITIZERS) target_link_libraries(${TESTNAME} -lubsan) target_compile_options(${TESTNAME} PRIVATE -fsanitize=undefined) + + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(COVERAGE_FLAGS -fprofile-instr-generate -fcoverage-mapping) + target_link_libraries(${TESTNAME} ${COVERAGE_FLAGS}) + target_compile_options(${TESTNAME} PRIVATE ${COVERAGE_FLAGS}) + endif() endif () if (CPP_CHANNEL_SANITIZE_THREADS) diff --git a/tests/blocking_iterator_test.cpp b/tests/blocking_iterator_test.cpp index fb943b3..8e92fae 100644 --- a/tests/blocking_iterator_test.cpp +++ b/tests/blocking_iterator_test.cpp @@ -2,12 +2,12 @@ #include +#include + #include #include #include -#include "msd/channel.hpp" - TEST(BlockingIteratorTest, Traits) { using type = int; diff --git a/tests/channel_benchmark.cpp b/tests/channel_benchmark.cpp index 197d03a..c57df59 100644 --- a/tests/channel_benchmark.cpp +++ b/tests/channel_benchmark.cpp @@ -1,9 +1,9 @@ +#include "msd/channel.hpp" + #include #include -#include "msd/channel.hpp" - /** Results on release build with CPU scaling disabled c++ (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0 @@ -17,20 +17,20 @@ L2 Unified 256 KiB (x4) L3 Unified 8192 KiB (x1) Load Average: 2.65, 1.61, 1.50 - ---------------------------------------------------------------------- - Benchmark Time CPU Iterations - ---------------------------------------------------------------------- - BM_ChannelWithQueueStorage 42602 ns 42598 ns 16407 - BM_ChannelWithVectorStorage 42724 ns 42723 ns 16288 - BM_ChannelWithArrayStorage 51332 ns 51328 ns 11776 + ------------------------------------------------------------------------------ + Benchmark Time CPU Iterations + ------------------------------------------------------------------------------ + bm_channel_with_queue_storage 42602 ns 42598 ns 16407 + bm_channel_with_vector_storage 42724 ns 42723 ns 16288 + bm_channel_with_vector_storage 51332 ns 51328 ns 11776 */ -static void BM_ChannelWithQueueStorage(benchmark::State& state) +static void bm_channel_with_queue_storage(benchmark::State& state) { msd::channel> channel{10}; std::string input(1000000, 'x'); - std::string out = ""; + std::string out{}; out.resize(input.size()); for (auto _ : state) { @@ -39,9 +39,9 @@ static void BM_ChannelWithQueueStorage(benchmark::State& state) } } -BENCHMARK(BM_ChannelWithQueueStorage); +BENCHMARK(bm_channel_with_queue_storage); -static void BM_ChannelWithVectorStorage(benchmark::State& state) +static void bm_channel_with_vector_storage(benchmark::State& state) { msd::channel> channel{10}; @@ -55,14 +55,14 @@ static void BM_ChannelWithVectorStorage(benchmark::State& state) } } -BENCHMARK(BM_ChannelWithVectorStorage); +BENCHMARK(bm_channel_with_vector_storage); -static void BM_ChannelWithArrayStorage(benchmark::State& state) +static void bm_channel_with_array_storage(benchmark::State& state) { msd::channel> channel{}; std::string input(1000000, 'x'); - std::string out = ""; + std::string out{}; out.resize(input.size()); for (auto _ : state) { @@ -71,6 +71,6 @@ static void BM_ChannelWithArrayStorage(benchmark::State& state) } } -BENCHMARK(BM_ChannelWithArrayStorage); +BENCHMARK(bm_channel_with_array_storage); BENCHMARK_MAIN(); diff --git a/tests/channel_test.cpp b/tests/channel_test.cpp index c541eb6..964a29c 100644 --- a/tests/channel_test.cpp +++ b/tests/channel_test.cpp @@ -2,6 +2,8 @@ #include +#include "msd/static_channel.hpp" + #include #include #include @@ -12,8 +14,6 @@ #include #include -#include "msd/static_channel.hpp" - TEST(ChannelTest, Traits) { using type = int; @@ -118,31 +118,31 @@ TEST(ChannelTest, PushAndFetchWithBufferedChannel) TEST(ChannelTest, PushAndFetchMultiple) { - msd::channel channel; + msd::channel channel; - int a = 1; - const int b = 3; - channel << a << 2 << b << std::move(a); + std::string non_const_value{"1"}; + const std::string const_value{"3"}; + channel << non_const_value << std::string{"2"} << const_value << std::move(non_const_value); - int out = 0; - int out2 = 0; + std::string out{}; + std::string out2{}; channel >> out; - EXPECT_EQ(1, out); + EXPECT_EQ("1", out); channel >> out; - EXPECT_EQ(2, out); + EXPECT_EQ("2", out); channel >> out >> out2; - EXPECT_EQ(3, out); - EXPECT_EQ(1, out2); + EXPECT_EQ("3", out); + EXPECT_EQ("1", out2); } TEST(ChannelTest, PushByMoveAndFetch) { msd::channel channel; - std::string in = "abc"; + std::string in{"abc"}; channel << std::move(in); channel << std::string{"def"}; @@ -183,18 +183,18 @@ TEST(ChannelTest, empty) TEST(ChannelTest, close) { - msd::channel channel; + msd::channel channel; EXPECT_FALSE(channel.closed()); - int in = 1; + std::string in{"1"}; channel << in; channel.close(); EXPECT_TRUE(channel.closed()); - int out = 0; + std::string out{}; channel >> out; - EXPECT_EQ(1, out); + EXPECT_EQ("1", out); EXPECT_NO_THROW(channel >> out); EXPECT_THROW(channel << in, msd::closed_channel); @@ -234,7 +234,7 @@ TEST(ChannelTest, Multithreading) { const int numbers = 10000; const std::int64_t expected = 50005000; - constexpr std::size_t kThreadsToReadFrom = 100; + constexpr std::size_t threads_to_read_from = 100; msd::channel channel{10}; @@ -246,7 +246,7 @@ TEST(ChannelTest, Multithreading) std::mutex mtx_wait{}; std::condition_variable cond_wait{}; - std::atomic wait_counter{kThreadsToReadFrom}; + std::atomic wait_counter{threads_to_read_from}; auto worker = [&] { // Wait until there is data on the channel @@ -266,7 +266,7 @@ TEST(ChannelTest, Multithreading) }; std::vector threads{}; - for (std::size_t i = 0U; i < kThreadsToReadFrom; ++i) { + for (std::size_t i = 0U; i < threads_to_read_from; ++i) { threads.emplace_back(worker); } @@ -294,9 +294,9 @@ TEST(ChannelTest, ReadWriteClose) { const int numbers = 10000; const std::int64_t expected_sum = 50005000; - constexpr std::size_t kThreadsToReadFrom = 20; + constexpr std::size_t threads_to_read_from = 20; - msd::channel channel{kThreadsToReadFrom}; + msd::channel channel{threads_to_read_from}; std::atomic sum{0}; std::atomic nums{0}; @@ -308,7 +308,7 @@ TEST(ChannelTest, ReadWriteClose) }); std::vector readers; - for (std::size_t i = 0; i < kThreadsToReadFrom; ++i) { + for (std::size_t i = 0; i < threads_to_read_from; ++i) { readers.emplace_back([&channel, &sum, &nums]() { while (true) { int value = 0; @@ -332,27 +332,30 @@ TEST(ChannelTest, ReadWriteClose) EXPECT_EQ(nums, numbers); } -class movable_only { +class MovableOnly { public: - explicit movable_only(int value) : value_{value} {} + explicit MovableOnly(int value) : value_{value} {} - movable_only() = default; + MovableOnly() = default; - movable_only(const movable_only&) + MovableOnly(const MovableOnly&) { std::cout << "Copy constructor should not be called"; std::abort(); } - movable_only(movable_only&& other) noexcept : value_{std::move(other.value_)} { other.value_ = 0; } + MovableOnly(MovableOnly&& other) noexcept : value_{other.value_} { other.value_ = 0; } - movable_only& operator=(const movable_only&) + MovableOnly& operator=(const MovableOnly& other) { + if (this == &other) { + return *this; + } std::cout << "Copy assignment should not be called"; std::abort(); } - movable_only& operator=(movable_only&& other) noexcept + MovableOnly& operator=(MovableOnly&& other) noexcept { if (this != &other) { value_ = other.value_; @@ -362,9 +365,9 @@ class movable_only { return *this; } - int getValue() const { return value_; } + int get_value() const { return value_; } - virtual ~movable_only() = default; + virtual ~MovableOnly() = default; private: int value_{0}; @@ -377,20 +380,20 @@ TEST(ChannelTest, Transform) std::atomic sum{0}; std::atomic nums{0}; - msd::channel input_chan{30}; + msd::channel input_chan{30}; msd::channel output_chan{10}; // Send to input channel const auto writer = [&input_chan]() { for (int i = 1; i <= numbers; ++i) { - input_chan.write(movable_only{i}); + input_chan.write(MovableOnly{i}); } input_chan.close(); }; // Transform input channel values from movable_only to int by multiplying by 2 and write to output channel const auto double_transformer = [&input_chan, &output_chan]() { - const auto double_value = [](const movable_only& value) { return value.getValue() * 2; }; + const auto double_value = [](const MovableOnly& value) { return value.get_value() * 2; }; #ifdef _MSC_VER for (auto&& value : input_chan) { output_chan.write(double_value(value)); @@ -404,7 +407,7 @@ TEST(ChannelTest, Transform) // Release: does not compile - warning C4702: unreachable code // Debug: compiles, but copies the movable_only object instead of moving it // - // Posibilities: + // Possibilities: // - I am doing something very wrong (see operator* in blocking_writer_iterator) // - MSVC has a bug // - https://github.com/ericniebler/range-v3/issues/1814