Inspired by Bartosz Milewski's article Getting Lazy with C++.
Created
February 16, 2016 07:50
-
-
Save mjhanninen/2b105b928ec00e0bac2a to your computer and use it in GitHub Desktop.
Delayed computation in C++
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
#pragma once | |
#include <atomic> | |
#include <mutex> | |
/*! A suspended (or delayed) computation that is evaluated exactly once upon | |
* fetching its value the first time. Lock-free after the evaluation. Can | |
* be safely copied. Thread-safe. */ | |
template <typename T> | |
class Delayed | |
{ | |
public: | |
explicit Delayed(std::function<T()> fn) | |
: state_(new State(fn)) | |
{ | |
} | |
T & | |
get() const | |
{ | |
std::shared_ptr<State> s = state_; | |
if (!s) | |
{ | |
throw std::runtime_error( | |
"Attempted to get from an empty suspension"); | |
} | |
return s->evaluate_.load()(*state_); | |
} | |
T & | |
operator*() const | |
{ | |
return get(); | |
} | |
explicit operator bool() const | |
{ | |
std::shared_ptr<State> s = state_; | |
return s && s->evaluate_ == &dereference_forced; | |
} | |
private: | |
struct State | |
{ | |
State(std::function<T()> fn) | |
: fn_(fn) | |
, evaluate_(&Delayed<T>::dereference_suspended) | |
{ | |
} | |
std::mutex mutex_; | |
std::function<T()> fn_; | |
T value_; | |
std::atomic<T &(*)(State &)> evaluate_; | |
}; | |
static T & | |
dereference_forced(State &state) | |
{ | |
return state.value_; | |
} | |
static T & | |
dereference_suspended(State &state) | |
{ | |
std::lock_guard<std::mutex> lock(state.mutex_); | |
if (state.evaluate_ == &Delayed<T>::dereference_suspended) | |
{ | |
state.value_ = state.fn_(); | |
state.evaluate_ = &Delayed<T>::dereference_forced; | |
} | |
return state.value_; | |
} | |
std::shared_ptr<State> state_; | |
}; | |
template <typename T> Delayed<T> | |
delay(std::function<T()> fn) | |
{ | |
return Delayed<T>(fn); | |
} |
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 "Delayed.hpp" | |
template <typename T> | |
std::string | |
yesno(T v) | |
{ | |
return bool(v) ? "Yes" : "No"; | |
} | |
int | |
main(int argc, char *argv[]) | |
{ | |
auto original = | |
delay<int>( | |
[&argc, &argv]() -> int | |
{ | |
for (int i = 0; i < argc; i++) | |
{ | |
std::cout << argv[i] << std::endl; | |
} | |
return argc; | |
}); | |
auto clone = original; | |
std::cout << "Clone evaluated? " << yesno(clone) << std::endl; | |
std::cout << "Original evaluated? " << yesno(original) << std::endl; | |
std::cout << "Number of arguments: " << clone.get() << std::endl; | |
std::cout << "Number of arguments: " << *original << std::endl; | |
std::cout << "Clone evaluated? " << yesno(clone) << std::endl; | |
std::cout << "Original evaluated? " << yesno(original) << std::endl; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment