79 lines
1.7 KiB
C++
79 lines
1.7 KiB
C++
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <tuple>
|
|
|
|
namespace autoopt {
|
|
|
|
template <typename T>
|
|
struct source {
|
|
T _value;
|
|
std::uint64_t _version = 0;
|
|
|
|
source() = default;
|
|
explicit source(T value) : _value(std::move(value)) {}
|
|
|
|
virtual T& get() { return _value; };
|
|
|
|
bool set(T value) {
|
|
if (value == _value) return false;
|
|
_value = std::move(value);
|
|
++_version;
|
|
return true;
|
|
}
|
|
};
|
|
|
|
template <typename O, typename... Is>
|
|
struct transform : source<O> {
|
|
std::function<O(Is&...)> _func;
|
|
std::tuple<source<Is>&...> _sources;
|
|
std::array<std::uint64_t, sizeof...(Is)> _versions{};
|
|
|
|
transform(std::function<O(Is&...)> func, source<Is>&... sources)
|
|
: _func(func), _sources(sources...) {
|
|
this->_value = std::apply([&](source<Is>&... sources) {
|
|
return _func(sources.get()...);
|
|
}, _sources);
|
|
this->_versions = current_versions();
|
|
}
|
|
|
|
virtual O& get() override {
|
|
auto cur_versions = current_versions();
|
|
if (_versions == cur_versions) return this->_value;
|
|
|
|
O next = std::apply([&](source<Is>&... sources) {
|
|
return _func(sources.get()...);
|
|
}, _sources);
|
|
|
|
if (next != this->_value) {
|
|
this->_value = next;
|
|
++this->_version;
|
|
}
|
|
return this->_value;
|
|
}
|
|
|
|
private:
|
|
std::array<std::uint64_t, sizeof...(Is)> current_versions() {
|
|
return std::apply([&](source<Is>&... sources){
|
|
|
|
return std::array<std::uint64_t, sizeof...(Is)>{sources._version...};
|
|
}, _sources);
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
struct sink {
|
|
source<T>& _upstream;
|
|
std::uint64_t _version;
|
|
std::function<void(T&)> _cb;
|
|
|
|
void maybe_update() {
|
|
if (_version != _upstream._version) {
|
|
_cb(_upstream.get());
|
|
_version = _upstream._version;
|
|
}
|
|
}
|
|
};
|
|
|
|
} // namespace autoopt
|