C++ - Specialize member function for Template Class - c++

I have a class-template representing a mathematical vector:
template<class Value_T, unsigned int N>
class VectorT
{
public:
void Normalize()
{
// normalize only double/float vectors here
}
private:
// elements in the vector
value_type elements[N];
// the number of elements
static const size_type size = N;
};
I would like to have a special treatment for vectors of integer types, as a vector normalization is not possible on this types. So I need a seperate (may be specialization) for the Normalize method that depends on the template argument Value_T of the VectorT class-template.
I have tried to use template specialization in different ways but did not get it to work. Do I have to make the Normalize function a template function itself? At the moment it just a normal member-method.

You can solve this with a tag dispatching technique:
#include <iostream>
#include <type_traits>
template<class Value_T, unsigned int N>
class VectorT
{
public:
void Normalize()
{
using tag = std::integral_constant<bool
, std::is_same<Value_T, double>::value
|| std::is_same<Value_T, float>::value>;
// normalize only double/float vectors here
Normalize(tag());
}
private:
void Normalize(std::true_type)
{
std::cout << "Normalizing" << std::endl;
}
void Normalize(std::false_type)
{
std::cout << "Not normalizing" << std::endl;
}
// elements in the vector
Value_T elements[N];
// the number of elements
static const std::size_t size = N;
};
DEMO

It seems that you want forbid Normalize for other type than floating point, so you may use static_assert to have good error message:
template<class Value_T, unsigned int N>
class VectorT
{
public:
void Normalize()
{
static_assert(std::is_floating_point<Value_T>::value, "Normalize available only for floating point");
// normalize only double/float vectors here
}
// Other stuff
};

Also you can use std::enable_if<>
#include <iostream>
#include <type_traits>
template<class Value_T>
class VectorT
{
public:
template<class T = Value_T>
typename std::enable_if<std::is_integral<T>::value, void>::type
Normalize()
{
std::cout << "Not normalizing" << std::endl;
}
template<class T = Value_T>
typename std::enable_if<!std::is_integral<T>::value, void>::type
Normalize()
{
std::cout << "Normalizing" << std::endl;
}
};
int main()
{
VectorT<int> vint;
VectorT<double> vdouble;
vint.Normalize();
vdouble.Normalize();
return 0;
}
DEMO

Yes, you can independently specialize a specific member function of a template class. However, function templates (including member function templates) do not allow partial specializations. Function templates only support explicit specializations. An explicit specialization in your case that would look as follows
// Header file
template<class Value_T, unsigned int N>
class VectorT
{
public:
void Normalize()
{
// normalize only double/float vectors here
}
...
};
// Declare an explicit specialization for <int, 5>
template <> void VectorT<int, 5>::Normalize();
and then
// Implementation file
// Define the explicit specialization for <int, 5>
template <> void VectorT<int, 5>::Normalize()
{
...
}
However, explicit initialization is not what you need, apparently, since you want to "fix" the type only and leave the size flexible, i.e. you need a partial specialization. This can be done using the std::enable_if functionality of C++11 (as shown in other answers) as well as through some basic tricks of C++98.
Of course if your class is relatively lightweight, i.e. it does not have much generic code besides that Normalize, you can simply partially specialize the whole class. It will take just a bit more typing.

Related

Making a function in a struct template [duplicate]

This question already has answers here:
How to define a template member function of a template class [duplicate]
(2 answers)
Why can templates only be implemented in the header file?
(17 answers)
Closed 7 months ago.
So i made a template struct cause i want to be able to decide what type i give to my val. But when creating a function i don't know how to do it.
Here's what i'm doing:
In my .hpp
template<typename T>
struct Integer
{
T val;
void setUint(const T &input);
};
Now i can set what variable i want in the val and what i want in the function.
But now in my cpp i don't know how to invoke the function.
void Integer<T>::setUint(const T &input)
{
val = input;
}
Error: identifier "T" is undefined.
A template function is a way to operate with generic types (you may consider the type as an argument). Your template parameter T allows to pass different types to a function when you invoke the function (which means, simply said, you may replace T with some other types int, double, ...)
Please, have a look at the following simple example.
#include <iostream>
#include <typeinfo>
// you may put it all in a hpp and thus include the hpp file in the CPP
template<typename T>
struct Integer
{
T val;
void setUint(const T &input){
val=input;
std::cout <<"the type is " << typeid(T).name() << " value is "<< val << std::endl;
}
};
// or look at Jarod42's implementation details.
/*
template<typename T>
void Integer<T>::setUint(const T &input){
val=input;
std::cout <<"the type is " << typeid(T).name() << " value is "<< val << std::endl;
}*/
// and here you have your cpp calling you template function with different types
int main()
{
Integer<double> value;
value.setUint(1500000);
Integer<int> value2;
value2.setUint(5);
return 0;
}
Syntax is
template <typename T>
void Integer<T>::setUint(const T &input)
{
val = input;
}
Also for templates, check if T actually matches your expectations. Your template will not make sense for types that are not Integers. Example on how to add these constraints here : https://godbolt.org/z/YsneKe7vj (both C++17 SFINAE, and C++20 concept)
#include <type_traits>
#include <string>
//-----------------------------------------------------------------------------------------------------------
// for C++17 setup a constexpr that will evaluate (at compile time, that's the constexpr bit)
// if a given type is an integer type
template<typename type_t>
constexpr bool is_integer_type_v = std::is_integral_v<type_t> && !std::is_same_v<type_t,bool>;
// create a template that can optionally be enabled. This is an example of a technique called SFINAE
template<typename type_t, typename enable = void>
struct Integer;
// for C++ define a specialization that will succesfully be enabled of integer types only
template<typename type_t>
struct Integer<type_t,std::enable_if_t<is_integer_type_v<type_t>>>
{
void Set(const type_t v)
{
value = v;
}
type_t value;
};
//-----------------------------------------------------------------------------------------------------------
// for C++20 use concepts
template<typename type_t>
concept is_integer = std::is_integral_v<type_t> && !std::is_same_v<type_t, bool>;
// note is_integer now replaces typename, and it can only accept types
// that satisfy the concept
template<is_integer integer_t>
struct IntegerCpp20
{
void Set(const integer_t v)
{
value = v;
}
integer_t value;
};
//-----------------------------------------------------------------------------------------------------------
int main()
{
//Integer<std::string> value; // will not compile;
Integer<int> value;
value.Set(2);
//IntegerCpp20<std::string> value; // will not compile;
IntegerCpp20<int> value20;
value20.Set(20);
return 0;
}

Using a templated function as template parameter

I'm writing a class with a function that is repeatedly called and decided to implement this as giving the function as template parameter.
As a concrete example of what I'm talking about, consider the following class:
#include <array>
template<double (*func)(std::array<double,3>)>
class MyClass
{
public:
MyClass()
{
std::array<double,3> v {{0.1,0.2,0.1}};
func(v);
}
};
which can then be instantiated with a function, as for example:
double func(std::array<double,3> x)
{
return x[0]*x[0]+x[1];
}
int main()
{
MyClass<func> entity;
}
However, I need the function to be callable with different types (the operations in the function are of course applicable to all of them), that is I want to have this function templated, as in:
template<typename scalartype, typename vectype>
scalartype func1(vectype x)
{
scalartype res = x[0]*x[0]+x[1];
return res;
}
I can still use this as template parameter, but the function parameter and return type are then fixed in the class. So how can I have the function as templated function available in the class? Such that I can, for example, call it with an std::vector of four integers and have an integer returned.
I tried using template template parameters, but I can't even figure out how to use two template parameters with them (as they only seem to allow the template ... syntax). I'm sorry if this is formulated unclearly, I am still a newcomer.
You can put your template function in a class, and then pass in that class to MyClass.
#include <iostream>
#include <vector>
#include <array>
struct templateHolder {
template <typename vectype, typename scalartype = typename vectype::value_type>
static scalartype func(vectype x) {
return x[0] + x[1];
}
};
template<typename T>
class MyClass
{
public:
MyClass()
{
std::vector<int> vec {1,2};
std::cout << T::func(vec) << std::endl;
std::array<double, 2> arr {0.5, 3.33};
std::cout << T::func(arr) << std::endl;
}
};
int main() {
MyClass<templateHolder> foo;
return 0;
}
I chose to deduce scalartype from vectype. Not necessarily what you want but it could be an option depending on your use-case.

How to allow only iterators with a ceratin value_type?

I want to write a class that takes a pair of iterators as parameters to the constructor, but I dont know how to raise an error at compile-time when those iterators' value_type doesn't match an expected type. This is what I tried using typeid:
#include <vector>
struct foo {
std::vector<double> data;
template <typename IT>
foo(IT begin, IT end){
typedef int static_assert_valuetype_is_double[
typeid(typename IT::value_type) == typeid(double) ? 1 : -1
];
std::cout << "constructor called \n";
data = std::vector<double>(begin,end);
}
};
int main()
{
std::vector<double> x(5);
foo f(x.begin(),x.end()); // double: ok
std::vector<int> y(10);
foo g(y.begin(),y.end()); // int: should not compile
}
Note that in this case, int to double would be fine, but thats just an example and in the real code the types have to match exactly. To my surprise in both cases, the constructor works without errors (there is only a warning about the unused typedef). Does the -1 sized array static assert trick not work when the typedef is declared inside a method? How do I produce an error when IT::value_type is the wrong type?
PS: would be nice if there was an easy C++98 solution, but if this gets too complicated, I could also accept a C++11 solution.
In modern C++, you could have used std::is_same and static_assert:
static_assert(std::is_same_v<typename std::iterator_traits<IT>::value_type, double>,
"wrong iterator");
See also std::iterator_traits: an iterator it is not guaranteed to have a value_type typedef, and one should use std::iterator_traits<it>::value_type instead.
In C++ 98, is_same is trivial to implement, static_assert needs a negative-size array trick or the BOOST_STATIC_ASSERT.
For a solution that works in C++98 and later.....
#include <iterator>
template<class T> struct TypeChecker
{};
template<> struct TypeChecker<double>
{
typedef double valid_type;
};
template <typename IT>
void foo(IT begin, IT end)
{
typename TypeChecker<typename std::iterator_traits<IT>::value_type>::valid_type type_checker;
(void)type_checker;
// whatever
}
Instantiations of foo() will succeed for any for an iterator for which value_type is double, and fail to compile otherwise.
The premise is that TypeChecker<x> does not have a valid_type for any x other than double, but we attempt to instantiate an instance of that type in foo(). The (void)type_checker prevents warnings, from some compilers about a variable that is never used, for valid types.
Here is a C++98 compliant way to implement it.....
First the fun part: Implementing a is_same is rather straightforward
template <typename T,typename U> struct is_same_type { static const bool value; };
template <typename T,typename U> const bool is_same_type<T,U>::value = false;
template <typename T> struct is_same_type<T,T> { static const bool value; };
template <typename T> const bool is_same_type<T,T>::value = true;
Now the not-so-fun part (C++11 really helps to statically assert without causing coworkers raising some eyebrows):
struct foo {
std::vector<double> data;
template <typename IT>
foo(IT begin, IT end) : data(begin,end) {
typedef int static_assert_valuetype_is_double[
is_same_type<double,typename IT::value_type>::value ? 1 : -1
];
std::cout << "constructor called \n";
}
};
int main(){
std::vector<double> x(5,2.3);
foo f(x.begin(),x.end());
for (std::vector<double>::iterator it = f.data.begin(); it != f.data.end();++it) std::cout << *it << " ";
//std::vector<int> y(10,3);
//foo g(y.begin(),y.end()); // THIS FAILS (AS EXPECTED)
}
As pointed out by others, I should actually be using std::iterator_traits<IT>::value_type as not every iterator has a value_type. However, in my case I rather want to restrict the possible iterators to a small set and disallowing iterators without a value_type isnt a problem in my specific case.
Also note that the code in the question assigned to the member, while it is of course better to use the initializer list.

How to generalise this further using (variadic) templates

I am having trouble going the second step or level in templating my code. I have stripped the code to its bare essentials for readability.
I have looked through a lot of templates questions, but I was not really able to solve my exact issue.
I currently have a class RIVRecord, which I templated like this
template <class T>
class RIVRecord
{
private:
std::vector<T> values;
public:
std::string name;
RIVRecord(std::string _name, std::vector<T> _values) { name = _name; values = _values; };
~RIVRecord(void) { };
size_t size() {
return values.size();
}
T* Value(int index) {
return &values[index];
}
}
Easy enough. The T types are usually primitive types such as floats and integers. Then I want to put these RIVRecords in a DataSet class. Here is where I am having more difficulty. Untemplated it would be something like this:
class RIVDataSet
{
private :
//How to template this??
vector<RIVRecord<float>> float_records;
vector<RIVRecord<int>> int_records;
public:
RIVDataSet(void);
~RIVDataSet(void);
//And this
void AddRecord(RIVRecord<float> record) {
//How would this work?
}
//And this?
RIVRecord<float> GetFloatRecord();
};
I come from a Java background, so there I could use the vector<?> and do type checking whenever I ask a RIVRecord. But this does not seem possible in C++. I tried using variadic templates but am unsure how to construct the vector using all types in the template :
template <class... Ts>
class RIVDataSet
{
private :
//For each T in Ts
vector<RIVRecord<T>> records;
public:
RIVDataSet(void);
~RIVDataSet(void);
//For each T in Ts
void AddRecord(RIVRecord<T> record) {
//How would this work?
}
//For each T in Ts, get the record by index.
RIVRecord<T> GetRecord(int index);
};
I already saw that this sort of iteration in C++ templates is not possible, but it is just to clarify what I would want.
Any help is very welcome, thank you.
EDIT:
There is no restriction on the number of types (floats, ints,...) for T
Also, GetRecord works by having an index, but I don't really care about it that much, as long as I can iterate over the records and get the right type.
Solving this via variadic templates is not very complicated, but requires some additional support types. Let us begin, by looking at the result:
template <typename... V>
class many_vectors
{
static_assert(are_all_different<V...>::value, "All types must be different!");
std::tuple<std::vector<V>...> _data;
public:
template<typename T>
std::vector<T>& data()
{ return std::get<index_of<T, V...>::value>(_data); }
template<typename T>
std::vector<T> const& data() const
{ return std::get<index_of<T, V...>::value>(_data); }
template<typename T>
void push_back(T&& arg)
{ data<typename std::remove_reference<T>::type>().push_back(std::forward<T>(arg)); }
template<typename T, typename... W>
void emplace_back(W&&... args)
{ data<T>().emplace_back(std::forward<W>(args)...); }
};
The static_assert defines a very important requirement: Since we are differentiating on types, we must ensure that all types are different. The _data member is a std::tuple of the vectors for the different types, and corresponds directly to your float_records and int_records members.
As an example of providing a member function that refers to one of the vectors by their type the data function exposes the individual vectors. It uses a helper template to figure out which element of the tuple corresponds to your type and gets the result.
The push_back function of the vectors is also exposed to show how to use that to provide functions on these. Here std::forward is used to implement perfect forwarding on the argument to provide optimal performance. However, using rvalue references in combination with templates parameter deduction can lead to slightly unexpected results. Therefore, any reference on the T parameter is removed, so this push_back will not work for a many_vectors containing reference types. This could be fixed by instead providing two overloads push_back<T>(T&) and push_back<T>(T const&).
Finally, the emplace_back exposes a function that cannot rely on template parameter argument deduction to figure out which vector it is supposed to utilize. By keeping the T template parameter first, we allow a usage scenario in which only T is explicitly specified.
Using this, you should be ably to implement arbitrary additional members with similar funcitonality (e.g. begin<T> and end<T>).
Helpers
The most important helper is very simple:
template<typename T, typename U, typename... V>
struct index_of : std::integral_constant<size_t, 1 + index_of<T, V...>::value>
{ };
template<typename T, typename... V>
struct index_of<T, T, V...> : std::integral_constant<size_t, 0>
{ };
This will fail with a fairly ugly error message, if the first argument is not one of the following at all, so you may wish to improve on that.
The other helper is not much more complicated:
template<typename T, typename... V>
struct is_different_than_all : std::integral_constant<bool, true>
{ };
template<typename T, typename U, typename... V>
struct is_different_than_all<T, U, V...>
: std::integral_constant<bool, !std::is_same<T, U>::value && is_different_than_all<T, V...>::value>
{ };
template<typename... V>
struct are_all_different : std::integral_constant<bool, true>
{ };
template<typename T, typename... V>
struct are_all_different<T, V...>
: std::integral_constant<bool, is_different_than_all<T, V...>::value && are_all_different<V...>::value>
{ };
Usage
Yes, usage is as simple as you might hope:
v.push_back(int(3));
v.push_back<float>(4);
v.push_back<float>(5);
v.push_back(std::make_pair('a', 'b'));
v.emplace_back<std::pair<char, char>>('c', 'd');
std::cout << "ints:\n";
for(auto i : v.data<int>()) std::cout << i << "\n";
std::cout << "\n" "floats:\n";
for(auto i : v.data<float>()) std::cout << i << "\n";
std::cout << "\n" "char pairs:\n";
for(auto i : v.data<std::pair<char, char>>()) std::cout << i.first << i.second << "\n";
With the expected result:
ints:
3
floats:
4
5
char pairs:
ab
cd
You can use a technique called type erasure, you'll have to include another level of indirection however. Some general feedback:
RIVRecord(std::string _name, std::vector<T> _values)
Is better as:
RIVRecord(const std::string& _name, const std::vector<T>& _values)
In order to avoid unnecessary copies, overall the rule of thumb is to accept arguments as const& for most things which aren't a primitive.
T* Value(int index) { return &values[index]; }
Is dangerous, if the size() goes beyond capacity() of your vector< T > it will reallocate and invalidate all your pointers. A better interface in my opinion would be to have a T GetValue< T >() & void SetValue< T >( T a_Value ).
On to type erasure, this is how RIVDataSet could look, I'm using a library called Loki here, if you don't want to use Loki I'll give you some pointers afterwards.
class RIVDataSet
{
private :
//How to template this??
struct HolderBase
{
virtual ~HolderBase() {}
};
template< typename T >
struct HolderImpl : HolderBase
{
// Use pointer to guarantee validity of returned record
std::vector< RIVRecord< T >* > m_Record;
};
typedef Loki::AssocVector< Loki::TypeInfo, HolderBase* > HolderMap;
HolderMap m_Records;
public:
~RIVDataSet()
{
for( HolderMap::iterator itrCur = m_Records.begin(); itrCur != m_Records.end(); ++itrCur ) delete itrCur->second;
}
//And this
template< typename T >
void AddRecord(const RIVRecord< T >& record )
{
HolderMap::iterator itrFnd = m_Records.find( typeid( T ) );
if( itrFnd == m_Records.end() )
itrFnd = m_Records.insert( std::make_pair( Loki::TypeInfo( typeid( T ) ), new HolderImpl< T >() ) ).first;
static_cast< HolderImpl< T >* >( itrFnd->second )->m_Record.push_back( new RIVRecord< T >( record ) );
}
template< typename T >
RIVRecord< T >* GetRecord()
{
HolderMap::iterator itrFnd = m_Records.find( typeid( T ) );
assert( itrFnd != m_Records.end() );
return itrFnd == m_Records.end() ? 0 : static_cast< HolderImpl< T >* >( itrFnd->second )->m_Record.front();
}
};
Loki::AssocVector can be substituted for std::map, you do however need Loki::TypeInfo, which is just a wrapper for std::type_info. It's fairly easy to implement one your self if you take a look at the code for it in Loki.
One horrible idea if you really must do it as general is using the "type erasure idiom". It goes something like this (haven't compiled that though but I think it will, and can be further improved by type traits that would link RIVRecordsIndex::Float to the type float and prevent error)
class BaseRIVRecord
{
};
template <class T>
class RIVRecord : public BaseRIVRecord
{
};
enum class RIVRecordsIndex
{
Float, Int
};
class RIVDataSet
{
public:
template<RIVRecordsIndex I, typename T>
void addRecord()
{
allmightyRecords.resize(I+1);
allmightyRecords[I].push_back(new RIVRecord<T>());
}
template<RIVRecordsIndex I, typename T>
RIVRecord<T>* get(unsigned int index)
{
return static_cast<RIVRecord<T>*>(allmighyRecords[I][index]);
}
private:
std::vector<std::vector<BaseRIVRecord*>> allmightyRecords;
};
int main()
{
RIVDataSet set;
set.addRecord<RIVRecordsIndex::Float, float>();
set.addRecord<RIVRecordsIndex::Float, float>();
set.addRecord<RIVRecordsIndex::Int, int>();
RIVRecord<int> r = set.get<RIVRecordsIndex::Int, int>(0);
}
If you decide to do this stuff make sure you do not slice the inherited type (i.e. use vector of pointers). Do use some kind of type traits to prevent error calls like set.get. Again I have no time to actually compile that, it is just an idea thrown to further develop.
You can't use variadic templates to create multiple members of the same name but different type. In fact, you can never have two members with the same name. However, you can use multiple inheritance, and put the member in your base classes using variadic base classes. You can then use a member template in your derived class to resolve the ambiguity.
The example below also uses perfect forwarding to make sure that if a temporary is passed to add(), its resources can be "stolen". You can read more about that here.
Here is the example:
#include <vector>
#include <utility>
// This templated base class holds the records for each type.
template <typename T>
class Base {
public:
// "T &&v" is a universal reference for perfect forwarding.
void add(T &&v) { records.push_back(std::forward<T>(v)); }
private:
std::vector<T> records;
};
// This inherits from Base<int>, Base<double>, for example, if you instantiate
// DataSet<int, double>.
template <typename... Ts>
class DataSet : public Base<Ts>... {
public:
// The purpose of this member template is to resolve ambiguity by specifying
// which base class's add() function we want to call. "U &&u" is a
// universal reference for perfect forwarding.
template <typename U>
void add(U &&u) {
Base<U>::add(std::forward<U>(u));
}
};
int main() {
DataSet<int, double> ds;
ds.add(1);
ds.add(3.14);
}

Template metaprogram converting type to unique number

I just started playing with metaprogramming and I am working on different tasks just to explore the domain. One of these was to generate a unique integer and map it to type, like below:
int myInt = TypeInt<AClass>::value;
Where value should be a compile time constant, which in turn may be used further in meta programs.
I want to know if this is at all possible, and in that case how. Because although I have learned much about exploring this subject I still have failed to come up with an answer.
(P.S. A yes/no answer is much more gratifying than a c++ solution that doesn't use metaprogramming, as this is the domain that I am exploring)
In principle, this is possible, although the solution probably isn't what you're looking for.
In short, you need to provide an explicit mapping from the types to the integer values, with one entry for each possible type:
template< typename T >
struct type2int
{
// enum { result = 0 }; // do this if you want a fallback value
};
template<> struct type2int<AClass> { enum { result = 1 }; };
template<> struct type2int<BClass> { enum { result = 2 }; };
template<> struct type2int<CClass> { enum { result = 3 }; };
const int i = type2int<T>::result;
If you don't supply the fallback implementation in the base template, this will fail for unknown types if T, otherwise it would return the fallback value.
Depending on your context, there might be other possibilities, too. For example, you could define those numbers within within the types themselves:
class AClass {
public:
enum { inta_val = 1 };
// ...
};
class BClass {
public:
enum { inta_val = 2 };
// ...
};
// ...
template< typename T >
struct type2int
{
enum { result = T::int_val }; // will fail for types without int_val
};
If you give more context, there might be other solutions, too.
Edit:
Actually there isn't any more context to it. I was looking into if it actually was possible, but without assigning the numbers itself.
I think Mike's idea of ordering is a good way to do this (again, for a fixed set of types) without having to explicitly assign numbers: they're implicitly given by the ordering. However, I think that this would be easier by using a type list. The index of any type in the list would be its number. I think something like the following might do:
// basic type list manipulation stuff
template< typename T1, typename T2, typename T3...>
struct type_list;
// meta function, List is assumed to be some instance of type_list
template< typename T, class List >
struct index_of {
enum { result = /* find index of T in List */ };
};
// the list of types you support
typedef type_list<AClass, BClass, CClass> the_type_list;
// your meta function
template< typename T >
struct type2int
{
enum { result = index_of<T, the_type_list>::result };
};
This does what you want. Values are assigned on need. It takes advantage of the way statics in functions are assigned.
inline size_t next_value()
{
static size_t id = 0;
size_t result = id;
++id;
return result;
}
/** Returns a small value which identifies the type.
Multiple calls with the same type return the same value. */
template <typename T>
size_t get_unique_int()
{
static size_t id = next_value();
return id;
}
It's not template metaprogramming on steroids but I count that as a good thing (believe me!)
Similiar to Michael Anderson's approach but this implementation is fully standards compliant and can be performed at compile time. Beginning with C++17 it looks like constexpr values will be allowed to be used as a template parameter for other template meta programming purposes. Also unique_id_type can be compared with ==, !=, >, <, etc. for sorting purposes.
// the type used to uniquely identify a list of template types
typedef void (*unique_id_type)();
// each instantiation of this template has its own static dummy function. The
// address of this function is used to uniquely identify the list of types
template <typename... Arguments>
struct IdGen {
static constexpr inline unique_id_type get_unique_id()
{
return &IdGen::dummy;
}
private:
static void dummy(){};
};
The closest I've come so far is being able to keep a list of types while tracking the distance back to the base (giving a unique value). Note the "position" here will be unique to your type if you track things correctly (see the main for the example)
template <class Prev, class This>
class TypeList
{
public:
enum
{
position = (Prev::position) + 1,
};
};
template <>
class TypeList<void, void>
{
public:
enum
{
position = 0,
};
};
#include <iostream>
int main()
{
typedef TypeList< void, void> base; // base
typedef TypeList< base, double> t2; // position is unique id for double
typedef TypeList< t2, char > t3; // position is unique id for char
std::cout << "T1 Posn: " << base::position << std::endl;
std::cout << "T2 Posn: " << t2::position << std::endl;
std::cout << "T3 Posn: " << t3::position << std::endl;
}
This works, but naturally I'd like to not have to specify a "prev" type somehow. Preferably figuring out a way to track this automatically. Maybe I'll play with it some more to see if it's possible. Definitely an interesting/fun puzzle.
I think it is possible to do it for a fixed set of types, but quite a bit of work. You'll need to define a specialisation for each type, but it should be possible to use compile-time asserts to check for uniqueness. I'll assume a STATIC_ASSERT(const_expr), like the one in Boost.StaticAssert, that causes a compilation failure if the expression is false.
Suppose we have a set of types that we want unique IDs for - just 3 for this example:
class TypeA;
class TypeB;
typedef int TypeC;
We'll want a way to compare types:
template <typename T, typename U> struct SameType
{
const bool value = false;
};
template <typename T> struct SameType<T,T>
{
const bool value = true;
};
Now, we define an ordering of all the types we want to enumerate:
template <typename T> struct Ordering {};
template <> struct Ordering<void>
{
typedef TypeC prev;
typedef TypeA next;
};
template <> struct Ordering<TypeA>
{
typedef void prev;
typedef TypeB next;
};
template <> struct Ordering<TypeB>
{
typedef TypeA prev;
typedef TypeC next;
};
template <> struct Ordering<TypeC>
{
typedef TypeB prev;
typedef void next;
};
Now we can define the unique ID:
template <typename T> struct TypeInt
{
STATIC_ASSERT(SameType<Ordering<T>::prev::next, T>::value);
static int value = TypeInt<T>::prev::value + 1;
};
template <> struct TypeInt<void>
{
static int value = 0;
};
NOTE: I haven't tried compiling any of this. It may need typename adding in a few places, and it may not work at all.
You can't hope to map all possible types to an integer field, because there are an unbounded number of them: pointer types with arbitrary levels of indirection, array types of arbitrary size and rank, function types with arbitrary numbers of arguments, and so on.
I'm not aware of a way to map a compile-time constant integer to a type, but I can give you the next best thing. This example demonstrates a way to generate a unique identifier for a type which - while it is not an integral constant expression - will generally be evaluated at compile time. It's also potentially useful if you need a mapping between a type and a unique non-type template argument.
struct Dummy
{
};
template<typename>
struct TypeDummy
{
static const Dummy value;
};
template<typename T>
const Dummy TypeDummy<T>::value = Dummy();
typedef const Dummy* TypeId;
template<typename T, TypeId p = &TypeDummy<T>::value>
struct TypePtr
{
static const TypeId value;
};
template<typename T, TypeId p>
const TypeId TypePtr<T, p>::value = p;
struct A{};
struct B{};
const TypeId typeA = TypePtr<A>::value;
const TypeId typeB = TypePtr<B>::value;
I developed this as a workaround for performance issues with ordering types using typeid(A) == typeid(B), which a certain compiler fails to evaluate at compile time. It's also useful to be able to store TypeId values for comparison at runtime: e.g. someType == TypePtr<A>::value
This may be doing some "bad things" and probably violates the standard in some subtle ways... but thought I'd share anyway .. maybe some one else can sanitise it into something 100% legal? But it seems to work on my compiler.
The logic is this .. construct a static member function for each type you're interested in and take its address. Then convert that address to an int. The bits that are a bit suspect are : 1) the function ptr to int conversion. and 2) I'm not sure the standard guarantees that the addresses of the static member functions will all correctly merge for uses in different compilation units.
typedef void(*fnptr)(void);
union converter
{
fnptr f;
int i;
};
template<typename T>
struct TypeInt
{
static void dummy() {}
static int value() { converter c; c.f = dummy; return c.i; }
};
int main()
{
std::cout<< TypeInt<int>::value() << std::endl;
std::cout<< TypeInt<unsigned int>::value() << std::endl;
std::cout<< TypeInt< TypeVoidP<int> >::value() << std::endl;
}
I don't think it's possible without assigning the numbers yourself or having a single file know about all the types. And even then you will run into trouble with template classes. Do you have to assign the number for each possible instantiation of the class?
type2int as compile time constant is impossible even in C++11. Maybe some rich guy should promise a reward for the anwser? Until then I'm using the following solution, which is basically equal to Matthew Herrmann's:
class type2intbase {
template <typename T>
friend struct type2int;
static const int next() {
static int id = 0; return id++;
}
};
template <typename T>
struct type2int {
static const int value() {
static const int id = type2intbase::next(); return id;
}
};
Note also
template <typename T>
struct type2ptr {
static const void* const value() {
return typeid(T).name();
}
};