C++ 编译时 class 契约(即模板元编程)
C++ compile-time class contracts (ie, template-metaprogramming)
是否有用于定义编译时 class 契约的任何模式?
我的目标是:
- 可维护性 - 在大型代码库中,指定和验证编译时契约非常有用,因此依赖关系很明显。
- 质量错误信息——与其抱怨模板深处的一行代码无法调用方法,还不如把合同履行的缺失浮出水面。
类似...
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);
我会接受:
- 编译时执行契约的方式
- 捕获/记录编译时契约的模式
- {静态方法、成员方法、typedef 等}子集的解决方案
- 重新定义我的问题陈述的解决方案
- 最佳实践
这个技巧似乎提供了面包屑(来自 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(){ ... }
};
嗯,如何将其编织成可行的模式?
此时我的首选方法是借用 Yakk 的 can_apply
元函数:
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...>;
把它贴在某个地方以便妥善保管。然后,我们可以将类型的契约定义为我们期望有效的表达式。在这种情况下,我们需要一个名为 Method
:
的非 const
成员函数
template <class T>
using StaticContract = decltype(std::declval<T&>().Method());
那么我们只需要它:
template <class T>
void DoSomething(T ) {
static_assert(can_apply<StaticContract, T>::value, "!");
}
合同也可以任意复杂。也许你需要 T
也可以复制分配和递增:
template <class T>
using StaticContract = decltype(
std::declval<T&>().Method(),
std::declval<T&>() = std::declval<T const&>(),
++std::declval<T&>()
);
如果您选择该方法而不是 static_assert
方法,那么这也自然适用于 SFINAE。
是否有用于定义编译时 class 契约的任何模式?
我的目标是:
- 可维护性 - 在大型代码库中,指定和验证编译时契约非常有用,因此依赖关系很明显。
- 质量错误信息——与其抱怨模板深处的一行代码无法调用方法,还不如把合同履行的缺失浮出水面。
类似...
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);
我会接受:
- 编译时执行契约的方式
- 捕获/记录编译时契约的模式
- {静态方法、成员方法、typedef 等}子集的解决方案
- 重新定义我的问题陈述的解决方案
- 最佳实践
这个技巧似乎提供了面包屑(来自 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(){ ... }
};
嗯,如何将其编织成可行的模式?
此时我的首选方法是借用 Yakk 的 can_apply
元函数:
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...>;
把它贴在某个地方以便妥善保管。然后,我们可以将类型的契约定义为我们期望有效的表达式。在这种情况下,我们需要一个名为 Method
:
const
成员函数
template <class T>
using StaticContract = decltype(std::declval<T&>().Method());
那么我们只需要它:
template <class T>
void DoSomething(T ) {
static_assert(can_apply<StaticContract, T>::value, "!");
}
合同也可以任意复杂。也许你需要 T
也可以复制分配和递增:
template <class T>
using StaticContract = decltype(
std::declval<T&>().Method(),
std::declval<T&>() = std::declval<T const&>(),
++std::declval<T&>()
);
如果您选择该方法而不是 static_assert
方法,那么这也自然适用于 SFINAE。