简介
我正在开发一个自定义的内存分配器,需要在每个分配块的头部添加一些簿记信息。有几种不同的块类型,簿记信息也不同。例如,对于在线程之间共享的块,需要添加引用计数器;对于单个线程使用的块,没有这样的需要。对于从内存池中获取的块,需要保留对原始内存池的引用,而对于从自由存储区获取的块则没有这样的需要。
问题
因此,我希望有一个通用接口,可以为给定的块布局添加和获取某些数据类型。通过尝试这个想法,我得出了一个类似于 std::tuple 的解决方案。但与元组不同的是,我添加到头部的每种类型都将是唯一的。我刚开始学习模板元编程和 c++ 的其他复杂性,但添加类型的部分对我来说很直观。
我遇到的问题是实现类似于 C++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;
}