I'm writing a chess engine, and I have a function that looks like this:
U64 find_moves(Piece type, Team side, uint8_t square, U64 occupied) {
switch (type) {
case PAWN: {
U64 result = 0;
result |= occupied & bb_normal_moves::pawn_caps[side][square];
if (!(occupied & bb_normal_moves::pawn_moves_x1[side][square])) {
result |= bb_normal_moves::pawn_moves_x1[side][square];
if (!(occupied & bb_normal_moves::pawn_moves_x2[side][square])) {
result |= bb_normal_moves::pawn_moves_x2[side][square];
}
}
return result;
}
case KNIGHT:
return bb_normal_moves::knight_moves[square];
case BISHOP:
return bb_magics::bishop_moves(square, occupied);
case ROOK:
return bb_magics::rook_moves(square, occupied);
case QUEEN:
return bb_magics::bishop_moves(square, occupied) | bb_magics::rook_moves(square, occupied);
case KING:
return bb_normal_moves::king_moves[square];
}
return 0; // Can't happen
}
It essentially delegates to another function call depending on the type parameter. In many places around the program, this function is called after looping through different Piece values, which happens to be an enum.
Unfortunately, that means that this function is called each time in that loop, so a lot of CPU time is wasted in this function branching.
What I'd like to do is change this function to allow the compiler to optimise the calls:
template <Piece type> U64 find_moves(Team side, uint8_t square, U64 occupied)
but then my loops would not compile as the target of the function call cannot be resolved at compile time.
Is there a way of optimising this function without manually unrolling all of my loops?
EDIT: Here is an example of one of the loops that calls find_moves:
for (uint8_t piece = 1; piece < 6; piece++) {
move.info.piece = piece;
U64 bb_piece = board.bb_pieces[team][piece];
while (bb_piece) {
uint8_t from = pop_bit(team, bb_piece);
move.info.from = from;
U64 bb_targets = find_moves((Piece) piece, team, from, board.bb_all) & mask;
while (bb_targets) {
uint8_t to = pop_bit(x_team, bb_targets);
move.info.to = to;
buf[buf_size++] = move;
}
}
}
Given that the values of your Piece enum are from 1 to 6, you can use templates, std::make_index_sequence, std::index_sequence to unroll.
Sorry but I can only prepare a minimal example (no move, no board, etc).
If you call something as
foo(std::make_index_sequence<6U>{});
in foo() you can call another function with singles values (you tagged C++17 so you can use template folding)
template <std::size_t ... Ps>
void foo (std::index_sequence<Ps...> const &)
{ (bar<Ps>(), ...); }
My idea is that in bar() you can place the content of the body of the for (uint8_t piece = 1; piece < 6; piece++) loop of your example; I place only the call to a trivial (no other arguments) find_moves() function.
template <std::size_t Ps>
void bar ()
{ find_moves<pieces(1+Ps)>(); }
Now you can develop six find_moves() template function using full specialization (I write only std::cout messages; you, using other arguments, can place the content of the cases in your switch.
template <pieces P>
void find_moves ();
template <>
void find_moves<Pawn> ()
{ std::cout << "Case Pawn" << std::endl; }
template <>
void find_moves<Knight> ()
{ std::cout << "Case Knight" << std::endl; }
template <>
void find_moves<Bishop> ()
{ std::cout << "Case Bishop" << std::endl; }
template <>
void find_moves<Rook> ()
{ std::cout << "Case Rook" << std::endl; }
template <>
void find_moves<Queen> ()
{ std::cout << "Case Queen" << std::endl; }
template <>
void find_moves<King> ()
{ std::cout << "Case King" << std::endl; }
The following is a full compiling example
#include <iostream>
#include <utility>
#include <type_traits>
enum pieces { Pawn = 1, Knight, Bishop, Rook, Queen, King };
template <pieces P>
void find_moves ();
template <>
void find_moves<Pawn> ()
{ std::cout << "Case Pawn" << std::endl; }
template <>
void find_moves<Knight> ()
{ std::cout << "Case Knight" << std::endl; }
template <>
void find_moves<Bishop> ()
{ std::cout << "Case Bishop" << std::endl; }
template <>
void find_moves<Rook> ()
{ std::cout << "Case Rook" << std::endl; }
template <>
void find_moves<Queen> ()
{ std::cout << "Case Queen" << std::endl; }
template <>
void find_moves<King> ()
{ std::cout << "Case King" << std::endl; }
template <std::size_t Ps>
void bar ()
{ find_moves<pieces(1+Ps)>(); }
template <std::size_t ... Ps>
void foo (std::index_sequence<Ps...> const &)
{ (bar<Ps>(), ...); }
int main ()
{
foo(std::make_index_sequence<6U>{});
}
Related
So I had this scenario where I want to call either function of a class, where the function in question has the same prototype but it's also overloaded. Since I know of pointer to members my immediate reaction was something like this:
struct test
{
int overloaded(char) {}
int overloaded(int) {}
int overloadedone(char) {}
int overloadedone(int) {}
} test;
int main()
{
(test.*(true ? (&test::overloaded) : (&test::overloadedone)))(1);
}
However it turned out the compiler (MSVC - 2019 Preview latest version with std C++ preview) can't deduce the type and I have to write:
(test.*(true ? static_cast<int (test::*)(int)>(&test::overloaded) : static_cast<int (test::*)(int)>(&test::overloadedone)))(1);
instead which made me return to the good old:
true ? test.overloaded(1) : test.overloadedone(1);
But I wonder if this is the defined behavior of requiring those cast. Even:
(test.*static_cast<int (test::*)(int)>(true ? (&test::overloaded) : (&test::overloadedone)))(1);
Doesn't work.
You have to write said cast on each of the two possibilities for the ternary as in the second example.
It isn't particularly elegant, but this approach can deduce an overload if you curry the member function pointer's arguments before passing the member function pointers themselves:
#include <iostream>
template <class... Args>
auto invoke_conditional_mem_fn(Args... args)
{
return [=] <class R, class X> (X x, bool b, R(X::*t)(Args...), R(X::*f)(Args...)) -> R
{
return (x.*(b ? t : f))(args...);
};
}
struct test
{
int overloaded(char) { std::cout << "overloaded(char) "; return 1; }
int overloaded(int) { std::cout << "overloaded(int) "; return 2; }
int overloadedone(char) { std::cout << "overloadedone(char) "; return 3; }
int overloadedone(int) { std::cout << "overloadedone(int) "; return 4; }
} test;
int main()
{
std::cout
<< invoke_conditional_mem_fn('1')(test, true, &test::overloaded, &test::overloadedone)
<< std::endl
<< invoke_conditional_mem_fn(1)(test, false, &test::overloaded, &test::overloadedone)
<< std::endl;
}
Thanks to #dyp and their example, we know that we can infer the return type and base of the member function pointers if we select which arguments to pass.
Alternatively, you could do something a little simpler like this, if it meets your needs. Just declare a lambda to work around the limitations of your ternary expression with an if and else statement since each branch of a ternary operator is required to be of the same type.
#include <iostream>
struct test
{
int overloaded(char) { std::cout << "overloaded(char) "; return 1; }
int overloaded(int) { std::cout << "overloaded(int) "; return 2; }
int overloadedone(char) { std::cout << "overloadedone(char) "; return 3; }
int overloadedone(int) { std::cout << "overloadedone(int) "; return 4; }
} test;
auto conditional = [] (struct test& test, bool cond, auto... args)
{
if (cond) return test.overloaded(args...);
else return test.overloadedone(args...);
};
int main()
{
std::cout << conditional(test, true, '1') << std::endl;
std::cout << conditional(test, false, 1) << std::endl;
}
I have some var = std::variant<std::monostate, a, b, c> when a, b, c is some types.
How, at runtime, do I check what type var contains?
In the official documentation I found information that if var contains a type and I write std::get<b>(var) I get an exception. So I thought about this solution:
try {
std::variant<a>(var);
// Do something
} catch(const std::bad_variant_access&) {
try {
std::variant<b>(var);
// Do something else
} catch(const std::bad_variant_access&) {
try {
std::variant<c>(var);
// Another else
} catch (const std::bad_variant_access&) {
// std::monostate
}
}
}
But it's so complicated and ugly! Is there a simpler way to check what type std::variant contains?
std::visit is the way to go:
There is even overloaded to allow inlined visitor:
// helper type for the visitor #4
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
// explicit deduction guide (not needed as of C++20)
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
and so:
std::visit(overloaded{
[](std::monostate&){/*..*/},
[](a&){/*..*/},
[](b&){/*..*/},
[](c&){/*..*/}
}, var);
To use chained if-branches instead, you might used std::get_if
if (auto* v = std::get_if<a>(var)) {
// ...
} else if (auto* v = std::get_if<b>(var)) {
// ...
} else if (auto* v = std::get_if<c>(var)) {
// ...
} else { // std::monostate
// ...
}
The most simple way is to switch based on the current std::variant::index(). This approach requires your types (std::monostate, A, B, C) to always stay in the same order.
// I omitted C to keep the example simpler, the principle is the same
using my_variant = std::variant<std::monostate, A, B>;
void foo(my_variant &v) {
switch (v.index()) {
case 0: break; // do nothing because the type is std::monostate
case 1: {
doSomethingWith(std::get<A>(v));
break;
}
case 2: {
doSomethingElseWith(std::get<B>(v));
break;
}
}
}
If your callable works with any type, you can also use std::visit:
void bar(my_variant &v) {
std::visit([](auto &&arg) -> void {
// Here, arg is std::monostate, A or B
// This lambda needs to compile with all three options.
// The lambda returns void because we don't modify the variant, so
// we could also use const& arg.
}, v);
}
If you don't want std::visit to accept std::monostate, then just check if the index is 0. Once again, this relies on std::monostate being the first type of the variant, so it is good practice to always make it the first.
You can also detect the type using if-constexpr inside the callable. With this approach, the arguments don't have to be in the same order anymore:
void bar(my_variant &v) {
std::visit([](auto &&arg) -> my_variant {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<std::monostate, T>) {
return arg; // arg is std::monostate here
}
else if constexpr (std::is_same_v<A, T>) {
return arg + arg; // arg is A here
}
else if constexpr (std::is_same_v<B, T>) {
return arg * arg; // arg is B here
}
}, v);
}
Note that the first lambda returns void because it just processes the current value of the variant. If you want to modify the variant, your lambda needs to return my_variant again.
You could use an overloaded visitor inside std::visit to handle A or B separately. See std::visit for more examples.
You can use standard std::visit
Usage example:
#include <variant>
#include <iostream>
#include <type_traits>
struct a {};
struct b {};
struct c {};
int main()
{
std::variant<a, b, c> var = a{};
std::visit([](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, a>)
std::cout << "is an a" << '\n';
else if constexpr (std::is_same_v<T, b>)
std::cout << "is a b" << '\n';
else if constexpr (std::is_same_v<T, c>)
std::cout << "is a c" << '\n';
else
std::cout << "is not in variant type list" << '\n';
}, var);
}
Well, with some macro magic, you can do something like:
#include <variant>
#include <type_traits>
#include <iostream>
#define __X_CONCAT_1(x,y) x ## y
#define __X_CONCAT(x,y) __X_CONCAT_1(x,y)
template <typename T>
struct __helper { };
// extract the type from a declaration
// we use function-type magic to get that: typename __helper<void ( (declaration) )>::type
// declaration is "int &x" for example, this class template extracts "int"
template <typename T>
struct __helper<void (T)> {
using type = std::remove_reference_t<T>;
};
#define variant_if(variant, declaration) \
if (bool __X_CONCAT(variant_if_bool_, __LINE__) = true; auto * __X_CONCAT(variant_if_ptr_, __LINE__) = std::get_if<typename __helper<void ( (declaration) )>::type>(&(variant))) \
for (declaration = * __X_CONCAT(variant_if_ptr_, __LINE__); __X_CONCAT(variant_if_bool_, __LINE__); __X_CONCAT(variant_if_bool_, __LINE__) = false)
#define variant_switch(variant) if (auto &__variant_switch_v = (variant); true)
#define variant_case(x) variant_if(__variant_switch_v, x)
int main() {
std::variant<int, long> v = 12;
std::variant<int, long> w = 32l;
std::cout << "variant_if test" << std::endl;
variant_if(v, int &x) {
std::cout << "int = " << x << std::endl;
}
else variant_if(v, long &x) {
std::cout << "long = " << x << std::endl;
}
std::cout << "variant_switch test" << std::endl;
variant_switch(v) {
variant_case(int &x) {
std::cout << "int = " << x << std::endl;
variant_switch (w) {
variant_case(int &x) {
std::cout << "int = " << x << std::endl;
}
variant_case(long &x) {
std::cout << "long = " << x << std::endl;
}
}
};
variant_case(long &x) {
std::cout << "long = " << x << std::endl;
variant_switch (w) {
variant_case(int &x) {
std::cout << "int = " << x << std::endl;
}
variant_case(long &x) {
std::cout << "long = " << x << std::endl;
}
}
};
}
return 0;
}
I tested this approach with GCC and Clang, no guarantees for MSVC.
How can I determine whether a function's parameter type is a function? I'm implementing a class called Queue which receives a single parameter. If the parameter is a function, it stores the function.
Here is the code:
template <class Type, typename Data>
class Queue {
public:
void Enqueue (Data& data) {
if (typeid(data).name() == int) {
intVector.push_back(data);
order.push_back("int");
}
else if (typeid(data).name() == bool) {
boolVector.push_back(data);
order.push_back("bool");
}
else if (typeid().name() == string) {
stringVector.push_back(data);
order.push_back("string");
}
// This will continue for:
// - double
// - char
// - function
}
auto Dequeue () {
auto temp;
switch (order.begin()) {
case "int":
temp = intVector.begin();
intVector.erase(intVector.begin());
order.erase(order.begin());
return temp;
// This will continue for:
// - "string"
// - "bool"
// - "char"
// - "double"
// - "function"
default:
cout << "An Error occurred while trying to Enqueue." << endl;
cout << "\tAddress: " << this << endl;
}
}
auto Start () {
// This function will run all of the processes...
}
Queue (Data& data) {
if (typeid(Type).name() == int) {
// Pseodo-code:
// if (data.type == function) {
// Enqueue (data);
// }
}
}
}
It can be initialised:
Queue queue1 = new Queue <int> (func ()); // func () is a function.
Queue queue2 = new Queue <int> (var); // var is a variable.
Oh my. This is a bit of an XY problem.
Anyway, after messing around with std::enable_if for a bit (which was kinda fun), I realised that the whole thing can be boiled down to this:
#include <vector>
#include <string>
#include <any>
#include <iostream>
#include <functional>
void call_if_function (void (* f) ()) { f (); }
void call_if_function (std::function <void ()> f) { f (); }
void call_if_function (std::any x) { (void) x; }
template <class T>
class Queue
{
public:
void Enqueue (const T& data)
{
// std::cout << "Enqueueing " << data << "\n";
v.push_back (data);
}
T Dequeue ()
{
T ret = v.front ();
// std::cout << "Dequeueing " << ret << "\n";
v.erase (v.begin ());
call_if_function (ret);
return ret;
}
private:
std::vector <T> v;
};
And, if I understand the OP's problem right, that is all all you need.
Test program:
void foo () { std::cout << "foo () called\n"; }
void bar (int x, int y) { std::cout << "bar () called, x = " << x << ", y = " << y << "\n"; }
int main ()
{
// Queue of int's
Queue <int> int_q;
int_q.Enqueue (42);
auto i = int_q.Dequeue ();
std::cout << "int_q.Dequeue () returned " << i << "\n\n";
// Queue of strings
Queue <std::string> string_q;
string_q.Enqueue ("Hello world");
auto s = string_q.Dequeue ();
std::cout << "string_q.Dequeue () returned " << s << "\n\n";
// Call function with no parameters
Queue <void (*)()> func_q;
func_q.Enqueue (foo);
auto f = func_q.Dequeue ();
std::cout << "func_q.Dequeue () returned " << (void *) f << "\n";
f ();
// Call function with arbitrary parameters
Queue <std::function <void ()>> func_qp;
func_qp.Enqueue ([] () { bar (21, 99); });
auto fp = func_qp.Dequeue ();
fp ();
}
Output:
int_q.Dequeue () returned 42
string_q.Dequeue () returned Hello world
foo () called
func_q.Dequeue () returned 0x4026fd
foo () called
bar () called, x = 21, y = 99
bar () called, x = 21, y = 99
Live demo.
Moral: KISS, there are far too many toys in the toybox these days. Enjoy the weekend people.
And, since I took the time to research it a bit (mainly because I wanted to learn a bit about it), here is a bit of super-simple SFINAE cobbled together from the wise ones.
#include <type_traits>
#include <iostream>
// Primary template (required)
template <class T, class Enable = void>
struct X { };
// Specialisation to take a function pointer
template <class T>
struct X <T, typename std::enable_if <std::is_function<T>::value>::type>
{
X (T func)
{
std::cout << "T is a function\n";
func ();
}
};
// Partial specialisation for anything else
template<class T>
struct X <T, typename std::enable_if <!std::is_function<T>::value>::type>
{
X (T x)
{
std::cout << "T is not a function (and x is " << x << ")\n";
}
};
void foo () { std::cout << "foo () called\n"; }
int main ()
{
X <void ()> x1 (foo);
X <int> x2 (42);
}
Output:
T is a function
foo () called
T is not a function (and x is 42)
Live demo.
Powerful stuff, but not the answer to every little problem.
How can I determine whether a function's parameter type is a function?
You can use the std::is_function to do that.
An implementation like
template <class Type, typename Data>
class Queue {
public:
Queue (Data& data) {
if (typeid(Type).name() == int) {
// Pseodo-code:
if (std::is_function<data.type>::value) {
Enqueue (data);
}
}
}
}
won't work though, since the part inside the if block scope is seen for other data types by the compiler.
To realize that you'll need to use SFINAE, and provide different specializations of your Queue constructor function using std::enable_if.
For a SFINAE example:
Queue(std::enable_if_t<std::is_function_v<Data>, Data>::type & data)
{
//data is a function
}
//I'm not sure if the enable_if is needed here, maybe you can just do Data& data
Queue(std::enable_if_t<!std::is_function_v<Data>, Data>::type & data)
{
//data is not a function
}
(You need to include <type_traits> to use std::is_function_v)
First, I don't understand why the compiler can't seem to resolve the offending code (wrapped by the ENABLE_OFFENDER macro). The two get() methods have very different call signatures. So, perhaps a c++ language lawyer could help explain why I'm getting the error.
Second, is there a way to provide more guidance to the compiler as to which get() it should use?
#include <iostream>
//switch offending code in/out
#define ENABLE_OFFENDER
/// Forward declare
template <typename T> class TableEntry;
/// Non-template base class
class TableEntryBase {
protected:
// prevent instantiation
TableEntryBase() { }
public:
virtual ~TableEntryBase() { }
template <typename T>
void set(const T& rvalue){
TableEntry<T>* entry = dynamic_cast<TableEntry<T> *>(this);
if(0 != entry){
entry->setValue(rvalue);
}
}
template <typename T>
T get(T& lvalue){
TableEntry<T>* entry = dynamic_cast<TableEntry<T> *>(this);
if(0 != entry){
return entry->getValue(lvalue);
} else {
return T();
}
}
template <typename T>
T get(){
TableEntry<T>* entry = dynamic_cast<TableEntry<T> *>(this);
if(0 != entry){
return entry->getValue();
} else {
return T();
}
}
};
template <typename T>
class TableEntry : public TableEntryBase {
private:
T m_value;
public:
TableEntry():TableEntryBase() { }
~TableEntry() { }
void setValue(const T& rvalue){
m_value = rvalue;
}
T getValue(T& lvalue){
lvalue = m_value;
return m_value;
}
T getValue(){
return m_value;
}
};
template <int N>
class Table {
private:
TableEntryBase* m_tableEntries[N];
int m_tableEntriesIndex;
public:
Table():m_tableEntriesIndex(0){}
virtual ~Table() { }
void addEntry(TableEntryBase* entry){
if(0 != entry)
m_tableEntries[m_tableEntriesIndex++] = entry;
}
template <typename T>
void setEntry(int entryIndex, const T& rvalue){
// I'm not sure why it's not set<T>(rvalue)
m_tableEntries[entryIndex]->set(rvalue);
}
template <typename T>
T getEntry(int entryIndex, T& lvalue){
return m_tableEntries[entryIndex]->get(lvalue);
}
#ifdef ENABLE_OFFENDER
template <typename T>
T getEntry(int entryIndex){
return m_tableEntries[entryIndex]->get();
}
#endif
};
int main(){
TableEntry<int> entry1;
// setting the value using base class set
entry1.set<int>(5);
TableEntry<double> entry2;
entry2.set<double>(3.14);
std::cout << "entry1 value = " << entry1.getValue() << std::endl;
std::cout << "entry2 value = " << entry2.getValue() << std::endl;
std::cout << "entry1 value = " << entry1.get<int>() << std::endl;
std::cout << "entry2 value = " << entry2.get<double>() << std::endl;
TableEntryBase* entry_ptr = &entry1;
std::cout << "entry1 value = " << entry_ptr->get<int>() << std::endl;
Table<2> table;
table.addEntry(&entry1);
table.addEntry(&entry2);
table.setEntry<int>(0, 10);
std::cout << "entry1 value = " << entry1.get<int>() << std::endl;
std::cout << "entry2 value = " << entry2.get<double>() << std::endl;
int val3 = 0;
int val4 = 0;
val4 = table.getEntry<int>(0, val3);
int val5 = 0;
#ifdef ENABLE_OFFENDER
val5 = table.getEntry<int>(0);
#endif
std::cout << "val3 = " << val3 << ", val4 = " << val4 << ", val5 = " << val5 << std::endl;
return 0;
}
/* GCC error
main.cpp:129:30: instantiated from here
main.cpp:96:95: error: no matching function for call to ‘TableEntryBase::get()’
main.cpp:96:95: note: candidates are:
main.cpp:26:4: note: template<class T> T TableEntryBase::get(T&)
main.cpp:36:4: note: template<class T> T TableEntryBase::get()
*/
I got the code to work on my older compilers by changing the offending code as such.
#ifdef ENABLE_OFFENDER
template <typename T>
T getEntry(int entryIndex){
// Error: expected primary-expression before ‘>’ token
//return m_tableEntries[entryIndex]->get<T>();
// Error: couldn't deduce template parameter ‘T’
//return m_tableEntries[entryIndex]->get();
TableEntryBase* entry = m_tableEntries[entryIndex];
return entry->get<T>();
}
#endif
The goal of the code below is to implement a histogram where the bucket limits are template parameters:
#include <iostream>
#include <limits>
#include "histogram.h"
int main ( int argc, char* argv[] )
{
//histogram_tuple<5,10,15,std::numeric_limits<int>::max()> histogram;
histogram_tuple<5,10,15> histogram;
histogram.count ( 9 );
histogram.count ( 10 );
histogram.count ( 11 );
histogram.count ( 15 );
std::cout << sizeof ( histogram ) << std::endl;
std::cout << '<' << histogram.limit() << ' ' << histogram.count() << ", "
<< '<' << histogram.rest().limit() << ' ' << histogram.rest().count() << ", "
<< '<' << histogram.rest().rest().limit() << ' ' << histogram.rest().rest().count() << ", "
<< ' ' << histogram.rest().rest().rest().count()
<< std::endl;
std::cout << "====" << std::endl;
std::cout << '<' << bucket_limit<0>(histogram) << ':'
<< bucket_count<0>(histogram) << std::endl;
std::cout << '<' << bucket_limit<1>(histogram) << ':'
<< bucket_count<1>(histogram) << std::endl;
std::cout << '<' << bucket_limit<2>(histogram) << ':'
<< bucket_count<2>(histogram) << std::endl;
// std::cout << '<' << bucket_limit<3>(histogram) << ':'
// << bucket_count<3>(histogram) << std::endl;
}
The above works fine. With the repeated rest() calls, the count of the final bucket (values >= 15) is printed.
However, when the final line of main() is uncommented, g++ 4.7.1 generates a compiler error that bucket_limit_entry<0u> and bucket_count_entry<0u> are incomplete.
Any advice on how to get the convenience functions bucket_limit<3> to compile, since the repeated rest() calls work?
Not really sure what's going on. Changing the index type to int and making the termination case -1 instead of 0 didn't work.
Here's histogram.h:
#pragma once
template <int ... Limits>
class histogram_tuple;
template<>
class histogram_tuple<>
{
int cnt_;
public:
histogram_tuple<>() :
cnt_ ( 0 )
{
}
void count ( int value )
{
++cnt_;
}
int count() const
{
return cnt_;
}
};
template <int First, int ... Rest>
class histogram_tuple <First,Rest...> :
private histogram_tuple<Rest...>
{
static const int limit_ = First;
int cnt_;
public:
histogram_tuple <First,Rest...>() :
cnt_ ( 0 )
{ }
int limit() const { return limit_; }
void count ( int value )
{
if ( value < limit_ )
++cnt_;
else
rest().count ( value );
}
int count() const
{
return cnt_;
}
const histogram_tuple<Rest...>& rest() const
{
return *this;
}
histogram_tuple<Rest...>& rest()
{
return *this;
}
};
template <unsigned index, int ... Limits>
struct bucket_count_entry;
template <int First, int ... Limits>
struct bucket_count_entry<0,First,Limits...>
{
static int value(histogram_tuple<First,Limits...> const& histogram)
{
return histogram.count();
}
};
template <unsigned index,int First, int ... Limits>
struct bucket_count_entry<index,First,Limits...>
{
static int value(histogram_tuple<First,Limits...> const& histogram)
{
return bucket_count_entry<index-1,Limits...>::value(histogram.rest());
}
};
template <unsigned index,int ... Limits>
int bucket_count( histogram_tuple<Limits...> const& histogram )
{
return bucket_count_entry<index,Limits...>::value(histogram);
}
template <unsigned index, int ... Limits>
struct bucket_limit_entry;
template <int First, int ... Limits>
struct bucket_limit_entry<0,First,Limits...>
{
static int value(histogram_tuple<First,Limits...> const& histogram)
{
return histogram.limit();
}
};
template <unsigned index,int First, int ... Limits>
struct bucket_limit_entry<index,First,Limits...>
{
static int value(histogram_tuple<First,Limits...> const& histogram)
{
return bucket_limit_entry<index-1,Limits...>::value(histogram.rest());
}
};
template <unsigned index,int ... Limits>
int bucket_limit( histogram_tuple<Limits...> const& histogram )
{
return bucket_limit_entry<index,Limits...>::value(histogram);
}
template <int First, int ... Limits>
bucket_limit_entry<0,First,Limits...>
won't match
bucket_limit_entry<0>
because First won't match nothing. (...Limits matches nothing, but First can only match one int).
So you need to add an additional template for the case where you've run out of limits:
template<>
struct bucket_limit_entry<0>
When you do that, you'll find that histogram<>::limit() is undefined, but you can easily fix that.
You'll need to do the same with bucket_count_entry, except that histogram<>::count() is defined.
The fact that you can't just define template<int...Limits> struct bucket_limit_entry<0, Limits...> {...} is a bit odd. The problem, as I understand it, is that both "Index is 0" and "Limits... has at least one element", are restrictions on the general template, and there is no arbitrary ordering between them. Consequently, template<int...Limits> struct X<0, Limits...> and template<unsigned index, int First, int...Rest> struct X<index, First, Rest...> are not ordered by the partial ordering for template specialization, and when both of them apply, you end up with an ambiguity.
But it seems to me that there is a simpler solution, since you can let the type of the histogram_tuple just be deduced:
template<unsigned Index> struct bucket_limit_entry {
template<typename Hist>
static int value(Hist const& histogram) {
return bucket_limit_entry<Index-1>::value(histogram.rest());
}
};
template<> struct bucket_limit_entry<0> {
template<typename Hist>
static int value(Hist const& histogram) {
return histogram.limit();
}
};
template<unsigned index, typename Hist>
int bucket_limit(Hist const& histogram ) {
return bucket_limit_entry<index>::value(histogram);
}