I have a function that scans the user's file system, fills a vector with the paths, then either sorts it or not. Since the user should be able to decide at compile-time whether he wants the vector sorted or not, I use templates and helper classes in place of a much desired (but not existing) "static if".
Consider this code:
enum class Sort{Alphabetic, Unsorted};
template<Sort TS> struct SortHelper;
template<> struct SortHelper<Sort::Alphabetic>
{
static void sort(vector<string>& mTarget) { sort(begin(mTarget), end(mTarget)); }
};
template<> struct SortHelper<Sort::Unsorted>
{
static void sort(vector<string>&) { }
};
template<Sort TS> struct DoSomethingHelper
{
static void(vector<string>& mTarget)
{
// do something with mTarget
SortHelper<TS>::sort(mTarget);
}
};
The code I've written above is GREATLY simplified from the original, which takes multiple template parameters to allow the user to customize even further the results of the function at compile-time.
Is there an alternative to using all of these helper classes? It gets really messy and hard to read.
Ideally, this is what I would like to write:
enum class Sort{Alphabetic, Unsorted};
template<Sort TS> struct DoSomethingHelper
{
static void(vector<string>& mTarget)
{
// do something with mTarget
static_if(TS == Sort::Unsorted) { /* do nothing */ }
static_if(TS == Sort::Alphabetic) { sort(begin(mTarget), end(mTarget)); }
}
};
Since your value is known at compile time (non-template type parameter) you can perfectly write a "normal" if:
template<Sort TS>
void someFunction(vector<string>& mTarget)
{
if (TS == Sort::Alphabetic) { sort(begin(mTarget), end(mTarget)); }
// else if (TS == Sort::Unsorted) {}
}
The compiler will perform constant folding and dead code elimination (if those optimisations are enabled, of course), and the result will be exactly the same as if you used the hypothetical static_if.
I am afraid there has been a misunderstanding about the usage of static_if.
Certainly you can use static_if (or whatever trick you wish really) to try and get some optimization, but that is not its first goal.
The first goal of static_if is semantical. Let me demonstrate this with std::advance. A typical implementation of std::advance will use a type switch to choose, at compile time, between an O(1) implementation (for Random Access Iterators) and an O(n) implementation (for the others):
template <typename It, typename D>
void advance_impl(It& it, D d, random_access_iterator_tag)
{
it += d;
}
template <typename It, typename D>
void advance_impl(It& it, D d, bidirectional_iterator_tag)
{
if (d > D(0)) { for (D i(0); i < d; ++i) { ++it; } }
else { for (D i(0); i > d; --i) { --it; } }
}
template <typename It, typename D>
void advance_impl(It& it, D d, input_iterator_tag)
{
for (D i(0); i < d; ++i) { ++it; }
}
And finally:
template <typename It, typename D>
void advance(It& it, D d)
{
typename std::iterator_traits<It>::iterator_category c;
advance_impl(it, d, c);
}
Why not use just a if in this case ? Because it would not compile.
a Bidirectional Iterator does not support +=
an Input Iterator (or Forward Iterator) does not support --
Thus, the only way to implement the functionality is to statically dispatch to a function only using the available operations on the given type.
What about template specialization?
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
enum class Sort {
Alphabetic,
Unsorted
};
template<Sort TS> struct DoSomethingHelper {
static void someFunction(vector<string>& mTarget)
{}
};
template<> struct DoSomethingHelper<Sort::Unsorted> {
static void someFunction(vector<string>& mTarget) {
}
};
template<> struct DoSomethingHelper<Sort::Alphabetic> {
static void someFunction(vector<string>& mTarget) {
sort(begin(mTarget), end(mTarget));
}
};
int main() {
vector<string> v = {{"foo", "bar", "foo2", "superman", ".."}};
DoSomethingHelper<Sort::Alphabetic> helper;
helper.someFunction(v);
for (string& s : v) {
cout << s << endl;
}
return 0;
}
Edit: I'm a idiot.
Related
Look at this example:
struct s77 {
char d[77];
};
struct s1 {
char d;
};
struct Foo: s77, s1 {
};
struct Off {
static const int v = std::size_t(static_cast<s1*>(static_cast<Foo*>(nullptr)+1)) - std::size_t(static_cast<Foo*>(nullptr)+1);
};
This code tries to put the offset of s1 in Foo into Off::v. This code compiles with GCC/clang (without any warnings), but fails to compile with VS2015/VS2017 (error C2131: expression did not evaluate to a constant)
Which compiler is correct?
Can I achieve this functionality in a standard conformant way? If it is not possible, is it possible to create a working solution which works with VS2015/VS2017? I'm willing to accept any working solution, even which has undefined behavior according to the standard (but happens to work with VS2015 and VS2017). Off::v must be a compile time constant.
My original problem is this: I have an own implementation of tuple, which is implemented with multiple inheritance (like clang's tuple). I'd like to create a compile-time constant "descriptor" for the tuple, which contains all of its members' offset in the tuple. This descriptor contains a function pointer for each tuple member too. If I'd create this descriptor by hand, it would look like this (for example):
struct Entry {
int offset;
void (*function)(void *member);
};
Entry descriptor[] = {
{ 0, &SomeType1::static_function },
{ 12, &SomeType2::static_function },
{ 20, &SomeType3::static_function }
};
The intention of this is that I could have a general function (which is not a template), which can use this descriptor to call a type-specific function on each tuple member:
void call(void *tuple, const Entry *entries, int n) {
for (int i=0; i<n; i++) {
entries[i].function(static_cast<char *>(tuple)+entries[i].offset);
}
}
(The reason of this solution instead of a templated call function is that call is actually a huge function in my real code, and entry[i].function calls cannot be factored out from it. I'd like to avoid massive code duplication.)
How about something like:
struct Entry {
void* (*data_member_getter)(void*);
void (*function)(void *member);
};
namespace details
{
template <std::size_t I, typename Tuple>
constexpr void* voidPGetter(void* tuple)
{
return &std::get<I>(*reinterpret_cast<Tuple*>(tuple));
}
template <typename Tuple, std::size_t I>
constexpr MakeEntry()
{
using type = std::tuple_element_t<I, Tuple>;
return { &voidPGetter<I, Tuple>, &type::static_function };
}
template <typename Tuple, std::size_t ... Is>
constexpr std::array<Entry, sizeof...(Is)>
ComputeEntryHelper(std::index_sequence<Is...>)
{
return {{MakeEntry<Is, Tuple>()...}};
}
}
template <typename Tuple>
constexpt auto ComputeEntry()
{
constexpr auto size = std::tuple_size<Tuple>::value;
return details::ComputeEntryHelper(std::make_index_sequence<size>());
}
And then
void call(void* tuple, const Entry* entries, int n) {
for (int i = 0; i != n; ++i) {
entries[i].function(entries[i].data_member_getter(tuple));
}
}
So instead of offset, having a function to get the data.
Say I have some code:
void barA() { }
void barB() { }
void fooA() {
// Duplicate code...
barA();
// More duplicate code...
}
void fooB() {
// Duplicate code...
barB();
// More duplicate code...
}
int main() {
fooA();
fooB();
}
And I want to remove the duplicate code between fooA and fooB I could use a number of dynamic techniques such as passing in a bool parameter, passing a function pointer or virtual methods but if I wanted a compile time technique I could do something like this:
struct A { };
struct B { };
template<typename Tag> void bar();
template<> void bar<A>() { }
template<> void bar<B>() { }
template<typename Tag> void foo() {
// Duplicate code
bar<Tag>();
// More duplicate code
}
int main() {
foo<A>();
foo<B>();
}
where I have introduced two empty "Tag" classes to indicate which bar to use and templated foo and bar based on the tag class. This seems to do the trick. Questions:
Does this technique have a name? is this an example of "Tag dispatching"? From what I read about Tag dispatching it is slightly different and involves function overloading with a tag parameter. A tag that may have come from a typedef in a trait class.
Is there a more idomatic compile-time technique of achieving the same thing?
Edit:
Another possibility would be to use function overloading of bar instead of template specialization and pass the tag class as a parameter:
struct A { };
struct B { };
void bar(A) { }
void bar(B) { }
template<typename Tag> void foo() {
// Duplicate code
bar(Tag());
// More duplicate code
}
int main() {
foo<A>();
foo<B>();
}
This isn't tag dispatching. As you rightly said in your question, that'd be if you used some compile time trait of A and B to distinguish between the two, and then use that to select between two different overloads.
An good example of tag dispatch would be how std::advance is typically implemented. The function's signature is
template< class InputIt, class Distance >
void advance( InputIt& it, Distance n );
it can be advanced n positions in a single operation if it meets the requirements of RandomAccessIterator. For lesser iterators we must advance it in a loop. So an implementation would probably do something similar to the following:
namespace detail
{
template<class InputIt, class Distance>
void advance(InputIt& it, Distance n, std::random_access_iterator_tag)
{
it += n;
}
template<class InputIt, class Distance>
void advance(InputIt& it, Distance n, std::bidirectional_iterator_tag)
{
if(n < 0) {
while(n++) --it;
} else {
while(n--) ++it;
}
}
template<class InputIt, class Distance>
void advance(InputIt& it, Distance n, std::input_iterator_tag)
{
assert(n >= 0);
while(n--) ++it;
}
}
template< class InputIt, class Distance >
void advance( InputIt& it, Distance n )
{
detail::advance(it, n,
typename std::iterator_traits<InputIt>::iterator_category());
}
I don't know of any specific name for what you're doing. It's just an example of how one would follow the DRY principle.
If bar took an instance of A and B as an argument, then I'd implement this differently. Instead of making bar a function template, and then providing specializations, I'd let overload resolution do the job for me.
void bar(A const&) { ... }
void bar(B const&) { ... }
But since that's not the case, providing explicit specializations seems the right way to do this.
This question already has answers here:
using std::is_same, why my function still can't work for 2 types
(4 answers)
Closed 2 years ago.
// template specialization
#include <iostream>
using namespace std;
// class template:
template <class T>
class mycontainer {
T element;
public:
mycontainer (T arg) {element=arg;}
T increase () {
//if(T.type==int)//how to do this or something similar?
//do this if an int
return ++element;
//if(T.type==char)
//if ((element>='a')&&(element<='z'))
//element+='A'-'a';
//return element;
}
};
I know how to write a template specialization and do a separate whole class def just for the char type.
But what if I wanted to handle everything in just one block of code?
How can I check if T is an int or a char?
You could use typeid:
if (typeid(T) == typeid(int))
Or you could use the std::is_same type trait:
if (std::is_same<T, int>::value)
What you want is probably something like a compile-time if.
Unfortunately, C++11 has no native support for such a language construct.
However, if you just want to check whether two types are identical, the std::is_same<> type trait should help you:
#include <type_traits> // <== INCLUDE THIS STANDARD HEADER
// class template:
template <class T>
class mycontainer
{
T element;
public:
mycontainer (T arg) {element=arg;}
T increase ()
{
if (std::is_same<T, int>::value) // <== THIS IS HOW YOU WOULD USE IT
return ++element;
if (std::is_same<T, char>::value) // <== THIS IS HOW YOU WOULD USE IT
{
if ((element>='a') && (element<='z'))
element+='A'-'a';
}
return element;
}
};
However, keep in mind that the condition is evaluated at run-time, even though the value of is_same<T, int>::value is known at compile-time. This means that both the true and the false branch of the if statement must compile!
For instance, the following would not be legal:
if (std::is_same<T, int>::value)
{
cout << element;
}
else if (std::is_same<T, my_class>::value)
{
element->print(); // Would not compile when T is int!
}
Also, as Xeo correctly pointed out in the comments, the compiler will likely issue warnings because your condition will always evaluate to true or to false, so one of the two branches will contain unreachable code.
You may use explicit template specialization
#include <iostream>
using namespace std;
// class template:
template <class T>
class mycontainer {
T element;
public:
mycontainer (T arg) {element=arg;}
T increase();
};
template<>
int mycontainer<int>::increase(){
return ++element;
}
template<>
char mycontainer<char>::increase(){
if ((element>='a')&&(element<='z'))
element+='A'-'a';
return element;
}
int main(){
mycontainer<int> A(10);
mycontainer<char> B('x');
cout << A.increase() <<endl;
cout << B.increase() <<endl;
return 0;
}
How about a simple overload?
// in the private section
static int& do_increase(int& i){ return ++i; }
static char& do_increase(char& c){
if(c >= 'a' && c <= 'z')
c += 'A' - 'a';
return c;
}
template<class U>
static U& do_increase(U& arg){
// some default implementation?
return arg;
}
(Note that the standard doesn't guarantee alphabetic order for the numeric values of a char.)
Then simply call that in increase as return do_increase(element);.
The usual solution here is to forward to an overloaded function
with an additional argument. Something like:
template <typename T>
class MyContainer
{
T increase( int const* ) { /* special treatment for int */ }
T increase( ... ) { /* default treatment */ }
public:
T increase()
{
return increase( (T const*)0 );
}
};
With a little imagination, you can come up with all sorts of
distinctions. If you make the target functions with the extra
arguments templates, you can even leverage off SFINAE: design
the dummy argument so that template type substitution fails, and
the function will not be considered in the overload set. And
since all of the functions are inline, it's probable that there
will be no extra overhead, provided that you optimize.
This is along the lines of Andy Prowls answer but is all done at compile-time using a minimal helper class with specialization.
In this instance you have a helper that actually does the specialization but you could also have the helper class just take a bool and then use something like std::is_same<T, int>::value to pass that value as a template parameter.
template <typename T>
struct myContainerHelper;
{
// General Case
static inline T increase(T element)
{
return ++element;
}
};
template <>
struct myContainerHelper<char>
{
// Specific case
static inline char increase(char element)
{
if ((element>='a')&&(element<='z')) element+='A'-'a';
return element;
}
};
template <class T>
class mycontainer
{
T element;
public:
mycontainer (T arg) {element=arg;}
T increase ()
{
return myContainerHelper<T>::increase(element);
}
};
This allows you to only specialize the single function instead of the entire class. I'm using a template class with statics because I'm used to VS2012 limitations with partial specialization for function templates.
The X: What I want to do:
I have the types: BaseType and DerivedType<int k> (see code below), and I need to handle a collection of K vectors of the derived types std::vector<DerivedType<k>>, k = 1...K. I'd like to access the objects in these vectors, and perform an operation on them that depends on k. K is a compile time constant. The problem is illustrated in the implementation:
The types are defined as:
#include <iostream>
#include <algorithm>
struct BaseType { // Interface of the DerivedTypes
virtual void print(){std::cout << "BaseType!" << std::endl; }
};
template< int k >
struct DerivedType : public BaseType {
static const int k_ = k;
// ... function calls templated on k ...
void print(){std::cout << "DerivedType: " << k_ << std::endl;}
};
template< int k >
void doSomething ( DerivedType<k>& object ) { object.print(); }
And what I want to do is:
int main() {
// My collection of vectors of the derived types:
std::vector<DerivedType<0>> derType0(2);
std::vector<DerivedType<1>> derType1(1);
std::vector<DerivedType<2>> derType2(3);
// ... should go to K: std::vector<DerivedType<K>> derTypeK;
// Iterate over the derived objects applying a k-dependent templated function:
std::for_each(begin(derType0),end(derType0),[](DerivedType<0>& object){
doSomething<0>(object);
});
std::for_each(begin(derType1),end(derType1),[](DerivedType<1>& object){
doSomething<1>(object);
});
std::for_each(begin(derType2),end(derType2),[](DerivedType<2>& object){
doSomething<2>(object);
});
return 0;
}
I want to avoid repeating code, such that I only have to change K, which is a compile time constant of O(10). Ideally, I would have something "more like" this:
// Pseudocode: do not try to compile this
create_derived_objects(DerivedType,K)
= std::vector< std::vector<DerivedType<k>>* > my_K_derived_types;
for each vector<DerivedType<k>>* derivedTypes in my my_K_derived_types
for each object in (*derivedTypes)
doSomething<k> on object of type derivedType<k>
// I could also restrict doSomething<k> to the base interface
Each vector of derived types contains O(10^6) to O(10^9) objects. The inner-most loops are the most time consuming part of my application making dynamic_cast only an option for the outer-most loop.
The Y: what I have tryed without succes.
I am at the moment studying the Abrahams C++ Template Metaprogramming book to see if I could use boost::mpl. I am also doing the tutorials on boost::fusion to see if I could use it too. However, the learning curve of these libraries is rather large, so I wanted to ask first before I invest a week in something when a better and simpler solution is available.
My first try was to wrapp my vectors std::vector<DerivedType<k>> such that I can create a vector<WrappedDerivedTypes*>, and access each of the single vectors separately within a for_each loop. However, in the loop I have a series of if(dynamic_cast<std::vector<DerivedType<0>>>(WrappedVector) != 0 ){ do for_each loop for the derived objects } else if( dynamic_cast...) { do...} ... that I wasn't able to eliminate.
What about a recursive solution based on a generic linked list of vectors, a strategy pattern and a thing that applies strategies recursively through the linked list? (note: see the improved version at the end):
#include <iostream>
#include <vector>
template <int j>
class holder {
public:
const static int k = j;
};
template <int j>
class strategy {
public:
void operator()(holder<j> t)
{
std::cout << "Strategy " << t.k << std::endl;
}
};
template <int k>
class lin_vector {
private:
std::vector<holder<k>> vec;
lin_vector<k-1> pred;
public:
lin_vector(const lin_vector<k-1> &pred, std::vector<holder<k>> vec)
: vec(vec), pred(pred) { }
std::vector<holder<k>> get_vec() { return vec; }
lin_vector<k-1> &get_pred() { return pred; }
};
template <>
class lin_vector<0> {
public:
lin_vector() { }
};
template <int k, template <int> class strategy>
class apply_strategy {
public:
void operator()(lin_vector<k> lin);
};
template <int k, template <int> class strategy>
void apply_strategy<k, strategy>::operator()(lin_vector<k> lin)
{
apply_strategy<k-1, strategy>()(lin.get_pred());
for (auto i : lin.get_vec())
strategy<k>()(i);
}
template <template <int> class strategy>
class apply_strategy<0, strategy>
{
public:
void operator()(lin_vector<0> lin) { /* does nothing */ }
};
template <int k>
lin_vector<k> build_lin()
{
return lin_vector<k>(build_lin<k-1>(), {holder<k>()});
}
template <>
lin_vector<0> build_lin()
{
return lin_vector<0>();
}
int main(void)
{
apply_strategy<5, strategy>()(build_lin<5>());
}
Compile it with a C++11 compiler.
Most probably you'll find unsatisfactory the fact that building a lin_vector requires a lot of copying, but you can specialize the structure to suit your needs (perhaps substituting the pred with a pointer or embedding the creation strategy straight into the linked list).
EDIT: here there is an improved version which avoids a lot of copying and handles list building and processing in a more coherent and uniform way:
#include <iostream>
#include <vector>
template <int j>
class holder {
public:
const static int k = j;
};
template <int k>
class lin_vector {
private:
std::vector<holder<k>> vec;
lin_vector<k-1> pred;
public:
std::vector<holder<k>> &get_vec() { return vec; }
lin_vector<k-1> &get_pred() { return pred; }
};
template <>
class lin_vector<0> {
public:
lin_vector() { }
};
template <int k, template <int> class strategy>
class apply_strategy {
public:
void operator()(lin_vector<k> &lin);
};
template <int k, template <int> class strategy>
void apply_strategy<k, strategy>::operator()(lin_vector<k> &lin)
{
apply_strategy<k-1, strategy>()(lin.get_pred());
strategy<k>()(lin.get_vec());
}
template <template <int> class strategy>
class apply_strategy<0, strategy>
{
public:
void operator()(lin_vector<0> &lin) { /* does nothing */ }
};
template <int j>
class strategy {
public:
void operator()(std::vector<holder<j>> &t)
{
std::cout << "Strategy " << j << ", elements: ";
for (auto v : t)
std::cout << v.k << " ";
std::cout << std::endl;
}
};
template <int j>
class build_strategy {
public:
void operator()(std::vector<holder<j>> &t)
{
for (unsigned int i = 0; i < j; i++)
t.push_back(holder<j>());
}
};
int main(void)
{
const int K = 5;
lin_vector<K> list;
apply_strategy<K, build_strategy>()(list);
apply_strategy<K, strategy>()(list);
}
A solution free of virtual dispatch is possible, though it's probably overkill.
The first thing you need is a function template doSomething<K>() that you specialise on each derived type:
template <int K>
void doSomething(vector<DerivedType<K> >& x);
template <>
void doSomething<1>(vector<DerivedType<1> >& x) { ... }
template <>
void doSomething<2>(vector<DerivedType<2> >& x) { ... } // etc.
You could then build a strongly-typed collection of vectors using a recursively defined struct template:
template <int K>
struct vov {
vov<K - 1> prev;
vector<DerivedType<K> > v;
};
template <>
struct vov<1> {
vector<DerivedType<1> > v;
};
Finally, you can write a recursive function template to process this structure:
template <int K>
void process(vov<K>& x) {
doSomething(x.v); // Type inference will find the right doSomething()
process(x.prev); // Here too
}
template <>
void process<1>(vov<1>& x) {
doSomething(x.v);
}
Now the main code will look like:
vov<42> foo;
process(foo);
Because the process() function call performs iteration through the use of recursion, it will probably use K stack frames unnecessarily; however it is tail recursion, which modern optimising C++ compilers can usually convert into plain iteration with no stack wastage. Using tail recursion forces us to process the vectors in "reverse" order, so that the DerivedType<1> vector is processed last, but if necessary this could be fixed with a slightly more elaborate template using 2 int template parameters (one will "count up" towards the other, instead of a single int parameter that "counts down" towards 1).
Observe that there is no benefit gained by deriving each DerivedType<k> from BaseType in this solution -- you may as well forget about BaseType altogether, unless you need it for a different reason.
There may well be MPL primitives that simplify some of these processes -- if anyone knows them, please feel free to edit.
I would like to know if it is possible to have sort of compile time loops.
For example, I have the following templated class:
template<class C, int T=10, int B=10>
class CountSketch
{
public:
CountSketch()
{
hashfuncs[0] = &CountSketch<C>::hash<0>;
hashfuncs[1] = &CountSketch<C>::hash<1>;
// ... for all i until i==T which is known at compile time
};
private:
template<int offset>
size_t hash(C &c)
{
return (reinterpret_cast<int>(&c)+offset)%B;
}
size_t (CountSketch::*hashfuncs[T])(C &c);
};
I would thus like to know if I can do a loop to initialize the T hash functions using a loop. The bounds of the loops are known at compile time, so, in principle, I don't see any reason why it couldn't be done (especially since it works if I unroll the loop manually).
Of course, in this specific example, I could just have made a single hash function with 2 parameters (although it would be less efficient I guess). I am thus not interested in solving this specific problem, but rather knowing if "compile time loops" existed for similar cases.
Thanks!
Nope, it's not directly possible. Template metaprogramming is a pure functional language. Every value or type defined through it are immutable. A loop inherently requires mutable variables (Repeatedly test some condition until X happens, then exit the loop).
Instead, you would typically rely on recursion. (Instantiate this template with a different template parameter each time, until you reach some terminating condition).
However, that can solve all the same problems as a loop could.
Edit: Here's a quick example, computing the factorial of N using recursion at compile-time:
template <int N>
struct fac {
enum { value = N * fac<N-1>::value };
};
template <>
struct fac<0> {
enum { value = 1 };
};
int main() {
assert(fac<4>::value == 24);
}
Template metaprogramming in C++ is a Turing-complete language, so as long as you don't run into various internal compiler limits, you can solve basically any problem with it.
However, for practical purposes, it may be worth investigating libraries like Boost.MPL, which contains a large number of data structures and algorithms which simplify a lot of metaprogramming tasks.
Yes. Possible using compile time recursion.
I was trying with your code but since it was not compilable here is a modified and compiling exmaple:
template<class C, int T=10>
class CountSketch
{
template<int N>
void Init ()
{
Init<N-1>();
hashfuncs[N] = &CountSketch<C>::template hash<N>;
cout<<"Initializing "<<N<<"th element\n";
}
public:
CountSketch()
{
Init<T>();
}
private:
template<int offset>
size_t hash(C &c)
{
return 0;
}
size_t (CountSketch::*hashfuncs[T])(C &c);
};
template<>
template<>
void CountSketch<int,10>::Init<0> ()
{
hashfuncs[0] = &CountSketch<int,10>::hash<0>;
cout<<"Initializing "<<0<<"th element\n";
}
Demo. The only constraint of this solution is that you have to provide the final specialized version as, CountSketch<int,10>::Init<0> for whatever type and size.
You need a combination of boost::mpl::for_each and boost::mpl::range_c.
Note: This will result in run-time code and this is what you actually need. Because there is no way to know the result of operator& at compile time. At least none that I'm aware of.
The actual difficulty with this is to build a struct that is templated on an int parameter (mpl::int_ in our case) and that does the assignment when operator() is called and we also need a functor to actually capture the this pointer.
This is somewhat more complicated than I anticipated but it's fun.
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/copy.hpp>
// aforementioned struct
template<class C, class I>
struct assign_hash;
// this actually evaluates the functor and captures the this pointer
// T is the argument for the functor U
template<typename T>
struct my_apply {
T* t;
template<typename U>
void operator()(U u) {
u(t);
}
};
template<class C, int T=10, int B=10>
class CountSketch
{
public:
CountSketch()
{
using namespace boost::mpl;
// we need to do this because range_c is not an ExtensibleSequence
typedef typename copy< range_c<int, 0, T>,
back_inserter< vector<> > >::type r;
// fiddle together a vector of the correct types
typedef typename transform<r, typename lambda< assign_hash<C, _1 > >::type >
::type assignees;
// now we need to unfold the type list into a run-time construct
// capture this
my_apply< CountSketch<C, T, B> > apply = { this };
// this is a compile-time loop which actually does something at run-time
for_each<assignees>(apply);
};
// no way around
template<typename TT, typename I>
friend struct assign_hash;
private:
template<int offset>
size_t hash(C& c)
{
return c;
// return (reinterpret_cast<int>(&c)+offset)%B;
}
size_t (CountSketch::*hashfuncs[T])(C &c);
};
// mpl uses int_ so we don't use a non-type template parameter
// but get a compile time value through the value member
template<class C, class I>
struct assign_hash {
template<typename T>
void operator()(T* t) {
t->hashfuncs[I::value] = &CountSketch<C>::template hash<I::value>;
}
};
int main()
{
CountSketch<int> a;
}
with C++20 and consteval compile time loops became possible without doing template hell unless the value can have multiple types:
consteval int func() {
int out = 0;
for(int i = 10; i--;) out += i;
return out;
}
int main() {
std::cout << func(); // outputs 45
}
There are compilers that will see the loop and unroll it. But it's not part of the language specification that it must be done (and, in fact, the language specification throws all sorts of barriers in the way of doing it), and there's no guarantee that it will be done, in a particular case, even on a compiler that "knows how".
There are a few languages that explicitly do this, but they are highly specialized.
(BTW, there's no guarantee that the "unrolled" version of your initializations would be done "at compile time" in a reasonably efficient fashion. But most compilers will, when not compiling to a debug target.)
Here is, I think, a better version of the solution given above.
You can see that we use the compile-time recursive on the function params.
This enables putting all the logic inside your class, and the base case of Init(int_<0>) is very clear - just do nothing :)
Just so you won't fear performance penalty, know that the optimizer will throw away these unused parameters.
As a matter of fact, all these function calls will be inlined anyway. that's the whole point here.
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <iostream>
using namespace std;
template <class C, int N = 10, int B = 10>
class CountSketch {
public:
CountSketch() {
memset(&_hashFunctions, sizeof(_hashFunctions), 0); // for safety
Init(int_<N>());
}
size_t HashAll(C& c)
{
size_t v = 0;
for(const auto& h : _hashFunctions)
{
v += (this->*h)(c); // call through member pointer
}
return v;
}
private:
template<int offset>
size_t hash(C &c)
{
return (reinterpret_cast<size_t>(&c)+offset)%B;
}
size_t (CountSketch::*_hashFunctions[N])(C &c);
private: // implementation detail
// Notice: better approach.
// use parameters for compile-time recursive call.
// you can just override for the base case, as seen for N-1 below
template <int M>
struct int_ {};
template <int M>
void Init(int_<M>) {
Init(int_<M - 1>());
_hashFunctions[M - 1] = &CountSketch<C, N, B>::template hash<M>;
printf("Initializing %dth element\n", M - 1);
}
void Init(int_<0>) {}
};
int main() {
int c;
CountSketch<int, 10> cs;
int i;
cin >> i;
printf("HashAll: %d", cs.HashAll(c));
return 0;
}
Compiler Explorer