Boost logo

Boost :

From: Andrzej Krzemienski (akrzemi1_at_[hidden])
Date: 2021-03-10 20:36:40


Hi Everyone,
Some feedback from playing with Boost.Describe for class types.

1. As others pointed out, it is confusing that BOOST_DESCRIBE_STRUCT() and
BOOST_DESCRIBE_CLASS() have such close names, but their interface and
requirements are so different. Maybe rename the latter to
BOOST_DESCRIBE_THIS_CLASS()?

2. Base class subobjects are in some applications treated equivalently to
member subobject; this is why I found it surprising that I cannot get the
names of base class subobjects. But I guess there are implementation
difficulties. Bases can be qualified names.

3. As with enums, _public_member_descriptor_fn in global namespace is UB or
invalid program as per http://eel.is/c++draft/lex.name#3.2.

4. As others pointed out, the part of the interface that uses enum
modifiers is not intuitive. I think that after a while it starts to make
sense: you decide on your use case (like doing RPC), form your enum
bitmask, and then pass it everywhere. It became more-less clear to me only
after I had a look at the implementation of member_filter:
https://github.com/pdimov/describe/blob/develop/include/boost/describe/members.hpp#L129
Maybe it makes sense to expose this in the docs.

5. Modifier mod_inherited is great for flattening the inheritance structure:

struct X {
    int a;
};

struct Y : X {
    int b;
};

struct Z : Y {
    int c;
};

BOOST_DESCRIBE_STRUCT(X, (), (a));
BOOST_DESCRIBE_STRUCT(Y, (X), (b));
BOOST_DESCRIBE_STRUCT(Z, (Y), (c));

int main() {
    namespace desc = boost::describe;
    using namespace std::literals::string_literals;
    constexpr auto mod = static_cast<desc::modifiers>(desc::mod_public |
desc::mod_inherited);
    using L = desc::describe_members<Z, mod>;
    assert((boost::mp11::mp_at_c<L, 0>::name == "a"s));
    assert((boost::mp11::mp_at_c<L, 1>::name == "b"s));
    assert((boost::mp11::mp_at_c<L, 2>::name == "c"s));
}

But it is not obvious at first. I recommend that the docs provide an
example like this.
It even works when base classes have members with same names:

struct X {
    int a;
};

struct Y {
    int a;
};

struct Z : X, Y {
    int b;
};

BOOST_DESCRIBE_STRUCT(X, (), (a));
BOOST_DESCRIBE_STRUCT(Y, (), (a));
BOOST_DESCRIBE_STRUCT(Z, (X, Y), (b));

int main() {
    constexpr auto mod =
static_cast<describe::modifiers>(describe::mod_public |
describe::mod_inherited);
    using L = describe::describe_members<Z, mod>;

    Z z = {{{3}}, {{6}}, {9}};
    assert((z.*mp_at_c<L, 0>::pointer == 3));
    assert((z.*mp_at_c<L, 1>::pointer == 6));
    assert((z.*mp_at_c<L, 2>::pointer == 9));
}

5. What is the use case for modifier mod_hidden? I cannot think of any
situation where I would need it.

6. Regarding the overloaded functions, I am no expert in preprocessor
metaprogramming, but maybe if there was a way to provide function
signatures along the name, you would have enough information to form the
pointers. Something like:

struct X {
    void f(int){}
    void f(void*){}
};

BOOST_DESCRIBE_STRUCT(X, (), ( SIGNATURE(void f(int)), SIGNATURE(void
f(void*)) );

7. Modifier mod_virtual works unintuitively. It does not affect the
filtering in describe_bases<>. It is only used for decorating the list
elements. Maybe the docs should be more explicit about what it is used for.
Again, is there a use case for this?

Regards,
&rzej;


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk