1 //===--- TrailingObjects.h - Variable-length classes ------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
11 /// This header defines support for implementing classes that have
12 /// some trailing object (or arrays of objects) appended to them. The
13 /// main purpose is to make it obvious where this idiom is being used,
14 /// and to make the usage more idiomatic and more difficult to get
17 /// The TrailingObject template abstracts away the reinterpret_cast,
18 /// pointer arithmetic, and size calculations used for the allocation
19 /// and access of appended arrays of objects, as well as asserts that
20 /// the alignment of the classes involved are appropriate for the
21 /// usage. Additionally, it ensures that the base type is final --
22 /// deriving from a class that expects data appended immediately after
23 /// it is typically not safe.
25 /// Users are expected to derive from this template, and provide
26 /// numTrailingObjects implementations for each trailing type,
27 /// e.g. like this sample:
30 /// class VarLengthObj : private TrailingObjects<VarLengthObj, int, double> {
31 /// friend TrailingObjects;
33 /// unsigned NumInts, NumDoubles;
34 /// size_t numTrailingObjects(OverloadToken<int>) const { return NumInts; }
35 /// size_t numTrailingObjects(OverloadToken<double>) const {
36 /// return NumDoubles;
41 /// You can access the appended arrays via 'getTrailingObjects', and
42 /// determine the size needed for allocation via
43 /// 'additionalSizeToAlloc' and 'totalSizeToAlloc'.
45 /// All the methods implemented by this class are are intended for use
46 /// by the implementation of the class, not as part of its interface
47 /// (thus, private inheritance is suggested).
49 //===----------------------------------------------------------------------===//
51 #ifndef LLVM_SUPPORT_TRAILINGOBJECTS_H
52 #define LLVM_SUPPORT_TRAILINGOBJECTS_H
55 #include <type_traits>
56 #include "llvm/Support/AlignOf.h"
57 #include "llvm/Support/Compiler.h"
58 #include "llvm/Support/type_traits.h"
62 /// The base class for TrailingObjects* classes.
63 class TrailingObjectsBase {
65 /// OverloadToken's purpose is to allow specifying function overloads
66 /// for different types, without actually taking the types as
67 /// parameters. (Necessary because member function templates cannot
68 /// be specialized, so overloads must be used instead of
70 template <typename T> struct OverloadToken {};
73 // Internally used to indicate that the user didn't supply this value,
74 // so the explicit-specialization for fewer args will be used.
75 class NoTrailingTypeArg {};
77 // TODO: Consider using a single variadic implementation instead of
78 // multiple copies of the TrailingObjects template? [but, variadic
79 // template recursive implementations are annoying...]
81 /// This is the two-type version of the TrailingObjects template; see
82 /// file docstring for details.
83 template <typename BaseTy, typename TrailingTy1,
84 typename TrailingTy2 = NoTrailingTypeArg>
85 class TrailingObjects : public TrailingObjectsBase {
87 // Contains static_assert statements for the alignment of the
88 // types. Must not be at class-level, because BaseTy isn't complete
89 // at class instantiation time, but will be by the time this
90 // function is instantiated.
91 static void verifyTrailingObjectsAssertions() {
92 static_assert(llvm::AlignOf<BaseTy>::Alignment >=
93 llvm::AlignOf<TrailingTy1>::Alignment,
94 "TrailingTy1 requires more alignment than BaseTy provides");
96 llvm::AlignOf<TrailingTy1>::Alignment >=
97 llvm::AlignOf<TrailingTy2>::Alignment,
98 "TrailingTy2 requires more alignment than TrailingTy1 provides");
101 static_assert(LLVM_IS_FINAL(BaseTy), "BaseTy must be final.");
105 // The next four functions are internal helpers for getTrailingObjects.
106 static const TrailingTy1 *getTrailingObjectsImpl(const BaseTy *Obj,
107 OverloadToken<TrailingTy1>) {
108 return reinterpret_cast<const TrailingTy1 *>(Obj + 1);
111 static TrailingTy1 *getTrailingObjectsImpl(BaseTy *Obj,
112 OverloadToken<TrailingTy1>) {
113 return reinterpret_cast<TrailingTy1 *>(Obj + 1);
116 static const TrailingTy2 *getTrailingObjectsImpl(const BaseTy *Obj,
117 OverloadToken<TrailingTy2>) {
118 return reinterpret_cast<const TrailingTy2 *>(
119 getTrailingObjectsImpl(Obj, OverloadToken<TrailingTy1>()) +
120 Obj->numTrailingObjects(OverloadToken<TrailingTy1>()));
123 static TrailingTy2 *getTrailingObjectsImpl(BaseTy *Obj,
124 OverloadToken<TrailingTy2>) {
125 return reinterpret_cast<TrailingTy2 *>(
126 getTrailingObjectsImpl(Obj, OverloadToken<TrailingTy1>()) +
127 Obj->numTrailingObjects(OverloadToken<TrailingTy1>()));
131 /// Returns a pointer to the trailing object array of the given type
132 /// (which must be one of those specified in the class template). The
133 /// array may have zero or more elements in it.
134 template <typename T> const T *getTrailingObjects() const {
135 verifyTrailingObjectsAssertions();
136 // Forwards to an impl function with overloads, since member
137 // function templates can't be specialized.
138 return getTrailingObjectsImpl(static_cast<const BaseTy *>(this),
142 /// Returns a pointer to the trailing object array of the given type
143 /// (which must be one of those specified in the class template). The
144 /// array may have zero or more elements in it.
145 template <typename T> T *getTrailingObjects() {
146 verifyTrailingObjectsAssertions();
147 // Forwards to an impl function with overloads, since member
148 // function templates can't be specialized.
149 return getTrailingObjectsImpl(static_cast<BaseTy *>(this),
153 /// Returns the size of the trailing data, if an object were
154 /// allocated with the given counts (The counts are in the same order
155 /// as the template arguments). This does not include the size of the
156 /// base object. The template arguments must be the same as those
157 /// used in the class; they are supplied here redundantly only so
158 /// that it's clear what the counts are counting in callers.
159 template <typename Ty1, typename Ty2,
160 typename std::enable_if<std::is_same<Ty1, TrailingTy1>::value &&
161 std::is_same<Ty2, TrailingTy2>::value,
163 static LLVM_CONSTEXPR size_t additionalSizeToAlloc(size_t Count1, size_t Count2) {
164 return sizeof(TrailingTy1) * Count1 + sizeof(TrailingTy2) * Count2;
167 /// Returns the total size of an object if it were allocated with the
168 /// given trailing object counts. This is the same as
169 /// additionalSizeToAlloc, except it *does* include the size of the base
171 template <typename Ty1, typename Ty2>
172 static LLVM_CONSTEXPR size_t totalSizeToAlloc(size_t Count1, size_t Count2) {
173 return sizeof(BaseTy) + additionalSizeToAlloc<Ty1, Ty2>(Count1, Count2);
177 /// This is the one-type version of the TrailingObjects template. See
178 /// the two-type version for more documentation.
179 template <typename BaseTy, typename TrailingTy1>
180 class TrailingObjects<BaseTy, TrailingTy1, NoTrailingTypeArg>
181 : public TrailingObjectsBase {
183 static void verifyTrailingObjectsAssertions() {
184 static_assert(llvm::AlignOf<BaseTy>::Alignment >=
185 llvm::AlignOf<TrailingTy1>::Alignment,
186 "TrailingTy1 requires more alignment than BaseTy provides");
189 static_assert(LLVM_IS_FINAL(BaseTy), "BaseTy must be final.");
193 static const TrailingTy1 *getTrailingObjectsImpl(const BaseTy *Obj,
194 OverloadToken<TrailingTy1>) {
195 return reinterpret_cast<const TrailingTy1 *>(Obj + 1);
198 static TrailingTy1 *getTrailingObjectsImpl(BaseTy *Obj,
199 OverloadToken<TrailingTy1>) {
200 return reinterpret_cast<TrailingTy1 *>(Obj + 1);
204 template <typename T> const T *getTrailingObjects() const {
205 verifyTrailingObjectsAssertions();
206 return getTrailingObjectsImpl(static_cast<const BaseTy *>(this),
210 template <typename T> T *getTrailingObjects() {
211 verifyTrailingObjectsAssertions();
212 return getTrailingObjectsImpl(static_cast<BaseTy *>(this),
216 template <typename Ty1,
217 typename std::enable_if<std::is_same<Ty1, TrailingTy1>::value,
219 static LLVM_CONSTEXPR size_t additionalSizeToAlloc(size_t Count1) {
220 return sizeof(TrailingTy1) * Count1;
223 template <typename Ty1>
224 static LLVM_CONSTEXPR size_t totalSizeToAlloc(size_t Count1) {
225 return sizeof(BaseTy) + additionalSizeToAlloc<Ty1>(Count1);
229 } // end namespace llvm