7

オブジェクト インターフェイスと、派生オブジェクトがサポートする可能性があるインターフェイスのオープン エンド コレクションがあります。

// An object
class IObject
{
    getAttribute() = 0
}

// A mutable object
class IMutable
{
    setAttribute() = 0
}

// A lockable object 
class ILockable
{
    lock() = 0
}

// A certifiable object 
class ICertifiable
{
    setCertification() = 0
    getCertification() = 0
}

一部の派生オブジェクトは次のようになります。

class Object1 : public IObject, public IMutable, public ILockable {}
class Object2 : public IObject, public ILockable, public ICertifiable {}
class Object3 : public IObject {}

ここに私の質問があります: これらのインターフェースの特定の組み合わせのみを取る関数を書く方法はありますか? 例えば:

void doSomething(magic_interface_combiner<IObject, IMutable, ILockable> object);

doSomething( Object1() )  // OK, all interfaces are available.
doSomething( Object2() )  // Compilation Failure, missing IMutable.
doSomething( Object3() )  // Compilation Failure, missing IMutable and ILockable.

私が見つけた最も近いものはboost::mpl::inheritです。私はいくつかの限られた成功を収めましたが、それは私が必要とすることを正確に行いません。

例えば:

class Object1 : public boost::mpl::inherit<IObject, IMutable, ILockable>::type
class Object2 : public boost::mpl::inherit<IObject, ILockable, ICertifiable>::type
class Object3 : public IObject

void doSomething(boost::mpl::inherit<IObject, ILockable>::type object);

doSomething( Object1() )  // Fails even though Object1 derives from IObject and ILockable.
doSomething( Object2() )  // Fails even though Object2 derives from IObject and ILockable.

boost::mpl::inherit に似たものだと思いますが、提供された型のすべての可能な順列を含む継承ツリーが生成される可能性があります。

この問題を解決するための他のアプローチにも興味があります。理想的には、実行時ではなくコンパイル時のチェックを行うもの (つまり、dynamic_cast なし) です。

4

5 に答える 5

3

static_assert関数内の型を確認するために使用する必要があります。

#include <type_traits>

template< typename T >
void doSomething( const T& t )
{
   static_assert( std::is_base_of<IObject,T>::value, "T does not satisfy IObject" );
   static_assert( std::is_base_of<IMutable,T>::value, "T does not satisfy IMutable" );

   // ...
}

これにより、どのインターフェースが満たされていないかを示す非常に優れたエラーメッセージが表示されます。関数をオーバーロードする必要があり、特定のインターフェイスの組み合わせでのみ使用できるバージョンが必要な場合は、次を使用することもできますenable_if

#include <type_traits>

template< typename T, typename... Is >
struct HasInterfaces;

template< typename T >
struct HasInterfaces< T > : std::true_type {};

template< typename T, typename I, typename... Is >
struct HasInterfaces< T, I, Is... >
  : std::integral_constant< bool,
      std::is_base_of< I, T >::value && HasInterfaces< T, Is... >::value > {};

template< typename T >
typename std::enable_if< HasInterfaces< T, IObject, IMutable >::value >::type
doSomething( const T& t )
{
  // ...
}

これにより、インターフェイスの要件が満たされない場合、オーバーロード セットから関数が消えます。

于 2013-02-21T17:16:41.557 に答える
3

再帰的な可変個引数継承を使用して、インターフェイス チェック クラスを作成できます。

template<typename... Interfaces>
struct check_interfaces;
template<>
struct check_interfaces<> {
   template<typename T> check_interfaces(T *) {}
};
template<typename Interface, typename... Interfaces>
struct check_interfaces<Interface, Interfaces...>:
public check_interfaces<Interfaces...> {
   template<typename T> check_interfaces(T *t):
      check_interfaces<Interfaces...>(t), i(t) {}
   Interface *i;
   operator Interface *() const { return i; }
};

例えば:

struct IObject { virtual int getAttribute() = 0; };
struct IMutable { virtual void setAttribute(int) = 0; };
struct ILockable { virtual void lock() = 0; };

void f(check_interfaces<IObject, IMutable> o) {
   static_cast<IObject *>(o)->getAttribute();
   static_cast<IMutable *>(o)->setAttribute(99);
}

struct MutableObject: IObject, IMutable {
   int getAttribute() { return 0; }
   void setAttribute(int) {}
};

struct LockableObject: IObject, ILockable {
   int getAttribute() { return 0; }
   void lock() {}
};

int main() {
   f(new MutableObject);
   f(new LockableObject);  // fails
}

check_interfacesチェックされたインターフェイスごとに 1 つのポインターのフットプリントがあることに注意してください。これは、宣言された実引数の型に対して型消去を実行するためです。

于 2013-02-21T17:18:00.933 に答える
2

std::enable_ifと を使用したソリューションstd::is_base_of:

#include <type_traits>

// An object
struct IObject
{
    virtual void getAttribute() = 0;
};

// A mutable object
struct IMutable
{
    virtual void setAttribute() = 0;
};

// A lockable object 
struct ILockable
{
    virtual void lock() = 0;
};

// A certifiable object 
struct ICertifiable
{
    virtual void setCertification() = 0;
    virtual void getCertification() = 0;
};

struct Object1 : public IObject, public IMutable, public ILockable
{
    void getAttribute() {}
    void setAttribute() {}
    void lock() {}
};

struct Object2 : public IObject, public ILockable, public ICertifiable
{
    void getAttribute() {}
    void lock() {}
    void setCertification() {}
    void getCertification() {}
};

struct Object3 : public IObject
{
    void getAttribute() {}
};

template<typename T>
void doSomething(
    typename std::enable_if<
        std::is_base_of<IObject, T>::value &&
        std::is_base_of<IMutable, T>::value &&
        std::is_base_of<ILockable, T>::value,
        T>::type& obj)
{
}

int main()
{
    Object1 object1;
    Object2 object2;
    Object3 object3;

    doSomething<Object1>(object1);  // Works
    doSomething<Object2>(object2);  // Compilation error
    doSomething<Object3>(object3);  // Compilation error
}
于 2013-02-21T19:22:23.577 に答える
1

C++11 を少しだけ紹介します。

再帰のない単一の型:

template <typename... Ts>
class magic_interface_combiner {
  typedef std::tuple<Ts*...> Tpl;
  Tpl tpl;

  template <typename T, int I>
  T *as_(std::false_type)
  {
    static_assert(I < std::tuple_size<Tpl>::value, "T not found");
    return as_<T, I+1>(std::is_same<T, typename std::tuple_element<I+1, Tpl>::type>{});
  }
  template <typename T, int I>
  T *as_(std::true_type) { return std::get<I>(tpl); }

public:
  template <typename T>
  magic_interface_combiner(T * t) : tpl(static_cast<Ts*>(t)...) {}

  template <typename T> T * as() { return as_<T, 0>(std::false_type{}); }
};

// no template    
void doSomething(magic_interface_combiner<IObject, IMutable, ILockable> object)
{
}

2 つのタイプですが、再帰はありません。

template <typename T>
class single_interface_combiner {
  T *p;
public:
  single_interface_combiner(T *t) : p(t) {}
  operator T* () { return p; }
};

template <typename... Ts>
struct magic_interface_combiner : single_interface_combiner<Ts>... {
  template <typename T>
  magic_interface_combiner(T* t) : single_interface_combiner<Ts>(t)... {}

  template <typename T>
  T * as() { return *this; }
};
于 2013-02-21T17:24:30.887 に答える
1

C++03 構文で行われているため、おそらく最もエレガントな方法ではありません。

template <typename T, typename TInterface>
void interface_checker(T& t)
{
    TInterface& tIfClassImplementsInterface = static_cast<TInterface&>(t);
}

これはトリックの精神をあなたに与えるためです。今あなたの場合:

template <typename T, typename TInterface1, typename TInterface2, typename TInterface3 >
void magic_interface_combiner(T& t)
{
    TInterface1& tIfClassImplementsInterface = static_cast<TInterface1&>(t);
    TInterface2& tIfClassImplementsInterface = static_cast<TInterface2&>(t);
    TInterface3& tIfClassImplementsInterface = static_cast<TInterface3&>(t);
}

C++11 の型特性を使用すると、よりスマートに実行できると思います。

于 2013-02-21T17:12:33.443 に答える