Non-type template argument, from constant function argument? - c++

I'm experimenting with C++ templates, and a kind of heterogenous type-safe map. Keys go with specific types. An example use would be something like a CSS stylesheet. I've got it to where I can things write:
styles.set<StyleKey::fontFamily>("Helvetica");
styles.set<StyleKey::fontSize>(23.0);
That type checks as desired; it won't compile calls where the key does not match it's intended value type.
But I'm wondering if there's also a way to write it like this:
styles.set(StyleKey::fontFamily, "Helvetica");
styles.set(StyleKey::fontSize, 23.0);
... and have it deduce the same thing, because the first argument is a constant.
Here's my flailing attempt, pasted below and on godbolt. The set2 template does not work.
#include <iostream>
#include <string>
using namespace std;
struct color {
float r,g,b;
};
ostream &operator <<(ostream &out, const color &c) {
return out << "[" << c.r << ',' << c.g << ',' << c.b << "]";
}
// Gives something that would have types: string, float, color, bool
enum class StyleKey {
fontFamily = 1, fontSize, fontColor, visible
};
template <StyleKey key>
struct KeyValueType {
};
struct StyleMap;
template <>
struct KeyValueType<StyleKey::fontFamily> {
typedef string value_type;
static void set(StyleMap *sm, value_type value);
};
template <>
struct KeyValueType<StyleKey::fontSize> {
typedef float value_type;
static void set(StyleMap *sm, value_type value);
};
struct StyleMap {
string fontFamily = "";
float fontSize = 14;
color fontColor = color{0,0,0};
bool visible = true;
template <StyleKey key>
void set(typename KeyValueType<key>::value_type value) {
cout << "set " << (int)key << " value: " << value << endl;
KeyValueType<key>::set(this, value);
}
template <StyleKey key>
void set2(StyleKey key2, typename KeyValueType<key>::value_type value) {
static_assert(key == key2);
cout << "set " << (int)key << " value: " << value << endl;
}
};
void KeyValueType<StyleKey::fontFamily>::set(StyleMap *sm, string str) {
sm->fontFamily = str;
}
void KeyValueType<StyleKey::fontSize>::set(StyleMap *sm, float sz) {
sm->fontSize = sz;
}
void print(const StyleMap &sm) {
cout << "font family : " << sm.fontFamily << endl;
cout << "font size : " << sm.fontSize << endl;
cout << "color : " << sm.fontColor << endl;
cout << "visible : " << sm.visible << endl;
}
int main() {
// Goal:
//
// StyleMap styles;
// styles[fontFamily] = "Helvetica";
// styles[fontSize] = 15.0;
// string fam = styles[fontFamily]
// float sz = styles[fontSize];
StyleMap styles;
// This works!
styles.set<StyleKey::fontFamily>("Helvetica");
styles.set<StyleKey::fontSize>(23.0);
// This won't compile, as desired
// styles.set<StyleKey::fontFamily>(20);
// But can we write it like this?
// styles.set2(StyleKey::fontFamily, "Helvetica");
// styles.set2(StyleKey::fontSize, 23.0);
print(styles);
}

You can't do SFINAE or specialization on function parameters, only template parameters. That means this can't work using your current approach.
What you could do is change your StyleKeys from being enum values to being empty tag structs with different types. Then you could specialize KeyValueType on each of those types and pass an object of one of those types to StyleMap::set. It can then use the type of that object to deduce the correct KeyValueType specialization to dispatch to.
namespace StyleKey {
static constexpr inline struct FontFamily {} fontFamily;
static constexpr inline struct FontSize {} fontSize;
};
template <typename KeyType>
struct KeyValueType {};
struct StyleMap;
template <>
struct KeyValueType<StyleKey::FontFamily> {
using value_type = std::string;
static void set(StyleMap *sm, value_type value);
};
template <>
struct KeyValueType<StyleKey::FontSize> {
using value_type = float;
static void set(StyleMap *sm, value_type value);
};
struct StyleMap {
std::string fontFamily = "";
float fontSize = 14;
template <auto key>
void set(typename KeyValueType<std::remove_const_t<decltype(key)>>::value_type value) {
KeyValueType<std::remove_const_t<decltype(key)>>::set(this, value);
}
template <typename KeyType>
void set2(KeyType key, typename KeyValueType<KeyType>::value_type value) {
KeyValueType<KeyType>::set(this, value);
}
};
void KeyValueType<StyleKey::FontFamily>::set(StyleMap *sm, std::string str) {
sm->fontFamily = str;
}
void KeyValueType<StyleKey::FontSize>::set(StyleMap *sm, float sz) {
sm->fontSize = sz;
}
Demo
Note: This approach will also work for the goal mentioned in the comment in your main function. Example.

Related

Getting field names with boost::pfr

Hi I'm using boost::pfr for basic reflection, it works fine, but the problem is it is only print or deal with the field values, like with boost::pfr::io it prints each member of the struct, but how can I print it as name value pairs, same issue with for_each_field, the functor only accepts values, but not names. How can I get the field names?
struct S {
int n;
std::string name;
};
S o{1, "foo"};
std::cout << boost::pfr::io(o);
// Outputs: {1, "foo"}, how can I get n = 1, name = "foo"?
If you think adapting a struct is not too intrusive (it doesn't change your existing definitions, and you don't even need to have it in a public header):
BOOST_FUSION_ADAPT_STRUCT(S, n, name)
Then you can concoct a general operator<< for sequences:
namespace BF = boost::fusion;
template <typename T,
typename Enable = std::enable_if_t<
// BF::traits::is_sequence<T>::type::value>
std::is_same_v<BF::struct_tag, typename BF::traits::tag_of<T>::type>>>
std::ostream& operator<<(std::ostream& os, T const& v)
{
bool first = true;
auto visitor = [&]<size_t I>() {
os << (std::exchange(first, false) ? "" : ", ")
<< BF::extension::struct_member_name<T, I>::call()
<< " = " << BF::at_c<I>(v);
};
// visit members
[&]<size_t... II>(std::index_sequence<II...>)
{
return ((visitor.template operator()<II>(), ...);
}
(std::make_index_sequence<BF::result_of::size<T>::type::value>{});
return os;
}
(Prior to c++20 this would require some explicit template types instead of the lambdas, perhaps making it more readable. I guess I'm lazy...)
Here's a live demo: Live On Compiler Explorer
n = 1, name = foo
Bonus: Correctly quoting string-like types
Live On Compiler Explorer
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/at_c.hpp>
#include <iostream>
#include <iomanip>
namespace MyLib {
struct S {
int n;
std::string name;
};
namespace BF = boost::fusion;
static auto inline pretty(std::string_view sv) { return std::quoted(sv); }
template <typename T,
typename Enable = std::enable_if_t<
not std::is_constructible_v<std::string_view, T const&>>>
static inline T const& pretty(T const& v)
{
return v;
}
template <typename T,
typename Enable = std::enable_if_t<
// BF::traits::is_sequence<T>::type::value>
std::is_same_v<BF::struct_tag, typename BF::traits::tag_of<T>::type>>>
std::ostream& operator<<(std::ostream& os, T const& v)
{
bool first = true;
auto visitor = [&]<size_t I>() {
os << (std::exchange(first, false) ? "" : ", ")
<< BF::extension::struct_member_name<T, I>::call()
<< " = " << pretty(BF::at_c<I>(v));
};
// visit members
[&]<size_t... II>(std::index_sequence<II...>)
{
return (visitor.template operator()<II>(), ...);
}
(std::make_index_sequence<BF::result_of::size<T>::type::value>{});
return os;
}
} // namespace MyLib
BOOST_FUSION_ADAPT_STRUCT(MyLib::S, n, name)
int main()
{
MyLib::S o{1, "foo"};
std::cout << o << "\n";
}
Outputs:
n = 1, name = "foo"
The library cannot offer any such functionality because it is currently impossible to obtain the name of a member of a class as value of an object.
If you want to output field names, you need to declare string objects mapped with the members and implement a operator<< which uses these strings manually.
To do this a more sophisticated reflection library would probably offer macros to use in the definition of the members. Macros can expand their argument(s) into a declaration using the provided name as identifier while also producing code using the name as string literal (via the # macro replacement operator).
It's stupid but hey, with a stringifying macro per field it could be enough for you.
C++14, no additional library
#include <boost/pfr.hpp>
struct S
{
int n;
std::string name;
static char const* const s_memNames[2];
};
char const* const S::s_memNames[2] = {"n", "name"};
// utility
template< size_t I, typename TR >
char const* MemberName()
{
using T = std::remove_reference_t<TR>;
if (I < std::size(T::s_memNames))
return T::s_memNames[I];
return nullptr;
}
// test:
#include <iostream>
using std::cout;
template< size_t I, typename T >
void StreamAt(T&& inst)
{
char const* n = MemberName<I,T>();
auto& v = boost::pfr::get<I>(inst);
cout << "(" << n << " = " << v << ")";
}
int main()
{
S s{2, "boo"};
boost::pfr::for_each_field(s, [&](const auto&, auto I)
{
StreamAt<decltype(I)::value>(s);
cout << "\n";
});
}
output:
(n = 2)
(name = boo)
(previous version of the suggestion, this one has more fluff so less interesting)
#include <boost/pfr.hpp>
// library additions:
static char const* g_names[100];
template< size_t V >
struct Id : std::integral_constant<size_t, V > {};
template< size_t I, typename T >
using TypeAt = boost::pfr::tuple_element_t<I, T>;
template<std::size_t Pos, class Struct>
constexpr int Ni() // name index
{
return std::tuple_element_t<Pos, typename std::remove_reference_t<Struct>::NamesAt >::value;
}
struct StaticCaller
{
template< typename Functor >
StaticCaller(Functor f) { f();}
};
///
/// YOUR CODE HERE
struct S
{
using NamesAt = std::tuple<Id<__COUNTER__>, Id<__COUNTER__>>; // add this
int n;
std::string name;
static void Init() // add this
{
g_names[Ni<0,S>()] = "n";
g_names[Ni<1,S>()] = "name";
}
};
StaticCaller g_sc__LINE__(S::Init); // add this
// utilities
template< size_t I, typename T >
auto GetValueName(T&& inst)
{
return std::make_pair(boost::pfr::get<I>(inst), g_names[Ni<I,T>()]);
}
// test:
#include <iostream>
using std::cout;
template< size_t I, typename T >
void StreamAt(T&& inst)
{
auto const& [v,n] = GetValueName<I>(inst);
cout << "(" << v << ", " << n << ")";
}
int main()
{
S s{2, "boo"};
boost::pfr::for_each_field(s, [&](const auto&, auto I)
{
StreamAt<decltype(I)::value>(s);
cout << "\n";
});
}
output
(2, n)
(boo, name)

Reading a WORD variable from a CArchive and casting to int at the same time

This might sound basic but:
WORD wSong = 0;
CArchive ar;
...
ar >> wSong;
m_sPublicTalkInfo.iSongStart = static_cast<int>(wSong);
At the moment I read the WORD into a specific variable and the cast it.
Can I read it in and cast at the same time?
Please note I can't serialize a int. It must be a WORD and cast to int.
Or
ar >> wSong;
m_sPublicTalkInfo.iSongStart = static_cast<int>(wSong);
I don't think there is a direct way. You could implement a helper function:
template <typename T, typename U>
T readAndCast (CArchive& ar) {
U x;
ar >> x;
return static_cast<T> (x);
}
m_sPublicTalkInfo.iSongStart = readAndCast<int, WORD>(ar);
It might be better to use the fixed-width integer types in your program, i.e. perhaps int_least16_t instead of int to be sure the type has the right size. WORD is fixed to 16bit, but int isn't. Also, WORD is unsigned and int isn't, so there could be an overflow during casting.
This is a example of how you could create a wrapper if you want the serialize syntax to remain consistent. It's designed to work with integrals and MFC unsigned types only.
#include <iostream>
#include <cstdint>
#include <sstream>
#include <type_traits>
// Fake the MFC types
using BYTE = std::uint8_t;
using WORD = std::uint16_t;
using DWORD = std::uint32_t;
using QWORD = std::uint64_t;
template<typename T>
struct is_valid_save_type : std::bool_constant<
std::is_same_v<BYTE, T> ||
std::is_same_v<WORD, T> ||
std::is_same_v<DWORD, T> ||
std::is_same_v<QWORD, T>
> {};
template<typename T>
struct is_valid_load_type : is_valid_save_type<T> {};
// Saves type T as a SaveType
template<typename T, typename SaveType>
struct save_as_type
{
explicit save_as_type(T value) : value(value) {}
explicit operator SaveType() const
{
return static_cast<SaveType>(value);
}
private:
T value;
// This class only works with integrals
static_assert(std::is_integral_v<T>);
// SaveType should be BYTE/WORD/DWORD/QWORD only
static_assert(is_valid_save_type<SaveType>::value);
};
// Loads type T as a LoadType
template<typename T, typename LoadType>
struct load_as_type
{
explicit load_as_type(T& value) : value_(value) {}
load_as_type& operator=(LoadType rhs)
{
value_ = rhs;
return *this;
}
private:
T& value_;
// T should be an integral
static_assert(std::is_integral_v<T>);
// T must be non-constant
static_assert(!std::is_const_v<T>);
// LoadType should be BYTE/WORD/DWORD/QWORD only
static_assert(is_valid_load_type<LoadType>::value);
};
class CArchive;
// Make the above types serializable
template<typename T, typename SaveType>
CArchive& operator<<(CArchive& ar, save_as_type<T, SaveType> const& s)
{
ar << static_cast<SaveType>(s);
}
template<typename T, typename LoadType>
CArchive& operator>>(CArchive& ar, load_as_type<T, LoadType> l)
{
LoadType t{};
ar >> t;
l = t;
}
// Use the following two functions in your code
template<typename SaveType, typename T>
save_as_type<T, SaveType> save_as(T const& t)
{
return save_as_type<T, SaveType>{ t };
}
template<typename LoadType, typename T>
load_as_type<T, LoadType> load_as(T& t)
{
return load_as_type<T, LoadType>{ t };
}
// Prevent loading into temporaries; i.e. load_as<BYTE>(11);
template<typename T, typename LoadType>
load_as_type<T, LoadType> load_as(const T&& t) = delete;
// Fake MFC Archive
class CArchive
{
public:
CArchive& operator<<(int i)
{
std::cout << "Saving " << i << " as an int\n";
return *this;
}
CArchive& operator<<(BYTE b)
{
std::cout << "Saving " << (int)b << " as a BYTE\n";
return *this;
}
CArchive& operator<<(WORD w)
{
std::cout << "Saving " << (int)w << " as a WORD\n";
return *this;
}
CArchive& operator<<(DWORD d)
{
std::cout << "Saving " << (int)d << " as a DWORD\n";
return *this;
}
CArchive& operator>>(int& i)
{
std::cout << "Loading as an int\n";
return *this;
}
CArchive& operator>>(BYTE& b)
{
std::cout << "Loading as a BYTE\n";
return *this;
}
CArchive& operator>>(WORD& w)
{
std::cout << "Loading as a WORD\n";
return *this;
}
CArchive& operator>>(DWORD& d)
{
std::cout << "Loading as a DWORD\n";
return *this;
}
};
int main()
{
CArchive ar;
int out_1 = 1;
int out_2 = 2;
int out_3 = 3;
int out_4 = 4;
ar << out_1 <<
save_as<BYTE>(out_2) <<
save_as<WORD>(out_3) <<
save_as<DWORD>(out_4);
std::cout << "\n";
int in_1 = 0;
int in_2 = 0;
int in_3 = 0;
int in_4 = 0;
ar >> in_1 >>
load_as<BYTE>(in_2) >>
load_as<WORD>(in_3) >>
load_as<DWORD>(in_4);
return 0;
}
Output:
Saving 1 as an int
Saving 2 as a BYTE
Saving 3 as a WORD
Saving 4 as a DWORD
Loading as an int
Loading as a BYTE
Loading as a WORD
Loading as a DWORD

use enum type to manage storage order

I'm trying to use enum types to indexig some array but I want to allow different ordering of the vector depending on some option. In the class I also want functions that take the enum variable as input and use it as it should.
The solution I found is the following
#include<iostream>
#include<array>
#include<vector>
struct A{
struct XYZ{
enum coord{X=0,Y,Z};
};
struct YZX{
enum coord{Y=0,Z,X};
};
struct ZXY{
enum coord{Z=0,X,Y};
};
std::array<std::vector<float>,3> val;
void resize(int opt, size_t dim){
val[opt].resize(dim);
return;
}
void printsize(){
for(auto & i : val){
std::cout << i.size() << " ";
}
std::cout << std::endl;
return;
}
};
int main(){
A foo1;
A foo2;
A foo3;
foo1.resize(XYZ::X,10);
foo2.resize(YZX::X,10);
foo3.resize(ZXY::X,10);
std::cout << "Size foo1\n";
foo1.printsize();
std::cout << "Size foo2\n";
foo2.printsize();
std::cout << "Size foo3\n";
foo3.printsize();
return 0;
}
What I don't like in this solution is that my function resize takes an integer type as input and there's no type control of the enum.
Is there any other smarter solution? Am I doing something considered as anti-pattern?
Thank you
I suggest you to modify the member function resize (three parameters instead of two) and exploit the type safety of the enum classes:
#include <stdio.h>
#include<iostream>
#include<array>
#include<vector>
struct A{
enum class Coordinate
{
X = 0,
Y = 1,
Z = 2
};
enum class Permutation
{
XYZ = 0,
ZXY = 1,
YZX = 2
};
std::array<std::vector<float>,3> val;
/* resize takes three parameters now */
void resize(Permutation p, Coordinate c, size_t dim)
{
int index = ( static_cast<int>(p) + static_cast<int>(c) ) % 3 ;
val[index].resize(dim);
return;
}
void printsize(){
for(auto & i : val){
std::cout << i.size() << " ";
}
std::cout << std::endl;
return;
}
};
int main()
{
A foo1;
A foo2;
A foo3;
foo1.resize(A::Permutation::XYZ, A::Coordinate::X,10);
foo2.resize(A::Permutation::YZX, A::Coordinate::X,10);
foo3.resize(A::Permutation::ZXY, A::Coordinate::X,10);
std::cout << "Size foo1\n";
foo1.printsize();
std::cout << "Size foo2\n";
foo2.printsize();
std::cout << "Size foo3\n";
foo3.printsize();
return 0;
}
How about an Index class, constructible from several enum classes?
struct A
{
enum class XYZ {X,Y,Z};
enum class YZX {Y,Z,X};
enum class ZXY {Z,X,Y};
struct Index
{
int value;
operator int() const {return value;}
Index(XYZ value) : value(int(value)) {}
Index(YZX value) : value(int(value)) {}
Index(ZXY value) : value(int(value)) {}
};
std::array<std::vector<float>, 3> val;
void resize(Index opt, size_t dim)
{
val[opt].resize(dim);
}
void printsize() const
{
for (const auto &i : val)
std::cout << i.size() << ' ';
std::cout << '\n';
}
};

c++ Unable to resolve overloaded function template

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

direct access of terminating case of variadic template class works, but convenience class access fails compilation

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);
}