HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Return sizeof a struct containing members ordered like given template parameters

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
containingstructtemplatereturnlikesizeofmembersparametersgivenordered

Problem

template
constexpr std::size_t sizeof_struct()
{
    // Helper
    auto alignto = [](auto x, auto a) { return (x + a - 1) / a * a; };
    // Algorithm
    std::size_t max_align{0};
    std::array, sizeof...(T)> arr{{
        {alignof(T), sizeof(T)}...
        }};
    auto sum = std::accumulate(
        std::begin(arr), std::end(arr), std::size_t{0},
        [&max_align, alignto] (auto s, auto&& it) {
            max_align = std::max(it.first, max_align);
            return alignto(s, it.first) + it.second;
        });
    return max_align ? alignto(sum, max_align) : 1;
}


Is there any type I can put in the parameter pack T... that can break this code? Anything else that doesn't look right? Anything I can clean or make more concise?

Solution

Looks reasonable to me.

I don't understand why you keep sum.first and sum.second as separate values; IIUC, sum.first is "the size of the struct so far, minus its last element", and sum.second is "the size of the last element". It seems like you could just keep "the size of the struct so far" as a single value. That is, isn't your code equivalent to the following?

template
constexpr std::size_t sizeof_struct()
{
    using meta_t = std::pair;
    auto alignto = [](auto x, auto a) { return (x + a - 1) / a * a; };
    size_t sum{0};
    std::size_t max_align{0};
    std::array arr{{ {alignof(T), sizeof(T)}... }};
    for (auto&& it : arr) {
        sum = alignto(sum, it.first) + it.second;
        max_align = std::max(it.first, max_align);
    }
    return sum ? alignto(sum, max_align) : 1;
}


Or if not, why not?

In the above version I also "corrected" your brace placement on the std::array initializer; I think it's more idiomatic (and in this case specifically, more readable) to bundle together the outer pair of braces {{ {x}... }} as opposed to staggering them out as
{ {{x}...} }.

Also for (x : y) is more idiomatic than for (x: y), and you had a trailing redundant semicolon on the for-loop's body.

If you're looking for template type parameters that break the above code, try sizeof_struct. Your code returns 1, but obviously it should return sizeof(char*). You can solve that easily, though.

And of course you have no guarantee that the compiler's struct layout algorithm is equivalent to the one you've written. It almost certainly is (modulo base classes, vptrs, bitfields, and most importantly the fact that the compiler can reorder public and private data members)... but technically you have no portable way to know that.

Code Snippets

template<typename... T>
constexpr std::size_t sizeof_struct()
{
    using meta_t = std::pair<std::size_t, std::size_t>;
    auto alignto = [](auto x, auto a) { return (x + a - 1) / a * a; };
    size_t sum{0};
    std::size_t max_align{0};
    std::array<meta_t, sizeof...(T)> arr{{ {alignof(T), sizeof(T)}... }};
    for (auto&& it : arr) {
        sum = alignto(sum, it.first) + it.second;
        max_align = std::max(it.first, max_align);
    }
    return sum ? alignto(sum, max_align) : 1;
}

Context

StackExchange Code Review Q#102271, answer score: 4

Revisions (0)

No revisions yet.