4

I'm trying to create a function that returns the total size of a parameter pack in bytes, and am having trouble finding a solution!

template <typename... TTypes>
struct Test {
  constexpr size_t getPackSizeInBytes() const {
   // sizeof(TTypes) gives `unexpanded pack` error
  }
};

// What I'm hoping for:
Test<uint8, uint16> test;
std::cout << test.getPackSizeInBytes() << std::endl;
// Should output 3;

Thanks for your help!

  • @songyuanyao why did you delete your answer? – Martin York Jul 29 '19 at 02:43
  • @MartinYork I think OP wants to call `sizeof` on every type and get the summary. – songyuanyao Jul 29 '19 at 02:48
  • When you say `// Should output 3;` you mean should be `sizeof(uint8) + sizeof(uint16)`. I don't want to assume the size of non standard types. Though `std::uint8_t` and `std::uint16_t` should be 24 bits. But that does not mean a byte is 8 bits. You would need to check `CHAR_BITS`. – Martin York Jul 29 '19 at 02:53
  • 2
    Please do note that computing the sum of individual sizes of types will often not yield the same value as a structure that encapsulates those same types as members. Alignment requirements will often mean that such a structure is bigger than its individual components. – Richard Hodges Jul 29 '19 at 06:20
  • 1
    Re above comment: https://coliru.stacked-crooked.com/a/26b84adb0d021345 – Richard Hodges Jul 29 '19 at 07:10

3 Answers3

4

You can use a unary fold in C++17:

return (sizeof(TTypes) + ... + 0);

If you don't have C++17 then you have to unpack it in a more manual, albeit uglier way:

int sum = 0;
using I = std::size_t[];
(void)(I{0u, sum += sizeof(TTypes)...});
return sum;
David G
  • 94,763
  • 41
  • 167
  • 253
  • 1
    Why not the binary fold `(sizeof(TTypes) + ... + 0)` ? What if the parameter pack is empty? – JFMR Jul 29 '19 at 09:30
2

Does this do what you want:

// Pre C++17 code (not sure but I belive it is easier in C++17)
#include <iostream>
#include <numeric>

template<typename... TTypes>
struct Test
{
    constexpr size_t getPackSizeInBytes() const
    {
        size_t data[] = {sizeof(TTypes)...};
        return std::accumulate(std::begin(data), std::end(data), 0);
    }
};

int main() 
{
    Test<std::uint8_t, std::uint16_t> test;
    std::cout << test.getPackSizeInBytes() << "\n";
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • Yes! Thank you for your response! Would you mind telling me what the `{sizeof(TTypes)...}` syntax is so I can look it up? – Dukuna_Matata Jul 29 '19 at 03:26
  • @Dukuna_Matata the elipses `...` simply mean expand the parameter pack as just used. So the `sizeof(TTypes)` part gets expanded once for each of the template parameters (comma separated). – Martin York Jul 29 '19 at 16:28
0

Alternate (much portable, but recursive) solution, based on this answer, not even "Pre C++17", but maybe even "Pre C++11" :) (Works even in Arduino, without any dependencies)

template <typename ... Args> 
struct count_bytes;

template <>
struct count_bytes<> {
    constexpr static size_t value = 0u;
};

template <typename T, typename... Args>
struct count_bytes<T, Args...> {
    constexpr static size_t value = sizeof(T) + count_bytes<Args...>::value;
};

// ----------------------------------------------------------------

// Compile-time testing
static_assert(count_bytes<int8_t, int16_t>::value             ==  3, "Test failed");
static_assert(count_bytes<int8_t, int16_t, int32_t>::value    ==  7, "Test failed");
static_assert(count_bytes<float, float, float, double>::value == 20, "Test failed");

// Test for known-size fixed array
static_assert(count_bytes<int, int[2]>::value                 == 12, "Test failed");

// Attention: sizeof(void) works for C, but not for C++. Reference: https://stackoverflow.com/a/1666232/
// static_assert(count_bytes<void>::value                     ==  1, "Test failed");
Inobelar
  • 59
  • 8