patterncppMinor
Return sizeof a struct containing members ordered like given template parameters
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
Or if not, why not?
In the above version I also "corrected" your brace placement on the
Also
If you're looking for template type parameters that break the above code, try
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.
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.