I have a class named memory_region, which is sort of like an untyped gsl::span (i.e. it's essentially a void* and a size_t), which I also use for type erasure. It thus has an as_span<T>() method.
With this class, I have a std::unordered_map<std::string, memory_region> my_map - which is used to pass type-erased spans between parts of my code which don't share headers, so they can't know about each others' types. The typical access to one of these looks like:
auto foo = my_map.at("foo").as_span<bar_t>();
This works just fine with code that has a fixed set of buffers and types and names. But - things get tricky when my code's buffers depend on a template parameter pack. Now, I've implemented a
std::string input_buffer_name(unsigned input_buffer_index);
function, so if I have an index sequence and my parameter pack I can do, for example
template<typename Ts..., std::size_t... Indices>
my_function(std::unordered_map<std::string, memory_region>& my map) {
compute_stuff_with_buffers(
my_map.at(input_buffer_name(Indices)).as_span<Ts>()...
);
}
(this is a variation on the infamous indices trick; note that the same type may appear more than once in the pack, so I can't "wrap the types in a tuple" and acces it by type.)
The thing is, though - my code doesn't have that index sequence in the template parameters; most of it is templated on just the parameter pack of types. So I find myself writing "helper functions/methods" all the time to be able to use that index sequence, e.g.:
template<typename Ts..., std::size_t... Indices>
my_function_helper(
std::unordered_map<std::string, memory_region>& my map
std::index_sequence<Indices...> /* unused */)
{
compute_stuff_with_buffers(
my_map.at(input_buffer_name(Indices)).as_span<Ts>()...
);
}
template<typename Ts...>
my_function(std::unordered_map<std::string, memory_region>& my map) {
my_function_helper(
my_map, std::make_index_sequence<sizeof...(Ts)> {}
);
}
What can I do instead, that will not involve so much code duplication?
In this case you can use simple pack expansion in the form of an array:
Demo
The pack expansion will be expanded in order ([temp.variadic]), and also evaluated in order (left to right) because we're using a braced initializer list (an unused integer array): [dcl.init.aggr]
Re:
I suppose we could take advantage of the fact that logical AND will sequence left to right ([expr.log.and]), and also a boolean can be promoted to
int:Demo 2