Как мне реализовать полиморфное поведение между двумя шаблонами mixin? - PullRequest
0 голосов
/ 02 апреля 2011

Я реализую миксины с использованием шаблонов C ++ для поддержки некоторых «расширенных» поведений для базового (шаблонного) класса.

template< class Ch > Base {...};

template< class T > M1 : public T {...};
template< class T > M2 : public T {...};
// etc.

Мне нужно изменить поведение одного миксина, если в графе наследования присутствует другой миксин. (В идеале это было бы независимо от того, какой из них смешался первым, но я готов принять ограничение, которое они должны выводить в определенном порядке.)

Другими словами, если у меня есть:

typedef Base< char > base;
typedef M2< M1< base >> foo;
typedef M2< base > bar;

Поведение методов M2 должно измениться, когда M1 смешан с - M1 предоставляет элементы данных, которые должны использоваться при вычислении результатов M2. С другой стороны, M2 дает определенные гарантии в отношении тех элементов данных, которые являются полностью недействительными, если M2 не смешан.

Тогда мой вопрос, как реализовать этот C ++ '03?

Я подозреваю, что есть способ использовать метод шаблона и специализацию, но мой шаблон-фу недостаточно (пока).


В ответ Дауфика я попробовал это, и оно работает. Необходимость полностью продублировать шаблон M2 настолько ужасна, что я собираюсь попробовать подход SFINAE дальше.

#include <iostream>
#include <UnitTest++.h>

SUITE( Mixin_specialization_tests ) {

    template< typename Ch > struct Base {
        typedef Ch * pointer_type;
        pointer_type p;
    };

    template< class T > struct M1 : public T {
        typedef typename T::pointer_type pointer_type;
        pointer_type m1p;
    };

    template< class T > struct M2 : public T {
        typedef typename T::pointer_type pointer_type;
        pointer_type m2p;

        int compute( ) {
            std::cout << "unspecialized compute()" << std::endl;
            return 0;
        }
    };

    template< >
    template< class B > struct M2< M1<B> > : public M1<B> {
        typedef typename M1< B >::pointer_type pointer_type;
        pointer_type m2p;

        int compute( ) {
            std::cout << "specialized compute()" << std::endl;
            int unused = M1< B >::m1p - m2p;
            return 1;
        }
    };

    typedef Base< char > Bch;
    typedef M1< Bch > M1b;
    typedef M2< Bch  > M2b;
    typedef M2< M1< Bch > > M2m1b;

    TEST( unspecialized ) {
        M2b m2b;
        CHECK_EQUAL( 0, m2b.compute() );
    }

    TEST( specialized ) {
        M2m1b m2m1b;
        CHECK_EQUAL( 1, m2m1b.compute( ) );
    }
}

Подумав немного, мне удалось заставить работать вариант SFINAE. Код в основном такой же, за исключением этой части:

    template< class Query > struct is_M1 {
    typedef char yes, (&no)[ 2 ];
    static no has_m1_member(...);

    template< class T, typename T::pointer_type T::* > struct dummy {};
    template< class T >
    static yes has_m1_member( T*, dummy<T, &T::m1p>* = 0 );
    BOOST_STATIC_CONSTANT( bool, value =
        ( sizeof( has_m1_member( ( Query * )0 ) ) == sizeof( yes ) )
    );
};

template< class T > struct M2 : public T {
    typedef typename T::pointer_type pointer_type;
    pointer_type m2p;

    int compute( ) {
        return compute_impl<T>( );
    }

    template< typename B >
    typename boost::enable_if< is_M1< B >, int >::type compute_impl( ) {
        std::cout << "sfinae: m1-compute" << std::endl;
        return 1;
    }

    template< typename B >
    typename boost::disable_if< is_M1<B>, int >::type compute_impl( ) {
        std::cout << "sfinae: non-m1-compute" << std::endl;
        return 0;
    }
};

Как я упоминал ниже, шаблон enable_if <> не обрабатывает логические выражения (для меня), поэтому я пошел с disable_if <>, который, кажется, делает свое дело.

Ни один из них не особенно радует, так как этот код будет добавлен в проект, который в настоящее время не использует boost, и поскольку возможность дублирования всего шаблона является кошмаром обслуживания. Но они оба выполняют свою работу. Я собираюсь считать решение для наддува ответом просто потому, что есть надежда, что код наддува станет стандартным, а значит, меньше головной боли.

Спасибо всем.

Ответы [ 2 ]

1 голос
/ 02 апреля 2011

Как предположил Дофик, вы можете просто сделать частичную специализацию класса M2 для случая, когда его аргумент - M1, и, конечно, вы можете сделать то же самое для M1 (с простым чередованием объявлений).

Однако это даст вам очень грубую специализацию, т. Е. Вам придется переопределить всех членов M2.Это может раздражать, если есть только несколько членов, которые должны отличаться, т. Е. Это решение плохо масштабируется.К сожалению и досадно, C ++ не предоставляет поддержки для самостоятельной специализации функций-членов.

Тем не менее, Sfinae может прийти вам на помощь, но это требует немного больше работы.Ниже приведен просто классический метод Sfinae, позволяющий специализировать отдельные функции-члены шаблона класса:

#include <iostream>
#include <boost/utility/enable_if.hpp>
#include <boost/config.hpp>

template <class Model>
struct is_M1
{
    typedef char yes;
    typedef char (&no)[2];

    template <class T, void (T::*)()>
    struct dummy {};

    template <class T>
    static yes has_m1_function1(T*, dummy<T,&T::function1>* = 0);
    static no has_m1_function1(...); 

    BOOST_STATIC_CONSTANT(
        bool
      , value = sizeof( has_m1_function1((Model*)0) ) == 1 );
};

template <typename T>
struct Base { T value; };

template <typename T>
struct M1 : public T {
  void function1() { }; //I presume M1 would have at least one defining characteristic or member function
};

template <typename T>
struct M2 : public T {
  void function2() { function2_impl<T>(); };
  template <typename B>
  typename boost::enable_if< is_M1<B>, void>::type function2_impl() { 
    std::cout << "M1 is mixed in T" << std::endl;
  };
  template <typename B>
  typename boost::enable_if< !is_M1<B>, void>::type function2_impl() { 
    std::cout << "M1 is not mixed in T" << std::endl;
  };
};

int main() {
  M2< M1< Base<int> > > m2m1b;
  M2< Base<int> > m2b;

  m2b.function2();
  m2m1b.function2();
};
1 голос
/ 02 апреля 2011

Ваш вопрос очень многословный, и я не уверен, правильно ли я понимаю, но я попробую.

Я думаю, вы хотите специализировать M2 для случаяtemplate M1.

template<class T> class M1 : public T { };

template<class T> class M2 : public T { };
template<> template<class B> class M2<M1<B> > : public M1<B> { };

В этом примере M2 специализируется на M1<B>, используя вложенный шаблон (B), чтобы он мог принимать любые экземпляры шаблона M1.

...