Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copy-assigning to a tuple from an iterator range in C++17

I've been hoping to use new C++17 features like fold expressions and std::apply() to simplify some C++11 code I have that uses tools like std::index_sequence and std::initializer_list for some operations on tuples. One task in particular is giving me a bit of trouble: copying a range of values (e.g. from a boost::tokenizer object) to a tuple. I have a working solution that calls std::apply() on the tuple to which the values are to be assigned, but which still has to use std::initializer_list internally (for simplicity I've replaced the boost tokenizer iterators with a simple vector:

#include <iostream>
#include <vector>
#include <tuple>

template<typename Tuple, typename Iterator>
size_t copy_range_to_tuple(Tuple& tup, Iterator begin, Iterator end) {
    size_t count = 0;
    auto copy = [&begin,&end,&count] (auto& value) -> bool {
        if (begin != end) {
            value = *(begin++);
            ++count;
            return true;
        } else {
            return false;
        }
    };
    std::apply([&copy](auto&... values) {
        std::initializer_list<bool>{copy(values)...};
    }, tup);
    return count;
}

int main(int,char**) {
    std::tuple<int,int,int,int> tup;
    std::vector<int> x{4,3,2};
    std::cout << "copy count = " << copy_range_to_tuple(tup, x.begin(), x.end()) << std::endl;
    std::cout << std::get<0>(tup) << std::endl;
    std::cout << std::get<1>(tup) << std::endl;
    std::cout << std::get<2>(tup) << std::endl;
    std::cout << std::get<3>(tup) << std::endl;
    
}

OUTPUT:

copy count = 3
4
3
2
0

This is already a big win versus my C++11/14 code, which (due to the absence of std::apply()) used index sequences to match the iterators to the different tuple elements. However, I still have a feeling, or hope, that this could be simplified further with a fold expression, eliminating the need for initializer list. To be precise, I was hoping that I could make a fold expression that expanded copy(values). Is that possible, or is there some other trick with modern C++ to make this less verbose? (Getting rid of the copy lambda would also be nice, but I think something like it is inevitable in order check for valid iterators).

like image 507
jwimberley Avatar asked Feb 24 '21 18:02

jwimberley


1 Answers

Version with fold expression may look like:

std::tuple<int,int,int,int> t;
std::vector<int> v{4,3};
auto beg = v.begin();
auto end = v.end();
std::apply([&](auto&... values){
    auto getValue = [&](){ return (beg != end) ? *beg++ : 0; }; // returns 0 if range is too small
    ((values = getValue()),...);
}, t);
std::cout << std::get<0>(t) << std::endl; // 4
std::cout << std::get<1>(t) << std::endl; // 3
std::cout << std::get<2>(t) << std::endl; // 0
std::cout << std::get<3>(t) << std::endl; // 0

Demo

like image 113
rafix07 Avatar answered Oct 05 '22 23:10

rafix07