boostorg / pfr

std::tuple like methods for user defined types without any macro or boilerplate code
https://boost.org/libs/pfr
Boost Software License 1.0
1.34k stars 161 forks source link

Iterate over aggregate fields with the name #171

Closed Baduit closed 2 months ago

Baduit commented 6 months ago

Add a new function for_each_field_with_name

The goal is to be able to do this:

struct Toto
{
    int a;
    char c;
};

Toto t {5, 'c'};
auto cb = [](std::string_view name, const auto& value){ std::cout << "Name: " << name << " Value: " << value << std::endl; };
boost::pfr::for_each_field_with_name(t, cb);

I think this is more user friendly than we way it can be done without this PR:

auto cb2 = []<std::size_t Index>(const auto& value, std::integral_constant<std::size_t, Index> i)
{
    std::cout << "Name: " << boost::pfr::get_name<Index, Toto>() << " Value: " << value << std::endl;
};
boost::pfr::for_each_field(t, cb2);

I tried to make the code of for_each_field_with_name as close as possible to for_each_field.

I will add new tests if you think this feature could be merged in the future.

I have explored a bit the idea of completing for_each_field instead of creating a new function in this branch https://github.com/Baduit/pfr/tree/feature/for_each_field_can_provide_name, most of the work is done and it makes possible to do stuff like this:

void plop () {
    std::map<std::string, std::string> m;
    auto fill = [&m](std::string_view name, const auto& value){
        m[std::string(name)] = value;
    };

    boost::pfr::for_each_field(SimpleStruct{ 'e', "test"}, fill);

    assert(m.size() == 2);
    assert(m["c"] == "e");
    assert(m["str"] == "test");
}

void plop2 () {
    std::map<std::string, std::string> m;
    std::map<std::string, std::size_t> mi;
    auto fill = [&m, &mi](std::string_view name, const auto& value, std::size_t i){
        m[std::string(name)] = value;
        mi[std::string(name)] = i;
    };

    boost::pfr::for_each_field(SimpleStruct{ 'e', "test"}, fill);
    assert(m.size() == 2);
    assert(m["c"] == "e");
    assert(m["str"] == "test");
    assert(mi.size() == 2);
    assert(mi["c"] == 0);
    assert(mi["str"] == 1);
}

Do you think it is a better idea ?

Baduit commented 5 months ago

Looks good!

Please see the comments to make the header changes smaller. Also some docs updates are required

I updated my PR according to your comments.

But on the github action I see a failure that I don't understand https://github.com/Baduit/pfr/actions/runs/9660279058/job/26645491447

Baduit commented 3 months ago

I updated my branch and thanks to your commit to fix the CI the builds are green

coveralls commented 2 months ago

Pull Request Test Coverage Report for Build 10586042298

Details


Totals Coverage Status
Change from base Build 10537504895: 0.0%
Covered Lines: 405
Relevant Lines: 405

💛 - Coveralls
apolukhin commented 2 months ago

Many thanks for the PR!