-
Notifications
You must be signed in to change notification settings - Fork 37
Description
I attempt to use flux with immer::vector (I know, that's a weird thing to do. immer iterators are already const and never invalidates). For the most part (except for .to<immer::vector<T>>()) it seems to work as long as this custom trait is defined:
The trait implementation
template<typename T>
struct flux::sequence_traits<immer::vector<T>>
{
using value_type = std::ranges::range_value_t<immer::vector<T>>;
static constexpr auto first(auto &) -> index_t { return index_t{0}; }
static constexpr auto is_last(auto &self, index_t idx) { return idx >= size(self); }
static constexpr auto inc(auto &self, index_t &idx)
{
FLUX_DEBUG_ASSERT(idx < size(self));
idx = num::checked_add(idx, distance_t{1});
}
static constexpr auto read_at(auto &self, index_t idx) -> decltype(auto)
{
indexed_bounds_check(idx, size(self));
return self[idx];
}
static constexpr auto read_at_unchecked(auto &self, index_t idx) -> decltype(auto)
{
return self[idx];
}
static constexpr auto dec(auto &, index_t &idx)
{
FLUX_DEBUG_ASSERT(idx > 0);
idx = num::checked_sub(idx, distance_t{1});
}
static constexpr auto last(auto &self) -> index_t { return size(self); }
static constexpr auto inc(auto &self, index_t &idx, distance_t offset)
{
FLUX_DEBUG_ASSERT(num::checked_add(idx, offset) <= size(self));
FLUX_DEBUG_ASSERT(num::checked_add(idx, offset) >= 0);
idx = num::checked_add(idx, offset);
}
static constexpr auto distance(auto &, index_t from, index_t to) -> distance_t
{
return num::checked_sub(to, from);
}
static constexpr auto size(auto &self) -> distance_t
{
return checked_cast<distance_t>(std::ranges::ssize(self));
}
static constexpr auto for_each_while(auto &self, auto &&pred) -> index_t
{
auto iter = std::ranges::begin(self);
auto const end = std::ranges::end(self);
while (iter != end) {
if (!std::invoke(pred, *iter)) {
break;
}
++iter;
}
return checked_cast<index_t>(iter - std::ranges::begin(self));
}
};You just duplicate the default implementation for contiguous containers and make minimal adaptation changes and it just works.
However, that's a lot of duplication you can't get rid of and sub-classing from flux::sequence_traits<std::vector<T>> won't help in this case, because the functions with calls to read_at needs to be shadowed by the derived class because they're not virtual methods, otherwise they will call the base version of read_at which then calls on .data() (immer::vector has no .data() because it's not contiguous).
virtual also doesn't seem to work in this case as the return type needs to be covariant and there's a lot of other limitations (no return type deduce, no templates, can't be static).
Relevant issue: P2279 We need a language mechanism for customization points
This is more of a "for your information" type of issue. I'm not aware of any good ways to make customization point easier to use. A workaround could be separating the trait into multiple different traits which the user can independently override, or offer a default implementation that looks for free functions which the user can provide, but that could make the customization point more confusing to use.