Boost logo

Boost :

From: Brad King (brad.king_at_[hidden])
Date: 2002-02-08 11:44:39


Hello,

I don't know if this has been discussed before, but there is a way to do
concept checks on MSVC. The trick has been to get the compiler to compile
the constraints function without any call to it. The current approach
works beautifully for GCC, but not at all for MSVC.

Here is one way to get MSVC to compile the constraints:

template <typename T>
struct AssignableConcept {
  virtual void constraints() { a = a; const_constraints(a); }
  void const_constraints(const T& b) { a = b; }
  T a;
};

template <typename T>
struct my_class {
  enum { C1 = sizeof(AssignableConcept<T>) };
};

In determining the size of the concept, MSVC decides to compile the
constraint function in order to place its entry in the vtable for the
concept class.

One drawback is that if my_class uses the assignment operator for T, then
the error message sometimes points to that use of it instead of to the
concept. This form of check can still be useful for checking whether more
abstract constraints are met (like ensuring some_trait<T>::value is true).

The code below demonstrates an implementation that allows boost's current
concept classes to be used, with only a change to make the constraint
method virtual.

Thoughts?
-Brad

// Select concept checking implementation.
#if defined(__GNUC__)
# define BOOST_STANDARD_CONCEPT_CHECKS
#elif defined(_MSC_VER)
# define BOOST_MSVC_CONCEPT_CHECKS
#else
# define BOOST_NO_CONCEPT_CHECKS
#endif

namespace boost {

// Sample concept.
template <typename T>
struct AssignableConcept {
  virtual void constraints() { a = a; const_constraints(a); }
  void const_constraints(const T& b) { a = b; }
  T a;
};

#if defined(BOOST_STANDARD_CONCEPT_CHECKS)
  
// Helper class to extract the concept from the function type wrapper
// used to get concepts with multiple arguments through the macro
// argument.
namespace detail {
 template <typename T> struct extract_concept;

 template <typename T>
 struct extract_concept<void (*)(T)> { typedef T type; };
}

// Use standard approach to compile constraint function.
# define BOOST_CONCEPT_REQUIRE(id, concept) \
    typedef typename boost::detail::extract_concept<void (*)concept >::type concept_##id; \
    template <void (concept_##id::*)()> struct concept_enforcer_##id; \
    typedef concept_enforcer_##id<&concept_##id::constraints> concept_enforce_##id

#elif defined(BOOST_MSVC_CONCEPT_CHECKS)

// Use MSVC vtable hack to compile constraint function.
# define BOOST_CONCEPT_REQUIRE(id, concept) \
    enum { concept_enforce_##id = sizeof concept }

#else

// No concept checking.
# define BOOST_CONCEPT_REQUIRE(id, concept) \
    enum { concept_enforce_##id = 0 }

#endif

} // namespace boost

// Sample class that requires a concept on its template argument.
template <typename T>
class my_class {
public:
  // Require T to be assignable. Note that the concept is enclosed
  // in parens to get it through the macro argument if there are commas
  // in the name.
  BOOST_CONCEPT_REQUIRE(T_assignable, (boost::AssignableConcept<T>));
};


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk