lundi 23 novembre 2015

Compile-time check of functor

I want to have a compile-time check in my code which ensures that a given class overloads the () operator, that this operator takes a const char * and a size_t as parameters and that its return type is an unsigned integer.

I have tried several code snippets taken from StackOverflow, but I am not satisfied with the solution I have written:

#include <type_traits>
#include <cstdint>
#include <iostream>
#include <memory>

template<class>
struct sfinae_true : std::true_type{};

namespace detail{
  template<class T>
  static auto test(int)
    -> sfinae_true<decltype(std::declval<T>()(static_cast<const char *>(nullptr), static_cast<size_t>(0u)))>;
  template<class>
  static auto test(long) -> std::false_type;
} // detail::

template<class T>
struct is_functor : decltype(detail::test<T>(0)){ };

template <typename T, typename HashFn,
      typename std::enable_if<std::is_unsigned<T>::value, int>::type = 0>
struct Calculation {
  Calculation() {
    static_assert(is_functor<HashFn>(), "BAD signature");
    typedef typename std::result_of<decltype(&HashFn::operator())(HashFn, const char *, size_t)>::type return_type;
    static_assert(std::is_unsigned<return_type>::value, "BAD return type");
  }

  T output() {
    return static_cast<T>(HashFn()(nullptr, 10));
  }
};

struct Hash {
  uint32_t operator ()(const char *buffer, size_t n) const {
    return 65;
  }
};

int main() {
  Calculation<uint64_t, Hash> c;
  c.output();
}

Sorry for the length of the code, I tried to keep it as small as possible.

Here is what I don't like about my code:

  1. If I substitute int to size_t in the parameter list when overloading the () operator, there is no error at compilation, because size_t can be implicitly cast to int.

  2. If the signature is incorrect (e.g. I remove the const when overloading the operator), the first assert fails. However, because compilation does not stop, I get three error messages, and the compiler output is somewhat cluttered

    rty.cpp: In instantiation of ‘Calculation<T, HashFn, <anonymous> >::Calculation() [with T = long unsigned int; HashFn = Hash; typename std::enable_if<std::is_unsigned<_Tp>::value, int>::type <anonymous> = 0]’:
    rty.cpp:41:31:   required from here
    rty.cpp:24:5: error: static assertion failed: BAD signature
     static_assert(is_functor<HashFn>(), "BAD signature");
     ^
    rty.cpp:25:104: error: no type named ‘type’ in ‘class std::result_of<unsigned int (Hash::*(Hash, const char*, long unsigned int))(char*, long unsigned int) const>’
     typedef typename std::result_of<decltype(&HashFn::operator())(HashFn, const char *, size_t)>::type return_type;
                                                                                                        ^
    rty.cpp:26:75: error: no type named ‘type’ in ‘class std::result_of<unsigned int (Hash::*(Hash, const char*, long unsigned int))(char*, long unsigned int) const>’
     static_assert(std::is_unsigned<return_type>::value, "BAD return type");
    
    
  3. I'd like to have a single call to static_assert, something like:

    static_assert(is_correct_functor<HashFn>(), "BAD implementation");
    
    

How can I achieve this? Thanks for your help.

I am using C++11 and compiling with g++4.8

Aucun commentaire:

Enregistrer un commentaire