Adrian Colomitchi Adrian Colomitchi - 19 days ago 6
C++ Question

Pack expansion calling templated method in a function template

Context: param pack passing over (not even an obscure rule of expansion) when calling templated method of a templated class from a templated function, all of the above involving non-type variadic params.

Q1: What is the syntax that will allow the following code to compile (and, hopefully, work as intended)?

Q2: what would you recommend me to read to get a better grok of this

[expletive deleted]
pack expansion. (Alexandrescu's variadic are funadic goes only that much and cppreference didn't help me a bit with this. Yeah, I know, the risk of nothing helping me is actual)

template<
typename T, typename ProcR,
T... which,
bool maxesInclusive=false,
T... available
>
void process_ranged_queries(
const std::vector<std::size_t>& mins, const std::vector<std::size_t>& maxes,
const Accumulator<T, available...>& src, // a templated class with a templated method
std::function<ProcR(std::unordered_map<T,std::size_t>)> processor,
std::vector<ProcR>& results
) {
const std::size_t rangeLen=std::min(mins.size(), maxes.size());
if(rangeLen>0){
const std::size_t srcLen=src.size();
results.clear();
results.reserve(rangeLen);
typename decltype(src)::map_o_prefix_sums prefixSums;


// HERE!!!
src.prepare_prefix_sums<which...>(prefixSums); // OOOPPPSssshhh! Why? How???
// etc
}
}


gcc 5.4.1 on linux (ubuntu) with
--std=c++11
spits the following errs:

../main.cpp: In function ‘void process_ranged_queries(const std::vector<long unsigned int>&, const std::vector<long unsigned int>&, const Accumulator<T, available ...>&, std::function<ProcR(std::unordered_map<T, long unsigned int>)>, std::vector<ProcR>&)’:
../main.cpp:483:34: error: expected ‘;’ before ‘...’ token
src.prepare_prefix_sums<which...>(prefixSums);
^
../main.cpp:483:50: error: parameter packs not expanded with ‘...’:
src.prepare_prefix_sums<which...>(prefixSums);
^
../main.cpp:483:50: note: ‘which’


godbold for the entire code - as non-compilable as it is (only the 2 errs above, don't worry)



For reference, I'm posting the entire code here as well:

#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <functional>

template <typename T, T... categories>
class Accumulator {
static const std::unordered_set<T> catset;

std::unordered_map<T, std::vector<bool>> marks_;
std::size_t size_;

void check(T val) {
if(catset.find(val)==catset.end()) {
throw std::logic_error("Unavailable cat (pick a dog)");
}
}
public:
Accumulator(std::size_t size) : marks_(), size_(size) {
for(auto c : catset) {
marks_[c]=std::vector<bool>(size, false);
}
}

std::size_t size() const {
return this->size_;
}

void clear() {
for(auto kvit : this->marks_) {
kvit.second.clear(); // sets the size to 0
kvit.second.resize(this->size_, false); // refills to size with false
}
}

bool marked(T which, std::size_t i) const {
// later we may go with DEBUG/NDEBUG and use/not-use check and at()/[]
bool ret=false;
check(which);
ret=this->marks_.find(which)->second.at(i);
return ret;
}


void mark(T which, std::size_t i, bool value=true) {
check(which);
this->marks_.find(which)->second.at(i)=value;
}

// can I go with a templated version for which? Yes I can!
template <T which> void mark(std::size_t i, bool value=true) {
check(which);
this->marks_.find(which)->second.at(i)=value;
}

// Well, maybe I can go with a variable templated version!
// *clickety-click* Well, yea, compiles and works!! Waddayaknow?!
using map_o_prefix_sums=std::unordered_map<T, std::vector<std::size_t>>;

template <T... which>
void prepare_prefix_sums(map_o_prefix_sums& cumulativeCounts) {
cumulativeCounts.clear();
// d'oh...!!! for(auto c : which...) {
constexpr T cats[]={ which... };
for(auto c : cats) {
check(c);
const std::vector<bool>& ticks=this->marks_[c]; // source

cumulativeCounts[c]=std::vector<std::size_t>(); // destinations
std::vector<std::size_t>& counts=cumulativeCounts[c];
counts.reserve(this->size_);
std::size_t sumSoFar=0;
for(bool tick : ticks) {
if(tick) {
sumSoFar++;
}
counts.push_back(sumSoFar);
}
}
}
};


template <typename T, T...cats>
const std::unordered_set<T> Accumulator<T, cats...>::catset={cats...};

template<
typename T, typename ProcR,
T... which,
bool maxesInclusive=false,
T... available
>
void process_ranged_queries(
const std::vector<std::size_t>& mins, const std::vector<std::size_t>& maxes,
const Accumulator<T, available...>& src,
std::function<ProcR(std::unordered_map<T,std::size_t>)> processor,
std::vector<ProcR>& results
) {
const std::size_t rangeLen=std::min(mins.size(), maxes.size());
if(rangeLen>0){
const std::size_t srcLen=src.size();
results.clear();
results.reserve(rangeLen);
typename decltype(src)::map_o_prefix_sums prefixSums;
src.prepare_prefix_sums<which...>(prefixSums); // OOOPPPSssshhh! Why? How???
// etc
}
}



int main() {
std::string s="GATACA";

std::size_t len=s.length();

Accumulator<char, 'A', 'C', 'T', 'G'> sumer(s.length());
for(std::size_t i=0; i<len; i++) {
sumer.mark(s[i], i);
}
sumer.clear();
for(std::size_t i=0; i<len; i++) {
switch(s[i]) {
case 'A':
case 'a':
sumer.mark<'A'>(i);
break;
case 'G':
case 'g':
sumer.mark<'G'>(i);
break;
case 'T':
case 't':
sumer.mark<'C'>(i);
break;
case 'C':
case 'c':
sumer.mark<'C'>(i);
break;
default:
break;
}
}

decltype(sumer)::map_o_prefix_sums resultsHere;
sumer.prepare_prefix_sums<'A', 'C'>(resultsHere); // works well here, the pack is fully specified
return 0;
}

Answer

You need to use the template keyword for a dependent template function

src.template prepare_prefix_sums<which...>(prefixSums); // OOOPPPSssshhh! Why? How???

You can check this question and its answers for more verbose explanation.

Comments