Tim Gradwell
Tim Gradwell

Reputation: 2402

Is it possible to print out the size of a C++ class at compile-time?

Is it possible to determine the size of a C++ class at compile-time?

I seem to remember a template meta-programming method, but I could be mistaken...


sorry for not being clearer - I want the size to be printed in the build output window

Upvotes: 72

Views: 24920

Answers (13)

Gabriel Staples
Gabriel Staples

Reputation: 52817

In both C and C++, you can also use an enum and switch case based technique.

See my full answer here: Printing the size of (sizeof()) a type or variable in an error message at compile time in both C and C++.

It allows you to do stuff like this, using my COMPILE_TIME_PRINT_SIZEOF_GLOBAL() and COMPILE_TIME_PRINT_SIZEOF_LOCAL() macros:

#include "sizeof_compile_time_lib.h"

#include <stdbool.h> // For `true` (`1`) and `false` (`0`) macros in C
#include <stdint.h>  // For `uint8_t`, `int8_t`, etc.
#include <stdio.h>   // For `printf()`


typedef struct My_struct_s
{
              // For my X86-64 Linux machine:
    bool b;   // 1 byte + 3 padding bytes
    int i;    // 4 bytes
    float f;  // 4 bytes
    char c;   // 1 byte + 3 padding bytes
    double d; // 8 bytes
} My_struct;  // 24 bytes total

struct My_struct2_s
{
              // For my X86-64 Linux machine:
    bool b;   // 1 byte + 3 padding bytes
    int i;    // 4 bytes
};            // 8 bytes total

COMPILE_TIME_PRINT_SIZEOF_GLOBAL(My_struct);                // 24
COMPILE_TIME_PRINT_SIZEOF_GLOBAL(struct My_struct2_s);      // 8

int main()
{
    printf("Testing 'sizeof_compile_time_lib.h'.\n\n");

    My_struct my_structs[10];
    COMPILE_TIME_PRINT_SIZEOF_LOCAL(My_struct);             // 24
    COMPILE_TIME_PRINT_SIZEOF_LOCAL(struct My_struct2_s);   // 8
    COMPILE_TIME_PRINT_SIZEOF_LOCAL(my_structs);            // 240

    return 0;
}

The output contains lines like this. Notice that This_is_the_size_of_your_type_on__line_82’ contains the line number, and error: case value ‘240’ not in enumerated type contains the size of the type in bytes (240 in this case):

sizeof_compile_time_lib.h:68:13: error: case value ‘240’ not in enumerated type ‘enum This_is_the_size_of_your_type_on__line_82’ [-Werror=switch]
   68 |             case sizeof(variable_or_data_type): \
      |             ^~~~
sizeof_compile_time_lib_test_BEST.c:82:5: note: in expansion of macro ‘COMPILE_TIME_PRINT_SIZEOF_LOCAL’
   82 |     COMPILE_TIME_PRINT_SIZEOF_LOCAL(my_structs);
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

See my full answer for details and definitions of the COMPILE_TIME_PRINT_SIZEOF_GLOBAL() and COMPILE_TIME_PRINT_SIZEOF_LOCAL() macros.

And a reminder: in C++, struct is a class, just with all members defaulting to public instead of private is all, so this should work fine with classes too.

Upvotes: 0

JavaMan
JavaMan

Reputation: 5044

EDITED (3jun2020) This trick works IN ALL C COMPILERS. For Visual C++:

struct X {
    int a,b;
    int c[10];
};
int _tmain(int argc, _TCHAR* argv[])
{
    int dummy;

    switch (dummy) {
    case sizeof(X):
    case sizeof(X):
        break;
    }
    return 0;
}

Example output:

------ Build started: Project: cpptest, Configuration: Debug Win32 ------
cpptest.cpp c:\work\cpptest\cpptest\cpptest.cpp(29): error C2196: case value '48' already used
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

For other compilers that only print "duplicate case value", see my answer to this question: How can I print the result of sizeof() at compile time in C?

Upvotes: 7

Thomas67
Thomas67

Reputation: 11

In g++ one can use option "-fdump-lang-class". Then g++ creates a new output file with extension .class, which contains the sizes of all classes defined in the compiled unit.

Upvotes: 1

ahcox
ahcox

Reputation: 9970

Compile-time sizeof as a Warning so Compilation can Continue

Here is a version which produces a warning rather than an error:

    /** Compile-time sizeof as a warning so
        compilation can continue */

    struct TestStruct
    {
      int i1;
      float f1;
      const char* pchar1;
      double d1;
      char c1;
      void* pv1;
      bool b1;
    };


    template<unsigned int n>
    struct PrintNum {
        enum { value = n };
    };

    template<int number> 
    struct _{ operator char() { return number + 256; } };

    #define PRINT_AS_WARNING(constant) char(_<constant>())    

    int main() 
    {
        PRINT_AS_WARNING(PrintNum<sizeof(TestStruct)>::value);
        return 0;
    }

See it running on Godbolt. As an aside, you can read the size(48) right out of the assembly there:

leaq    -1(%rbp), %rax
movq    %rax, %rdi
call    _<48>::operator char()
movl    $0, %eax
leave
ret

Upvotes: 1

Viatorus
Viatorus

Reputation: 1903

I developed a tool, named compile-time printer, to output values and types during compilation.

You can try it online under: https://viatorus.github.io/compile-time-printer/

The repository can be found here: https://github.com/Viatorus/compile-time-printer

To get the size of any type as output would be:

constexpr auto unused = ctp::print(sizeof(YourType));

Upvotes: 0

aalpern
aalpern

Reputation: 662

To answer the updated question -- this may be overkill, but it will print out the sizes of your classes at compile time. There is an undocumented command-line switch in the Visual C++ compiler which will display the complete layouts of classes, including their sizes:

That switch is /d1reportSingleClassLayoutXXX, where XXX performs substring matches against the class name.

https://devblogs.microsoft.com/cppblog/diagnosing-hidden-odr-violations-in-visual-c-and-fixing-lnk2022/

Upvotes: 13

MattCochrane
MattCochrane

Reputation: 3090

This macro is based off grep's answer. Define the macro like below:

#define COMPILE_TIME_SIZEOF(t)      template<int s> struct SIZEOF_ ## t ## _IS; \
                                    struct foo { \
                                        int a,b; \
                                    }; \
                                    SIZEOF_ ## t ## _IS<sizeof(t)> SIZEOF_ ## t ## _IS;

Then use it like this:

COMPILE_TIME_SIZEOF(long);

And you'll get an output similar to below:

error: 'SIZEOF_long_IS<4> SIZEOF_long_IS' redeclared as different kind of symbol
                                         SIZEOF_ ## t ## _IS<sizeof(t)> SIZEOF_ ## t ## _IS;

Still a bit of a workaround, but easy enough to use.

Upvotes: 3

tonso
tonso

Reputation: 1768

This is the snippet, that I use:

template <typename T>
void get_sizeof() {
    switch (*((int*)0x1234)) { case sizeof(T): case sizeof(T):; }
}

To get the size, instantiate the function anywhere in the code, for example, within a statement:

struct S { long long int ill; };
get_sizeof<S>;

The error will look like:

error: duplicate case value '8'
switch (*((int*)0x1234)) { case sizeof(T): case sizeof(T):; }
                                                ^

Upvotes: 0

JavaMan
JavaMan

Reputation: 5044

Yet, another trick causing the VC++2010 compiler to complain about incorrect use of compile time integer:

// cpptest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
struct X {
    int a[11];
    char c[2];
};
void proc1(void* s[1]) {
}
int _tmain(int argc, _TCHAR* argv[])
{
    int b[sizeof(X)];
    proc1(b);
    return 0;
}

1>------ Build started: Project: cpptest, Configuration: Release Win32 ------ 1> cpptest.cpp 1>cpptest.cpp(14): error C2664: 'proc1' : cannot convert parameter 1 from 'int [48]' to 'void *[]' 1>
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast ========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Hence sizeof (struct X) is 48. This also works for C code.

Upvotes: 0

elcuco
elcuco

Reputation: 9208

Whats wrong with sizeof? This should work on objects and classes.

void foo( bar* b )
{
  int i = sizeof bar;
  int j = sizeof *b;

  // please remember, that not always i==j !!!
}

Edit:

This is the example I was thinking of, but for some reason it's not working. Can anyone tell me what's wrong?

#include <iostream>
using namespace std;
class bar {
public: int i;
        bar( int ii ) { i = ii; }
        virtual ~bar(){ i = 0; }
        virtual void d() = 0;
};

class bar2: public bar {
public: long long j;
        bar2( int ii, long long jj ):bar(ii){ j=jj; }
        ~bar2() { j = 0; }
        virtual void d() { cout <<  "virtual" << endl; };
};

void foo( bar *b )
{
        int i = sizeof (bar);
        int j = sizeof *b;
        cout << "Size of bar = " << i << endl;
        cout << "Size of *b  = " << j << endl;
        b->d();
}


int main( int arcc, char *argv[] )
{
        bar2 *b = new bar2( 100, 200 );
        foo( b );
        delete b;
        return 0;
}

The application been run on linux (gcc 4.4.2):

[elcuco@pinky ~/tmp] ./sizeof_test
Size of bar = 8
Size of *b  = 8
virtual

Upvotes: 4

Maxim Razin
Maxim Razin

Reputation: 9466

If you really need to to get sizeof(X) in the compiler output, you can use it as a parameter for an incomplete template type:

template<int s> struct Wow;
struct foo {
    int a,b;
};
Wow<sizeof(foo)> wow;

$ g++ -c test.cpp
test.cpp:5: error: aggregate ‘Wow<8> wow’ has incomplete type and cannot be defined

Upvotes: 148

Gaim
Gaim

Reputation: 6844

There is operator sizeof( int ), sizeof( char ) so I think that it is possible and call probably look like sizeof( MyClass )

Upvotes: 0

R Samuel Klatchko
R Samuel Klatchko

Reputation: 76541

sizeof() determines the size at compile time.

It doesn't work until compile time, so you can't use it with the preprocessor.

Upvotes: 2

Related Questions