4

I would like to iterate through a struct which is defined in other library whose source is not under my control. So any lib which requires to define the struct with its own macros/adaptors like previous questions is not usable here. I found the closest way is using boost::hana. However, it still requires to fill up an adaptor before I can iterate through it. I attached an example here. I wonder is there any way I can automate the BOOST_HANA_ADAPT_STRUCT then I do not need to fill up all the struct member names in there (those structs in total have more than hundred members).

#include <iostream>
#include <boost/hana.hpp>
#include <typeinfo>
namespace hana=boost::hana;
struct adapt_test
{
    std::string name;
    int data;
};
BOOST_HANA_ADAPT_STRUCT(
    adapt_test
    , name
    , data
);
auto names = hana::transform(hana::accessors<adapt_test>(), hana::first);
int main() {
    hana::for_each(
        names, 
        [] (auto item)
        {
            std::cout << hana::to<char const *>(item) << std::endl;
        }
    );
    adapt_test s1{"a", 2};
    hana::for_each(
        s1, 
        [] (auto pair)
        {
        std::cout << hana::to<char const *>(hana::first(pair)) << "=" << hana::second(pair) << std::endl;
        }
    );
    return 0;
}

Wang
  • 7,250
  • 4
  • 35
  • 66
  • 1
    If you don't need name, and if structure has some properties (constexpr aggregate initializable and must not contain references nor bitfields), [magic_get](https://github.com/apolukhin/magic_get) might help. – Jarod42 Feb 25 '20 at 16:25
  • Else you can still write a script to generate the adaptator. – Jarod42 Feb 25 '20 at 16:27
  • Fixed `throw`="through". `throw` is a C++ keyword (throwing exceptions) – MSalters Feb 25 '20 at 16:35
  • Does this answer your question? [Iterating over a struct in C++](https://stackoverflow.com/questions/17660095/iterating-over-a-struct-in-c) – Wyck Feb 25 '20 at 16:37
  • @Jarod42 the magic_get is very interesting. – Wang Feb 25 '20 at 17:27
  • @Wyck please read first 2 sentence in my post. That post apparently does not meet those requirement. – Wang Feb 25 '20 at 17:57

2 Answers2

5

You can use Boost Flat Reflection like:

struct adapt_test
{
    std::string name;
    int data;
};
adapt_test s1{"a", 2};

std::cout << boost::pfr::get<0>(s1)  << std::endl;
std::cout << boost::pfr::get<1>(s1)  << std::endl;

boost::pfr::flat_for_each_field(s1, [] (const auto& field) { std::cout << field << std::endl; } );

P.S. Respect for @apolukhin for this library.

Victor Gubin
  • 2,782
  • 10
  • 24
  • The only thing missing is to get the field name. I guess this is rather difficult. Any insight about this? – Wang Feb 25 '20 at 17:59
  • @Wang I'm pretty sure it's not possible. – HolyBlackCat Feb 25 '20 at 18:06
  • It is a cool library, but without names it is nearly useless. – Maxim Egorushkin Feb 25 '20 at 18:33
  • @Wang C++ don't have real reflection yet. If you need names it seems like only solution is construct a wrapper std::tuple< > self-made type meta, i.e. `auto meta = std::make_tuple( std::make_pair("name", s1.name), std::make_pair("data", s1.data)); ` and then use `boost::hana::tuple_for_each` – Victor Gubin Feb 25 '20 at 18:59
1

The basic answer to your question is no.

C++ does not treat identifiers as string literal (it could be indeed useful in some cases), and there is no bridge unfortunately between these kind of strings.

Hopefully, some standard one day will bring this ability, relieving us from having to go through macros or code generation, or maybe doing differently like this: telling "please treat my struct A { int x, y; } as a pair", where the meaning would be to match type of first and second to the members x and y and then building the types so that it works, it would be really useful for tuples as well. A kind of structured template matching.

Currently the best that can be done to my knowledge is to match structs to tuple without the names (as of C++17) because of the above limitation, such as with boost::hana or boost::fusion as you do.

armel
  • 2,497
  • 1
  • 24
  • 30