Введение
Я работаю над специализированным распределителем памяти и должен добавить некоторую информацию о бухгалтерии в заголовок каждого выделенного фрагмента. Существует несколько разных типов блоков, а информация о бухгалтерском учете также отличается. Например, для кусков, разделяемых между потоками, необходимо добавить счетчик ссылок, для кусков, используемых одним потоком, такой потребности нет. Для кусков, взятых из пула памяти, необходимо сохранить ссылку на исходный пул, для кусков, взятых из бесплатного хранилища, нет такой необходимости.
Проблема
Итак, я хотел бы иметь общий интерфейс для добавления и получения определенных типов данных для данного фрагмента. Экспериментируя с этой идеей, я пришел к решению, которое похоже на std:: tuple. Однако, в отличие от кортежей, каждый тип, который я добавляю в заголовок, будет уникальным. Я только начал изучать мета-программирование шаблонов и другие тонкости С++, однако часть с добавлением типа была для меня понятна.
Проблема, с которой я столкнулся, заключается в реализации метода, аналогичного С++ 14 std::get
по типу функции шаблона для кортежей. Я решил, что нет необходимости писать много кода для этого, поскольку компилятор способен сопоставить правильный базовый класс в вызове метода. Сначала я поместил метод get
в шаблонный класс макета. Однако компилятор в этом случае не соответствует соответствующему классу. Проблема была решена путем перемещения метода get
на другой, добавленный вручную, уровень иерархии классов.
Приведенный ниже код демонстрирует проблему. Определение HAVE_GET_IN_LAYOUT до 0 создает рабочее решение при определении его на 1, создает разбитое решение [по крайней мере, с clang++ 3.5 и 3.6]
Вопрос в том, что в этом случае нарушено?
#include <cstddef>
#include <iostream>
#ifndef HAVE_GET_IN_LAYOUT
#define HAVE_GET_IN_LAYOUT 0
#endif
constexpr std::size_t Align(std::size_t size, std::size_t offset) {
return (size < 0x8
? (offset + 0x3) & ~0x3
: (size < 0x10 ? (offset + 0x7) & ~0x7 : (offset + 0xf) & ~0xf));
}
template <std::size_t Start, typename... Ts> struct Layout {
static constexpr std::size_t Size = 0;
static constexpr std::size_t Offset = Start;
static constexpr std::size_t TotalSize = Start;
};
template <std::size_t Start, typename T, typename... Ts>
struct Layout<Start, T, Ts...>
: public Layout<Align(sizeof(T), Start) + sizeof(T), Ts...> {
using Type = T;
static constexpr std::size_t Size = sizeof(Type);
static constexpr std::size_t Offset = Align(Size, Start);
static constexpr std::size_t TotalSize = Layout<Offset + Size, Ts...>::TotalSize;
Type value = Offset - Start; // no particular meaning, just for testing.
#if HAVE_GET_IN_LAYOUT
template <typename U, std::size_t X, typename... Us>
U &helper(Layout<X, U, Us...> *c) { return c->value; }
template <typename U> U &get() { return helper<U>(this); }
#endif
};
template <typename... Ts> struct Result : public Layout<0, Ts...> {
#if !HAVE_GET_IN_LAYOUT
template <typename U, std::size_t X, typename... Us>
U &helper(Layout<X, U, Us...> *c) { return c->value; }
template <typename U> U &get() { return helper<U>(this); }
#endif
};
int main() {
std::cout << "layout size <> = " << Layout<0>::TotalSize << std::endl;
std::cout << "layout size <int> = " << Layout<0, int>::TotalSize << std::endl;
std::cout << "layout size <long> = " << Layout<0, long>::TotalSize << std::endl;
std::cout << "layout size <int,int> = " << Layout<0, int, int>::TotalSize << std::endl;
std::cout << "layout size <int,long> = " << Layout<0, int, long>::TotalSize << std::endl;
std::cout << "layout size <long,int> = " << Layout<0, long, int>::TotalSize << std::endl;
std::cout << "layout size <long,long> = " << Layout<0, long, long>::TotalSize << std::endl;
std::cout << "get: " << Result<int, long, long double>{}.get<long>() << std::endl;
return 0;
}