Files
2026-10-06 17:20:47 +02:00

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