Defining traits for templatised classes - c++

I understand how to create type traits and then specialise for a particular class, but in my case I would like to specialise for a class template. The code below does not compile, but the idea is that the specialisation of Traits for MyTemplatisedClass should work for which ever type the user decides to use with MyTemplatisedType.
class Traits
{
public:
static bool someProperty(void) { return false; }
};
template<typename Type>
class MyTemplatisedClass
{
};
template<typename Type>
template<>
class Traits< MyTemplatisedClass<Type> >
{
public:
static bool someProperty(void) { return true; }
};
int main(int argc, char* argv[])
{
std::cout << Traits< MyTemplatisedClass<float> >::someProperty() <<std::endl; //This should be true
return 0;
}
Is this possible or am I asking too much? According to the compiler the first problem is
error C2989: 'Traits' : class template has already been declared as a non-class template
Which is correct, but how do I fix this? If it makes any difference I don't need it to work for non-templatised classes, just templatised ones is fine. Edit: Actually it would be nice if it worked for both templatised and non-templatised classes.

Traits needs to be a template in order to be specialized.
In the specialization drop the line with empty <>: this isn't a template nested within a template or something.
template <typename Type> //can only specialize templates
class Traits
{
public:
static bool someProperty(void) { return false; }
};
template<typename Type>
class MyTemplatisedClass
{
};
template<typename Type>
//template<> //Too much here
class Traits< MyTemplatisedClass<Type> >
{
public:
static bool someProperty(void) { return true; }
};
But if you meant the specialization for any template with one type argument, then that would be:
template < template <class> class SomeTemplatizedType, class Type>
// ^^^^^^^^^^^^^^^^^^^^^^
// names a template, not type
class Traits< SomeTemplatizedType<Type> >;
// ^^^^^^^^^^^^^^^^^^^ ^
// template name |
// argument

The initial class needs to be the "base case", that is, templated to accept any type argument. Then you can worry about what other specializations you'd like to invoke.
template<typename T> class Traits
{
public:
static bool someProperty(void) { return false; }
};
template<typename Type>
class MyTemplatisedClass
{
};
template<typename Type> class Traits< MyTemplatisedClass<Type> >
{
public:
static bool someProperty(void) { return true; }
};
In order to actually use this in compile-time computation, you will need to make it an ICE- integral constant expression. A function cannot be constexpr even if, trivially, it's value is knowable at compile-time. As it stands, I cannot do, for example,
template<typename T> std::enable_if<Traits<T>::value, sometype> somefunc();

The problem is that you've declared Traits as a class, not a class template. Just add template<typename> to the definition of Traits, and remove the spurious template<> from the specialisation and it should be fine.
template<typename> // <--- Add this
class Traits
{
public:
static bool someProperty(void) { return false; }
};
template<typename Type>
class MyTemplatisedClass
{
};
template<typename Type>
// template<> // <--- Remove this
class Traits< MyTemplatisedClass<Type> >
{
public:
static bool someProperty(void) { return true; }
};
int main(int argc, char* argv[])
{
std::cout << Traits< MyTemplatisedClass<float> >::someProperty() <<std::endl; //This should be true
return 0;
}

Related

Idiomatic C++11 for delegating template specialisations to a default implementation

I'm making a struct Box<T> that handles some data. The specifics are unimportant.
An important note however is that Box<T> can store a pointer, but it might not. So both Box<int> and Box<int *> are valid. Obviously, if we own Box.data, we're going to need to delete data if it is a pointer type.
Here's a solution I came up with that works in C++11:
template <typename T> struct BoxTraits;
template <typename T> struct Box {
using traits_t = BoxTraits<T>;
T data;
~Box() = default; // not required, I know
T get_data() { return traits_t::get_data(this); }
};
template <typename T> struct Box<T *> {
using traits_t = BoxTraits<T *>;
T *data;
~Box() { delete data; }
T *get_data() { return traits_t::get_data(this); }
};
template <typename T> struct BoxTraits {
static T get_data(Box<T> *const box) { return box->data; }
};
Box::get_data is here to illustrate an issue with this design pattern. For every single method I want to add to Box, I need to add some boiler plate in each specialisation. Note that I would also need a Box<T *const> specialisation.
This seems like quite a rubbish solution. In C++14, I could use if constexpr with a is_ptr<T> trait and only have to write extra code in the methods that need specialising... Is there any way I can do this is in C++11?
This solution is shorter, cleaner and works for Box<U *const>!
template <typename T> struct is_ptr { static const bool value = false; };
template <typename U> struct is_ptr<U *> { static const bool value = true; };
template <typename U> struct is_ptr<U *const> {
static const bool value = true;
};
template <typename T> struct Box {
T data;
~Box() {
if constexpr (is_ptr<T>::value) {
delete data;
}
}
T get_data() { return data; }
};
First off, C++11 already has std::is_pointer, no need to roll your own. You can see that it inherits from std::true_type or std::false_type instead of defining its own value member. The reason for that is tag dispatching, that can effectively replace if constexpr in this situation:
template <typename T> struct Box {
T data;
~Box() {
destroy(std::is_pointer<T>{});
}
private:
void destroy(std::true_type) {
delete data;
}
void destroy(std::false_type) {} // nothing to do
};
Demo
I think this is the most idiomatic way in C++11 for delegating to different implementations based on type traits. In many situations, tag dispatching can replace if constexpr (from C++17, not C++14), and I believe the latter always replaces the former in addition to being clearer. Tag dispatching can also be used before C++11 if you roll your own type traits.
Last note: you don't need to use the standard type traits, you can do something like this:
template <typename T> struct is_ptr { static const bool value = false; };
template <typename T> struct is_ptr<T*> { static const bool value = true; };
template <typename T> struct is_ptr<T* const> { static const bool value = true; };
template <typename T> struct is_ptr<T* volatile> { static const bool value = true; };
template <typename T> struct is_ptr<T* const volatile> { static const bool value = true; };
template<bool b>
struct bool_constant {};
template<typename T>
struct Box {
T data;
~Box() {
destroy(bool_constant<is_ptr<T>::value>{});
}
private:
void destroy(bool_constant<true>) {
delete data;
}
void destroy(bool_constant<false>) {} // nothing to do
};
Demo
However, this pretty much amounts to recreating the standard type traits, but probably worse. Just use the standard library when possible.
I think you had the right idea with the helper type, but I'd do it like the following example illustrates.
template <typename B, typename T>
struct BoxTraits {
static T& get_data(B *const box) { return box->data; }
// ^--- important
static T const& get_data(B const* const box) { return box->data; }
};
template <typename T>
struct BoxTraits<Box<T*>, T> {
static T& get_data(Box<T*>* const box) { return *box->data; }
static T const& get_data(Box<T*> const* const box) { return *box->data; }
};
Both versions always return T, so you can use them the same regardless of your Box's payload. You could add a type alias in Box so you don't have to pass the template arguments:
typedef Traits BoxTraits<Box, T>; // in Box class

How to specialize a non-templated-member function of a template class for multiple types?

I'm biting of my nails on the syntax required to partially specialize a member function for multiple types. Here is what I have:
#include <cstdint>
#include <string>
class Property
{
public:
virtual int read(uint8_t *) = 0;
};
template<typename T>
class PropertyValue
{
T value_;
public:
int read(uint8_t *);
};
// specialized for std::string
template<>
int PropertyValue<std::string>::read(uint8_t *buf) { /* put string-value to buf */}
Now I would want to specialize the read-function for different enum-types. I tried a combination of enable_if and is_same which looks promissing, then putting it inside the template-declaration (compiler told me there are now 2 template arguments whereas 1 was expected).
Putting it inside the class-definition was not working either. Outside ... well, here's what I currently have.
// specialize for some enums
template<typename T>
typename std::enable_if<std::is_same<T, enum Enum1>::value ||
std::is_same<T, enum Enum2>::value, int>::type
PropertyValue<T>::read(uint8_t *buf)
{
return encode_enum(buf, value_);
}
Where is my thinking wrong?
EDIT: Writing it like this compiles and works:
template<>
int PropertyValue<Enum 1>::read(uint8_t *buf)
{
return encode_enum(buf, value_);
}
template<>
int PropertyValue<Enum 2>::read(uint8_t *buf)
{
return encode_enum(buf, value_);
}
PropertyValue::value itself is not a template. It's not a template class, it's not a template function. It's a member of a template class, which is not the same thing as being a template itself.
You have to specialize the entire class.
template<>
class PropertyValue<std::string>
{
std::string value_;
public:
int read(uint8_t *)
{
// Your specialization goes here.
}
};
Even if read() itself was a template, you must still specialize its class, before you can specialize a template class's template member.
Of course, if your template class has many other members and methods, every one of them have to be specialized here, leading to plenty of code getting duplicated. At that point, you will be faced with several options for refactoring out that duplicated code. The best approach for that depends on the particular details.
But that's how it's done...
EDIT: one common approach is to use a helper template class:
template<typename T> class PropertyValue; // Forward declaration
template<typename T> class do_read {
public:
static int do_it( PropertyValue<T> &me, uint8_t *p )
{
// your default implementation
}
};
template<> class do_read<std::string> {
public:
static int do_it( PropertyValue<std::string> &me, uint8_t *p )
{
// your specialization
}
};
template<typename T>
class PropertyValue
{
T value_;
public:
int read(uint8_t *p)
{
return do_read<T>::do_it(*this, p);
}
};

Use template parameter of template template parameter

I'm currently playing around with templates in C++ and got stuck with template template parameters.
Lets say I have the following classes:
template<typename T>
struct MyInterface
{
virtual T Foo() = 0;
}
class MyImpl : public MyInterface<int>
{
public:
int Foo() { /*...*/ }
};
template< template<typename T> typename ImplType>
class MyHub
{
public:
static T Foo()
{
ImplType i;
return i.Foo();
}
private:
MyHub() { }
~MyHub() { }
};
In essence I would like to have a static class like MyHub that accepts an implementation of MyInterface and provides certain static methods to use them like static T Foo().
Then I tried to use MyHub:
int main()
{
int i = MyHub<MyImpl>::Foo();
return 0;
}
Unfortunately I always end up getting an error saying that the type T (of static T Foo() in MyHub) does not name a type.
I would expect that it works because
the template parameter of the template parameter Impl is named T
MyHub is a templated class with one template parameter and contains a method Foo
So far I couldn't find a solution for this after digging through documentations and google results so I hope some of you can help me.
You can use typedefs. Also, since your implementation classes are not template class, there is no need for template template parameters.
#include <iostream>
#include <string>
template<typename T>
struct MyInterface
{
virtual T Foo() = 0;
typedef T Type;
};
class MyIntImpl : public MyInterface<int>
{
public:
int Foo() { return 2; }
};
class MyStringImpl : public MyInterface<std::string>
{
public:
std::string Foo() { return "haha"; }
};
template<class ImplType>
class MyHub
{
public:
static typename ImplType::Type Foo()
{
ImplType i;
return i.Foo();
}
private:
MyHub() { }
~MyHub() { }
};
int main()
{
std::cout << MyHub<MyIntImpl>::Foo() << "\n"; // prints 2
std::cout << MyHub<MyStringImpl>::Foo() << "\n"; // print haha
return 0;
}
Here is an example.
MyImpl is not a class template; so can't be passed as the template parameter of MyInterface.
You could change your MyInterface, MyImpl and MyHub classes to:
template<typename T>
class MyInterface{
public:
virtual T foo() = 0;
};
class MyImpl: public MyInterface<int>{
public:
using value_type = int;
value_type foo(){ return 1; /* dummy */ }
};
template<typename Impl, typename = std::enable_if_t<std::is_base_of<Impl, MyInterface<typename Impl::value_type>>::value>>
class MyHub{
public:
static auto foo(){
static Impl i;
return i.foo();
}
};
Which lets you use it the same way you are in your example.
The std::is_base_of check might be a little unnecessary in this case; but, this way you can't accidentally pass in another class that isn't derived from MyInterface with a method foo().
The STL uses value_type as a place holder for the underlying type of a template class. You could possibly do the same for your solution.
template<typename T>
struct MyInterface
{
typedef T value_type;
virtual T Foo() = 0;
}
class MyImpl : public MyInterface<int>
{
public:
int Foo() { /*...*/ }
};
template<typename ImplType>
class MyHub
{
public:
static typename ImplType::value_type Foo()
{
ImplType i;
return i.Foo();
}
private:
MyHub() { }
~MyHub() { }
};
Also note that in c++14, typename ImplType::value_type can be replaced by auto:
static auto Foo()
{
ImplType i;
return i.Foo();
}
The names of template parameters of template template parameters are effectively a purely documentational construct—they don't get included in the containing template's scope.
There's good reason for that: there is nothing to whcih they could refer in the containing template. When you have a template template parameter, you must pass a template as the argument to it, and not an instantiation of a template. In other words, you're passing a template without arguments as the argument.
This means your code is simply wrong—you're using MyImpl as an argument for MyHub, but MyImpl is a class. MyHub expects a template, not a class. The correct instantiation of MyHub would be MyHub<MyInterface>. Not that there are no template arguments after this use of MyInterface; we are passing in the template itself, not an instantiation of it.
Template template parameters are used rather rarely in practice. You only use them if you want to instantiate the parameter template with your own types. So I would expect your MyHub code to do something like this:
template <template <class> class ImplTemplate>
struct MyHub
{
typedef ImplTemplate<SomeMyHub_SpecificType> TheType;
// ... use TheType
};
This doesn't seem to be what you want to do. I believe you want a normal type template parameter, and provide a nested typedef for its T. Like this:
template <class T>
struct MyInterface
{
typedef T ParamType; // Added
virtual T Foo() = 0;
};
template<class ImplType>
class MyHub
{
typedef typename ImplType::ParamType T;
public:
static T Foo()
{
ImplType i;
return i.Foo();
}
private:
MyHub() { }
~MyHub() { }
};
int main()
{
int i = MyHub<MyImpl>::Foo();
return 0;
}

c++ template specialization for base class

I would like to have a special formatter for BASECLASS and all derived classes. I have the following classes:
struct BASECLASS { ... };
struct SPECIALFORMAT : BASECLASS { ... }
struct ANOTHERSPECIALFORMAT : BASECLASS { ... }
template <class T>
struct LISTFORMATTER {
list<T> l;
bool format() {
};
}
bool LISTFORMATTER<BASECLASS>::format() { ... }
LISTFORMATTER<BASECLASS> bcFt;
LISTFORMATTER<SPECIALFORMAT> spFt;
LISTFORMATTER<ANOTHERSPECIALFORMAT> aspFt;
bcFt.format(); // <-- ok
spFt.format(); // <-- Calling standard format(), not specialized
aspFt.format(); // <-- Calling standard format(), not specialized
How can I specialize a method for a base class and all inherited classes?
EDIT preferible not using boost. c++ (not c++11)
First, you need is_base_of. If you don't want to use Boost or C++11, then grab one here:
How does `is_base_of` work?
Then, you can do this:
template <bool B> struct bool_ {};
// ...
bool format() { do_format(bool_<is_base_of<BASECLASS, T>::value>()); }
bool do_format(bool_<false>) {
// not inheriting BASECLASS
}
bool do_format(bool_<true>) {
// inheriting BASECLASS
}
BTW, there is, AFAIK, no way of doing this non-intrusively, i.e. simply by adding a specialization.
Edit: Actually, you can probably do it without is_base_of:
// ...
bool format() { do_format((T*)0); }
bool do_format(void*) { /* not inheriting */ }
bool do_format(BASECLASS*) { /* inheriting */ }
This works because derived->base is a better conversion than class->void.
I think you could do this with enable_if and is_base_of (either from c++11 or boost).
Tag dispatching may help:
struct BASECLASS { };
struct SPECIALFORMAT : BASECLASS { };
struct ANOTHERSPECIALFORMAT : BASECLASS { };
template <typename T>
struct IsDerivedFromBASECLASS {
static const bool value = false; //std::is_base_of<BASECLASS, T>::value;
};
template <>
struct IsDerivedFromBASECLASS<BASECLASS> { static const bool value = true; };
template <>
struct IsDerivedFromBASECLASS<SPECIALFORMAT> { static const bool value = true; };
template <>
struct IsDerivedFromBASECLASS<ANOTHERSPECIALFORMAT> { static const bool value = true; };
template <class T>
struct LISTFORMATTER {
//list<T> l;
bool format();
};
template <typename T, bool IsABASECLASS>
struct helper_format {
bool operator() (LISTFORMATTER<T>&)
{
// default implementation
return true;
}
};
template <typename T>
struct helper_format<T, false>
{
bool operator() (LISTFORMATTER<T>&)
{
// specialization implementation
return false;
}
};
template<typename T>
bool LISTFORMATTER<T>::format() {
return helper_format<T, IsDerivedFromBASECLASS<T>::value>(*this);
}

Ambiguous template arguments not excluded by enable_if

I want to automatically choose the right pointer-to-member among overloaded ones based on the "type" of the member, by removing specializations that accept unconcerned members (via enable_if).
I have the following code:
class test;
enum Type
{
INT_1,
FLOAT_1,
UINT_1,
CHAR_1,
BOOL_1,
INT_2,
FLOAT_2,
UINT_2,
CHAR_2,
BOOL_2
};
template<typename T, Type Et, typename func> struct SetterOk { static const bool value = false; };
template<typename T> struct SetterOk<T,INT_1,void (T::*)(int)> { static const bool value = true; };
template<typename T> struct SetterOk<T,FLOAT_1,void (T::*)(float)> { static const bool value = true; };
template<typename T> struct SetterOk<T,UINT_1,void (T::*)(unsigned int)> { static const bool value = true; };
template<typename T> struct SetterOk<T,CHAR_1,void (T::*)(char)> { static const bool value = true; };
template<typename T> struct SetterOk<T,BOOL_1,void (T::*)(bool)> { static const bool value = true; };
template<typename T> struct SetterOk<T,INT_2,void (T::*)(int,int)> { static const bool value = true; };
template<typename T> struct SetterOk<T,FLOAT_2,void (T::*)(float,float)> { static const bool value = true; };
template<typename T> struct SetterOk<T,UINT_2,void (T::*)(unsigned int, unsigned int)> { static const bool value = true; };
template<typename T> struct SetterOk<T,CHAR_2,void (T::*)(char,char)> { static const bool value = true; };
template<typename T> struct SetterOk<T,BOOL_2,void (T::*)(bool,bool)> { static const bool value = true; };
template <bool, class T = void> struct enable_if {};
template <class T> struct enable_if<true, T> { typedef T type; };
template<typename T, Type Et>
struct Helper
{
template<typename U>
static void func(U method, typename enable_if<SetterOk<T,Et,U>::value>::type* dummy = 0)
{
}
};
class test
{
public:
void init()
{
Helper<test,INT_2>::func(&test::set);
}
void set2(int);
void set(int);
void set(int,int);
void set(float,float);
};
int main()
{
test t;
t.init();
return 0;
}
I'm expecting it to choose the right function between all possible. The problem is that the compiler says "cannot deduce template argument as function argument is ambiguous".
It seems I don't know how to use enable_if, because if so the compiler would only allow the specialization if the specified function has the right type...
Note that I want to have C++03 solutions (if possible) - my code must compile on some old compilers.
Thanks in advance
You can never refer to an overloaded function without disambiguating it (means: static_casting it to the correct type). When you instantiate Helper::func the type of the function argument cannot be known without ever disambiguating it.
The reason it doesn't compile is quite simply that there are several different overloaded functions and it doesn't know which one you mean. Granted, only one of these (void set(int,int)) would actually compile, given the specialization Helper<test,INT_2>. However, this is not enough for the compiler to go on.
One way of getting this to compile would be to explicitly cast &test::set to the appropriate type:
Helper<test,INT_2>::func(static_cast<void (test::*)(int,int)>(&test::set));
Another way would be to use explicit template specialization:
Helper<test,INT_2>::func<void (test::*)(int,int)>((&test::set));
Either way, you need to let the compiler know which of the set functions you are trying to refer to.
EDIT:
As I understand it, you want to be able to deduce, from the use of a Type, which function type should be used. The following alternative achieves this:
template<typename T, Type Et> struct SetterOK{};
template<typename T> struct SetterOK<T,INT_1> {typedef void (T::*setter_type)(int);};
template<typename T> struct SetterOK<T,FLOAT_1> {typedef void (T::*setter_type) (float);};
// ...
template<typename T> struct SetterOK<T,INT_2> {typedef void (T::*setter_type)(int,int);};
// ....
template<typename T, Type Et>
struct Helper
{
template<typename U>
static void func(U method)
{
}
};
class test
{
public:
void init()
{
Helper<test,INT_2>::func<SetterOK<test,INT_2>::setter_type >(&test::set);
}
void set2(int);
void set(int);
void set(int,int);
void set(float,float);
};
int main()
{
test t;
t.init();
return 0;
}
ADDITIONAL EDIT:
A thought just occurred to me. In this special case which you've done, where U is SetterOK::setter_type, things can be simplified further by completely removing the template arguments for func:
static void func(typename SetterOK<T,Et>::setter_type method)
{
}
This would make the init method a simpler:
void init()
{
Helper<test,INT_2>::func(&test::set);
}