1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-11-01 11:16:16 +00:00
Files
CLK/Numeric/CompileTimeCounter.hpp
Thomas Harte 51186e615f Add warning.
2025-10-17 16:10:23 -04:00

99 lines
2.7 KiB
C++

//
// CompileTimeCounter.hpp
// Clock Signal
//
// Created by Thomas Harte on 17/10/2025.
// Copyright © 2025 Thomas Harte. All rights reserved.
//
#pragma once
namespace Numeric::Counter {
// Intended usage:
//
// (1) define a struct to make this count unique; if desired then give it a `static constexpr int base` to
// dictate the first value returned by next (see SeqBase for an optional helper);
// (2) use next<YourTag>() to get the next number in the sequence, current<YourTag>() to get whatever the last
// call to next returned.
//
// Implementation decision: an initial call to next is expected before calls to current. Calling current before
// any calls to next will result in base - 1.
//
// WARNING: this implementation uses compile-time recursion, so the counter can count up to only 512 on Clang,
// and probably similar limits on other compilers.
template <typename, int> struct CounterImpl {
private:
struct Count {
consteval friend bool has_passed(CounterImpl) {
return true;
}
};
consteval friend bool has_passed(CounterImpl);
public:
// SFINAE: these functions will exist only if has_passed exists
// for CounterImpl and the prospective count.
//
// Double definition because the fallback cases differ.
template <typename instantiation = CounterImpl, bool = has_passed(instantiation())>
static consteval bool test_and_generate(int) {
return true;
}
template <typename instantiation = CounterImpl, bool = has_passed(instantiation())>
static consteval bool test(int) {
return true;
}
// Fallback cases: possibly generate a new has_passed but otherwise
// indicate that the prosepctive count is as-yet unused.
static consteval bool test_and_generate(...) {
[[maybe_unused]] Count c;
return false;
}
static consteval bool test(...) {
return false;
}
};
/// A convenience struct for declaring counter bases locally.
/// @c Owner is purely to give uniqueness.
template <typename Owner, int b>
struct SeqBase {
static constexpr int base = b;
};
// Both of the below use a tail-recursive loop to find the first instance of
// CounterImpl that indicates that it hasn't yet been counted.
template <typename Seq>
consteval int base() {
if constexpr (requires {Seq::base;}) {
return Seq::base;
}
return 0;
}
template <typename Seq, int count = 0, typename = decltype([]{})>
consteval int next() {
if constexpr (!CounterImpl<Seq, count>::test_and_generate(0)) {
return count + base<Seq>();
} else {
return next<Seq, count+1>();
}
}
template <typename Seq, int count = 0, typename = decltype([]{})>
consteval int current() {
if constexpr (!CounterImpl<Seq, count>::test(0)) {
return count + base<Seq>() - 1;
} else {
return current<Seq, count+1>();
}
}
}