-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
255 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
#ifndef SNITCH_ANY_HPP | ||
#define SNITCH_ANY_HPP | ||
|
||
#include "snitch/snitch_config.hpp" | ||
#include "snitch/snitch_error_handling.hpp" | ||
#include "snitch/snitch_function.hpp" | ||
#include "snitch/snitch_type_id.hpp" | ||
|
||
#include <array> | ||
#include <cstddef> | ||
#include <utility> | ||
|
||
namespace snitch { | ||
namespace impl { | ||
template<typename T> | ||
void delete_object(char* storage) noexcept { | ||
reinterpret_cast<T*>(storage)->~T(); | ||
} | ||
} // namespace impl | ||
|
||
template<std::size_t MaxSize> | ||
class inplace_any { | ||
std::array<char, MaxSize> storage = {}; | ||
function_ref<void(char*) noexcept> deleter = [](char*) noexcept {}; | ||
type_id_t id = type_id<void>(); | ||
|
||
void release() noexcept { | ||
deleter = [](char*) noexcept {}; | ||
id = type_id<void>(); | ||
} | ||
|
||
public: | ||
constexpr inplace_any() = default; | ||
|
||
inplace_any(const inplace_any&) = delete; | ||
|
||
constexpr inplace_any(inplace_any&& other) noexcept : | ||
storage(other.storage), deleter(other.deleter), id(other.id) { | ||
other.release(); | ||
} | ||
|
||
inplace_any& operator=(const inplace_any&) = delete; | ||
|
||
constexpr inplace_any& operator=(inplace_any&& other) noexcept { | ||
reset(); | ||
storage = other.storage; | ||
deleter = other.deleter; | ||
id = other.id; | ||
other.release(); | ||
return *this; | ||
} | ||
|
||
template<typename T, typename... Args> | ||
explicit inplace_any(std::in_place_type_t<T>, Args&&... args) { | ||
emplace<T>(std::forward<Args>(args)...); | ||
} | ||
|
||
~inplace_any() { | ||
reset(); | ||
} | ||
|
||
bool has_value() const noexcept { | ||
return id != type_id<void>(); | ||
} | ||
|
||
type_id_t type() const noexcept { | ||
return id; | ||
} | ||
|
||
template<typename T, typename... Args> | ||
void emplace(Args&&... args) { | ||
static_assert( | ||
sizeof(T) <= MaxSize, | ||
"This type is too large to fit in this inplace_any, increase storage size"); | ||
|
||
reset(); | ||
new (storage.data()) T(std::forward<Args>(args)...); | ||
deleter = &impl::delete_object<T>; | ||
id = type_id<T>(); | ||
} | ||
|
||
// Requires: not empty and stored type == T. | ||
template<typename T> | ||
const T& get() const { | ||
if (!has_value()) { | ||
assertion_failed("inplace_any is empty"); | ||
} | ||
if (type() != type_id<T>()) { | ||
assertion_failed("inplace_any holds an object of a different type"); | ||
} | ||
|
||
return *reinterpret_cast<const T*>(storage.data()); | ||
} | ||
|
||
// Requires: not empty and stored type == T. | ||
template<typename T> | ||
T& get() { | ||
return const_cast<T&>(const_cast<const inplace_any*>(this)->get<T>()); | ||
} | ||
|
||
void reset() noexcept { | ||
deleter(storage.data()); | ||
id = type_id<void>(); | ||
} | ||
}; | ||
} // namespace snitch | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
#include "testing.hpp" | ||
#include "testing_assertions.hpp" | ||
|
||
namespace { | ||
struct state_monitor { | ||
int* state = nullptr; | ||
|
||
state_monitor() = default; | ||
|
||
explicit state_monitor(int* s) : state(s) { | ||
*state += 1; | ||
} | ||
|
||
~state_monitor() { | ||
*state -= 1; | ||
} | ||
}; | ||
} // namespace | ||
|
||
TEST_CASE("any", "[utility]") { | ||
constexpr std::size_t max_size = 16; | ||
|
||
int state1 = 0; | ||
int state2 = 0; | ||
|
||
SECTION("default construct") { | ||
snitch::inplace_any<max_size> storage; | ||
} | ||
|
||
SECTION("construct in-place") { | ||
{ | ||
snitch::inplace_any<max_size> storage(std::in_place_type_t<state_monitor>{}, &state1); | ||
CHECK(storage.has_value()); | ||
CHECK(storage.type() == snitch::type_id<state_monitor>()); | ||
CHECK(state1 == 1); | ||
CHECK(storage.get<state_monitor>().state == &state1); | ||
} | ||
CHECK(state1 == 0); | ||
} | ||
|
||
SECTION("move constructor") { | ||
{ | ||
snitch::inplace_any<max_size> storage1(std::in_place_type_t<state_monitor>{}, &state1); | ||
snitch::inplace_any<max_size> storage2(std::move(storage1)); | ||
CHECK(!storage1.has_value()); | ||
CHECK(storage2.has_value()); | ||
CHECK(state1 == 1); | ||
} | ||
CHECK(state1 == 0); | ||
} | ||
|
||
SECTION("move assignment on empty") { | ||
{ | ||
snitch::inplace_any<max_size> storage2; | ||
{ | ||
snitch::inplace_any<max_size> storage1( | ||
std::in_place_type_t<state_monitor>{}, &state1); | ||
storage2 = std::move(storage1); | ||
CHECK(!storage1.has_value()); | ||
} | ||
|
||
CHECK(storage2.has_value()); | ||
CHECK(state1 == 1); | ||
} | ||
CHECK(state1 == 0); | ||
} | ||
|
||
SECTION("move assignment on full") { | ||
{ | ||
snitch::inplace_any<max_size> storage2(std::in_place_type_t<state_monitor>{}, &state2); | ||
{ | ||
snitch::inplace_any<max_size> storage1( | ||
std::in_place_type_t<state_monitor>{}, &state1); | ||
storage2 = std::move(storage1); | ||
CHECK(!storage1.has_value()); | ||
} | ||
|
||
CHECK(storage2.has_value()); | ||
CHECK(state1 == 1); | ||
CHECK(state2 == 0); | ||
} | ||
CHECK(state1 == 0); | ||
CHECK(state2 == 0); | ||
} | ||
|
||
SECTION("emplace and reset") { | ||
{ | ||
snitch::inplace_any<max_size> storage; | ||
storage.emplace<state_monitor>(&state1); | ||
CHECK(storage.has_value()); | ||
CHECK(storage.type() == snitch::type_id<state_monitor>()); | ||
CHECK(state1 == 1); | ||
CHECK(storage.get<state_monitor>().state == &state1); | ||
|
||
storage.reset(); | ||
CHECK(!storage.has_value()); | ||
CHECK(state1 == 0); | ||
} | ||
CHECK(state1 == 0); | ||
CHECK(state2 == 0); | ||
} | ||
|
||
SECTION("emplace over existing") { | ||
{ | ||
snitch::inplace_any<max_size> storage; | ||
storage.emplace<state_monitor>(&state1); | ||
storage.emplace<state_monitor>(&state2); | ||
CHECK(storage.has_value()); | ||
CHECK(storage.type() == snitch::type_id<state_monitor>()); | ||
CHECK(state1 == 0); | ||
CHECK(state2 == 1); | ||
CHECK(storage.get<state_monitor>().state == &state2); | ||
} | ||
CHECK(state1 == 0); | ||
CHECK(state2 == 0); | ||
} | ||
|
||
SECTION("reset empty") { | ||
snitch::inplace_any<max_size> storage; | ||
storage.reset(); | ||
CHECK(!storage.has_value()); | ||
} | ||
|
||
#if SNITCH_WITH_EXCEPTIONS | ||
SECTION("get empty") { | ||
assertion_exception_enabler enabler; | ||
snitch::inplace_any<max_size> storage; | ||
|
||
CHECK_THROWS_WHAT( | ||
storage.get<state_monitor>(), assertion_exception, "inplace_any is empty"); | ||
} | ||
|
||
SECTION("get wrong type") { | ||
assertion_exception_enabler enabler; | ||
snitch::inplace_any<max_size> storage; | ||
storage.emplace<int>(0); | ||
|
||
CHECK_THROWS_WHAT( | ||
storage.get<state_monitor>(), assertion_exception, | ||
"inplace_any holds an object of a different type"); | ||
} | ||
#endif | ||
} |