2

Are there any patterns for defining compile-time class contracts?

My goals are:

  • maintainability - in a large codebase, it would be very useful to specify and validate the compile-time contracts so the dependencies are obvious.
  • quality error messages - instead of complaining that a line of code deep inside the template can't call a method, it would be better to surface the lack of contract fulfillment.

Something like...

class StaticContract {
  static void Method();
};

class MeetsContract {
  static void Method();
};

// T must have all methods of StaticContract, or compile-time error.
template <class T /*   : StaticContract   */>
void DoSomething(T t);

I'd accept:

  • way of enforcing the contract at compile time
  • patterns for capturing / documenting compile-time contracts
  • solutions for the subsets of {static methods, member methods, typedefs, etc}
  • solutions that redefine my problem statement
  • best practices
Dave Dopson
  • 41,600
  • 19
  • 95
  • 85
  • 4
    What are you trying to achieve here? Doesn't the [SFINAE](http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence) technique serve your purposes? – πάντα ῥεῖ Apr 20 '16 at 18:25
  • answered in the question body: maintainability and better error messages. – Dave Dopson Apr 20 '16 at 18:30
  • Could you make `StaticContract` an abstract class and have `DoSomething` take a `StaticContract*` argument? If the derived class fails to override a pure virtual function, it will be uninstantiable. – Brian Bi Apr 20 '16 at 18:30
  • That somewhat works for member methods, but not static methods. One also has to be careful (using `final`) to ensure that the dispatch isn't virtual. – Dave Dopson Apr 20 '16 at 18:32

2 Answers2

4

My preferred approach at this point is to borrow Yakk's can_apply metafunction:

namespace details {
    template <class...>
    using void_t = void;    

    template <template <class...> class Z, class, class...>
    struct can_apply : std::false_type {};

    template <template <class...> class Z, class...Ts>
    struct can_apply<Z, void_t<Z<Ts...>>, Ts...> : std::true_type{};
}

template <template <class...> class Z, class...Ts>
using can_apply = details::can_apply<Z, void, Ts...>;

Stick that somewhere for safe keeping. Then, we can define contracts for our types as just expressions which we expect to be valid. In this case, we need a non-const member function named Method:

template <class T>
using StaticContract = decltype(std::declval<T&>().Method());

So then we just require it:

template <class T>
void DoSomething(T ) {
    static_assert(can_apply<StaticContract, T>::value, "!");
}

The contract can be arbitrarily complex too. Maybe you need T to be copy assignable and incrementable too:

template <class T>
using StaticContract = decltype(
    std::declval<T&>().Method(),
    std::declval<T&>() = std::declval<T const&>(),
    ++std::declval<T&>()
    );

This is all naturally SFINAE-able too, if you choose that approach instead of the static_assert approach.

Barry
  • 286,269
  • 29
  • 621
  • 977
0

This trick seems to offer a breadcrumb (from detecting typedef at compile time (template metaprogramming))

template<typename T>
struct void_ { typedef void type; };

template<typename T, typename = void>
struct Foo {};

template<typename T>
struct Foo <T, typename void_<typename T::const_iterator>::type> {
      void do_stuff(){ ... }
};

Hmm, how to weave it into a workable pattern?

Community
  • 1
  • 1
Dave Dopson
  • 41,600
  • 19
  • 95
  • 85