Re: Constraints => Traits
Greg Herlihy wrote:
Michael Aaron Safyan wrote:
I presently have types CopyableCONSTRAINT<T> and
CloneableCONSTRAINT<T> defined to require these respective
functions and to generate informative compiler errors in their
absence.
I would like very much to define CopyableTrait<T> and
CloneableTrait<T> using these constraints. My current definition
for CopyableTrait<T> looks as follows (and CloneableTrait follows
the same pattern):
template<typename T>
class CopyableTrait
: public Trait<false>{};
template<typename T>
class CopyableTrait< typename CopyableCONSTRAINT<T>::copyabletype
: public Trait<true>{};
Unfortunately, the definition above does not seem to work
With some template metaprogramming, it is possible to determine
whether a class has a clone() or copy() method with the desired
signature. (Though I am curious what is the difference between a
clone and a copy?).
Here's one technique that I found it on the web:
#include <iostream>
#include <ios>
template<class T, T (T::*)() const>
struct mf_bind
{
typedef T type;
};
template<class T1, class T2>
struct has_copy_member
{
static const bool value = false;
};
template<class T>
struct has_copy_member<T,
typename mf_bind<T, &T::copy>::type>
{
static const bool value = true;
};
// A and B test classes
class A
{
public:
A copy() const;
};
class B {};
using std::cout;
int main()
{
cout << std::boolalpha;
cout << "A has copy member? ";
cout << has_copy_member<A, A>::value << "\n";
cout << "B has copy member? ";
cout << has_copy_member<B, B>::value << "\n";
}
Program Output:
A has copy member? true
B has copy member? false
To use these templates for your purposes, just declare
CopyableTrait as shown below (no specialization is needed):
template<typename T>
class CopyableTrait
: public Trait< has_copy_member<T, T>::value>
{};
Greg
I have been struggling with this issue too. SFINAE works well when
you know the exact signature of the function, but...
CloneableTrait should (IMHO) also be true for polymorphic types:
struct A
{
virtual A* Clone() const;
};
struct B: public A
{
A* Clone() const;
};
But one could also write B using a covariant return type.
So the constraint on CloneableTrait<T> is not to have a
T* Clone() const function, but rather S* Clone() const, where S
is either T or one of it's base classes.
And to some extent, CopyableTrait<T> should (still IMHO) be true for
any S Copy() const where S is publicly convertible to T (if this
leads to a split, my understanding is that the implementor of T is
the one who should be blamed).
Such compile-time constraints are kinda easy to assert:
template<typename T>
class CopyableConstraint
{
template<typename Any>
static void Helper(Any (T::*)() const)
{
// here, some static assert to ensure that
// Any is convertible to T
}
static void Enforce()
{
Helper(&T::Copy);
}
public:
CopyableConstraint()
{
void (*enforce)() = &Enforce;
}
};
However, no matter how hard I tortured that kind of code to try to
make it fit the SFINAE traits design, I couldn't manage it as of
today.
Managing to push SFINAE beyond the limit of having to know the exact
function signature would definitely allow some decent compile-time
reflection, but all the search I done on so far the subject yielded
no interesting results. :(
The only solution I found so far to have a trait class reflecting
this kind of complex constraints, it to specialize the trait.
Cheers,
--
IR
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]