Question

I have read that C++11 has sufficient static checking (compile time) so as to implement a big part of what should have been Concepts checking of C++11(removed). (I've read this in the comments of a recent question about Concepts removed... - that question was fastly closed as not constructive).

The C++03 code below only check the presence of a member function in a class (on which my template class wants to work). Here are 4 member functions that are searched for, and I always use the same pattern:

  • a typedef to define a typedef of the prototype of the function
  • a call to static_cast that breaks compilation if ever the typename TExtension doesn't define such a member function, or if it has different prototype

Here is the code:

template <typename TExtension>
class
{
...
    void checkTemplateConcept()
    {
        typedef unsigned long (TExtension::*memberfunctionRequestedId)();
        static_cast<memberfunctionRequestedId>(&TExtension::getRequestId);

        typedef eDirection (TExtension::*memberfunctionDirection)();
        static_cast<memberfunctionDirection>(&TExtension::getDirection);

        typedef eDriveWay (TExtension::*memberfunctionDriveWay)();
        static_cast<memberfunctionDriveWay>(&TExtension::getDriveWay);

        typedef unsigned long (TExtension::*memberfunctionCycleId)();
        static_cast<memberfunctionCycleId>(&TExtension::getCycleId);
    }
}

This was in some part of my code, but it was completely based on C++03. I would rewrite it gladly, with those new C++11 features... what should be used here instead ?

Was it helpful?

Solution

With C++11, you could make the compiler to print the good error messages with static_assert as:

typedef unsigned long (TExtension::*required_type)();
typedef decltype(&TExtension::getRequestId) actual_type;

static_assert(std::is_same<required_type, actual_type>::value,
     "The type of getRequestId must be unsigned long (C::*)()");

Now if the type of member function doesn't match, the compiler will print this helpful message:

"The type of getRequestId must be unsigned long (C::*)()"

You could make it more descriptive if you want to. :-)

OTHER TIPS

Yes in C++11, SFINAE was extended to expressions, so you can define a trait like is_t_extension to detect for the presences of the member functions, like this:

template<class... T>
struct holder
{
    typedef void type;
};

template<class T, class=void>
struct is_t_extension
: std::false_type
{};

template<class T, class=void>
struct is_t_extension<T, typename holder<
    decltype(std::declval<T>().getRequestId),
    decltype(std::declval<T>().getDirection),
    decltype(std::declval<T>().getDriveWay),
    decltype(std::declval<T>().getCycleId)
>::type>
: std::true_type
{};

Now this just checks for their presence. With some work you could extend the above to detect valid signatures, or you could use the Tick library to write the trait instead, which looks a lot cleaner:

TICK_TRAIT(is_t_extenstion)
{
    template<class T>
    auto requires_(T&& x) -> TICK_VALID(
        returns<unsigned long>(x.getRequestId),
        returns<eDirection>(x.getDirection),
        returns<eDriveWay>(x.getDriveWay),
        returns<unsigned long>(x.getCycleId)
    );
};

Then in your class you could just use a static_assert to report the error:

template <typename TExtension>
class foo
{
    static_assert(is_t_extension<TExtension>(), "Not a TExtension");
};

Or if you want to allow for specializations, you can use TICK_CLASS_REQUIRES:

template <typename TExtension, class=void>
class foo;

template <typename TExtension>
class foo<TExtension, TICK_CLASS_REQUIRES(is_t_extension<TExtension>())>
{
    ...
};
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top