Удалите повторяющиеся записи из вариативных аргументов шаблона С++ 11.

Я использую вариативные шаблоны с множественным виртуальным наследованием в C++ для объединения типов в одно определение структуры.

Вот примерный набор структур:

struct meas { int i; };
struct meas2 : public virtual meas { int j; };
struct meas3 : public virtual meas { int k; };

Затем я объединяю их, используя множественное виртуальное наследование:

template <typename... Args>
struct zipper : public virtual Args... {};

Затем я могу сделать:

typedef zipper<meas, meas2> meas_type;
meas* m = new meas_type;

Затем они могут каскадироваться:

typedef zipper<meas3, meas_type> meas_type2;

Однако получившийся объект довольно громоздкий:

$46 = (zipper<meas3, zipper<meas, meas2> >) {
  <meas3> = {
    <meas> = {
      i = 0
    }, 
    members of meas3: 
    _vptr.meas3 = 0x400ec8, 
    k = 0
  }, 
  <zipper<meas, meas2>> = {
    <meas2> = {
      members of meas2: 
      _vptr.meas2 = 0x400ee0, 
      j = 6299120
    }, 
    members of zipper<meas, meas2>: 
    _vptr.zipper = 0x400eb0
  }, <No data fields>}

по данным ГДБ.

Существует также вторичная проблема при попытке заархивировать тот же базовый тип:

typedef zipper<meas, meas> meas_type2;

Вышеприведенное приводит к ошибке компилятора «недопустимый дубликат базового класса meas» в G++ 4.6.3.

Таким образом, вопрос двоякий:

  1. Есть ли способ преобразовать zipper<meas3, zipper<meas, meas2>> в zipper<meas3, meas2>?
  2. Есть ли способ при выполнении пункта 1 удалить повторяющиеся записи в списке типов?

Спасибо!


person Devin Lane    schedule 11.12.2012    source источник
comment
Вы хотите сгладить, а также удалить дубликаты.   -  person Nawaz    schedule 11.12.2012
comment
Что касается вашего вопроса 1, вы хотели превратиться в zipper<meas3, meas, meas2>? Вы, кажется, опустили часть meas.   -  person Nicu Stiurca    schedule 12.12.2012
comment
Что касается фактического решения проблемы, я подозреваю, что вам лучше всего углубиться в <type_traits> и попытаться использовать такие вещи, как std::conditional и std::enable_if, для создания специализаций вашего шаблона zipper для случая, когда аргумент сам является zipper. Если вы заставите это работать, я подозреваю, что это будет изумительный образец безумного волшебства. Удачи тебе.   -  person Nicu Stiurca    schedule 12.12.2012
comment
@SchighSchagh meas2 и meas3 виртуально наследуются от meas, поэтому в окончательной застежке-молнии нужны только meas2 и meas3. Для проверки есть std::is_base_of.   -  person Devin Lane    schedule 12.12.2012


Ответы (1)


Моя стратегия решения этой проблемы заключается в использовании нескольких уровней косвенности.

  • zipper ‹ Args... > отправляет обработку своих аргументов функции process_zipper_arguments, наследуя:

Пример:

template < typename... Args >
struct zipper : zipper < typename process_zipper_arguments < Args... >::type > {};
  • используйте template < typename... Args > struct typelist {}, чтобы отслеживать типы объектов, от которых вы хотите наследоваться.
  • Специализируйте struct zipper < typelist < Args... > >: public virtual Args... для фактического наследования

Чтобы избавиться от повторяющихся родительских типов, в process_zipper_arguments используются две вспомогательные функции:

  • is_in < CandidateType, typelist< Args... > >::type равно true_type или false_type и может быть определено рекурсивно
  • add_unique < CandidateType, typelist< Args... > >::type — это typelist <...>, к которому либо добавлен CandidateType, либо нет. Он вызывает is_in, чтобы определить это.

Вот полный код, который компилируется по крайней мере с g++ (GCC) 4.6.3 с --std=c++0x. Критика по нему приветствуется.

// Forward declarations
template < typename... Args >
struct zipper;

// Two types meaning true and false
struct true_type {};
struct false_type {};

// The only purpose of this struct is to be associated with Types...
template < typename... Types >
struct typelist {};


// ===================================================
// is_in < type, typelist<...> >::type
//     is true_type if type is in typelist
//     is false_type if type is not in typelist

// Assume TElement is not in the list unless proven otherwise
template < typename TElement, typename TList >
struct is_in {
  typedef false_type type;
};

// If it matches the first type, it is definitely in the list
template < typename TElement, typename... TTail >
struct is_in < TElement, typelist < TElement, TTail... > >
{
  typedef true_type type;
};

// If it is not the first element, check the remaining list
template < typename TElement, typename THead, typename... TTail >
struct is_in < TElement, typelist < THead, TTail... > >
{
  typedef typename is_in < TElement, typelist < TTail... > >::type type;
};

// ===================================================
// add_unique < TNew, typelist<...> >::type
//     is typelist < TNew, ... > if TNew is not already in the list
//     is typelist <...> otherwise

// Append a type to a type_list unless it already exists
template < typename TNew, typename TList,
  typename Tis_duplicate = typename is_in < TNew, TList >::type
  >
struct add_unique;

// If TNew is in the list, return the list unmodified
template < typename TNew, typename... TList >
struct add_unique < TNew, typelist < TList... >, true_type >
{
  typedef typelist < TList... > type;
};

// If TNew is not in the list, append it
template < typename TNew, typename... TList >
struct add_unique < TNew, typelist < TList... >, false_type >
{
  typedef typelist < TNew, TList... > type;
};

// ===================================================
// process_zipper_arguments < Args... >::type
//     returns a typelist of types to be inherited from.
//
// It performs the following actions:
// a) Unpack zipper<...> and typelist <...> arguments
// b) Ignore values that are already in the list

template < typename... Args >
struct process_zipper_arguments;

// Unpack a zipper in the first argument
template < typename... ZipperArgs, typename... Args >
struct process_zipper_arguments < zipper < ZipperArgs... >, Args... >
{
  typedef typename process_zipper_arguments < ZipperArgs..., Args... >::type type;
};

// Unpack a typelist in the first argument
template < typename... TypeListArgs, typename... Args >
struct process_zipper_arguments < typelist < TypeListArgs... >, Args... >
{
  typedef typename process_zipper_arguments < TypeListArgs..., Args... >::type type;
};

// End the recursion if the list is empty
template < >
struct process_zipper_arguments < >
{
  typedef typelist < > type;
};

// Construct the list of unique types by appending them one by one
template < typename THead, typename... TTail >
struct process_zipper_arguments < THead, TTail... >
{
  typedef typename
    add_unique < THead,
      typename process_zipper_arguments < TTail... >::type
    >::type type;
};


// ===================================================
// The zipper class that you might want


// If the list of types is not yet known, process it.
// The inheritance is ugly, but there is a workaround
template < typename... Args >
struct zipper : zipper < typename process_zipper_arguments < Args... >::type >
{
  // // Instead of inheriting, you can use zipper as a factory.
  // // So this:
  // typedef zipper < meas2, zipper < meas1, meas > > mymeas;
  // // Turns to:
  // typedef typename zipper < meas2, zipper < meas1, meas > >::type mymeas;
  typedef zipper < typename process_zipper_arguments < Args... >::type > type;
};

// If the list of types is known, inherit from each type
template < typename... Args >
struct zipper < typelist < Args... > >
: public virtual Args...
{};

// ===================================================
// Short usage demo, replace with your own code

struct meas {
    int i;
};

struct meas2 {
    int j;
};

struct meas3 {
    int k;
};


typedef zipper < meas, meas, meas3 > meas_type;
typedef zipper < meas2, meas_type, meas2 > meas_type2;

typedef typename zipper < meas_type2 >::type nicer_meas_type2;


int main ( int, char** )
{
    meas * m = new meas_type2;
    meas_type2 n;
    nicer_meas_type2 o;

    return 0;
}

Отладка дает следующий результат (точка останова на строке return 0;):

(gdb) print *m
$1 = {i = 0}
(gdb) print n
$2 = {<zipper<typelist<meas, meas3, meas2> >> = {<meas> = {i = 4196320}, <meas3> = {k = 0}, <meas2> = {j = 0}, 
    _vptr.zipper = 0x400928}, <No data fields>}
(gdb) print o
$3 = {<meas> = {i = 4195719}, <meas3> = {k = 0}, <meas2> = {j = 1}, _vptr.zipper = 0x4009a8 <VTT for zipper<typelist<meas, meas3, meas2> >>}
person mars    schedule 12.12.2012
comment
Это эпично и именно то, что я искал! Ваш шаблон foo довольно силен :) - person Devin Lane; 12.12.2012
comment
Спасибо, но, честно говоря, я столкнулся с очень похожей проблемой неделю назад, что заняло у меня некоторое время, поэтому она была еще свежа в моей голове. - person mars; 13.12.2012