Swift について、そしてそれをいくつかの言語と比較する興味深い記事が投稿されているのを読みました。
≫ Swiftの強力な機能であるstaticメソッド制約の紹介と、Kotlin, TypeScript, Java, Scala, C++との比較
比較対象になっている言語の内で私がよく知っているのは C++ だけなので他の記述は流し読みではあるのですが、ある型の性質を強制する仕組みとして C++ では「コンセプト」という概念がかなり前から検討されています。 (テンプレートが受け入れ可能な型の性質を記述する形で使い、ある型が特定のインターフェイスを満たしていることを表現するものではないという点でプロトコルとは異なるようです。) それがなかなか決定に至らないので、まわりくどい様々なやり方で制約を表現する方法が生み出されてきました。 今回は、あるクラスが特定の名前と型をもつメンバを持っているかどうかを判定する方法を紹介します。
元記事の IsLoadable
を置き換える形で説明します。 元記事の C++ 版はあるクラスが loadable であるということの表現をふたつに分離しています。
- loadable の要件を満たす (クラスがメンバ関数
load
を持つ) - loadable であると定義する (
IsLoadable<T>::value
が真値である)
のふたつです。
この機能を使うプロジェクトできちんと規約を守ることができるならばよいのですが、可能であれば
- loadable の要件を満たすならば loadable である
という形に一本化したいわけです。
それを満たすトレイト is_loadable
は以下のように定義できます。
template<class T>
class is_loadable {
template<class U, U (*)(Iterator<std::string>&)>
struct helper_t { typedef T type; };
template<class U, class V = T>
struct helper : std::false_type {};
template<class U>
struct helper<U, typename helper_t<U, &U::load>::type> : std::true_type {};
public:
static const bool value = helper<T>::value;
};
クラス T
が load
を持ち、なおかつその型が T (*)(Iterator<std::string>&)
であれば is_loadable<T>::value
は真です。
クラス Employee
が loadable であることを陽に表したいのであれば、Employee
を定義した後に以下の記述を入れておけばよいでしょう。
static_assert(is_loadable<Employee>::value, "Employee is not loadable");
あるいはマクロにしてしまった方がいいかもしれません。
#define declare_loadable(t) \
static_assert(is_loadable<t>::value, #t " is not loadable")
declare_loadable(Employee);
以上は応用範囲の広い手法なので、活用すればできることは多いと思います。