Fixed bugs in TypeTraits' scalar, array, const and volatile detection.
Added Enum- and pointer-to-member-function-detection code. git-svn-id: svn://svn.code.sf.net/p/loki-lib/code/trunk@90 7ec92016-0320-0410-acc4-a06ded1c099a
This commit is contained in:
parent
20107644b0
commit
621b2addce
2 changed files with 455 additions and 283 deletions
|
@ -1,6 +1,6 @@
|
||||||
Loki VC 6.0 Port or how to produce C1001 - Internal Compiler Errors
|
Loki VC 6.0 Port or how to produce C1001 - Internal Compiler Errors
|
||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
Version: 0.3a
|
Version: 0.3b
|
||||||
|
|
||||||
Introduction/Compatibility:
|
Introduction/Compatibility:
|
||||||
---------------------------
|
---------------------------
|
||||||
|
@ -30,35 +30,44 @@ If you use Singletons with longevity you must add Singleton.cpp to your project/
|
||||||
Fixes:
|
Fixes:
|
||||||
------
|
------
|
||||||
|
|
||||||
Jan 12, 2003:
|
Jan 30, 2003:
|
||||||
-------------
|
-------------
|
||||||
* changed the signature of SmallObject's op new. Now it
|
* In TypeTraits.h: Fixed bugs in TypeTraits' scalar and array detection.
|
||||||
matches the corresponding op delete.
|
const and volatile detection is now based on techniques from boost's type traits
|
||||||
|
(see http://www.boost.org/libs/type_traits/)
|
||||||
|
Added Enum- and pointer-to-member-function-detection code.
|
||||||
|
Thanks to M. Yamada.
|
||||||
|
|
||||||
|
|
||||||
|
Jan 12, 2003:
|
||||||
|
-------------
|
||||||
|
* changed the signature of SmallObject's op new. Now it
|
||||||
|
matches the corresponding op delete.
|
||||||
Thanks to M.Yamada for the hint and the solution.
|
Thanks to M.Yamada for the hint and the solution.
|
||||||
|
|
||||||
Dec 08, 2002:
|
Dec 08, 2002:
|
||||||
-------------
|
-------------
|
||||||
* In HierarchyGenerators.h: Sergey Khachatrian reported a bug
|
* In HierarchyGenerators.h: Sergey Khachatrian reported a bug
|
||||||
in GenScatterHierarchy when used with a typelist containing
|
in GenScatterHierarchy when used with a typelist containing
|
||||||
equal types (e.g. GenScatterHierarchy<TYPELIST_2(int, int), UnitWrapper>
|
equal types (e.g. GenScatterHierarchy<TYPELIST_2(int, int), UnitWrapper>
|
||||||
resp. Tuple<TYPELIST_2(int, int)>)
|
resp. Tuple<TYPELIST_2(int, int)>)
|
||||||
Fixing the bug I found another MSVC6-Problem in the Field-function.
|
Fixing the bug I found another MSVC6-Problem in the Field-function.
|
||||||
The workaround for this problems results in an interface change.
|
The workaround for this problems results in an interface change.
|
||||||
|
|
||||||
please refer to the section "Interface changes" below for further information.
|
please refer to the section "Interface changes" below for further information.
|
||||||
|
|
||||||
Dec 03, 2002
|
Dec 03, 2002
|
||||||
-------------
|
-------------
|
||||||
* In MSVC6Helpers.h: The original version failed to qualify some types from the
|
* In MSVC6Helpers.h: The original version failed to qualify some types from the
|
||||||
Private-Namespace.
|
Private-Namespace.
|
||||||
Thanks to Adi Shavit for pointing that out
|
Thanks to Adi Shavit for pointing that out
|
||||||
|
|
||||||
* In Threads.h: Changed wrong ctor/dtor names in ObjectLevelLockable.
|
* In Threads.h: Changed wrong ctor/dtor names in ObjectLevelLockable.
|
||||||
Thanks to Adi Shavit for pointing that out
|
Thanks to Adi Shavit for pointing that out
|
||||||
|
|
||||||
Nov 19, 2002:
|
Nov 19, 2002:
|
||||||
-------------
|
-------------
|
||||||
* In SmartPtr.h: Changed template ctors. See Notes.
|
* In SmartPtr.h: Changed template ctors. See Notes.
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
------
|
------
|
||||||
|
@ -73,90 +82,90 @@ F. return statements with an expression of type cv in functions with a return ty
|
||||||
|
|
||||||
Unfortunately the MSVC 6.0 supports neither of them.
|
Unfortunately the MSVC 6.0 supports neither of them.
|
||||||
|
|
||||||
A. I used various techniques to simulate partial template specialization. In some cases
|
A. I used various techniques to simulate partial template specialization. In some cases
|
||||||
these techniques allowed me to retain the original interfaces but often that was not
|
these techniques allowed me to retain the original interfaces but often that was not
|
||||||
possible (or better: i did not find a proper solution). In any case it leads
|
possible (or better: i did not find a proper solution). In any case it leads
|
||||||
to increasing code complexity :-)
|
to increasing code complexity :-)
|
||||||
|
|
||||||
B. One way to simulate template template parameters is to replace the template class with
|
B. One way to simulate template template parameters is to replace the template class with
|
||||||
a normal class containing a nested template class. You then move the original functionality
|
a normal class containing a nested template class. You then move the original functionality
|
||||||
to the nested class.
|
to the nested class.
|
||||||
The problem with this approach is MSVC's 'dependent template typedef bug'. MSVC 6.0 does not
|
The problem with this approach is MSVC's 'dependent template typedef bug'. MSVC 6.0 does not
|
||||||
allow something like this:
|
allow something like this:
|
||||||
|
|
||||||
[code]
|
[code]
|
||||||
template <class APolicy, class T>
|
template <class APolicy, class T>
|
||||||
struct Foo
|
struct Foo
|
||||||
{
|
{
|
||||||
// 'error C1001 - Internal Compiler Error' here
|
// 'error C1001 - Internal Compiler Error' here
|
||||||
typedef typename APolicy::template In<T> type;
|
typedef typename APolicy::template In<T> type;
|
||||||
};
|
};
|
||||||
|
|
||||||
[/code]
|
[/code]
|
||||||
|
|
||||||
To make a long story short, I finally decided to use boost::mpl's apply-technique to
|
To make a long story short, I finally decided to use boost::mpl's apply-technique to
|
||||||
simulate template template parameters. This approach works fine with MSVC 6.0. But be warned,
|
simulate template template parameters. This approach works fine with MSVC 6.0. But be warned,
|
||||||
this technique uses not valid C++.
|
this technique uses not valid C++.
|
||||||
Of course, replacing template template parameters always results in some interface changes.
|
Of course, replacing template template parameters always results in some interface changes.
|
||||||
|
|
||||||
C. I added dummy-Parameters to (Member-)Functions that depend on explicit template
|
C. I added dummy-Parameters to (Member-)Functions that depend on explicit template
|
||||||
argument specification. These dummy-Parameters help the compiler in deducing the template
|
argument specification. These dummy-Parameters help the compiler in deducing the template
|
||||||
parameters that otherwise need to be explicitly specified.
|
parameters that otherwise need to be explicitly specified.
|
||||||
Example:
|
Example:
|
||||||
[code]
|
[code]
|
||||||
struct Foo
|
struct Foo
|
||||||
{
|
{
|
||||||
template <class T>
|
template <class T>
|
||||||
T Func();
|
T Func();
|
||||||
};
|
};
|
||||||
[/code]
|
[/code]
|
||||||
becomes
|
becomes
|
||||||
[code]
|
[code]
|
||||||
struct Foo
|
struct Foo
|
||||||
{
|
{
|
||||||
template <class T>
|
template <class T>
|
||||||
T Func(T* pDummy1);
|
T Func(T* pDummy1);
|
||||||
};
|
};
|
||||||
[/code]
|
[/code]
|
||||||
in this port.
|
in this port.
|
||||||
|
|
||||||
Update:
|
Update:
|
||||||
-------
|
-------
|
||||||
The MSVC 6.0 sometimes does not overload normal functions depending
|
The MSVC 6.0 sometimes does not overload normal functions depending
|
||||||
on explicit argument specification correctly (see: Microsoft KB Article - 240871)
|
on explicit argument specification correctly (see: Microsoft KB Article - 240871)
|
||||||
The following code demonstrates the problem:
|
The following code demonstrates the problem:
|
||||||
[code]
|
[code]
|
||||||
template <unsigned i, class T>
|
template <unsigned i, class T>
|
||||||
void BugDemonstration(T p)
|
void BugDemonstration(T p)
|
||||||
{
|
{
|
||||||
printf("BugDemonstration called with i = %d\n", i);
|
printf("BugDemonstration called with i = %d\n", i);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
GenScatterHierarchy<TYPELIST_3(int, int, int), TestUnitWrapper> Bla;
|
GenScatterHierarchy<TYPELIST_3(int, int, int), TestUnitWrapper> Bla;
|
||||||
// will always print: "BugDemonstration called with i = 2";
|
// will always print: "BugDemonstration called with i = 2";
|
||||||
BugDemonstration<0>(Bla);
|
BugDemonstration<0>(Bla);
|
||||||
BugDemonstration<1>(Bla);
|
BugDemonstration<1>(Bla);
|
||||||
BugDemonstration<2>(Bla);
|
BugDemonstration<2>(Bla);
|
||||||
}
|
}
|
||||||
[/code]
|
[/code]
|
||||||
|
|
||||||
As a workaround i added dummy-parameters.
|
As a workaround i added dummy-parameters.
|
||||||
|
|
||||||
D. Virtual functions that use covariant return types (e.g. return a pointer to Derived)
|
D. Virtual functions that use covariant return types (e.g. return a pointer to Derived)
|
||||||
in the original library were changed so that they have exactly the
|
in the original library were changed so that they have exactly the
|
||||||
same return type as the original virtual function (e.g. return a pointer to Base).
|
same return type as the original virtual function (e.g. return a pointer to Base).
|
||||||
|
|
||||||
E. All template parameters that have a default type of void in the original lib now
|
E. All template parameters that have a default type of void in the original lib now
|
||||||
have int as default type.
|
have int as default type.
|
||||||
|
|
||||||
F. In Functor.h I changed a ResultType of type void to VoidAsType (an udt). This change is transparent
|
F. In Functor.h I changed a ResultType of type void to VoidAsType (an udt). This change is
|
||||||
for the user of Functor.
|
transparent to the user of Functor.
|
||||||
Because I could not think of any general and transparent workaround I followed different
|
Because I could not think of any general and transparent workaround I followed different
|
||||||
strategies. In Visitor.h for example I created new classes (and macros) for the void-case.
|
strategies. In Visitor.h for example I created new classes (and macros) for the void-case.
|
||||||
In other places (for example: MultiMethod.h) this port simply fails to support void as
|
In other places (for example: MultiMethod.h) this port simply fails to support void as
|
||||||
return type :-(
|
return type :-(
|
||||||
|
|
||||||
Some words to template-ctors resp. template assignment operators:
|
Some words to template-ctors resp. template assignment operators:
|
||||||
The MSVC 6.0 introduces an order-dependency for template ctor
|
The MSVC 6.0 introduces an order-dependency for template ctor
|
||||||
|
@ -168,11 +177,11 @@ So instead of
|
||||||
template <class T>
|
template <class T>
|
||||||
struct Foo
|
struct Foo
|
||||||
{
|
{
|
||||||
Foo(const Foo&)
|
Foo(const Foo&)
|
||||||
{}
|
{}
|
||||||
template <class U>
|
template <class U>
|
||||||
Foo(const Foo<U>& r)
|
Foo(const Foo<U>& r)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
[/code]
|
[/code]
|
||||||
you *need* to write:
|
you *need* to write:
|
||||||
|
@ -180,12 +189,12 @@ you *need* to write:
|
||||||
template <class T>
|
template <class T>
|
||||||
struct Foo
|
struct Foo
|
||||||
{
|
{
|
||||||
template <class U>
|
template <class U>
|
||||||
Foo(const Foo<U>& r)
|
Foo(const Foo<U>& r)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Foo(const Foo& r)
|
Foo(const Foo& r)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
[/code]
|
[/code]
|
||||||
|
|
||||||
|
@ -198,12 +207,12 @@ the form of a copy-ctor. If you write something like this (as in the functor-cla
|
||||||
template <class T>
|
template <class T>
|
||||||
struct Foo
|
struct Foo
|
||||||
{
|
{
|
||||||
template <class Fun>
|
template <class Fun>
|
||||||
Foo(Fun r)
|
Foo(Fun r)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Foo(const Foo& r)
|
Foo(const Foo& r)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
[/code]
|
[/code]
|
||||||
then the VC will no longer find a copy-ctor.
|
then the VC will no longer find a copy-ctor.
|
||||||
|
@ -214,111 +223,122 @@ Interface changes:
|
||||||
------------------
|
------------------
|
||||||
1. In Threads.h:
|
1. In Threads.h:
|
||||||
|
|
||||||
* Thread-Policies changed from class templates to normal classes containing a
|
* Thread-Policies changed from class templates to normal classes containing a
|
||||||
nested class template 'In'.
|
nested class template 'In'.
|
||||||
|
|
||||||
consequences:
|
consequences:
|
||||||
This change is not very dramatic because it won't break code using this port when
|
This change is not very dramatic because it won't break code using this port when
|
||||||
switching to the original library (only new Thread-Policies must be changed)
|
switching to the original library (only new Thread-Policies must be changed)
|
||||||
|
|
||||||
2. In Singleton.h:
|
2. In Singleton.h:
|
||||||
|
|
||||||
* The Creation- and Lifetime-Policies are no longer class templates. Instead they all use
|
* The Creation- and Lifetime-Policies are no longer class templates. Instead they all use
|
||||||
Member-Templates.
|
Member-Templates.
|
||||||
|
|
||||||
consequences:
|
consequences:
|
||||||
Again this change will only break new Policies when switching to the
|
Again this change will only break new Policies when switching to the
|
||||||
original library.
|
original library.
|
||||||
|
|
||||||
3. In Functor.h:
|
3. In Functor.h:
|
||||||
|
|
||||||
* No covariant return types.
|
* No covariant return types.
|
||||||
|
|
||||||
consequences:
|
consequences:
|
||||||
DoClone always returns a FunctorImplBase<R, ThreadingModel>* where R is the functor's return
|
DoClone always returns a FunctorImplBase<R, ThreadingModel>* where R is the functor's return
|
||||||
type and ThreadingModel its current ThreadingModel.
|
type and ThreadingModel its current ThreadingModel.
|
||||||
|
|
||||||
4. TypeTraits.h
|
4. TypeTraits.h
|
||||||
|
|
||||||
* Because VC 6.0 lacks partial template specialization, the TypeTraits-Class provides not
|
* Because VC 6.0 lacks partial template specialization, the TypeTraits-Class
|
||||||
all the stuff provided by the original library's version.
|
fails to provide the following typedefs:
|
||||||
|
PointeeType, ReferredType, NonVolatileType and UnqualifiedType.
|
||||||
|
|
||||||
|
* Since the VC 6 does not differentiate
|
||||||
|
between void, const void, volatile void and const volatile void the following
|
||||||
|
assertions will fail:
|
||||||
|
assert(TypeTraits<const void>::isConst == 1)
|
||||||
|
assert(TypeTraits<volatile void>::isVolatile == 1)
|
||||||
|
assert(TypeTraits<const volatile void>::isConst == 1)
|
||||||
|
assert(TypeTraits<const volatile void>::isVolatile == 1)
|
||||||
|
|
||||||
|
* This port adds isEnum and isMemberFuncPointer
|
||||||
|
|
||||||
|
|
||||||
5. HierarchyGenerator.h
|
5. HierarchyGenerator.h
|
||||||
|
|
||||||
* I used Mat Marcus' approach to port GenScatterHierarchy.
|
* I used Mat Marcus' approach to port GenScatterHierarchy.
|
||||||
See http://lists.boost.org/MailArchives/boost/msg20915.php) for the consequences.
|
See http://lists.boost.org/MailArchives/boost/msg20915.php) for the consequences.
|
||||||
|
|
||||||
* Same for GenLinearHierarchy
|
* Same for GenLinearHierarchy
|
||||||
|
|
||||||
* Unit is no longer a template template parameter.
|
* Unit is no longer a template template parameter.
|
||||||
|
|
||||||
consequences:
|
consequences:
|
||||||
For every concrete unit-template there must be a normal class containing
|
For every concrete unit-template there must be a normal class containing
|
||||||
a nested-template class called 'In'. 'In' should only contain a typedef to the
|
a nested-template class called 'In'. 'In' should only contain a typedef to the
|
||||||
concrete Unit.
|
concrete Unit.
|
||||||
|
|
||||||
Update:
|
Update:
|
||||||
The port's original version of GenScatterHierarchy does not work when used
|
The port's original version of GenScatterHierarchy does not work when used
|
||||||
with typelists containing equal types.
|
with typelists containing equal types.
|
||||||
The problem is due to a VC bug. The VC fails to compile code similar
|
The problem is due to a VC bug. The VC fails to compile code similar
|
||||||
to this, although it is perfectly legal.
|
to this, although it is perfectly legal.
|
||||||
[code]
|
[code]
|
||||||
template <class T>
|
template <class T>
|
||||||
class Wrapper
|
class Wrapper
|
||||||
{};
|
{};
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
struct B : public Wrapper<T>
|
struct B : public Wrapper<T>
|
||||||
{};
|
{};
|
||||||
|
|
||||||
// ERROR: 'A<T>' : direct base 'Wrapper<T>' is inaccessible; already a base of 'B<T>'
|
// ERROR: 'A<T>' : direct base 'Wrapper<T>' is inaccessible; already a base of 'B<T>'
|
||||||
template <class T>
|
template <class T>
|
||||||
class A : public B<T>, public Wrapper<T>
|
class A : public B<T>, public Wrapper<T>
|
||||||
{};
|
{};
|
||||||
[/code]
|
[/code]
|
||||||
|
|
||||||
Unfortunately my workaround has a big drawback.
|
Unfortunately my workaround has a big drawback.
|
||||||
GenScatterHierarchy now has to generate a lot more classes.
|
GenScatterHierarchy now has to generate a lot more classes.
|
||||||
Alexandrescu's original implementation generates 3*n classes (n - number of types in the typelist)
|
Alexandrescu's original implementation generates 3*n classes (n - number of types in the typelist)
|
||||||
The old version of my port creates 4 * n + 1
|
The old version of my port creates 4 * n + 1
|
||||||
The new version will create 5 * n
|
The new version will create 5 * n
|
||||||
|
|
||||||
The fix also reveals the "Explicitly Specified Template Functions Not Overloaded Correctly"-Bug
|
The fix also reveals the "Explicitly Specified Template Functions Not Overloaded Correctly"-Bug
|
||||||
(Microsoft KB Article - 240871) in the Field-Function taking a nontype int Parameter.
|
(Microsoft KB Article - 240871) in the Field-Function taking a nontype int Parameter.
|
||||||
|
|
||||||
This leads to an interface change:
|
This leads to an interface change:
|
||||||
Instead of: Field<0>(obj)
|
Instead of: Field<0>(obj)
|
||||||
one now has to write
|
one now has to write
|
||||||
Field(obj, Int2Type<0>());
|
Field(obj, Int2Type<0>());
|
||||||
|
|
||||||
I added a macro FIELD. Using this macro one can write
|
I added a macro FIELD. Using this macro one can write
|
||||||
FIELD(obj, 0)
|
FIELD(obj, 0)
|
||||||
|
|
||||||
|
|
||||||
6. Factory.h
|
6. Factory.h
|
||||||
|
|
||||||
* The Error-Policy for Factory and CloneFactory is no longer a template template parameter.
|
* The Error-Policy for Factory and CloneFactory is no longer a template template parameter.
|
||||||
Use a class with member-templates instead.
|
Use a class with member-templates instead.
|
||||||
|
|
||||||
consequences:
|
consequences:
|
||||||
This change will only break new Policies when switching to the
|
This change will only break new Policies when switching to the
|
||||||
original library.
|
original library.
|
||||||
|
|
||||||
7. AbstractFactory.h
|
7. AbstractFactory.h
|
||||||
|
|
||||||
* no covariant return types
|
* no covariant return types
|
||||||
|
|
||||||
* no template template parameters
|
* no template template parameters
|
||||||
For every concrete Factory-Unit there must be a normal class containing
|
For every concrete Factory-Unit there must be a normal class containing
|
||||||
a nested-template class called 'In'. 'In' shall contain a typedef to the
|
a nested-template class called 'In'. 'In' shall contain a typedef to the
|
||||||
concrete Factory-Unit.
|
concrete Factory-Unit.
|
||||||
|
|
||||||
* Added a dummy-Parameter to AbstractFactory::Create (see C.)
|
* Added a dummy-Parameter to AbstractFactory::Create (see C.)
|
||||||
Calling syntax changed from:
|
Calling syntax changed from:
|
||||||
ConcProduct* p = aFactory.Create<ConcProduct>();
|
ConcProduct* p = aFactory.Create<ConcProduct>();
|
||||||
to
|
to
|
||||||
ConcProduct* p = aFactory.Create((ConcProduct*)0);
|
ConcProduct* p = aFactory.Create((ConcProduct*)0);
|
||||||
|
|
||||||
|
|
||||||
8. SmartPtr.h
|
8. SmartPtr.h
|
||||||
|
@ -330,26 +350,27 @@ Interface changes:
|
||||||
|
|
||||||
9. Visitor.h
|
9. Visitor.h
|
||||||
|
|
||||||
* no template template parameters
|
* no template template parameters
|
||||||
(see 7.for a description of the consequences)
|
(see 7.for a description of the consequences)
|
||||||
|
|
||||||
* This port fails to correctly support void return types. As a workaround it provides
|
* This port fails to correctly support void return types. As a workaround it provides
|
||||||
a set of complete new classes (and macros) for void. Default arguments of type void
|
a set of complete new classes (and macros) for void. Default arguments of type void
|
||||||
were replaced by arguments of type int.
|
were replaced by arguments of type int.
|
||||||
|
|
||||||
|
|
||||||
10. MultiMethods.h
|
10. MultiMethods.h
|
||||||
|
|
||||||
* replaced all template template parameters with 'normal' parameters (see 7.
|
* replaced all template template parameters with 'normal' parameters (see 7.
|
||||||
for a description of the consequences)
|
for a description of the consequences)
|
||||||
|
|
||||||
* This port does not support functions with return type void.
|
* This port does not support functions with return type void.
|
||||||
|
|
||||||
* dummy parameters were added to functions that otherwise would depend on
|
* dummy parameters were added to functions that otherwise would depend on
|
||||||
explicit template argument specification (14.8.1).
|
explicit template argument specification (14.8.1).
|
||||||
|
|
||||||
|
|
||||||
More info:
|
More info:
|
||||||
----------
|
----------
|
||||||
The original Loki library can be found here: http://moderncppdesign.com
|
The original Loki library can be found here: http://moderncppdesign.com
|
||||||
For Rani Sharoni's VC 7.0 port see: http://www.geocities.com/rani_sharoni/LokiPort.html
|
For Rani Sharoni's VC 7.0 port see: http://www.geocities.com/rani_sharoni/LokiPort.html
|
||||||
|
|
||||||
|
|
|
@ -2,26 +2,47 @@
|
||||||
// The Loki Library
|
// The Loki Library
|
||||||
// Copyright (c) 2001 by Andrei Alexandrescu
|
// Copyright (c) 2001 by Andrei Alexandrescu
|
||||||
// This code accompanies the book:
|
// This code accompanies the book:
|
||||||
// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design
|
// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design
|
||||||
// Patterns Applied". Copyright (c) 2001. Addison-Wesley.
|
// Patterns Applied". Copyright (c) 2001. Addison-Wesley.
|
||||||
// Permission to use, copy, modify, distribute and sell this software for any
|
// Permission to use, copy, modify, distribute and sell this software for any
|
||||||
// purpose is hereby granted without fee, provided that the above copyright
|
// purpose is hereby granted without fee, provided that the above copyright
|
||||||
// notice appear in all copies and that both that copyright notice and this
|
// notice appear in all copies and that both that copyright notice and this
|
||||||
// permission notice appear in supporting documentation.
|
// permission notice appear in supporting documentation.
|
||||||
// The author or Addison-Welsey Longman make no representations about the
|
// The author or Addison-Welsey Longman make no representations about the
|
||||||
// suitability of this software for any purpose. It is provided "as is"
|
// suitability of this software for any purpose. It is provided "as is"
|
||||||
// without express or implied warranty.
|
// without express or implied warranty.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Last update: Oct 05, 2002
|
// Last update: Jan 31, 2003
|
||||||
// Most of the code is taken from Rani Sharoni's Loki VC 7 port
|
// This VC 6 port of TypeTraits is based on Rani Sharoni's Loki VC 7 port.
|
||||||
// Reference, pointer and array detection is based on boost's type traits
|
// Reference, pointer, array, const and volatile detection is based on
|
||||||
|
// boost's type traits.
|
||||||
|
// see http://www.boost.org/libs/type_traits/
|
||||||
|
//
|
||||||
|
// The code for Enum- and pointer-to-member-function detection is based on
|
||||||
|
// ideas from M. Yamada - many thanks :)
|
||||||
|
//
|
||||||
// AdjReference has moved to namespace-scope. Explicit specialization is
|
// AdjReference has moved to namespace-scope. Explicit specialization is
|
||||||
// only allowed there.
|
// only allowed there.
|
||||||
|
//
|
||||||
|
// known bugs:
|
||||||
|
// assert(TypeTraits<const void>::isConst == 1) fails.
|
||||||
|
// assert(TypeTraits<volatile void>::isVolatile == 1) fails.
|
||||||
|
// assert(TypeTraits<const volatile void>::isConst == 1) fails.
|
||||||
|
// assert(TypeTraits<const volatile void>::isVolatile == 1) fails.
|
||||||
|
// This is because the VC 6 does not differentiate
|
||||||
|
// between void, const void, volatile void and const volatile void.
|
||||||
|
|
||||||
#ifndef TYPETRAITS_INC_
|
#ifndef TYPETRAITS_INC_
|
||||||
#define TYPETRAITS_INC_
|
#define TYPETRAITS_INC_
|
||||||
|
|
||||||
|
//
|
||||||
|
// Ignore forcing value to bool 'true' or 'false' (performance warning)
|
||||||
|
//
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning (disable: 4800)
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "Typelist.h"
|
#include "Typelist.h"
|
||||||
|
|
||||||
namespace Loki
|
namespace Loki
|
||||||
|
@ -29,7 +50,7 @@ namespace Loki
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// class template IsCustomUnsignedInt
|
// class template IsCustomUnsignedInt
|
||||||
// Offers a means to integrate nonstandard built-in unsigned integral types
|
// Offers a means to integrate nonstandard built-in unsigned integral types
|
||||||
// (such as unsigned __int64 or unsigned long long int) with the TypeTraits
|
// (such as unsigned __int64 or unsigned long long int) with the TypeTraits
|
||||||
// class template defined below.
|
// class template defined below.
|
||||||
// Invocation: IsCustomUnsignedInt<T> where T is any type
|
// Invocation: IsCustomUnsignedInt<T> where T is any type
|
||||||
// Defines 'value', an enum that is 1 iff T is a custom built-in unsigned
|
// Defines 'value', an enum that is 1 iff T is a custom built-in unsigned
|
||||||
|
@ -42,12 +63,12 @@ namespace Loki
|
||||||
struct IsCustomUnsignedInt
|
struct IsCustomUnsignedInt
|
||||||
{
|
{
|
||||||
enum { value = 0 };
|
enum { value = 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// class template IsCustomSignedInt
|
// class template IsCustomSignedInt
|
||||||
// Offers a means to integrate nonstandard built-in unsigned integral types
|
// Offers a means to integrate nonstandard built-in unsigned integral types
|
||||||
// (such as unsigned __int64 or unsigned long long int) with the TypeTraits
|
// (such as unsigned __int64 or unsigned long long int) with the TypeTraits
|
||||||
// class template defined below.
|
// class template defined below.
|
||||||
// Invocation: IsCustomSignedInt<T> where T is any type
|
// Invocation: IsCustomSignedInt<T> where T is any type
|
||||||
// Defines 'value', an enum that is 1 iff T is a custom built-in signed
|
// Defines 'value', an enum that is 1 iff T is a custom built-in signed
|
||||||
|
@ -60,7 +81,7 @@ namespace Loki
|
||||||
struct IsCustomSignedInt
|
struct IsCustomSignedInt
|
||||||
{
|
{
|
||||||
enum { value = 0 };
|
enum { value = 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// class template IsCustomFloat
|
// class template IsCustomFloat
|
||||||
|
@ -77,7 +98,7 @@ namespace Loki
|
||||||
struct IsCustomFloat
|
struct IsCustomFloat
|
||||||
{
|
{
|
||||||
enum { value = 0 };
|
enum { value = 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Helper types for class template TypeTraits defined below
|
// Helper types for class template TypeTraits defined below
|
||||||
|
@ -91,7 +112,7 @@ namespace Loki
|
||||||
typedef TYPELIST_3(bool, char, wchar_t) StdOtherInts;
|
typedef TYPELIST_3(bool, char, wchar_t) StdOtherInts;
|
||||||
typedef TYPELIST_3(float, double, long double) StdFloats;
|
typedef TYPELIST_3(float, double, long double) StdFloats;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// class template TypeTraits
|
// class template TypeTraits
|
||||||
// Figures out various properties of any given type
|
// Figures out various properties of any given type
|
||||||
|
@ -146,13 +167,94 @@ namespace Loki
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
namespace Private
|
namespace Private
|
||||||
{
|
{
|
||||||
struct pointer_helper
|
template <class T> struct Wrap {};
|
||||||
|
|
||||||
|
// this array-detection approach is based on boost's
|
||||||
|
// Type-Traits. See: boost::array_traits.hpp
|
||||||
|
template <class U>
|
||||||
|
struct ArrayTester
|
||||||
{
|
{
|
||||||
pointer_helper(const volatile void*);
|
private:
|
||||||
|
|
||||||
|
// This function can only be used for non-array-types, because
|
||||||
|
// functions can't return arrays.
|
||||||
|
template<class T>
|
||||||
|
static T(* IsArrayTester1(Wrap<T>) )(Wrap<T>);
|
||||||
|
static char IsArrayTester1(...);
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
static NO IsArrayTester2(T(*)(Wrap<T>));
|
||||||
|
static YES IsArrayTester2(...);
|
||||||
|
public:
|
||||||
|
enum {
|
||||||
|
value = sizeof(IsArrayTester2(IsArrayTester1(Wrap<U>())))
|
||||||
|
== sizeof(YES)
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EnumFalse{ enum {value = 0};};
|
// const-detection based on boost's
|
||||||
struct EnumTrue{ enum {value = 1};};
|
// Type-Traits. See: boost\type_traits\is_const.hpp
|
||||||
|
YES IsConstTester(const volatile void*);
|
||||||
|
NO IsConstTester(volatile void *);
|
||||||
|
|
||||||
|
template <bool is_ref, bool array>
|
||||||
|
struct IsConstImpl
|
||||||
|
{
|
||||||
|
template <class T> struct In {enum {value=0};};
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct IsConstImpl<false,false>
|
||||||
|
{
|
||||||
|
template <typename T> struct In
|
||||||
|
{
|
||||||
|
static T* t;
|
||||||
|
enum {value = sizeof(YES) == sizeof(IsConstTester(t))};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct IsConstImpl<false,true>
|
||||||
|
{
|
||||||
|
template <typename T> struct In
|
||||||
|
{
|
||||||
|
static T t;
|
||||||
|
enum { value = sizeof(YES) == sizeof(IsConstTester(&t)) };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// this volatile-detection approach is based on boost's
|
||||||
|
// Type-Traits. See: boost\type_traits\is_volatile.hpp
|
||||||
|
YES IsVolatileTester(void const volatile*);
|
||||||
|
NO IsVolatileTester(void const*);
|
||||||
|
|
||||||
|
template <bool is_ref, bool array>
|
||||||
|
struct IsVolatileImpl
|
||||||
|
{
|
||||||
|
template <typename T> struct In
|
||||||
|
{
|
||||||
|
enum {value = 0};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct IsVolatileImpl<false,false>
|
||||||
|
{
|
||||||
|
template <typename T> struct In
|
||||||
|
{
|
||||||
|
static T* t;
|
||||||
|
enum {value = sizeof(YES) == sizeof(IsVolatileTester(t))};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct IsVolatileImpl<false,true>
|
||||||
|
{
|
||||||
|
template <typename T> struct In
|
||||||
|
{
|
||||||
|
static T t;
|
||||||
|
enum {value = sizeof(YES) == sizeof(IsVolatileTester(&t))};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
template<bool IsRef>
|
template<bool IsRef>
|
||||||
struct AdjReference
|
struct AdjReference
|
||||||
{
|
{
|
||||||
|
@ -166,25 +268,33 @@ namespace Loki
|
||||||
template<typename U>
|
template<typename U>
|
||||||
struct In { typedef U Result; };
|
struct In { typedef U Result; };
|
||||||
};
|
};
|
||||||
|
struct PointerHelper
|
||||||
|
{
|
||||||
|
PointerHelper(const volatile void*);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
NO EnumDetection(T);
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
YES EnumDetection(...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class TypeTraits
|
class TypeTraits
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
static T MakeT(...);
|
private:
|
||||||
template <class U> struct Wrap{};
|
static Private::YES IsPointer(Private::PointerHelper);
|
||||||
struct pointer_helper
|
static Private::NO IsPointer(...);
|
||||||
{
|
|
||||||
pointer_helper(const volatile void*);
|
|
||||||
};
|
|
||||||
static Private::YES is_pointer(pointer_helper);
|
|
||||||
static Private::NO is_pointer(...);
|
|
||||||
|
|
||||||
// With the VC 6. Rani Sharoni's approach to detect references unfortunately
|
// With the VC 6. Rani Sharoni's approach to detect references unfortunately
|
||||||
// results in an error C1001: INTERNER COMPILER- FEHLER
|
// results in an error C1001: INTERNAL COMPILER-ERROR
|
||||||
//
|
//
|
||||||
|
// this reference-detection approach is based on boost's
|
||||||
|
// Type-Traits. See: boost::composite_traits.h
|
||||||
|
//
|
||||||
// is_reference_helper1 is a function taking a Wrap<T> returning
|
// is_reference_helper1 is a function taking a Wrap<T> returning
|
||||||
// a pointer to a function taking a Wrap<T> returning a T&.
|
// a pointer to a function taking a Wrap<T> returning a T&.
|
||||||
// This function can only be used if T is not a reference-Type.
|
// This function can only be used if T is not a reference-Type.
|
||||||
|
@ -194,109 +304,150 @@ namespace Loki
|
||||||
// references.
|
// references.
|
||||||
// In order to detect a reference, use the return-type of is_reference_helper1
|
// In order to detect a reference, use the return-type of is_reference_helper1
|
||||||
// with is_reference_helper2.
|
// with is_reference_helper2.
|
||||||
//
|
//
|
||||||
// this reference-detection approach is based on boost's
|
template <class U>
|
||||||
// Type-Traits. See: boost::composite_traits.h
|
static U&(* IsReferenceHelper1(Private::Wrap<U>) )(Private::Wrap<U>);
|
||||||
template <class T>
|
static Private::NO IsReferenceHelper1(...);
|
||||||
static T&(* is_reference_helper1(Wrap<T>) )(Wrap<T>);
|
|
||||||
static Private::NO is_reference_helper1(...);
|
|
||||||
|
|
||||||
template <class T>
|
template <class U>
|
||||||
static Private::NO is_reference_helper2(T&(*)(Wrap<T>));
|
static Private::NO IsReferenceHelper2(U&(*)(Private::Wrap<U>));
|
||||||
static Private::YES is_reference_helper2(...);
|
static Private::YES IsReferenceHelper2(...);
|
||||||
|
|
||||||
template <class T, class U>
|
|
||||||
static Private::YES is_pointer2member(U T::*);
|
|
||||||
static Private::NO is_pointer2member(...);
|
|
||||||
|
|
||||||
// This function can only be used for non-array-types, because
|
template <class U, class Z>
|
||||||
// functions can't return arrays.
|
static Private::YES IsPointer2Member(Z U::*);
|
||||||
// this array-detection approach is based on boost's
|
static Private::NO IsPointer2Member(...);
|
||||||
// Type-Traits. See: boost::array_traits.h
|
struct DummyType{};
|
||||||
template <class T>
|
|
||||||
static T(* is_array_helper1(Wrap<T>) )(Wrap<T>);
|
|
||||||
static Private::NO is_array_helper1(...);
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
static Private::NO is_array_helper2(T(*)(Wrap<T>));
|
|
||||||
static Private::YES is_array_helper2(...);
|
|
||||||
|
|
||||||
template<typename U>
|
|
||||||
static Private::YES is_const(Wrap<const U>);
|
|
||||||
static Private::NO is_const(...);
|
|
||||||
|
|
||||||
template<typename U>
|
|
||||||
static Private::YES is_volatile(Wrap<volatile U>);
|
|
||||||
static Private::NO is_volatile(...);
|
|
||||||
public:
|
public:
|
||||||
|
enum {isArray = Private::ArrayTester<T>::value};
|
||||||
enum {isReference = sizeof(
|
enum {isReference = sizeof(
|
||||||
is_reference_helper2(
|
IsReferenceHelper2(
|
||||||
is_reference_helper1(Wrap<T>()))) == sizeof(Private::YES)};
|
IsReferenceHelper1(Private::Wrap<T>()))) == sizeof(Private::YES)};
|
||||||
enum {isPointer = sizeof(is_pointer(MakeT())) == sizeof(Private::YES)};
|
|
||||||
enum {isMemberPointer = sizeof(is_pointer2member(MakeT())) == sizeof(Private::YES)};
|
|
||||||
enum {isArray = sizeof(
|
|
||||||
is_array_helper1(
|
|
||||||
is_array_helper2(Wrap<T>()))) == sizeof(Private::YES)};
|
|
||||||
enum {isVoid = Private::IsVoid<T>::value};
|
enum {isVoid = Private::IsVoid<T>::value};
|
||||||
|
typedef typename Select<isArray || isVoid,DummyType, T>::Result NewT;
|
||||||
|
|
||||||
enum { isStdUnsignedInt =
|
private:
|
||||||
|
static NewT MakeT();
|
||||||
|
enum {isMemberPointerTemp =
|
||||||
|
sizeof(IsPointer2Member(MakeT())) == sizeof(Private::YES)};
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum {isPointer = sizeof(IsPointer(MakeT())) == sizeof(Private::YES)};
|
||||||
|
enum { isStdUnsignedInt =
|
||||||
TL::IndexOf<Private::StdUnsignedInts, T>::value >= 0 };
|
TL::IndexOf<Private::StdUnsignedInts, T>::value >= 0 };
|
||||||
enum { isStdSignedInt =
|
enum { isStdSignedInt =
|
||||||
TL::IndexOf<Private::StdSignedInts, T>::value >= 0 };
|
TL::IndexOf<Private::StdSignedInts, T>::value >= 0 };
|
||||||
enum { isStdIntegral = isStdUnsignedInt || isStdSignedInt ||
|
enum { isStdIntegral = isStdUnsignedInt || isStdSignedInt ||
|
||||||
TL::IndexOf<Private::StdOtherInts, T>::value >= 0 };
|
TL::IndexOf<Private::StdOtherInts, T>::value >= 0 };
|
||||||
enum { isStdFloat = TL::IndexOf<Private::StdFloats, T>::value >= 0 };
|
enum { isStdFloat = TL::IndexOf<Private::StdFloats, T>::value >= 0 };
|
||||||
enum { isStdArith = isStdIntegral || isStdFloat };
|
enum { isStdArith = isStdIntegral || isStdFloat };
|
||||||
enum { isStdFundamental = isStdArith || isStdFloat || isVoid };
|
enum { isStdFundamental = isStdArith || isStdFloat || isVoid };
|
||||||
|
|
||||||
enum { isUnsignedInt = isStdUnsignedInt || IsCustomUnsignedInt<T>::value };
|
enum { isUnsignedInt = isStdUnsignedInt || IsCustomUnsignedInt<T>::value };
|
||||||
enum { isSignedInt = isStdSignedInt || IsCustomSignedInt<T>::value };
|
enum { isSignedInt = isStdSignedInt || IsCustomSignedInt<T>::value };
|
||||||
enum { isIntegral = isStdIntegral || isUnsignedInt || isSignedInt };
|
enum { isIntegral = isStdIntegral || isUnsignedInt || isSignedInt };
|
||||||
enum { isFloat = isStdFloat || IsCustomFloat<T>::value };
|
enum { isFloat = isStdFloat || IsCustomFloat<T>::value };
|
||||||
enum { isArith = isIntegral || isFloat };
|
enum { isArith = isIntegral || isFloat };
|
||||||
enum { isFundamental = isStdFundamental || isArith || isFloat };
|
enum { isFundamental = isStdFundamental || isArith || isFloat };
|
||||||
enum {
|
enum {
|
||||||
isConst =
|
isConst = Private::IsConstImpl
|
||||||
sizeof(is_const(Wrap<T>())) == sizeof(Private::YES)
|
<isReference, isArray>::template In<T>::value
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
isVolatile =
|
isVolatile = Private::IsVolatileImpl
|
||||||
sizeof(is_volatile(Wrap<T>())) == sizeof(Private::YES)
|
<isReference, isArray>::template In<T>::value
|
||||||
};
|
};
|
||||||
enum { isScalar = isStdArith || isPointer || isMemberPointer};
|
|
||||||
private:
|
private:
|
||||||
typedef typename Private::AdjReference<isReference || isVoid>::
|
typedef typename Private::AdjReference<isReference || isVoid>::
|
||||||
template In<T>::Result AdjType;
|
template In<T>::Result AdjType;
|
||||||
|
|
||||||
public:
|
struct is_scalar
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
struct BoolConvert { BoolConvert(bool); };
|
||||||
|
|
||||||
|
static Private::YES check(BoolConvert);
|
||||||
|
static Private::NO check(...);
|
||||||
|
|
||||||
|
struct NotScalar {};
|
||||||
|
|
||||||
|
typedef typename Select
|
||||||
|
<
|
||||||
|
isVoid || isReference || isArray,
|
||||||
|
NotScalar, T
|
||||||
|
>
|
||||||
|
::Result RetType;
|
||||||
|
|
||||||
|
static RetType get();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
|
||||||
typedef typename Select
|
enum { value = sizeof(check(get())) == sizeof(Private::YES) };
|
||||||
|
|
||||||
|
|
||||||
|
}; // is_scalar
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum { isScalar = is_scalar::value};
|
||||||
|
typedef typename Select
|
||||||
<
|
<
|
||||||
isScalar || isArray, T, AdjType
|
isScalar || isArray, T, AdjType
|
||||||
>
|
>
|
||||||
::Result ParameterType;
|
::Result ParameterType;
|
||||||
|
|
||||||
//
|
|
||||||
|
//
|
||||||
// We get is_class for free
|
// We get is_class for free
|
||||||
// BUG - fails with functions types (ICE) and unknown size array
|
// BUG - fails with functions types (ICE) and unknown size array
|
||||||
// (but works for other incomplete types)
|
// (but works for other incomplete types)
|
||||||
// (the boost one (Paul Mensonides) is better)
|
// (the boost one (Paul Mensonides) is better)
|
||||||
//
|
//
|
||||||
enum { isClass =
|
enum { isClass =
|
||||||
!isScalar &&
|
!isScalar &&
|
||||||
!isArray &&
|
!isArray &&
|
||||||
!isReference &&
|
!isReference &&
|
||||||
!isVoid
|
!isVoid
|
||||||
};
|
};
|
||||||
|
private:
|
||||||
|
typedef typename Loki::Select
|
||||||
|
<
|
||||||
|
isScalar,
|
||||||
|
T,
|
||||||
|
int
|
||||||
|
>::Result MayBeEnum;
|
||||||
|
public:
|
||||||
|
// enum types are the only scalar types that can't be initialized
|
||||||
|
// with 0.
|
||||||
|
// Because we replace all non scalars with int,
|
||||||
|
// template <class T>
|
||||||
|
// YES EnumDetection(...);
|
||||||
|
// will only be selected for enums.
|
||||||
|
enum {
|
||||||
|
isEnum = sizeof(Private::YES) ==
|
||||||
|
sizeof (Private::EnumDetection<MayBeEnum>(0))
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
isMemberFuncPointer = isScalar && !isArith && !isPointer &&
|
||||||
|
!isMemberPointerTemp && !isEnum
|
||||||
|
};
|
||||||
|
enum {isMemberPointer = isMemberPointerTemp || isMemberFuncPointer};
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning (default: 4800)
|
||||||
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Change log:
|
// Change log:
|
||||||
// June 20, 2001: ported by Nick Thurn to gcc 2.95.3. Kudos, Nick!!!
|
// June 20, 2001: ported by Nick Thurn to gcc 2.95.3. Kudos, Nick!!!
|
||||||
// May 10, 2002: ported by Rani Sharoni to VC7 (RTM - 9466)
|
// May 10, 2002: ported by Rani Sharoni to VC7 (RTM - 9466)
|
||||||
// Oct 05, 2002: ported by Benjamin Kaufmann to MSVC 6
|
// Oct 05, 2002: ported by Benjamin Kaufmann to MSVC 6
|
||||||
|
// Jan 31, 2003: fixed bugs in scalar and array detection.
|
||||||
|
// Added isMemberFuncPointer and isEnum. B.K.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#endif // TYPETRAITS_INC_
|
#endif // TYPETRAITS_INC_
|
Loading…
Add table
Add a link
Reference in a new issue