mirror of
https://github.com/TomHarte/CLK.git
synced 2025-03-21 09:29:41 +00:00
Reformat SubrangeDispatcher; test.
This commit is contained in:
parent
b00eac4a34
commit
6261ac24b4
@ -13,52 +13,11 @@
|
||||
|
||||
namespace Reflection {
|
||||
|
||||
/// Provides glue for a run of calls like:
|
||||
///
|
||||
/// SequencerT.perform<0>(...)
|
||||
/// SequencerT.perform<1>(...)
|
||||
/// SequencerT.perform<2>(...)
|
||||
/// ...etc...
|
||||
///
|
||||
/// Allowing the caller to execute any subrange of the calls.
|
||||
template <typename SequencerT>
|
||||
struct RangeDispatcher {
|
||||
|
||||
/// Perform @c target.perform<n>() for the input range `start <= n < end`.
|
||||
template <typename... Args>
|
||||
static void dispatch(SequencerT &target, int start, int end, Args&&... args) {
|
||||
|
||||
// Minor optimisation: do a comparison with end once outside the loop and if it implies so
|
||||
// then do no further comparisons within the loop. This is somewhat targetted at expected
|
||||
// use cases.
|
||||
if(end < SequencerT::max) {
|
||||
dispatch<true>(target, start, end, args...);
|
||||
} else {
|
||||
dispatch<false>(target, start, end, args...);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <bool use_end, typename... Args> static void dispatch(SequencerT &target, int start, int end, Args&&... args) {
|
||||
static_assert(SequencerT::max < 2048);
|
||||
|
||||
// Yes, macros, yuck. But I want an actual switch statement for the dispatch to start
|
||||
// and to allow a simple [[fallthrough]] through all subsequent steps up until end.
|
||||
// So I don't think I have much in the way of options here.
|
||||
//
|
||||
// Sensible choices by the optimiser are assumed.
|
||||
|
||||
#define index(n) \
|
||||
case n: \
|
||||
if constexpr (n <= SequencerT::max) { \
|
||||
if constexpr (n == SequencerT::max) return; \
|
||||
if constexpr (n < SequencerT::max) { \
|
||||
if(use_end && end == n) return; \
|
||||
} \
|
||||
target.template perform<n>(start, end, args...); \
|
||||
} \
|
||||
[[fallthrough]];
|
||||
|
||||
// Yes, macros, yuck. But I want an actual switch statement for the dispatch to start
|
||||
// and to allow a simple [[fallthrough]] through all subsequent steps up until end.
|
||||
// So I don't think I have much in the way of options here.
|
||||
//
|
||||
// Sensible choices by the optimiser are assumed.
|
||||
#define index2(n) index(n); index(n+1);
|
||||
#define index4(n) index2(n); index2(n+2);
|
||||
#define index8(n) index4(n); index4(n+4);
|
||||
@ -71,12 +30,106 @@ private:
|
||||
#define index1024(n) index512(n); index512(n+512);
|
||||
#define index2048(n) index1024(n); index1024(n+1024);
|
||||
|
||||
switch(start) {
|
||||
default: assert(false);
|
||||
index2048(0);
|
||||
/// Provides glue for a run of calls like:
|
||||
///
|
||||
/// SequencerT.perform<0>(...)
|
||||
/// SequencerT.perform<1>(...)
|
||||
/// SequencerT.perform<2>(...)
|
||||
/// ...etc...
|
||||
///
|
||||
/// Allowing the caller to execute any subrange of the calls.
|
||||
template <typename SequencerT>
|
||||
struct RangeDispatcher {
|
||||
static_assert(SequencerT::max < 2048);
|
||||
|
||||
/// Perform @c target.perform<n>() for the input range `begin <= n < end`.
|
||||
template <typename... Args>
|
||||
static void dispatch(SequencerT &target, int begin, int end, Args&&... args) {
|
||||
|
||||
// Minor optimisation: do a comparison with end once outside the loop and if it implies so
|
||||
// then do no further comparisons within the loop. This is somewhat targetted at expected
|
||||
// use cases.
|
||||
if(end < SequencerT::max) {
|
||||
dispatch<true>(target, begin, end, args...);
|
||||
} else {
|
||||
dispatch<false>(target, begin, end, args...);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <bool use_end, typename... Args> static void dispatch(SequencerT &target, int begin, int end, Args&&... args) {
|
||||
#define index(n) \
|
||||
case n: \
|
||||
if constexpr (n <= SequencerT::max) { \
|
||||
if constexpr (n == SequencerT::max) return; \
|
||||
if constexpr (n < SequencerT::max) { \
|
||||
if(use_end && end == n) return; \
|
||||
} \
|
||||
target.template perform<n>(args...); \
|
||||
} \
|
||||
[[fallthrough]];
|
||||
|
||||
switch(begin) {
|
||||
default: assert(false);
|
||||
index2048(0);
|
||||
}
|
||||
|
||||
#undef index
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/// An optional target for a RangeDispatcher which uses a classifier to divide the input region into typed ranges, issuing calls to the target
|
||||
/// only to begin and end each subrange, and for the number of cycles spent within.
|
||||
template <typename ClassifierT, typename TargetT>
|
||||
struct SubrangeDispatcher {
|
||||
static_assert(ClassifierT::max < 2048);
|
||||
|
||||
template <typename... Args> static void dispatch(TargetT &target, int begin, int end, Args&&... args) {
|
||||
#define index(n) \
|
||||
case n: \
|
||||
if constexpr (n <= ClassifierT::max) { \
|
||||
constexpr auto region = ClassifierT::region(n); \
|
||||
if constexpr (n == find_begin(n)) { \
|
||||
if(n >= end) { \
|
||||
return; \
|
||||
} \
|
||||
target.template begin<region>(n); \
|
||||
} \
|
||||
if constexpr (n == find_end(n) - 1) { \
|
||||
const auto clipped_begin = std::max(begin, find_begin(n)); \
|
||||
const auto clipped_end = std::min(end, find_end(n)); \
|
||||
target.template advance<region>(clipped_end - clipped_begin); \
|
||||
\
|
||||
if(clipped_end == n + 1) { \
|
||||
target.template end<region>(n + 1); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
[[fallthrough]];
|
||||
|
||||
switch(begin) {
|
||||
default: assert(false);
|
||||
index2048(0);
|
||||
}
|
||||
|
||||
#undef index
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int find_begin(int n) {
|
||||
const auto type = ClassifierT::region(n);
|
||||
while(n && ClassifierT::region(n - 1) == type) --n;
|
||||
return n;
|
||||
}
|
||||
|
||||
static constexpr int find_end(int n) {
|
||||
const auto type = ClassifierT::region(n);
|
||||
while(n < ClassifierT::max && ClassifierT::region(n) == type) ++n;
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
#undef index2
|
||||
#undef index4
|
||||
#undef index8
|
||||
@ -88,51 +141,6 @@ private:
|
||||
#undef index512
|
||||
#undef index1024
|
||||
#undef index2048
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/// An optional target for a RangeDispatcher which uses a classifier to divide the input region into typed ranges, issuing calls to the target
|
||||
/// only to begin and end each subrange, and for the number of cycles spent within.
|
||||
template <typename ClassifierT, typename TargetT>
|
||||
struct SubrangeDispatcher {
|
||||
static constexpr int max = ClassifierT::max;
|
||||
|
||||
template <typename... Args> SubrangeDispatcher(Args&&... args) :
|
||||
target(std::forward<Args>(args)...) {}
|
||||
|
||||
template <int n>
|
||||
void perform(int begin, int end) {
|
||||
constexpr auto region = ClassifierT::region(n);
|
||||
const auto clipped_start = std::max(begin, find_begin(n));
|
||||
const auto clipped_end = std::min(end, find_end(n));
|
||||
|
||||
if constexpr (n == find_begin(n)) {
|
||||
target.template begin<region>(clipped_start);
|
||||
}
|
||||
|
||||
target.template advance<region>(clipped_end - clipped_start);
|
||||
|
||||
if constexpr (n + 1 == find_end(n)) {
|
||||
target.template end<region>(clipped_end);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int find_begin(int n) {
|
||||
const auto type = ClassifierT::region(n);
|
||||
while(n && ClassifierT::region(n - 1) == type) --n;
|
||||
return n;
|
||||
}
|
||||
|
||||
static constexpr int find_end(int n) {
|
||||
const auto type = ClassifierT::region(n);
|
||||
while(n < ClassifierT::max && ClassifierT::region(n) == type) ++n;
|
||||
return n;
|
||||
}
|
||||
|
||||
TargetT target;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
struct DoStep {
|
||||
static constexpr int max = 100;
|
||||
template <int n> void perform(int, int) {
|
||||
template <int n> void perform() {
|
||||
assert(n < max);
|
||||
performed[n] = true;
|
||||
}
|
||||
@ -54,7 +54,7 @@ enum class RangeType {
|
||||
};
|
||||
|
||||
struct RangeClassifier {
|
||||
static constexpr int max = 50;
|
||||
static constexpr int max = 200;
|
||||
|
||||
static constexpr RangeType region(int x) {
|
||||
return x >= 10 && x < 20 ? RangeType::Sync : RangeType::Border;
|
||||
@ -70,16 +70,21 @@ struct RangeTarget {
|
||||
RangeType range_type;
|
||||
int length = 0;
|
||||
|
||||
Event(Type event_type, RangeType range_type) : event_type(event_type), range_type(range_type) {}
|
||||
Event(Type event_type, RangeType range_type, int length) : event_type(event_type), range_type(range_type), length(length) {}
|
||||
|
||||
bool operator ==(const Event &rhs) const {
|
||||
if(rhs.event_type != event_type) return false;
|
||||
if(rhs.range_type != range_type) return false;
|
||||
return rhs.length == length;
|
||||
}
|
||||
};
|
||||
std::vector<Event> events;
|
||||
|
||||
template <RangeType type> void begin(int) {
|
||||
events.emplace_back(Event::Type::Begin, type);
|
||||
template <RangeType type> void begin(int position) {
|
||||
events.emplace_back(Event::Type::Begin, type, position);
|
||||
}
|
||||
template <RangeType type> void end(int) {
|
||||
events.emplace_back(Event::Type::End, type);
|
||||
template <RangeType type> void end(int position) {
|
||||
events.emplace_back(Event::Type::End, type, position);
|
||||
}
|
||||
template <RangeType type> void advance(int length) {
|
||||
events.emplace_back(Event::Type::Advance, type, length);
|
||||
@ -87,11 +92,29 @@ struct RangeTarget {
|
||||
};
|
||||
|
||||
- (void)testRanges {
|
||||
RangeTarget target;
|
||||
using Dispatcher = Reflection::SubrangeDispatcher<RangeClassifier, RangeTarget>;
|
||||
Dispatcher dispatcher;
|
||||
Reflection::RangeDispatcher<Dispatcher>::dispatch(dispatcher, 0, 10);
|
||||
Dispatcher::dispatch(target, 0, 11);
|
||||
|
||||
printf("");
|
||||
XCTAssertEqual(target.events.size(), 5);
|
||||
XCTAssert(target.events[0] == RangeTarget::Event(RangeTarget::Event::Type::Begin, RangeType::Border, 0));
|
||||
XCTAssert(target.events[1] == RangeTarget::Event(RangeTarget::Event::Type::Advance, RangeType::Border, 10));
|
||||
XCTAssert(target.events[2] == RangeTarget::Event(RangeTarget::Event::Type::End, RangeType::Border, 10));
|
||||
XCTAssert(target.events[3] == RangeTarget::Event(RangeTarget::Event::Type::Begin, RangeType::Sync, 10));
|
||||
XCTAssert(target.events[4] == RangeTarget::Event(RangeTarget::Event::Type::Advance, RangeType::Sync, 1));
|
||||
|
||||
Dispatcher::dispatch(target, 11, 75);
|
||||
Dispatcher::dispatch(target, 75, 100);
|
||||
Dispatcher::dispatch(target, 100, 200);
|
||||
|
||||
XCTAssertEqual(target.events.size(), 12);
|
||||
XCTAssert(target.events[5] == RangeTarget::Event(RangeTarget::Event::Type::Advance, RangeType::Sync, 9));
|
||||
XCTAssert(target.events[6] == RangeTarget::Event(RangeTarget::Event::Type::End, RangeType::Sync, 20));
|
||||
XCTAssert(target.events[7] == RangeTarget::Event(RangeTarget::Event::Type::Begin, RangeType::Border, 20));
|
||||
XCTAssert(target.events[8] == RangeTarget::Event(RangeTarget::Event::Type::Advance, RangeType::Border, 55));
|
||||
XCTAssert(target.events[9] == RangeTarget::Event(RangeTarget::Event::Type::Advance, RangeType::Border, 25));
|
||||
XCTAssert(target.events[10] == RangeTarget::Event(RangeTarget::Event::Type::Advance, RangeType::Border, 100));
|
||||
XCTAssert(target.events[11] == RangeTarget::Event(RangeTarget::Event::Type::End, RangeType::Border, 200));
|
||||
}
|
||||
|
||||
@end
|
||||
|
Loading…
x
Reference in New Issue
Block a user