Skip to content

Instantly share code, notes, and snippets.

@fadi-botros
Created November 2, 2019 11:39
Show Gist options
  • Save fadi-botros/f958fb3111fad998136a7ee5dddc575f to your computer and use it in GitHub Desktop.
Save fadi-botros/f958fb3111fad998136a7ee5dddc575f to your computer and use it in GitHub Desktop.
#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