Как написать цикл для последовательности Hana?

У меня есть последовательность Boos.Hana, и я хотел бы распечатать ее на экране, разделенные запятыми. Однако запятые разделяют только элементы, поэтому я должен проверить, нахожусь ли я на последнем элементе.

В настоящее время мой хак довольно плох (смотрит на указатель и приводит к void*,

template<class P, class... Ts>
decltype(auto) operator<<(
    std::ostream& os, 
    boost::hana::tuple<Ts...> const& tpl
){  
    os << "{";
    boost::hana::for_each(
        tpl, [&](auto& x){
            os << x;
            if((void*)&boost::hana::back(tpl) != (void*)&x) os << ", ";
        }
    );
    return os << "}";
}

В случае Boost.Fusion это было сложнее, потому что я использую итераторы fusion (boost::fusion::begin а также boost::fusion::end) но по крайней мере я мог бы сравнить итераторы. (bool last = result_of::equal_to<typename result_of::next<First>::type, Last>::value).

Другой способ задать этот вопрос - есть ли (мета) итераторы в Hana.

4 ответа

Решение

Во-первых, чтобы ответить на ваш комментарий, drop_back делает копию. Все алгоритмы в Hana делают копии и готовы, как описано здесь.

Во-вторых, вы могли бы использовать hana::intersperse добавить запятую между каждым элементом, в результате чего-то вроде

template<class P, class... Ts>
decltype(auto) operator<<(
    std::ostream& os, 
    boost::hana::tuple<Ts...> const& tpl
){  
    os << "{";
    boost::hana::for_each(boost::hana::intersperse(tpl, ", "), 
        [&](auto const& x){
            os << x;
        });
    return os << "}";
}

Тем не менее, лучшее решение, вероятно, будет использовать experimental::print, который делает именно то, что вы хотите:

#include <boost/hana/experimental/printable.hpp>
#include <boost/hana/tuple.hpp>
#include <iostream>

int main() {
    auto ts = hana::make_tuple(1, 2, 3);
    std::cout << hana::experimental::print(ts);
}

редактировать

Если вы хотите использовать intersperse Решение, но не хотите делать копию последовательности, вы можете сделать следующее:

#include <boost/hana.hpp>
#include <functional>
#include <iostream>
namespace hana = boost::hana;

template <class... Ts>
decltype(auto) operator<<(std::ostream& os, hana::tuple<Ts...> const& tpl) {
    os << "{";
    char const* sep = ", ";
    auto refs = hana::transform(tpl, [](auto const& t) { return std::ref(t); });
    hana::for_each(hana::intersperse(refs, std::ref(sep)),
        [&](auto const& x){
            os << x.get();
        });
    return os << "}";
}

Но на самом деле, вы, вероятно, должны использовать hana::experimental::print, И если ваш вариант использования критичен к производительности, и вы хотите избежать создания std::string Я бы усомнился в использовании std::ostream на первом месте.

Конец редактирования

Это решение, основанное на указателях, чтобы избежать как копий, так и std::cref,

template<class P, class... Ts>
decltype(auto) operator<<(
    std::ostream& os, 
    boost::hana::tuple<Ts...> const& tpl
){  
    os << "{";
    std::string sep = ", ";
    hana::for_each(
        hana::intersperse(
            hana::transform(tpl, [](auto& t){return &t;}),
            &sep
        ), [&](auto x){os << *x;}
    );
    return os << "}";
}

Благодаря @cv_and_he я смог найти решение. Хотя это не выглядит самым элегантным, потому что это приведет к дублированию кода (а также к копированию).

template<class P, class... Ts>
decltype(auto) operator<<(
    std::ostream& os, 
    boost::hana::tuple<Ts...> const& tpl
){  
    os << "{";
    boost::hana::for_each(
        boost::hana::drop_back(tpl), [&](auto const& x){
            os << x << ", ";
        }
    );
    os << boost::hana::back(x);
    return os << "}";
}

То же, что и оригинал, но меньше взлома, так как он использует boost::hana::equal сравнить тождества.

template<class P, class... Ts>
decltype(auto) operator<<(
    std::ostream& os, 
    boost::hana::tuple<Ts...> const& tpl
){  
    os << "{";
    boost::hana::for_each(
        tpl, [&](auto& x){
            os << x;
            if(not boost::hana::equal(&x, &boost::hana::back(tpl))){p << ", ";}
        }
    );
    return os << "}";
}
Другие вопросы по тегам