Skip to content

Instantly share code, notes, and snippets.

@mjhanninen
Created February 16, 2016 07:50
Show Gist options
  • Save mjhanninen/2b105b928ec00e0bac2a to your computer and use it in GitHub Desktop.
Save mjhanninen/2b105b928ec00e0bac2a to your computer and use it in GitHub Desktop.
Delayed computation in C++
#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);
}
#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