Added class Lazy<T>

This commit is contained in:
Daniel Wolf 2016-07-19 21:33:07 +02:00
parent ddcadad710
commit 17b43ad205
3 changed files with 112 additions and 0 deletions

View File

@ -224,6 +224,7 @@ set(SOURCE_FILES
src/tupleHash.h
src/ThreadPool.cpp src/ThreadPool.h
src/ObjectPool.h
src/Lazy.h
)
add_executable(rhubarb ${SOURCE_FILES})
target_link_libraries(rhubarb ${Boost_LIBRARIES} cppFormat sphinxbase pocketSphinx flite webRTC)
@ -239,6 +240,7 @@ set(TEST_FILES
tests/pairsTests.cpp
tests/tokenizationTests.cpp
tests/g2pTests.cpp
tests/LazyTests.cpp
src/stringTools.cpp src/stringTools.h
src/Timeline.h
src/TimeRange.cpp src/TimeRange.h
@ -249,6 +251,7 @@ set(TEST_FILES
src/g2p.cpp src/g2p.h
src/logging.cpp src/logging.h
src/tools.cpp src/tools.h
src/Lazy.h
)
add_executable(runTests ${TEST_FILES})
target_link_libraries(runTests gtest gmock gmock_main flite cppFormat)

51
src/Lazy.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#include <memory>
#include <mutex>
template<typename T>
class Lazy {
public:
using value_type = T;
explicit Lazy(std::function<T()> createValue) :
createValue(createValue)
{}
explicit operator bool() const {
return static_cast<bool>(_value);
}
T& value() {
init();
return *_value;
}
const T& value() const {
init();
return *_value;
}
T* operator->() {
return &value();
}
const T* operator->() const {
return &value();
}
T& operator*() {
return value();
}
const T& operator*() const {
return value();
}
private:
void init() const {
std::call_once(initialized, [&] { _value = std::make_unique<T>(createValue()); });
}
std::function<T()> createValue;
mutable std::once_flag initialized;
mutable std::unique_ptr<T> _value;
};

58
tests/LazyTests.cpp Normal file
View File

@ -0,0 +1,58 @@
#include <gmock/gmock.h>
#include "Lazy.h"
using namespace testing;
using std::make_unique;
// Not copyable, no default constrctor, movable
struct Foo {
const int value;
Foo(int value) : value(value) {}
Foo() = delete;
Foo(const Foo&) = delete;
Foo& operator=(const Foo &) = delete;
Foo(Foo&&) = default;
Foo& operator=(Foo&&) = default;
};
TEST(Lazy, basicUsage) {
bool lambdaCalled = false;
Lazy<Foo> lazy([&lambdaCalled] { lambdaCalled = true; return Foo(42); });
EXPECT_FALSE(lambdaCalled);
EXPECT_FALSE(static_cast<bool>(lazy));
EXPECT_EQ(42, (*lazy).value);
EXPECT_EQ(42, lazy.value().value);
EXPECT_EQ(42, lazy->value);
EXPECT_TRUE(lambdaCalled);
EXPECT_TRUE(static_cast<bool>(lazy));
}
TEST(Lazy, constUsage) {
bool lambdaCalled = false;
const Lazy<Foo> lazy([&lambdaCalled] { lambdaCalled = true; return Foo(42); });
EXPECT_FALSE(lambdaCalled);
EXPECT_FALSE(static_cast<bool>(lazy));
EXPECT_EQ(42, (*lazy).value);
EXPECT_EQ(42, lazy.value().value);
EXPECT_EQ(42, lazy->value);
EXPECT_TRUE(lambdaCalled);
EXPECT_TRUE(static_cast<bool>(lazy));
}
using Expensive = Foo;
#define member value;
TEST(Lazy, demo) {
// Constructor takes function
Lazy<Expensive> lazy([] { return Expensive(42); });
// Multiple ways to access value
Expensive& a = *lazy;
Expensive& b = lazy.value();
auto c = lazy->member;
// Check if initialized
if (lazy) { /* ... */ }
}