У меня есть последовательность 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.
Во-первых, чтобы ответить на ваш комментарий, drop_back
делает копию. Все алгоритмы в Хане делают копии и готовы, как задокументировано Вот.
Во-вторых, вы могли бы использовать 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
на первом месте.
Конец редактирования
Благодаря @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 << "}";
}
Это решение, основанное на указателях, чтобы избежать как копий, так и 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 << "}";
}