Created
November 2, 2019 11:39
-
-
Save fadi-botros/f958fb3111fad998136a7ee5dddc575f to your computer and use it in GitHub Desktop.
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
#include <iostream> | |
#include <optional> | |
#include <string> | |
#include <memory> | |
// Protocol oriented approach for lazy initialization. | |
// Would be better of course when Concepts would be released. | |
// Demonstrated in int, std::unique_ptr, and std::optional | |
template<typename T> | |
struct Defaulted { | |
}; | |
template<> | |
struct Defaulted<int> { | |
static constexpr int defaultValue() { return 0; } | |
}; | |
template<typename U> | |
struct Defaulted<std::unique_ptr<U>> { | |
static constexpr std::unique_ptr<U> defaultValue() { return nullptr; } | |
}; | |
template<typename U> | |
struct Defaulted<std::optional<U>> { | |
static constexpr std::optional<U> defaultValue() { return std::nullopt; } | |
}; | |
template<typename T> | |
struct Extractor { | |
}; | |
template<> | |
struct Extractor<int> { | |
template<typename Callable> | |
inline static auto extract(int i, Callable func) { return func(i); } | |
}; | |
template<typename U> | |
struct Extractor<std::unique_ptr<U>> { | |
// Mainly used the callback method so that it would be unlikely to do something insecure like escaping | |
// a pointer or a reference, if returned a reference to this unique_ptr, it would be extremely dangerous. | |
// Also this would mitigate null pointer dereferencing. | |
template<typename Callable> | |
inline static auto extract(std::unique_ptr<U> &i, Callable func) { return func(*i); } | |
}; | |
template<typename U> | |
struct Extractor<std::optional<U>> { | |
template<typename Callable> | |
inline static auto extract(std::optional<U> &i, Callable func) { | |
if (auto l = i) { | |
return func(*l); | |
} else { | |
std::cout << "An unlikely null happened\n"; | |
} | |
} | |
}; | |
template<typename T, typename Initializer> | |
class Lazy { | |
T value; | |
Initializer initializer; | |
public: | |
Lazy(Initializer initializer): initializer(initializer) { | |
T def = Defaulted<T>::defaultValue(); | |
value = std::move(def); | |
} | |
template<typename Callable> | |
inline auto getValue(Callable callable) { | |
T def = Defaulted<T>::defaultValue(); | |
if (value == def) { | |
value = std::move(initializer()); | |
} | |
// The initializer also may return a default value (which is like null) | |
if (value == def) { | |
throw std::invalid_argument("Initializer returned a default value"); | |
} | |
Extractor<T>::extract(value, callable); | |
} | |
}; | |
template<typename T> | |
struct LazyHelper { | |
template<typename Initializer> | |
static Lazy<T, Initializer> makeLazy(Initializer initializer) { | |
return Lazy<T, Initializer>(initializer); | |
} | |
}; | |
int main() { | |
std::cout << "Integers\n"; | |
auto lazyInt = LazyHelper<int>::makeLazy([](){ | |
std::cout << "Called the int initializer\n"; | |
return 7; | |
}); | |
std::cout << "Now would get the value\n"; | |
lazyInt.getValue([](auto i){ std::cout << i << "\n"; }); | |
std::cout << "Now would get the value again\n"; | |
lazyInt.getValue([](auto i){ std::cout << i << "\n"; }); | |
auto shouldntBeCalled = LazyHelper<int>::makeLazy([](){ | |
std::cout << "Unlikely called the int initializer\n"; | |
return 5; | |
}); | |
std::cout << "Unique Pointers\n"; | |
auto lazyString = LazyHelper<std::unique_ptr<std::string>>::makeLazy([](){ | |
std::cout << "Called the int initializer\n"; | |
return std::make_unique<std::string>("A string"); | |
}); | |
std::cout << "Now would get the value\n"; | |
lazyString.getValue([](auto &&i){ std::cout << i << "\n"; }); | |
std::cout << "Now would get the value again\n"; | |
lazyString.getValue([](auto &&i){ std::cout << i << "\n"; }); | |
auto shouldntBeCalled2 = LazyHelper<std::unique_ptr<std::string>>::makeLazy([](){ | |
std::cout << "Unlikely called the int initializer\n"; | |
return std::make_unique<std::string>("Something here"); | |
}); | |
std::cout << "Optionals\n"; | |
auto lazyStringOpt = LazyHelper<std::optional<std::string>>::makeLazy([](){ | |
std::cout << "Called the int initializer\n"; | |
return std::make_optional<std::string>("Optional String"); | |
}); | |
std::cout << "Now would get the value\n"; | |
lazyStringOpt.getValue([](auto &&i){ std::cout << i << "\n"; }); | |
std::cout << "Now would get the value again\n"; | |
lazyStringOpt.getValue([](auto &&i){ std::cout << i << "\n"; }); | |
auto shouldntBeCalled3 = LazyHelper<std::unique_ptr<std::string>>::makeLazy([](){ | |
std::cout << "Unlikely called the int initializer\n"; | |
return std::make_optional<std::string>("Something here"); | |
}); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment