Рекурсивное объявление типа шаблона [решено] - PullRequest
1 голос
/ 04 августа 2020

Я пытаюсь создать структуру, которая содержит N вложенных шаблонных типов:

Код, который я должен сделать, это:


// slab (base case essentially)
template<typename T, uint32_t nvecs = 8, align_policy ap = align_policy::none>
struct slab {
T t;
};

// wrapper for either slab or other super_slabs
template<typename T,
         uint32_t nvecs        = 8,
         uint32_t inner_nvec   = 8,
         typename inner_slab_t = slab<T, inner_nvec>>
struct super_slab {
inner_slab_t ist;
};

// hopefully correct functions to extract Nth values for argument pack
template<typename... Vals>
constexpr uint32_t
_get_0(uint32_t v, Vals... vals) {
    return v;
}

template<typename... Vals>
constexpr uint32_t
_get_N(int32_t n, uint32_t v, Vals... vals) {
    return n <= 0 ? _get_0(vals...) : _get_N(n - 1, vals...);
}

template<typename... Vals>
constexpr uint32_t
get_N(int32_t n, Vals... vals) {
    return _get_N(n, vals...);
}


// first approach I tried
#ifdef APPROACH_A
template<typename T, uint32_t nlevels, uint32_t level, uint32_t... other_nvecs>
struct type_helper {
    using type = typename std::conditional<
        level <= 1,
        slab<T, other_nvecs...>,
        super_slab<T,
                   get_N(nlevels - level, other_nvecs...),
                   get_N(nlevels - (level + 1), other_nvecs...),
                   typename type_helper<T, nlevels, level - 1, other_nvecs...>::
                       type>>::type;
};
#endif

// second approach I tried
#ifdef APPROACH_B
template<typename T, uint32_t nlevels, uint32_t level, uint32_t... other_nvecs>
struct type_helper;
template<typename T, uint32_t nlevels, uint32_t level, uint32_t... other_nvecs>
struct type_helper {
    using type = super_slab<
        T,
        get_N(nlevels - level, other_nvecs...),
        get_N(nlevels - (level + 1), other_nvecs...),
        typename type_helper<T, nlevels, level - 1, other_nvecs...>::type>;
};

template<typename T, uint32_t nlevels, uint32_t level, uint32_t other_nvecs>
struct type_helper {
    using type = slab<T, nvecs>;
};
#endif

// struct I want to use for API.
template<typename T, uint32_t levels, uint32_t... level_nvecs>
struct slab_manager {
    using slab_t =
        typename type_helper<T, levels, levels, level_nvecs...>::type;
};

APPROACH_A компилируется, но когда я пытаюсь создать экземпляр это с:

slab_manager<uint64_t, 1, 8> m;

Я получаю сообщение об ошибке:

slab_manager.h: In instantiation of ‘struct type_helper<long unsigned int, 1, 4294966399, 8>’:
slab_manager.h:39:36:   recursively required from ‘struct type_helper<long unsigned int, 1, 0, 8>’
slab_manager.h:39:36:   required from ‘struct type_helper<long unsigned int, 1, 1, 8>’
slab_manager.h:63:70:   required from ‘struct slab_manager<long unsigned int, 1, 8>’
obj_slab_test.cc:213:34:   required from here
slab_manager.h:36:25: fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
                    get_N(nlevels - level, other_nvecs...),
                    ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.

Я действительно не понимаю, как ‘struct type_helper<long unsigned int, 1, 4294966399, 8>’: когда-либо происходит, поскольку передача 1 - начальный уровень должен вызывать это просто поразить базовый регистр в std::conditional. Я предполагал, что проблема заключалась в том, что std::conditional полностью разворачивает оба варианта, но когда я пытаюсь приблизиться, BI получаю следующие ошибки:

slab_manager.h:43:68: error: template parameter ‘unsigned int ...other_nvecs’
 template<typename T, uint32_t nlevels, uint32_t level, uint32_t... other_nvecs>
                                                                    ^~~~~~~~~~~
slab_manager.h:55:8: error: redeclared here as ‘unsigned int other_nvecs’
 struct type_helper {
        ^~~~~~~~~~~
slab_manager.h: In instantiation of ‘struct type_helper<long unsigned int, 1, 4294966399, 8>’:
slab_manager.h:51:75:   recursively required from ‘struct type_helper<long unsigned int, 1, 0, 8>’
slab_manager.h:51:75:   required from ‘struct type_helper<long unsigned int, 1, 1, 8>’
slab_manager.h:63:70:   required from ‘struct slab_manager<long unsigned int, 1, 8>’
obj_slab_test.cc:213:34:   required from here
slab_manager.h:49:14: fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
         get_N(nlevels - level, other_nvecs...),
         ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Это означает, что причина сбоя A НЕ связана с std::conditional, но меня смущает больше, потому что метод повторного объявления типов с базовым случаем был тем, что я видел в каждом руководстве, которое я видел до сих пор.

Я предполагаю, что вызов slab_manager<uint64_t, 1, 8> m; установит slab_t = slab<uint64_t, 8> и, например, slab_manager<uint64_t, 3, 2, 4, 8> m; установит slab_t = super_slab<uint64_t, 2, 4, super_slab<uint64_t, 4, 8, slab<uint64_t, 8>. Любая помощь будет оценена. Спасибо!

Редактировать для потомков:

В этом вопросе было ОЧЕНЬ НЕПРАВИЛЬНО. как заметил @ cdhow ie, я неправильно следовал руководствам. Вот решение, которое сработало:

template<typename... Vals>
constexpr int32_t
_get_0(int32_t v, Vals... vals) {
    return v;
}


template<typename... Vals>
constexpr int32_t
_get_0(int32_t v) {
    return v;
}

template<typename... Vals>
constexpr int32_t
_get_N(int32_t n, Vals... vals) {
    return n <= 0 ? _get_0(vals...) : _get_N(n - 1, vals...);
}


template<typename... Vals>
constexpr int32_t
get_N(int32_t n, Vals... vals) {
    return _get_N(n, vals...);
}


template<typename T, int32_t nlevels, int32_t level, int32_t... other_nvecs>
struct type_helper;


template<typename T, int32_t nlevel, int32_t... other_nvecs>
struct type_helper<T, nlevel, 0, other_nvecs...> {
    typedef slab<T, get_N(nlevel, other_nvecs...)> type;
};

template<typename T, int32_t nlevels, int32_t level, int32_t... other_nvecs>
struct type_helper {
    typedef super_slab<T,
                       get_N(nlevels - level, other_nvecs...),
                       get_N(nlevels - (level + 1), other_nvecs...),
                       typename type_helper<T, nlevels, level - 1, other_nvecs...>::type>
        type;
};


template<typename T, int32_t levels, int32_t... level_nvecs>
struct slab_manager {
    using slab_t = typename type_helper<T, levels, levels - 1, level_nvecs...>::type;
};

1 Ответ

1 голос
/ 04 августа 2020

В подходе A у вас есть целочисленное опустошение во время компиляции, поскольку оба типа в двух «ветвях» std::conditional все еще создаются, даже если условное выражение выбирает другой. В результате ваш type_helper вызывает бесконечную рекурсию, пытающуюся создать экземпляр.

Ошибка компиляции с подходом B заключается в том, что ваш синтаксис для специализации неверен. Это:

template<typename T, uint32_t nlevels, uint32_t level, uint32_t other_nvecs>
struct type_helper {
    using type = slab<T, nvecs>;
};

Должно быть так:

template<typename T, uint32_t nlevels, uint32_t level, uint32_t other_nvecs>
struct type_helper<T, nlevels, level, other_nvecs> {
    using type = slab<T, nvecs>;
};

Подход B становится жертвой точно такой же проблемы с бесконечной рекурсией, что и подход A: type_helper<T, nlevels, level, other_nvecs...> всегда будет создавать экземпляр type_helper<T, nlevels, level - 1, other_nvecs...>. level исчерпывается, и рекурсия продолжается до тех пор, пока компилятор не откажется.

В этом случае вы бы прекратили рекурсию, чтобы определить особый случай, когда level равно нулю:

template<typename T, uint32_t nlevels, uint32_t... other_nvecs>
struct type_helper<T, nlevels, 0, other_nvecs...> {
    using type = // whatever makes sense in your case
};

Я не знаю, что type должно быть здесь (но я предполагаю, что вы знаете).

У вас также может быть последний завершающий случай, когда level равно нулю, а other_nvecs пусто:

template<typename T, uint32_t nlevels>
struct type_helper<T, nlevels, 0> {
    using type = // something
};
...