From 7f24e103c7cf6b9093c893bde35cba004fc78cbe Mon Sep 17 00:00:00 2001 From: Florian Schoppmann Date: Wed, 19 Jul 2023 14:09:37 -0700 Subject: [PATCH] Introduce out<> and in_out<> --- BUILD.bazel | 36 +++++++++++++++ CMakeLists.txt | 4 ++ include/fixed_containers/in_out.hpp | 70 +++++++++++++++++++++++++++++ include/fixed_containers/out.hpp | 70 +++++++++++++++++++++++++++++ test/in_out_test.cpp | 67 +++++++++++++++++++++++++++ test/out_test.cpp | 67 +++++++++++++++++++++++++++ 6 files changed, 314 insertions(+) create mode 100644 include/fixed_containers/in_out.hpp create mode 100644 include/fixed_containers/out.hpp create mode 100644 test/in_out_test.cpp create mode 100644 test/out_test.cpp diff --git a/BUILD.bazel b/BUILD.bazel index b9715158..a0df02e1 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -187,6 +187,13 @@ cc_library( copts = ["-std=c++20"], ) +cc_library( + name = "in_out", + hdrs = ["include/fixed_containers/in_out.hpp"], + includes = ["include"], + copts = ["-std=c++20"], +) + cc_library( name = "index_or_value_storage", hdrs = ["include/fixed_containers/index_or_value_storage.hpp"], @@ -227,6 +234,13 @@ cc_library( copts = ["-std=c++20"], ) +cc_library( + name = "out", + hdrs = ["include/fixed_containers/out.hpp"], + includes = ["include"], + copts = ["-std=c++20"], +) + cc_library( name = "pair", hdrs = ["include/fixed_containers/pair.hpp"], @@ -513,6 +527,17 @@ cc_test( copts = ["-std=c++20"], ) +cc_test( + name = "in_out_test", + srcs = ["test/in_out_test.cpp"], + deps = [ + ":in_out", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], + copts = ["-std=c++20"], +) + cc_test( name = "index_range_predicate_iterator_test", srcs = ["test/index_range_predicate_iterator_test.cpp"], @@ -540,6 +565,17 @@ cc_test( copts = ["-std=c++20"], ) +cc_test( + name = "out_test", + srcs = ["test/out_test.cpp"], + deps = [ + ":out", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], + copts = ["-std=c++20"], +) + cc_test( name = "pair_test", srcs = ["test/pair_test.cpp"], diff --git a/CMakeLists.txt b/CMakeLists.txt index d88f07e8..c09190e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,12 +153,16 @@ if(BUILD_TESTS) add_test_dependencies(fixed_set_test) add_executable(fixed_vector_test test/fixed_vector_test.cpp) add_test_dependencies(fixed_vector_test) + add_executable(in_out_test test/in_out_test.cpp) + add_test_dependencies(in_out_test) add_executable(index_range_predicate_iterator_test test/index_range_predicate_iterator_test.cpp) add_test_dependencies(index_range_predicate_iterator_test) add_executable(instance_counter_test test/instance_counter_test.cpp) add_test_dependencies(instance_counter_test) add_executable(macro_countermeasures_test test/macro_countermeasures_test.cpp) add_test_dependencies(macro_countermeasures_test) + add_executable(out_test test/out_test.cpp) + add_test_dependencies(out_test) add_executable(pair_test test/pair_test.cpp) add_test_dependencies(pair_test) add_executable(pair_view_test test/pair_view_test.cpp) diff --git a/include/fixed_containers/in_out.hpp b/include/fixed_containers/in_out.hpp new file mode 100644 index 00000000..56f3dc8f --- /dev/null +++ b/include/fixed_containers/in_out.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include + +namespace fixed_containers +{ +/** + * Class template that wraps a non-const reference. + * + * This class is meant to replace non-const reference function parameters that are both read from + * *and* written to. This class is not meant to be used as general reference wrapper. In particular, + * `in_out` objects are neither copyable nor assignable. + * + * To call a function that has an `in_out` parameter, pass the argument by value using the explicit + * constructor. + * Example use in a function: + * ```c++ + * void increase_value(in_out value) + * { + * ++*value; + * } + * + * int main() + * { + * int value = 3; + * increase_value(in_out{value}); + * } + * ``` + * + * In order to pass an `in_out` parameter as argument to another function that also expects an + * `in_out` parameter, create a new `in_out` object. Example: + * ```c++ + * void decrease_value(in_out value) + * { + * internal_decrease_value(in_out{*value}); + * } + * ``` + * + * @tparam T type of the reference + */ +template +class in_out +{ +public: + constexpr explicit in_out(T& t) noexcept + : t_{t} + { + } + + template + requires(std::convertible_to && !std::same_as) + constexpr in_out(in_out&& original) noexcept + : t_{*original} + { + } + + constexpr in_out(const in_out& original) noexcept = delete; + constexpr in_out(in_out&& original) noexcept = default; + constexpr in_out& operator=(const in_out& original) = delete; + constexpr in_out& operator=(in_out&& original) = delete; + + constexpr T* operator->() const noexcept { return &t_; } + constexpr T* operator&() const noexcept { return &t_; } + constexpr T& operator*() const noexcept { return t_; } + +private: + T& t_; +}; + +} // namespace fixed_containers diff --git a/include/fixed_containers/out.hpp b/include/fixed_containers/out.hpp new file mode 100644 index 00000000..de852b8f --- /dev/null +++ b/include/fixed_containers/out.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include + +namespace fixed_containers +{ +/** + * Class template that wraps a non-const reference. + * + * This class is meant to replace non-const reference function parameters that are *only* written + * to. This class is not meant to be used as general reference wrapper. In particular, `out` objects + * are neither copyable nor assignable. + * + * To call a function that has an `out` parameter, pass the argument by value using the explicit + * constructor. + * Example use in a function: + * ```c++ + * void set_value(out value) + * { + * *value = 5; + * } + * + * int main() + * { + * int value = 3; + * set_value(out{value}); + * } + * ``` + * + * In order to pass an `out` parameter as argument to another function that also expects an + * `out` parameter, create a new `out` object. Example: + * ```c++ + * void set_value_wrapper(out value) + * { + * set_another_value(out{*value}); + * } + * ``` + * + * @tparam T type of the reference + */ +template +class out +{ +public: + constexpr explicit out(T& t) noexcept + : t_{t} + { + } + + template + requires(std::convertible_to && !std::same_as) + constexpr out(out&& original) noexcept + : t_{*original} + { + } + + constexpr out(const out& original) noexcept = delete; + constexpr out(out&& original) noexcept = default; + constexpr out& operator=(const out& original) = delete; + constexpr out& operator=(out&& original) = delete; + + constexpr T* operator->() const noexcept { return &t_; } + constexpr T* operator&() const noexcept { return &t_; } + constexpr T& operator*() const noexcept { return t_; } + +private: + T& t_; +}; + +} // namespace fixed_containers diff --git a/test/in_out_test.cpp b/test/in_out_test.cpp new file mode 100644 index 00000000..5982fe81 --- /dev/null +++ b/test/in_out_test.cpp @@ -0,0 +1,67 @@ +#include "fixed_containers/in_out.hpp" + +#include + +namespace fixed_containers +{ +namespace +{ +struct SomeStruct +{ + int a{}; + char b{}; +}; + +constexpr void add_to_int(int input, in_out output) { *output += input; } +constexpr void increment_struct(in_out s) +{ + s->a += 1; + s->b += 2; +} + +} // namespace + +TEST(InOut, Usage1) +{ + { + constexpr int result = []() + { + int input = 10; + int output = 200; + add_to_int(input, in_out{output}); + return output; + }(); + + static_assert(210 == result); + } + + { + int input = 10; + int output = 200; + add_to_int(input, in_out{output}); + EXPECT_EQ(210, output); + } +} + +TEST(InOut, Usage2) +{ + { + constexpr SomeStruct result = []() + { + SomeStruct s{10, 20}; + increment_struct(in_out{s}); + return s; + }(); + + static_assert(11 == result.a); + static_assert(22 == result.b); + } + + { + SomeStruct s{10, 20}; + increment_struct(in_out{s}); + EXPECT_EQ(11, s.a); + EXPECT_EQ(22, s.b); + } +} +} // namespace fixed_containers diff --git a/test/out_test.cpp b/test/out_test.cpp new file mode 100644 index 00000000..d53003ff --- /dev/null +++ b/test/out_test.cpp @@ -0,0 +1,67 @@ +#include "fixed_containers/out.hpp" + +#include + +namespace fixed_containers +{ +namespace +{ +struct SomeStruct +{ + int a{}; + char b{}; +}; + +constexpr void set_int(int input, out output) { *output = input; } +constexpr void set_struct(out s) +{ + s->a = 1; + s->b = 2; +} + +} // namespace + +TEST(Out, Usage1) +{ + { + constexpr int result = []() + { + int input = 1; + int output = 0; + set_int(input, out{output}); + return output; + }(); + + static_assert(1 == result); + } + + { + int input = 1; + int output = 0; + set_int(input, out{output}); + EXPECT_EQ(1, output); + } +} + +TEST(Out, Usage2) +{ + { + constexpr SomeStruct result = []() + { + SomeStruct s{0, 0}; + set_struct(out{s}); + return s; + }(); + + static_assert(1 == result.a); + static_assert(2 == result.b); + } + + { + SomeStruct s{0, 0}; + set_struct(out{s}); + EXPECT_EQ(1, s.a); + EXPECT_EQ(2, s.b); + } +} +} // namespace fixed_containers