Search code examples
c++templatesvisual-c++variadic-templatestemplate-meta-programming

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!


Solution

  • 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
     }