Combining Checks to Form a Concept
Okay, so we've seen how we can check for individual member functions; now it's time to combine the individual checks into a whole concept check. This is relatively straightforward to do: Just combine all the traits together:
template<typename T>
struct is_lockable:
std::integral_constant<bool,
std::is_class<T>::value &&
has_lock_member_function<T>::value &&
has_unlock_member_function<T>::value &&
has_try_lock_member_function<T>::value>
{};
You can then use this combined concept test just as you would any other concept test, including using it with enable_if.
For example, the Boost Library provides a set of function templates named lock. One set matches the C++0x std::lock function, and takes a number of lockable objects; when it returns all the supplied lockable objects have been locked. This is done in a way that avoids deadlock if the same set of lockable objects is locked on another thread in any order.
The second form of boost::lock takes a pair of iterators pointing to lockable objects, and locks every element in the supplied range using a similar algorithm. This means that there are two overloads of boost::lock that take two identical parameters -- one that accepts two lockable objects, and one that takes two iterators.
Our is_lockable concept can thus be used with enable_if to distinguish between these cases:
template<typename M1,typename M2>
typename std::enable_if<
is_lockable<M1>::value &&
is_lockable<M2>::value,void>::type
lock(M1& m1,M2& m2)
{
// nifty deadlock-free lock algorithm
}
template<typename Iterator>
typename std::enable_if<!is_lockable<Iterator>::value,void>::type
lock(Iterator begin,Iterator end)
{
// nifty deadlock-free lock algorithm for a range
}
Conclusion
One of the key features of the C++0x template facility was the ability to specify requirements on the functions that must be available on a type in order to match a concept, and to overload functions to provide different implementations based on whether or not a type matched a type. In this article, I've demonstrated that a limited form of this is available without a special Concepts language feature, and shown how this can be used to write concepts and overload on them.
Source Code
#ifndef IS_LOCKABLE_HPP
#define IS_LOCKABLE_HPP
#include <type_traits>
typedef char small_type;
struct large_type
{
small_type dummy[2];
};
template<typename T>
struct class_has_lock_member_function_returning_void_with_no_params
{
template<void (T::*)()> struct tester;
template<typename U>
static small_type has_matching_member(tester<&U::lock>*); // A
template<typename U>
static large_type has_matching_member(...); // B
static const bool value=sizeof(has_matching_member<T>(0))==sizeof(small_type);
};
template<typename T>
struct class_has_unlock_member_function_returning_void_with_no_params
{
template<void (T::*)()> struct tester;
template<typename U>
static small_type has_matching_member(tester<&U::unlock>*); // A
template<typename U>
static large_type has_matching_member(...); // B
static const bool value=sizeof(has_matching_member<T>(0))==sizeof(small_type);
};
template<typename T>
struct class_has_try_lock_member_function_returning_bool_with_no_params
{
template<bool (T::*)()> struct tester;
template<typename U>
static small_type has_matching_member(tester<&U::try_lock>*); // A
template<typename U>
static large_type has_matching_member(...); // B
static const bool value=sizeof(has_matching_member<T>(0))==sizeof(small_type);
};
template<typename T,bool is_class_type=std::is_class<T>::value>
struct has_lock_member_function:
std::false_type
{};
template<typename T>
struct has_lock_member_function<T,true>:
std::integral_constant<bool,
class_has_lock_member_function_returning_void_with_no_params<T>::value>
{};
template<typename T,bool is_class_type=std::is_class<T>::value>
struct has_unlock_member_function:
std::false_type
{};
template<typename T>
struct has_unlock_member_function<T,true>:
std::integral_constant<bool,
class_has_unlock_member_function_returning_void_with_no_params<T>::value>
{};
template<typename T,bool is_class_type=std::is_class<T>::value>
struct has_try_lock_member_function:
std::false_type
{};
template<typename T>
struct has_try_lock_member_function<T,true>:
std::integral_constant<bool,
class_has_try_lock_member_function_returning_bool_with_no_params<T>::value>
{};
template<typename T>
struct is_lockable:
std::integral_constant<bool,
std::is_class<T>::value &&
has_lock_member_function<T>::value &&
has_unlock_member_function<T>::value &&
has_try_lock_member_function<T>::value>
{};
#endif
References
BOOST_STATIC_ASSERT documentation.
Boost::enable_if documentation
Compiler support for C++0x features including static_assert
std::enable_if documentation for Microsoft Visual Studio


