1

I'm trying to use Boost MPL and Fusion to calculate the size of a struct exclusive of any padding. This is my current best attempt:

Live example

template<class T>
constexpr std::size_t sizeof_members(void)
{
    using namespace std;
    namespace mpl = boost::mpl;
    namespace fusion = boost::fusion;

    //This works, but only for structs containing exactly 4 members...
    typedef typename mpl::apply<mpl::unpack_args<mpl::vector<mpl::_1, mpl::_2, mpl::_3, mpl::_4>::type >, T>::type member_types;

    typedef typename mpl::transform<member_types, mpl::sizeof_<mpl::_1> >::type member_sizes;
    typedef typename mpl::accumulate<member_sizes, mpl::int_<0>, mpl::plus<mpl::_1, mpl::_2> >::type sum;
    return sum();
}

BOOST_FUSION_DEFINE_STRUCT(
    (), Foo_t,
    (std::uint8_t,  a)
    (std::uint16_t, b)
    (std::uint32_t, c)
    (std::uint64_t, d)
);
static_assert(sizeof_members<struct Foo_t>() == 15);

int main()
{
    std::cout << "sizeof_members = " << sizeof_members<struct Foo_t>() << std::endl;
    std::cout << "sizeof = " << sizeof(struct Foo_t) << std::endl;

    return 0;
}

Expected output:

sizeof_members<struct Foo_t>() = 15
sizeof(struct Foo_t) = 16

I can transform a Sequence of types to a Sequence of integers containing the size of each type, and I can compute the sum over that Sequence, but I'm having trouble with the first step of turning the struct into a Sequence of types. The Fusion docs say that BOOST_FUSION_DEFINE_STRUCT generates boilerplate to define and adapt an arbitrary struct as a model of Random Access Sequence, which I believe should be compatible with mpl::transform, however there seems to be some glue code that I'm missing to make this work. My current approach using mpl::unpack_args works but only for structs with exactly four fields.

How can I extend this to arbitrary structs with more or fewer fields?

Barry
  • 286,269
  • 29
  • 621
  • 977
bhillam
  • 193
  • 6
  • `How can I extend this to arbitrary structs?` What are "arbitrary structs"? You mean, to structs not declared using `BOOST_FUSION`? You can't - C++ doesn't have reflection. – KamilCuk Jun 02 '20 at 09:42
  • Actually, you can do it at least for AggregateTypes in C++14 and C++17. The idea is, you can count the max. number of arguments you can pass to the default aggregate initialization (SFINAE). Then you can delegate to a structural decomposition and you have the types. – lorro Jun 02 '20 at 10:11
  • 1
    @KamilCuk any struct adapted with Boost Fusion but with more or fewer fields. My current implementation only works for the specific example given with four fields. – bhillam Jun 02 '20 at 10:34

1 Answers1

4

Since you tagged this C++17, the answer is: don't use Boost.MPL. Given C++11 even, the metaprogramming library you want is Boost.Mp11 - it's substantially better in all respects.

The newer, easier-to-use, ridiculously-more-compile-time-efficient version of Boost.Fusion that you want to use is Boost.Hana:

struct Foo_t {
    BOOST_HANA_DEFINE_STRUCT(Foo_t,
        (std::uint8_t, a),
        (std::uint16_t, b),
        (std::uint32_t, c),
        (std::uint64_t, d)
    );
};

And the reason you want to use Boost.Hana is because sizeof_members (for any number of members) can be written as:

template <typename T>
constexpr auto sizeof_members() -> size_t
{
    return hana::fold(hana::accessors<T>(), size_t{},
        [](size_t s, auto mem){
            return s + sizeof(hana::second(mem)(std::declval<T>()));
        });
}

static_assert(sizeof_members<Foo_t>() == 15);

This mostly reads exactly like what you actually want to do: you want to fold over all the members, starting at 0, with an accumulation function that adds the size of the next member (accessors<T>() gives you a Sequence of pairs where the first is the name of the accessor and the second is a function that takes an object and returns that member).

Demo.

Barry
  • 286,269
  • 29
  • 621
  • 977