Let's say I have a class Base
, which has N
children, Derived0, Derived1, ... ,DerivedN
. I'm trying to create a pool allocator for objects inheriting from Base
, and the implementation relies on knowing the size of the largest child of Base
, since the pools much each be large enough to contain one of those objects. Here's a simple example with trivial classes and N = 2
. In reality, N
may be larger and Base
's children may not be simple POD classes.
class Base {
public:
virtual ~Base() = 0;
};
class Derived0 : public Base {
int a;
};
class Derived1 : public Base {
int a, b;
};
class Derived2 : public Base {
int a, b, c;
};
So let's say that I want to create a buffer large enough to store 10 children. I've been banging my head on this for a while, and this is all I can come up with:
static const size_t NUMBER_OF_POOLS = 10;
static const size_t LARGEST_CHILD_SIZE =
sizeof(Derived0) > sizeof(Derived1) ?
sizeof(Derived0) > sizeof(Derived2) ?
sizeof(Derived0) :
sizeof(Derived2) :
sizeof(Derived1) > sizeof(Derived2) ?
sizeof(Derived1) :
sizeof(Derived2);
char buffer[NUMBER_OF_POOLS * LARGEST_CHILD_SIZE];
This works, but it's pretty easy to see how this gets pretty cluttered as N
starts to grow. Is there any scalable way to implement this, where you don't need to manually build a nested "unary tree" (for lack of a better term) which grows into a giant mess as N
increases?
Here are the constraints/environment I'm working with:
Any help/suggestions are appreciated.
You could consider using a boost::variant
and take its size:
sizeof (boost::variant< Derived0, Derived1, Derived2 > )
This will return the size of the largest element.
By the way, it could also simplify your buffer management, by making it a buffer of this variant, and indexing directly the right element. The good news is that boost takes then care of alignment requirements:
typedef boost::variant< Derived0, Derived1, Derived2 > DerivedVar;
DerivedVar buffer[NUMBER_OF_POOLS];
Works only for POD classes:
union AllDerived {
Derived0 _0;
Derived1 _1;
Derived2 _2;
};
static const size_t LARGEST_CHILD_SIZE = sizeof(AllDerived);
static const size_t NUMBER_OF_POOLS = 10;
char buffer[NUMBER_OF_POOLS * LARGEST_CHILD_SIZE];
And this solution works not only for POD:
template <int Value1, int Value2>
struct static_max {
static const int value = (Value1 < Value2) ? Value2 : Value1 ;
};
template<typename T, typename U>
struct TypeList {
typedef T Head;
typedef U Tail;
};
class NullType {};
template
<
typename T1 = NullType, typename T2 = NullType, typename T3 = NullType,
typename T4 = NullType, typename T5 = NullType, typename T6 = NullType,
typename T7 = NullType, typename T8 = NullType, typename T9 = NullType,
typename T10 = NullType, typename T11 = NullType, typename T12 = NullType,
typename T13 = NullType, typename T14 = NullType, typename T15 = NullType,
typename T16 = NullType, typename T17 = NullType, typename T18 = NullType
>
struct MakeTypelist
{
private:
typedef typename MakeTypelist
<
T2 , T3 , T4 ,
T5 , T6 , T7 ,
T8 , T9 , T10,
T11, T12, T13,
T14, T15, T16,
T17, T18
>
::Result TailResult;
public:
typedef TypeList<T1, TailResult> Result;
};
template<>
struct MakeTypelist<>
{
typedef NullType Result;
};
template<typename TList>
struct MaxTypeSize;
template <>
struct MaxTypeSize<NullType> {
enum { value=0 };
};
template<typename T, typename U>
struct MaxTypeSize<TypeList<T,U>> {
enum { value = static_max<sizeof(T), MaxTypeSize<U>::value>::value };
};
typedef MakeTypelist<Derived0, Derived1, Derived2>::Result AllTypes;
static const size_t LARGEST_CHILD_SIZE = MaxTypeSize<AllTypes>::value;
static const size_t NUMBER_OF_POOLS = 10;
char buffer[NUMBER_OF_POOLS * LARGEST_CHILD_SIZE];
Here we use type list and compile-type max function. Type list implementation you can find in Loki library. Compile-time function MaxTypeSize calculate maximum type size in list.
Modify your code by introducing a helper macro, resulting better code format. When a new derived class is added, just insert a STATIC_MAX(sizeof(...
line and add a )
. This works in C++03.
#define STATIC_MAX(a, b) ((a) > (b) ? (a) : (b))
static const size_t LARGEST_CHILD_SIZE =
STATIC_MAX(sizeof(Derived0),
STATIC_MAX(sizeof(Derived1),
STATIC_MAX(sizeof(Derived2),
0)));
And if C++1x compiler is an option, you can use "variadic template" and "constexpr function" to get a more simple solution. When a new derived class is added, just insert the class name as template parameter in the last line.
template <typename T>
static constexpr T static_max(T a, T b) {
return a < b ? b : a;
}
template <typename T, typename... Ts>
static constexpr T static_max(T a, Ts... bs) {
return static_max(a, static_max(bs...));
}
template <typename... Ts>
constexpr size_t max_sizeof() {
return static_max(sizeof(Ts)...);
};
static constexpr size_t LARGEST_CHILD_SIZE =
max_sizeof<Derived0, Derived1, Derived2>();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With