summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/unique_function.h62
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/common/unique_function.cpp108
4 files changed, 172 insertions, 0 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index a6fa9a85d..c05b78cd5 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -188,6 +188,7 @@ add_library(common STATIC
188 tiny_mt.h 188 tiny_mt.h
189 tree.h 189 tree.h
190 uint128.h 190 uint128.h
191 unique_function.h
191 uuid.cpp 192 uuid.cpp
192 uuid.h 193 uuid.h
193 vector_math.h 194 vector_math.h
diff --git a/src/common/unique_function.h b/src/common/unique_function.h
new file mode 100644
index 000000000..ca0559071
--- /dev/null
+++ b/src/common/unique_function.h
@@ -0,0 +1,62 @@
1// Copyright 2021 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <utility>
9
10namespace Common {
11
12/// General purpose function wrapper similar to std::function.
13/// Unlike std::function, the captured values don't have to be copyable.
14/// This class can be moved but not copied.
15template <typename ResultType, typename... Args>
16class UniqueFunction {
17 class CallableBase {
18 public:
19 virtual ~CallableBase() = default;
20 virtual ResultType operator()(Args&&...) = 0;
21 };
22
23 template <typename Functor>
24 class Callable final : public CallableBase {
25 public:
26 Callable(Functor&& functor_) : functor{std::move(functor_)} {}
27 ~Callable() override = default;
28
29 ResultType operator()(Args&&... args) override {
30 return functor(std::forward<Args>(args)...);
31 }
32
33 private:
34 Functor functor;
35 };
36
37public:
38 UniqueFunction() = default;
39
40 template <typename Functor>
41 UniqueFunction(Functor&& functor)
42 : callable{std::make_unique<Callable<Functor>>(std::move(functor))} {}
43
44 UniqueFunction& operator=(UniqueFunction&& rhs) noexcept = default;
45 UniqueFunction(UniqueFunction&& rhs) noexcept = default;
46
47 UniqueFunction& operator=(const UniqueFunction&) = delete;
48 UniqueFunction(const UniqueFunction&) = delete;
49
50 ResultType operator()(Args&&... args) const {
51 return (*callable)(std::forward<Args>(args)...);
52 }
53
54 explicit operator bool() const noexcept {
55 return static_cast<bool>(callable);
56 }
57
58private:
59 std::unique_ptr<CallableBase> callable;
60};
61
62} // namespace Common
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 96bc30cac..c4c012f3d 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -5,6 +5,7 @@ add_executable(tests
5 common/host_memory.cpp 5 common/host_memory.cpp
6 common/param_package.cpp 6 common/param_package.cpp
7 common/ring_buffer.cpp 7 common/ring_buffer.cpp
8 common/unique_function.cpp
8 core/core_timing.cpp 9 core/core_timing.cpp
9 core/network/network.cpp 10 core/network/network.cpp
10 tests.cpp 11 tests.cpp
diff --git a/src/tests/common/unique_function.cpp b/src/tests/common/unique_function.cpp
new file mode 100644
index 000000000..ac9912738
--- /dev/null
+++ b/src/tests/common/unique_function.cpp
@@ -0,0 +1,108 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string>
6
7#include <catch2/catch.hpp>
8
9#include "common/unique_function.h"
10
11namespace {
12struct Noisy {
13 Noisy() : state{"Default constructed"} {}
14 Noisy(Noisy&& rhs) noexcept : state{"Move constructed"} {
15 rhs.state = "Moved away";
16 }
17 Noisy& operator=(Noisy&& rhs) noexcept {
18 state = "Move assigned";
19 rhs.state = "Moved away";
20 }
21 Noisy(const Noisy&) : state{"Copied constructed"} {}
22 Noisy& operator=(const Noisy&) {
23 state = "Copied assigned";
24 }
25
26 std::string state;
27};
28} // Anonymous namespace
29
30TEST_CASE("UniqueFunction", "[common]") {
31 SECTION("Capture reference") {
32 int value = 0;
33 Common::UniqueFunction<void> func = [&value] { value = 5; };
34 func();
35 REQUIRE(value == 5);
36 }
37 SECTION("Capture pointer") {
38 int value = 0;
39 int* pointer = &value;
40 Common::UniqueFunction<void> func = [pointer] { *pointer = 5; };
41 func();
42 REQUIRE(value == 5);
43 }
44 SECTION("Move object") {
45 Noisy noisy;
46 REQUIRE(noisy.state == "Default constructed");
47
48 Common::UniqueFunction<void> func = [noisy = std::move(noisy)] {
49 REQUIRE(noisy.state == "Move constructed");
50 };
51 REQUIRE(noisy.state == "Moved away");
52 func();
53 }
54 SECTION("Move construct function") {
55 int value = 0;
56 Common::UniqueFunction<void> func = [&value] { value = 5; };
57 Common::UniqueFunction<void> new_func = std::move(func);
58 new_func();
59 REQUIRE(value == 5);
60 }
61 SECTION("Move assign function") {
62 int value = 0;
63 Common::UniqueFunction<void> func = [&value] { value = 5; };
64 Common::UniqueFunction<void> new_func;
65 new_func = std::move(func);
66 new_func();
67 REQUIRE(value == 5);
68 }
69 SECTION("Default construct then assign function") {
70 int value = 0;
71 Common::UniqueFunction<void> func;
72 func = [&value] { value = 5; };
73 func();
74 REQUIRE(value == 5);
75 }
76 SECTION("Pass arguments") {
77 int result = 0;
78 Common::UniqueFunction<void, int, int> func = [&result](int a, int b) { result = a + b; };
79 func(5, 4);
80 REQUIRE(result == 9);
81 }
82 SECTION("Pass arguments and return value") {
83 Common::UniqueFunction<int, int, int> func = [](int a, int b) { return a + b; };
84 REQUIRE(func(5, 4) == 9);
85 }
86 SECTION("Destructor") {
87 int num_destroyed = 0;
88 struct Foo {
89 Foo(int* num_) : num{num_} {}
90 Foo(Foo&& rhs) : num{std::exchange(rhs.num, nullptr)} {}
91 Foo(const Foo&) = delete;
92
93 ~Foo() {
94 if (num) {
95 ++*num;
96 }
97 }
98
99 int* num = nullptr;
100 };
101 Foo object{&num_destroyed};
102 {
103 Common::UniqueFunction<void> func = [object = std::move(object)] {};
104 REQUIRE(num_destroyed == 0);
105 }
106 REQUIRE(num_destroyed == 1);
107 }
108}