如何为 Hana 序列编写 for 循环?
How to write a for loop for a Hana sequence?
我有一个 Boos.Hana 序列,我想将它打印到屏幕上并用逗号分隔。但是逗号只分隔元素,所以我必须检查我是否在最后一个元素。
目前我的 hack 非常糟糕(查看指针并转换为 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 的情况下,它更复杂,因为我使用了融合迭代器(boost::fusion::begin
和 boost::fusion::end
),但至少我可以比较迭代器。 (bool last = result_of::equal_to<typename result_of::next<First>::type, Last>::value
).
问这个问题的另一种方法是 Hana 中是否有(元)迭代器。
感谢@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 << "}";
}
首先,为了回答您的评论,drop_back
确实复制了一份。 Hana 中的所有算法都会进行复制并且很急切,如文档所述 here。
其次,您可以使用 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
的用法。
编辑结束
与原始版本相同,但修改较少,因为它使用 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 << "}";
}
我有一个 Boos.Hana 序列,我想将它打印到屏幕上并用逗号分隔。但是逗号只分隔元素,所以我必须检查我是否在最后一个元素。
目前我的 hack 非常糟糕(查看指针并转换为 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 的情况下,它更复杂,因为我使用了融合迭代器(boost::fusion::begin
和 boost::fusion::end
),但至少我可以比较迭代器。 (bool last = result_of::equal_to<typename result_of::next<First>::type, Last>::value
).
问这个问题的另一种方法是 Hana 中是否有(元)迭代器。
感谢@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 << "}";
}
首先,为了回答您的评论,drop_back
确实复制了一份。 Hana 中的所有算法都会进行复制并且很急切,如文档所述 here。
其次,您可以使用 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
的用法。
编辑结束
与原始版本相同,但修改较少,因为它使用 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 << "}";
}