Roger Law
Roger Law

Reputation: 25

How to find the largest possible sizeof child class

Given a class, I would like to find the largest sizeof() all child classes of it in compile-time. In this case, you will need to properly define the value of B::BIGGEST_TYPE_SIZE, preferably in the class itself.

It is possible to do so in a separate chunk of code with the usage of std::max() as shown in the last line, but it's some what duplicate code and unelegant, as I will have to continuously modify that line as more classes inherit from B.

I would like a nice scalable solution instead.

struct B 
{
    static const int BIGGEST_TYPE_SIZE;
};

struct D1 : public B
{
    int i;
};

struct D2 : public B
{
    std::vector<int> vec;
};

struct D3 : public B
{
    std::string s;
};

const int B::BIGGEST_TYPE_SIZE = std::max(sizeof(D1), std::max(sizeof(D2), sizeof(D3)));

The value of BIGGEST_TYPE_SIZE should be "32", due to std::string.

Any elegant solutions for this? The sexier the templates, the better. Thanks!

Upvotes: 1

Views: 281

Answers (2)

max66
max66

Reputation: 66200

It is possible to do so in a separate chunk of code with the usage of std::max as shown in the last line, but it's some what duplicate code and unelegant, as I will have to continuously modify that line as more classes inherit from B.

I would like a nice scalable solution instead.

Unfortunately, I don't know a way to automatically know all derived types (I don't think it's possible) so I fear that you needs "to continuously modify that line as more classes inherit form B".

In LogicStuff's answer you see an elegant way to simplify that line and I also remember that exist the std::max() version that receive a std::initializer_list (constexpr starting from C++14) so you can also write (but the biggest_size_v way is better, IMHO)

const int B::BIGGEST_TYPE_SIZE
   = std::max({sizeof(D1), sizeof(D2), sizeof(D3)});

avoiding the multiple std::max() calls.

A little off topic, I suppose, but I propose you a semi-automatic way to check, compile-time, that B::BIGGEST_TYPE_SIZE is bigger (or equal) to the sizeof() of all derived types (all instantiated derived type, at least).

If you modify B adding a constructor with a static_assert() in it (or SFINAE enabled, if you prefer)

struct B 
 {
   static const int BIGGEST_TYPE_SIZE;

   template <std::size_t DerSize>
   B (std::integral_constant<std::size_t, DerSize>)
    { static_assert( DerSize <= BIGGEST_TYPE_SIZE, "!" ); }
 };

and add a template C struct that inherit from B

template <typename Der>
struct C : public B
 {
   C() : B{std::integral_constant<std::size_t, sizeof(Der)>{}}
    { }
 };

if you modify your Dx classes to inheriting B passing through C<Dx> (so using CRTP)

struct D1 : public C<D1>
 { int i; };

struct D2 : public C<D2>
 { std::vector<int> vec; };

struct D3 : public C<D3>
 { std::string s; };

you auto-magically enable the compile-time check inside B constructor.

So if you add, by example, the following D4 class

struct D4 : public C<D4>
 { int a[42]; };

and forget to modify the BIGGEST_TYPE_SIZE initialization adding sizeof(D4) in the list, declaring a D4 object you get a compilation error

D4 d4; // compilation error

The following is a full compiling example

#include <vector>
#include <iostream>
#include <algorithm>

struct B 
 {
   static const int BIGGEST_TYPE_SIZE;

   template <std::size_t DerSize>
   B (std::integral_constant<std::size_t, DerSize>)
    { static_assert( DerSize <= BIGGEST_TYPE_SIZE, "!" ); }
 };

template <typename Der>
struct C : public B
 {
   C() : B{std::integral_constant<std::size_t, sizeof(Der)>{}}
    { }
 };

struct D1 : public C<D1>
 { int i; };

struct D2 : public C<D2>
 { std::vector<int> vec; };

struct D3 : public C<D3>
 { std::string s; };

struct D4 : public C<D4>
 { int a[42]; };

const int B::BIGGEST_TYPE_SIZE
   = std::max({sizeof(D1), sizeof(D2), sizeof(D3)}); // <-- sizeof(D4) forgotten !!!

int main ()
 {
   D1 d1;
   D2 d2;
   D3 d3;
   // D4 d4;  compilation error
 }

Upvotes: 1

LogicStuff
LogicStuff

Reputation: 19607

Take for example std::variant, it knows its size from the template arguments. Your best shot is also to use a variadic template. First, you implement the variadic max function template, then you use it:

template <typename ... Ts>
constexpr bool biggest_size_v = max(sizeof(Ts)...);

If you're asking to automatically get a list of all derived classes at compile-time. You can't. You still have to list them:

const int B::BIGGEST_TYPE_SIZE = biggest_size_v<D1, D2, D3>;

Upvotes: 2

Related Questions