More TMP with boost::mp11

published at 20.03.2018 23:05

A short blog post on 3 little functions I've written with mp11, to show a bit more how one can work with mp11. The first two are related to working with tags, the last is an easy way to get the member names of a fusion adpated struct into an std::array.

My last blogpost focused on showing some of the basics in calculating types, and old + new TMP techniques.

The background for these functions is, that I'm working on a Qt Model for fusion adapted structs, thanks to verdigris this can even be a template. But work on that isn't finished. A few years ago in the 3rd part of my Qt Tutorial I gave a good overview on Qt Model/View, so this could give you a glipse on what this all is going to get combined with. When working on this code, I realized that a way to hide members from a struct is needed, and as this will all be done via tag lists, I decided to simply add a "NoEditOrDisplay" tag to the few tags I use for testing. With this two new functions were needed: first a function to calculate the columns needed for the model:

template< class ...Tags >
constexpr size_t count_editable_tags()
    return sizeof...(Tags) -  boost::mp11::mp_count<boost::mp11::mp_list<Tags...>,uitags::NoEditOrDisplay >::value;

There is different ways to achieve this, here I use mp_count to count the tags of NoEditOrDisplay, which I substract from the size of the template parameter pack. Mp11 offers a mp_count_if 'function', but I couldn't figure out how to get this working, probably because doing TMP as a late night thing isn't the best.

Update: on the next morning I did get mp_count_if to work, here is the code:

template< class T>
using is_not_NoEditNoDisplay = typename std::integral_constant< bool,!std::is_same< T,NoEditOrDisplay>::value>;

template< class ...Tags>
constexpr size_t count_editable_tags()
    return boost::mp11::mp_count_if< boost::mp11::mp_list< Tags...>, is_not_NoEditNoDisplay>::value; 

Where in C++17 you could replace std::integral_constant with either bool_constant or even std::negation.

Models in Qt are often index based, e.g. columns and rows are ints. This means, when I want to hide a column, I have to calculate a column index, translating the models index into the actual types index in the fusion sequence. This is what this code does:

template< class ...Tags, typename R = std::array<size_t,  boost::mp11::mp_size< boost::mp11::mp_remove< boost::mp11::mp_list< Tags...>,uitags::NoEditOrDisplay> >::value>>
constexpr R make_edit_index_array()
    R index_array{};
    int x =0;
    using taglist = boost::mp11::mp_list< Tags...>;
    boost::mp11::mp_for_each< boost::mp11::mp_iota_c< sizeof...(Tags)>>(
                [&]( auto I ){
                  if(!std::is_same< boost::mp11::mp_at_c< taglist,I>,uitags::NoEditOrDisplay>::value)
                      index_array[x++]= I;
                } );
    return index_array;

Again, the count is needed, this time its achieved with removing the not-to-count members from the list, and then taking the size of the type list. I have to check if the current index is not a NoEditOrDisplay tag. If thats the case I assign the index to index_array and increment the counter. This works as mp_for_each lets me visit the indexes created with mp_itoa_c, so that the generic lambda is called once for each index.

A similar function creates an std::array with the member names of a fusion adapted struct:

template< class Seq>
constexpr std::array< const char*,boost::fusion::result_of::size< Seq>::value> get_member_names()
    std::array< const char*,boost::fusion::result_of::size< Seq>::value> members{};
    boost::mp11::mp_for_each< boost::mp11::mp_iota_c< boost::fusion::result_of::size< Seq>::value>>(
                [&]( auto I ){
                  members[I]=boost::fusion::extension::struct_member_name< Seq,I>::call();
                } );
    return members;

These member names are again needed by the model, as they end up becoming the header text for each column.


Join the Meeting C++ patreon community!
This and other posts on Meeting C++ are enabled by my supporters on patreon!