-
Notifications
You must be signed in to change notification settings - Fork 0
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
3 changed files
with
150 additions
and
1 deletion.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
#pragma once | ||
|
||
#include <mutex> | ||
#include <utility> | ||
#include <memory> | ||
|
||
namespace sw | ||
{ | ||
template <typename ValueType, typename InitType = std::move_only_function<typename ValueType()>> | ||
// TODO(jrgfogh): Constraint InitType. | ||
class lazy_init final | ||
{ | ||
mutable std::unique_ptr<ValueType> data_; | ||
mutable InitType init_; | ||
|
||
void ensure_initialized() const | ||
{ | ||
if (!data_) | ||
{ | ||
data_ = std::make_unique<ValueType>(init_()); | ||
} | ||
} | ||
public: | ||
using value_type = ValueType; | ||
using init_type = InitType; | ||
|
||
explicit lazy_init(InitType init) : | ||
init_{std::move(init)} | ||
{ | ||
} | ||
|
||
auto operator*() -> ValueType& | ||
{ | ||
ensure_initialized(); | ||
return *data_; | ||
} | ||
|
||
auto operator*() const -> ValueType const & | ||
{ | ||
ensure_initialized(); | ||
return *data_; | ||
} | ||
}; | ||
} |
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,104 @@ | ||
#include "../sw/lazy_init.h" | ||
|
||
#include "gtest_unwarn.h" | ||
|
||
#include <type_traits> | ||
#include <utility> | ||
#include <memory> | ||
#include <string> | ||
#include <ranges> | ||
|
||
template<typename T> | ||
concept lazy_init_specialization = | ||
std::is_constructible_v<T, typename T::init_type> && | ||
std::is_same_v<decltype(*std::declval<T>()), typename T::value_type&> && | ||
std::is_same_v<decltype(*std::declval<T const>()), typename T::value_type const&> | ||
// I'm not sure if this is a bug or a feature, but at least it's documented here: | ||
//!std::is_move_assignable_v<T> && | ||
//!std::is_move_constructible_v<T> && | ||
//!std::is_copy_assignable_v<T> && | ||
//!std::is_copy_constructible_v<T> | ||
; | ||
|
||
struct not_default_constructible alignas(16) | ||
{ | ||
explicit not_default_constructible() = delete; | ||
explicit not_default_constructible(int) {} | ||
}; | ||
|
||
static_assert(lazy_init_specialization<sw::lazy_init<double, int(*)()>>); | ||
static_assert(lazy_init_specialization<sw::lazy_init<not_default_constructible, not_default_constructible(*)()>>); | ||
static_assert(lazy_init_specialization<sw::lazy_init<std::string, std::string(*)()>>); | ||
static_assert(lazy_init_specialization<sw::lazy_init<std::shared_ptr<int>, std::nullptr_t(*)()>>); | ||
|
||
TEST(LazyInit, IsLazy) | ||
{ | ||
bool called = false; | ||
sw::lazy_init<int> li{ | ||
[&] { | ||
called = true; | ||
return 5; | ||
}}; | ||
EXPECT_FALSE(called); | ||
} | ||
|
||
TEST(LazyInit, InitExactlyOnce) | ||
{ | ||
int callCount = 0; | ||
sw::lazy_init<int> li{ | ||
[&] { | ||
callCount++; | ||
return 5; | ||
}}; | ||
*li; | ||
*li; | ||
*li; | ||
EXPECT_EQ(callCount, 1); | ||
} | ||
|
||
TEST(LazyInit, RetryOnFailure) | ||
{ | ||
bool called = false; | ||
sw::lazy_init<int> li{ | ||
[&] { | ||
if (!called) | ||
{ | ||
called = true; | ||
throw std::exception{}; | ||
} | ||
return 5; | ||
}}; | ||
EXPECT_THROW((*li), std::exception); | ||
EXPECT_EQ(*li, 5); | ||
} | ||
|
||
TEST(LazyInit, DoesNotRequireDefaultConstructibleData) | ||
{ | ||
sw::lazy_init<not_default_constructible> li{ | ||
[] { | ||
return not_default_constructible{5}; | ||
}}; | ||
} | ||
|
||
TEST(LazyInit, StarOperator) | ||
{ | ||
for (auto i : std::ranges::iota_view(5, 10)) | ||
{ | ||
sw::lazy_init<int> li{ | ||
[&] { | ||
return i; | ||
} }; | ||
sw::lazy_init<int> const& const_ref = li; | ||
EXPECT_EQ(*li, i); | ||
EXPECT_EQ(*const_ref, i); | ||
} | ||
} | ||
|
||
// TODO: | ||
// Implement operators | ||
// Arrow | ||
// Equality (conditionally) | ||
// Spaceship (conditionally) | ||
// Ensure thread safety. | ||
// Null-check function pointers. | ||
// Copy-only function types |