Is there some vendor-specific type inference mechanism in Microsoft Visual C++ 2008, similar to the standardized auto or decltype in C++0x?
No, nothing like that, standard nor vendor specific nor addon. You'll have to upgrade to VS2010, it implements auto.
Quoting from a Boost mailing list article by Arkadiy Vertleyb:
God only knows what else can be found
inside this compiler if one is willing
to dig real deep.
For example, Igor' Chesnokov from RSDN
(Russian Software Development Network)
has found a way to implement typeof()
that does not require registration,
and probably has compile-time
performance of a native typeof.
How? Apparently, some weird "feature"
of Visual C++ allowed him to twick a
template body at the moment of
instantiation, when additional context
is available, thus "registering"
classes on the fly, at the moment of
taking typeof().
Microsoft-specific "bugfeatures" are
generally not in the area of my
interests. However I do realize that,
on the Microsoft compiler, this might
look much more attractive then
anything Peder and I have implemented.
And even though I realize that this
might be a serious competition, I
would feel really bad not to mention
this here:
http://rsdn.ru/Forum/?mid=1094305
And quoting Igor Chesnokov's code from the page referenced by the quoted link above:
// type_of() evil implementation for VC7
//
// (c) Chez
// mailto:chezu#pisem.net
#include "stdafx.h"
// This file contains:
// 1) type_id(type)
// 2) var_type_id(expersssion)
// 3) type_of(expression)
// IMPLEMENTATION
template<int ID>
class CTypeRegRoot
{
public:
class id2type;
};
template<typename T, int ID>
class CTypeReg : public CTypeRegRoot<ID>
{
public:
class CTypeRegRoot<ID>::id2type // This uses nice VC6-VC7 bugfeature
{
public:
typedef T Type;
};
typedef void Dummy;
};
template<int N>
class CCounter;
// TUnused is required to force compiler to recompile CCountOf class
template<typename TUnused, int NTested = 0>
class CCountOf
{
public:
enum
{
__if_exists(CCounter<NTested>) { count = CCountOf<TUnused, NTested + 1>::count }
__if_not_exists(CCounter<NTested>) { count = NTested }
};
};
template<class TTypeReg, class TUnused, int NValue> // Helper class
class CProvideCounterValue
{
public:
enum { value = NValue };
};
// type_id
#define type_id(type) \
(CProvideCounterValue< \
/*register TYPE--ID*/ typename CTypeReg<type, CCountOf<type >::count>::Dummy, \
/*increment compile-time Counter*/ CCounter<CCountOf<type >::count>, \
/*pass value of Counter*/CCountOf<type >::count \
>::value)
// Lets type_id() be > than 0
class __Increment_type_id { enum { value = type_id(__Increment_type_id) }; };
template<int NSize>
class sized
{
private:
char m_pad[NSize];
};
template<typename T>
typename sized<type_id(T)> VarTypeID(T&);
template<typename T>
typename sized<type_id(const T)> VarTypeID(const T&);
template<typename T>
typename sized<type_id(volatile T)> VarTypeID(volatile T&);
template<typename T>
typename sized<type_id(const volatile T)> VarTypeID(const volatile T&);
// Unfortunatelly, var_type_id() does not recognize references
#define var_type_id(var) \
(sizeof(VarTypeID(var)))
// type_of
#define type_of(expression) \
/* This uses nice VC6-VC7 bugfeature */ \
CTypeRegRoot<var_type_id(expression)>::id2type::Type
// auto_operator
#define auto_operator(arg1, arg2, op) \
type_of(instance(arg1) op instance(arg2)) operator op
// TEST
class A
{
public:
friend static const char* operator +(const A& a, const A& b)
{
return "chijik-pijik";
}
};
template<typename T>
class Plus
{
public:
friend static type_of(T() + T()) operator +(const Plus<T>& a, const Plus<T>& b)
{
return a.m + b.m;
}
T m;
};
int _tmain(int argc, _TCHAR* argv[])
{
Plus<A> a1, a2;
const char* x = a1 + a2;
return 0;
}
Now I haven't tried this code, and, since it uses compiler-specific stuff, do note that it's for MSVC 7.x. So it may or may not work with a later version. Hopefully it does?
Cheers & hth.,
Use BOOST. Or if you don't want to trudge with all of BOOST, here is a snippet that will work with Visual Studio 2008 (but probably no other version):
namespace typeid_detail {
template <int N>
struct encode_counter : encode_counter<N - 1> {};
template <>
struct encode_counter<0> {};
char (*encode_index(...))[5];
// need to default to a larger value than 4, as due to MSVC's ETI errors. (sizeof(int) = 4)
struct msvc_extract_type_default_param {};
template <typename ID, typename T = msvc_extract_type_default_param>
struct msvc_extract_type;
template <typename ID>
struct msvc_extract_type<ID, msvc_extract_type_default_param> {
template <bool>
struct id2type_impl;
typedef id2type_impl<true> id2type;
};
template <typename ID, typename T>
struct msvc_extract_type : msvc_extract_type<ID,msvc_extract_type_default_param> {
template <>
struct id2type_impl<true> { // VC8.0 specific bugfeature
typedef T type;
};
template <bool>
struct id2type_impl;
typedef id2type_impl<true> id2type;
};
template <typename T, typename ID>
struct msvc_register_type : msvc_extract_type<ID, T> {
};
template <int i>
struct int_ {
enum { value = i };
};
template <int ID>
struct msvc_typeid_wrapper {
typedef typename msvc_extract_type<int_<ID> >::id2type id2type;
typedef typename id2type::type type;
};
template <>
struct msvc_typeid_wrapper<1> {
typedef msvc_typeid_wrapper<1> type;
};
// workaround for ETI-bug for VC6 and VC7
template <>
struct msvc_typeid_wrapper<4> {
typedef msvc_typeid_wrapper<4> type;
};
// workaround for ETI-bug for VC7.1
#define TYPEOF_INDEX(T) (sizeof(*encode_index((encode_counter<405/*1005*/>*)0))) // this needs to be lower for VS 2008, otherwise causes too deep templates
#define TYPEOF_NEXT_INDEX(next) friend char (*encode_index(encode_counter<next>*))[next];
template <typename T>
struct encode_type {
static const unsigned value = TYPEOF_INDEX(T);
// get the next available compile time constants index
typedef typename msvc_register_type<T,int_<value> >::id2type type;
// instantiate the template
static const unsigned next = value + 1;
// set the next compile time constants index
TYPEOF_NEXT_INDEX(next);
// increment the compile time constant (only needed when extensions are not active)
};
template <class T>
struct sizer {
typedef char(*type)[encode_type<T>::value];
};
template <typename T> typename sizer<T>::type encode_start(T const&);
// a function that converts a value to size-encoded type (not implemented, only needed for type inference)
template <typename Organizer, typename T>
msvc_register_type<T, Organizer> typeof_register_type(const T&, Organizer* = 0);
} // ~typeid_detail
#define type_of(expr) \
typeid_detail::msvc_typeid_wrapper<sizeof(*typeid_detail::encode_start(expr))>::type
And use e.g. as:
int x = 123;
float y = 456.0f;
typedef type_of(x+y) int_plus_float;
int_plus_float value = x + y;
And of course refer to BOOST license when using this.
Related
I'm trying to build a trait for member detector when the member is const and private. I can use the idiom DetectX from wikibook
template<typename T>
class DetectX
{
struct Fallback { int X; }; // add member name "X"
struct Derived : T, Fallback { };
template<typename U, U> struct Check;
typedef char ArrayOfOne[1]; // typedef for an array of size one.
typedef char ArrayOfTwo[2]; // typedef for an array of size two.
template<typename U>
static ArrayOfOne & func(Check<int Fallback::*, &U::X> *);
template<typename U>
static ArrayOfTwo & func(...);
public:
typedef DetectX type;
enum { value = sizeof(func<Derived>(0)) == 2 };
};
But the idiom doesn't differentiate between private const member function and private non-const member function.It just happens to detect both non-const and const private member function.
I think the const information is lost when you take address of (&U::X) .
How do i detect a private member function and differentiate between const and non-const private member function of the same class ?
After looking at Tyker's code i could get a C++03 implementation using friend . I'm still waiting to see a solution without friend.
/* Start - decltype simulation in c++03 */
template <size_t>
struct TypeId;
#define REGISTER_TYPE(T, id) \
template <> \
struct TypeId<id> { \
char value[id]; \
typedef T type; \
static char const* const name; \
}; \
char const* const TypeId<id>::name = #T; \
TypeId<id> type_to_id(T);
#define TYPEID_(value) TypeId<sizeof(type_to_id(value))>
#define TYPEOF(value) typename TYPEID_(value)::type
#define TYPENAME(value) TYPEID_(value)::name
REGISTER_TYPE(int, 1)
REGISTER_TYPE(unsigned int, 2)
/* End - decltype simulation - c++03 */
template <typename T>
class DetectX {
template <typename V>
struct Check;
template <typename U>
static char& func(
Check<TYPEOF((*(const U*)0).mem())>*); // use const U* or U*
template <typename U>
static short& func(...);
public:
typedef DetectX type;
enum { value = sizeof(func<T>(0)) }; // returns 1 or 2
};
class bar {
private:
int mem() const;
template <typename T>
friend class DetectX;
};
/* Register class and function signatures */
#define REGISTER_CLASS(X) \
typedef int (X::*pf)(); \
typedef int (X::*pfc)() const;
REGISTER_CLASS(bar)
REGISTER_TYPE(pf, 3)
REGISTER_TYPE(pfc, 4)
int main() { std::cout << DetectX<bar>::value << std::endl; }
Generate an object from std::declval<T>(), then cast it to const, then invoke the member function. Do this in a SFINAE function. The function should have two overloading versions with different return types. Use decltype and std::is_same to check the return type in order to know if the member function is const-qualified.
it is possible with to detect const private members with the following code.
the only drawback is a friend declaration, but i don't think it can be done without.
template<typename T, typename...>
using first_type = T;
class private_const_test {
template<typename = void>
static constexpr std::false_type has_const_test_impl(...) {
return std::false_type();
}
template<typename T>
static constexpr first_type<std::true_type, decltype(std::declval<const T>().test())> has_const_test_impl(T) {
return std::true_type();
}
public:
template<typename T>
static constexpr bool has_const_test = decltype(has_const_test_impl(std::declval<T>()))::value;
};
class public_const_test {
template<typename = void>
static constexpr std::false_type has_const_test_impl(...) {
return std::false_type();
}
template<typename T>
static constexpr first_type<std::true_type, decltype(std::declval<const T>().test())> has_const_test_impl(T) {
return std::true_type();
}
public:
template<typename T>
static constexpr bool has_const_test = decltype(has_const_test_impl(std::declval<T>()))::value;
};
template<typename T>
constexpr bool has_private_const_test = private_const_test::has_const_test<T> && !public_const_test::has_const_test<T>;
struct A {
friend class private_const_test;
private:
void test() const {}
};
struct B {
friend class private_const_test;
void test() const {}
};
int main()
{
std::cout << has_private_const_test<A> << std::endl; // true
std::cout << has_private_const_test<B> << std::endl; // false
std::cout << has_private_const_test<int> << std::endl; // false
}
As far my understanding goes I want to restrict only 2 types int and string for the following class, just like in java template definition with extends.
template<typename T>
typename std::enable_if<std::is_same<T,int>::value || std::is_same<T,string>::value>::type
class ExchangeSort
{
public:
void bubbleSort(T buffer[], int s);
void print(T buffer[], int s);
void bubbleSort(const vector<T>& vec);
};
But if I'm instantiating like below
ExchangeSort<int>* sortArray = new ExchangeSort<int>;
I'm getting errors for the above line ExchangeSort is undefined. What is the problem ?
SFINAE can be used to conditionally disable function overloads or template specialisations. It makes no sense to try to use it to disable a class template, since class templates cannot be overloaded. You'll be better off using a static assertion:
template<typename T>
class ExchangeSort
{
static_assert(std::is_same<T,int>::value || std::is_same<T,string>::value, "ExchangeSort requires int or string");
public:
// The rest as before
As to the errors you were getting, the code didn't make syntactic sense. enable_if can only appear where a type is expected. With functions, it's often used to wrap the return type, in which case it's syntactically similar to what you wrote. But with classes, there's no type between template and the class definition.
Here's another way which allows the possibility of adding further specialisations down the line:
#include <type_traits>
#include <vector>
#include <string>
//
// Step 1 : polyfill missing c++17 concepts
//
namespace notstd {
template<class...> struct disjunction : std::false_type { };
template<class B1> struct disjunction<B1> : B1 { };
template<class B1, class... Bn>
struct disjunction<B1, Bn...>
: std::conditional_t<bool(B1::value), B1, disjunction<Bn...>> { };
}
//
// Step 2 : create a test to see if a type is in a list
//
namespace detail {
template<class T, class...Us>
struct is_one_of : notstd::disjunction< std::is_same<T, Us>... > {};
}
template<class T, class...Us>
static constexpr auto IsOneOf = detail::is_one_of<T, Us...>::value;
//
// Step 3 : declare the default specialisation, but don't define it
//
template<class T, typename Enable = void>
struct ExchangeSort;
//
// Step 4 : define the specialisations we want
//
template<typename T>
class ExchangeSort<T, std::enable_if_t<IsOneOf<T, int, std::string>>>
{
public:
void bubbleSort(T buffer[], int s);
void print(T buffer[], int s);
void bubbleSort(const std::vector<T>& vec);
};
//
// Test
//
int main()
{
auto esi = ExchangeSort<int>();
auto ess = ExchangeSort<std::string>();
// won't compile
// auto esd = ExchangeSort<double>();
}
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);
}
Is there a way, presumably using templates, macros or a combination of the two, that I can generically apply a function to different classes of objects but have them respond in different ways if they do not have a specific function?
I specifically want to apply a function which will output the size of the object (i.e. the number of objects in a collection) if the object has that function but will output a simple replacement (such as "N/A") if the object doesn't. I.e.
NO_OF_ELEMENTS( mySTLMap ) -----> [ calls mySTLMap.size() to give ] ------> 10
NO_OF_ELEMENTS( myNoSizeObj ) --> [ applies compile time logic to give ] -> "N/A"
I expect that this might be something similar to a static assertion although I'd clearly want to compile a different code path rather than fail at build stage.
From what I understand, you want to have a generic test to see if a class has a certain member function. This can be accomplished in C++ using SFINAE. In C++11 it's pretty simple, since you can use decltype:
template <typename T>
struct has_size {
private:
template <typename U>
static decltype(std::declval<U>().size(), void(), std::true_type()) test(int);
template <typename>
static std::false_type test(...);
public:
typedef decltype(test<T>(0)) type;
enum { value = type::value };
};
If you use C++03 it is a bit harder due to the lack of decltype, so you have to abuse sizeof instead:
template <typename T>
struct has_size {
private:
struct yes { int x; };
struct no {yes x[4]; };
template <typename U>
static typename boost::enable_if_c<sizeof(static_cast<U*>(0)->size(), void(), int()) == sizeof(int), yes>::type test(int);
template <typename>
static no test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(yes) };
};
Of course this uses Boost.Enable_If, which might be an unwanted (and unnecessary) dependency. However writing enable_if yourself is dead simple:
template<bool Cond, typename T> enable_if;
template<typename T> enable_if<true, T> { typedef T type; };
In both cases the method signature test<U>(int) is only visible, if U has a size method, since otherwise evaluating either the decltype or the sizeof (depending on which version you use) will fail, which will then remove the method from consideration (due to SFINAE. The lengthy expressions std::declval<U>().size(), void(), std::true_type() is an abuse of C++ comma operator, which will return the last expression from the comma-separated list, so this makes sure the type is known as std::true_type for the C++11 variant (and the sizeof evaluates int for the C++03 variant). The void() in the middle is only there to make sure there are no strange overloads of the comma operator interfering with the evaluation.
Of course this will return true if T has a size method which is callable without arguments, but gives no guarantees about the return value. I assume wou probably want to detect only those methods which don't return void. This can be easily accomplished with a slight modification of the test(int) method:
// C++11
template <typename U>
static typename std::enable_if<!is_void<decltype(std::declval<U>().size())>::value, std::true_type>::type test(int);
//C++03
template <typename U>
static typename std::enable_if<boost::enable_if_c<sizeof(static_cast<U*>(0)->size()) != sizeof(void()), yes>::type test(int);
There was a discussion about the abilities of constexpr some times ago. It's time to use it I think :)
It is easy to design a trait with constexpr and decltype:
template <typename T>
constexpr decltype(std::declval<T>().size(), true) has_size(int) { return true; }
template <typename T>
constexpr bool has_size(...) { return false; }
So easy in fact that the trait loses most of its value:
#include <iostream>
#include <vector>
template <typename T>
auto print_size(T const& t) -> decltype(t.size(), void()) {
std::cout << t.size() << "\n";
}
void print_size(...) { std::cout << "N/A\n"; }
int main() {
print_size(std::vector<int>{1, 2, 3});
print_size(1);
}
In action:
3
N/A
This can be done using a technique called SFINAE. In your specific case you could implement that using Boost.Concept Check. You'd have to write your own concept for checking for a size-method. Alternatively you could use an existing concept such as Container, which, among others, requires a size-method.
You can do something like
template< typename T>
int getSize(const T& t)
{
return -1;
}
template< typename T>
int getSize( const std::vector<T>& t)
{
return t.size();
}
template< typename T , typename U>
int getSize( const std::map<T,U>& t)
{
return t.size();
}
//Implement this interface for
//other objects
class ISupportsGetSize
{
public:
virtual int size() const= 0;
};
int getSize( const ISupportsGetSize & t )
{
return t.size();
}
int main()
{
int s = getSize( 4 );
std::vector<int> v;
s = getSize( v );
return 0;
}
basically the most generic implementation is always return -1 or "NA" but for vector and maps it will return the size. As the most general one always matches there is never a build time failure
Here you go. Replace std::cout with the output of your liking.
template <typename T>
class has_size
{
template <typename C> static char test( typeof(&C::size) ) ;
template <typename C> static long test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
template<bool T>
struct outputter
{
template< typename C >
static void output( const C& object )
{
std::cout << object.size();
}
};
template<>
struct outputter<false>
{
template< typename C >
static void output( const C& )
{
std::cout << "N/A";
}
};
template<typename T>
void NO_OF_ELEMENTS( const T &object )
{
outputter< has_size<T>::value >::output( object );
}
You could try something like:
#include <iostream>
#include <vector>
template<typename T>
struct has_size
{
typedef char one;
typedef struct { char a[2]; } two;
template<typename Sig>
struct select
{
};
template<typename U>
static one check (U*, select<char (&)[((&U::size)!=0)]>* const = 0);
static two check (...);
static bool const value = sizeof (one) == sizeof (check (static_cast<T*> (0)));
};
struct A{ };
int main ( )
{
std::cout << has_size<int>::value << "\n";
std::cout << has_size<A>::value << "\n";
std::cout << has_size<std::vector<int>>::value << "\n";
}
but you have to be careful, this does neither work when size is overloaded, nor when it is a template. When you can use C++11, you can replace the above sizeof trick by decltype magic
I want to define an operator<< for all enums, to cout the value and print that it is an enum like this:
code:
enum AnyEnum{A,B,C};
AnyEnum enm = A;
cout << enm <<endl;
output:
This is an enum which has a value equal to 0
I know a way of doing this with Boost library by using is_enum struct. But I don’t understand how it works. So that's why, in general, I am interested how to identify if the veriable is a class type, union type or an enum (in compile time).
Determining class types you could use the fact that member pointers exist
template<typename A, typename B>
struct issame { };
template<typename A>
struct issame<A, A> { typedef void type; };
template<typename> struct tovoid { typedef void type; };
template<typename T, typename = void>
struct isclass { static bool const value = false; };
template<typename C>
struct isclass<C, typename tovoid<int C::*>::type> {
static bool const value = true;
};
You cannot detect the difference of an union and a non-union class. At least I don't know how, and boost doesn't know either.
I think detecting enums could work by making sure T isn't a class, function or integral type, and then trying to assign to an integral type. You could
template<typename E, typename = void>
struct isenum {
struct No { char x; };
struct Yes { No n1; No n2; };
struct nullsink {};
static No checkI(nullsink*); // accept null pointer constants
static Yes checkI(...);
static Yes checkE(int);
static No checkE(...);
static bool const value = (sizeof(checkI(E())) == sizeof(Yes)) &&
(sizeof(checkE(E())) == sizeof(Yes));
};
// class
template<typename E>
struct isenum<E, typename tovoid<int E::*>::type> {
static bool const value = false;
};
// reference
template<typename R>
struct isenum<R&, void> {
static bool const value = false;
};
// function (FuntionType() will error out).
template<typename F>
struct isenum<F, typename issame<void(F), void(F*)>::type> {
static bool const value = false;
};
// array (ArrayType() will error out)
template<typename E>
struct isenum<E[], void> {
static bool const value = false;
};
template<typename E, int N>
struct isenum<E[N], void> {
static bool const value = false;
};
Quick & dirty test (works on GCC/clang/comeau):
enum A { };
struct B { };
typedef int &C;
typedef void D();
typedef int E;
typedef long F;
typedef int const G;
typedef int H[1];
template<typename T, bool E>
struct confirm { typedef char x[(T::value == E) ? 1 : -1]; };
int main() {
confirm< isenum<A>, true >();
confirm< isenum<B>, false >();
confirm< isenum<C>, false >();
confirm< isenum<D>, false >();
confirm< isenum<E>, false >();
confirm< isenum<F>, false >();
confirm< isenum<G>, false >();
confirm< isenum<H>, false >();
}
I am interested how to identify if the veriable is a class type, union type or an enum (in compile time).
boost::type_traits
Even C++ TR1 has got a <type_traits> header to support that functionality. In C++0x everything's gonna be a lot better.
For example the following machinery makes use of SFINAE to check whether the argument passed is a class type:
template<typename T>struct Check_If_T_Is_Class_Type
{
template<typename C> static char func (char C::*p);
template<typename C> static long func (...);
enum{val = CHECKER(func,Check_If_T_Is_Class_Type)};
};
The MACRO CHECKER is
#define CHECKER(func_name,class_name) \
sizeof(class_name<T>::template func_name<T>(0)) == 1
To understand how type_traits work you need to have some basic knowledge of templates including template metaprogramming and SFINAE.
This is usually done with compiler hooks. The compiler has special functions that "fill" the template with the apropriate value (at least in C++0x where type_traits has been standardized).
For instance the is_pod trait uses the __is_pod compiler hook under VC 10 to get the apropriate information.
its not possible to know the variable type at compile time.