Boost logo

Boost :

From: Maximilian Riemensberger (riemaxi_at_[hidden])
Date: 2021-03-02 20:16:48


Hi,

Thank you, Peter, for sharing this library.

While reading through the documentation of describe and considering how to use it in a C++14 code base a few questions came to my mind.

1) I saw that describe does not support describing reference member variables. Is there a fundamental limitation that prohibits this? We have quite a few `boost::asio::io_context&` members or similar members. (Yes, we haven't found time to update them to executor members.) We wouldn't be able to describe those.

2) Overloaded functions are not supported. Again, what are the reasons for this limitation? Is it fundamentally impossible?

3) The first and obvious use case is clearly enums. They get a pretty big share of the examples as well. However, Just describing the enums to have a name value descriptor

template<class D> struct enum_descriptor
{
     // can't use auto here because of the need to supply the definitions below
     static constexpr decltype(D::value()) value = D::value();
     static constexpr decltype(D::name()) name = D::name();
};

is only part of what would be generally useful.

Is it possible to support attaching extra data to the enum or their descriptor. As an example consider an error code enum. Without describe we would have something like

enum class my_errors {
     success,
     some_thing_went_wrong,
     invalid_path,
     wrong_argument,
     unknown_error
};

char const* my_errors_s[5] = {
     "success",
     "some thing went wrong",
     "invalid path",
     "wrong argument",
     "unknown error"
};

std::errc my_errors_map[] = {
     0,
     std::errc::io_error,
     std::errc::file_or_directory_not_found,
     std::errc::invalid_argument,
     std::errc::io_error
};

With describe I can easily get rid of the `my_errors_s` array. But even this is only the case if the string can be programmatically computed from the names. Even better would be if I could just attach arbitrary strings and the mapping info as well. So I would be looking for something like

enum class my_errors {
     success,
     some_thing_went_wrong,
     invalid_path,
     wrong_argument,
     unknown_error
};

BOOST_DESCRIBE_ENUM_CLASS_WITH_DATA(
     my_errors,
     std::errc,
     (success, 0),
     (some_thing_went_wrong, std::errc::io_error),
     (invalid_path, std::errc::file_or_directory_not_found),
     (wrong_argument, std::errc::invalid_argument),
     (unknown_error, std::errc::io_error),
};

which would create descriptors like

template<class D> struct enum_descriptor
{
     // can't use auto here because of the need to supply the definitions below
     static constexpr decltype(D::value()) value = D::value();
     static constexpr decltype(D::name()) name = D::name();
     static constexpr decltype(D::data()) data = D::data();
};

Another similar example would be associating message types with ids, e.g.

enum class message {
     Hello,
     Bye,
     Ping,
     Pong,
...
};

BOOST_DESCRIBE_ENUM_CLASS_WITH_TYPE(
     message,
     (Hello, HelloMessage),
     (Bye, ByeMessage),
     (Ping, EchoMessage),
     (Pong, EchoMessage),
...
};

Is something like this possible and withing scope? With enums I find myself often in the situation that there is some kind of extra to be attached with it.

I can understand if something like this is not considered within the scope of describe. However, from a user perspective it would make less attractive if I have to define the enum, descibe it, and then repeat it to some other piece of code again to attach the extra data (or put the extra data into (a) separate array(s).

4) I like that there are quite a few examples that show how to get and use the information from describe. However, when looking at those, I think that a few convenience helpers to easily iterate at compile time or at runtime would be nice (even if its in the end just a simple wrapper calling mp_for_each and describe_*. For example the runtime printing example which essentially creates an array of value,name pairs needs quite a lot boilerplate

template<class E> struct enum_descriptor
{
     E value;
     char const * name;
};

template<class E, template<class... T> class L, class... T>
   constexpr std::array<enum_descriptor<E>, sizeof...(T)>
     describe_enumerators_as_array_impl( L<T...> )
{
     return { { { T::value, T::name }... } };
}

template<class E> constexpr auto describe_enumerators_as_array()
{
     return describe_enumerators_as_array_impl<E>( boost::describe::describe_enumerators<E>() );
}

that seems usefult to me to provide it as part of the library itself (in a separate header so that you only include it if you need it).

I have the same impression with the simple mapping methods like enum_to_string and string_to_enum, but also member_to_pointer and the opposite direction, etc. Those look very fundamental to me. And they could ease the user experience for lots of simple and standard use cases for describe.

Cheers
Max


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