【问题标题】:How to write a for loop for a Hana sequence?如何为 Hana 序列编写 for 循环?
【发布时间】:2016-01-10 06:17:30
【问题描述】:

我有一个 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::beginboost::fusion::end),但至少我可以比较迭代器。 (bool last = result_of::equal_to&lt;typename result_of::next&lt;First&gt;::type, Last&gt;::value)。

问这个问题的另一种方法是 Hana 中是否有(元)迭代器。

【问题讨论】:

  • 我不是图书馆方面的专家,但我认为你可以使用front/drop_front(或drop_back/back)来实现你想要的。
  • 谢谢。不过应该有更好的方法。如果drop_back 复制,文档中并不清楚。另外我必须更改为auto const&amp; x 但可以。 boost::hana::for_each(boost::hana::drop_back(tpl), [&amp;](auto const&amp; x){p &lt;&lt; x &lt;&lt; ", "});。有趣的是,Hana 有Iterable 的概念,但没有iterators。

标签: c++ boost boost-fusion boost-hana


【解决方案1】:

首先,为了回答您的评论,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::intersperse 也制作副本吗? intersperse 可以写成更简单的hana 函数吗?所以Hana中没有(元)iteratorsbeginend
  • 是的,就像 all Hana 算法一样,它会复制。 `intersperse 可以用更简单的 Hana 函数来编写,就像你刚才在回答中所做的那样。但是,它的编译时效率必然较低,运行时效率也可能较低。
  • 这意味着intersperse 解决方案并不比drop_back 解决方案差(模编译器优化)? 正确。 有没有办法让intersperse 引用引用(比如使用std::ref 或其他东西)? 是的,请查看我的更新答案。
  • 我认为hana::transform(tuple, std::ref) 是合理的。您必须通过额外的 lambda 的唯一原因是因为 std::ref 被指定为重载函数,并且该语言不允许将重载函数传递给算法。这在 Hana 中不是问题,而是在标准库或语言中。 Hana 不能为每个可能的重载函数提供一个特殊的xxx_transform
  • 不,这仍然是不可能的。允许这样做需要对Sequence 概念进行重大更改,这可能最终会发生,但不是一个简单的决定。
【解决方案2】:

感谢@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 << "}";
}

【讨论】:

    【解决方案3】:

    与原始版本相同,但更少hack,因为它使用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 << "}";
    }
    

    【讨论】:

    • 其实我更喜欢==这个版本。事实上,由于您在比较地址,==hana::equal 是等价的。然而,使用hana::equal 使其目的不太明显,因为hana::equal 经常用于进行编译时 比较(例如hana::equal(hana::int_c&lt;1&gt;, hana::long_c&lt;1&gt;),它返回hana::true_c)。真的只是个人喜好。
    • hana::equal 版本,如果我理解正确,如果(指针的)类型不同,它将给出编译时间false。如果类型相同,它将是一个运行时true/false。我的印象是,最佳解决方案应该始终是编译时间常数。
    • 不可能总是给出编译时常量,因为&amp;hana::back(tpl) 的实际地址是运行时值。例如,如果tpl 分配在堆上会发生什么?如何在编译时知道它的地址?
    • @LouisDionne,并非总是如此,但当类型不同时,可能会忽略参数类型 false 总是返回,如果我错了,请纠正我template&lt;class T1, class T2&gt; constexpr bool equal(T1 const&amp;, T2 const&amp;){return false;}template&lt;class T&gt; bool equal(T const&amp; t1, T const&amp; t2){...runtime code...;} ??
    • 或多或少,是的。事实上,如果两个指针的类型不同无法比较,那么编译时会返回一个false值。
    【解决方案4】:

    这是一种基于指针的解决方案,可以避免复制和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 << "}";
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-08
      • 2010-09-08
      • 2022-01-27
      • 1970-01-01
      相关资源
      最近更新 更多