Skip to content

Commit

Permalink
Introduce out<> and in_out<>
Browse files Browse the repository at this point in the history
  • Loading branch information
fschopp authored and alexkaratarakis committed Jul 19, 2023
1 parent 3208113 commit ea79937
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 0 deletions.
36 changes: 36 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down Expand Up @@ -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"],
Expand Down Expand Up @@ -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"],
Expand Down Expand Up @@ -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"],
Expand Down
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
64 changes: 64 additions & 0 deletions include/fixed_containers/in_out.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#pragma once

#include <concepts>

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<int> 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<int> value)
* {
* internal_decrease_value(in_out{*value});
* }
* ```
*
* @tparam T type of the reference
*/
template <typename T>
class in_out
{
public:
constexpr explicit in_out(T& t) noexcept
: t_{t}
{
}

constexpr in_out(const in_out& original) noexcept = delete;
constexpr in_out(in_out&& original) noexcept = delete;
constexpr in_out& operator=(const in_out& original) = delete;
constexpr in_out& operator=(in_out&& original) = delete;

// non-cost overloads only, to prevent passing by `const`/`const&`
constexpr T* operator->() noexcept { return &t_; }
constexpr T* operator&() noexcept { return &t_; }
constexpr T& operator*() noexcept { return t_; }

private:
T& t_;
};

} // namespace fixed_containers
64 changes: 64 additions & 0 deletions include/fixed_containers/out.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#pragma once

#include <concepts>

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<int> 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<int> value)
* {
* set_another_value(out{*value});
* }
* ```
*
* @tparam T type of the reference
*/
template <typename T>
class out
{
public:
constexpr explicit out(T& t) noexcept
: t_{t}
{
}

constexpr out(const out& original) noexcept = delete;
constexpr out(out&& original) noexcept = delete;
constexpr out& operator=(const out& original) = delete;
constexpr out& operator=(out&& original) = delete;

// non-cost overloads only, to prevent passing by `const`/`const&`
constexpr T* operator->() noexcept { return &t_; }
constexpr T* operator&() noexcept { return &t_; }
constexpr T& operator*() noexcept { return t_; }

private:
T& t_;
};

} // namespace fixed_containers
67 changes: 67 additions & 0 deletions test/in_out_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include "fixed_containers/in_out.hpp"

#include <gtest/gtest.h>

namespace fixed_containers
{
namespace
{
struct SomeStruct
{
int a{};
char b{};
};

constexpr void add_to_int(int input, in_out<int> output) { *output += input; }
constexpr void increment_struct(in_out<SomeStruct> 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
67 changes: 67 additions & 0 deletions test/out_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include "fixed_containers/out.hpp"

#include <gtest/gtest.h>

namespace fixed_containers
{
namespace
{
struct SomeStruct
{
int a{};
char b{};
};

constexpr void set_int(int input, out<int> output) { *output = input; }
constexpr void set_struct(out<SomeStruct> 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

0 comments on commit ea79937

Please sign in to comment.