【问题标题】:Binding a pretty-printer to boost::phoenix actors when iterating with boost::fusion使用 boost::fusion 迭代时将漂亮的打印机绑定到 boost::phoenix 演员
【发布时间】:2016-06-25 11:01:11
【问题描述】:

这个问题是Pointers to class members when iterating with boost::fusion 的后续问题,接受的解决方案在该处有效。

现在,我不仅想将(原始)值添加到属性映射中,还想使用漂亮的打印机来改进值的显示方式。这种机制也将用于值不易于打印的情况。

所以,有一些像这样的漂亮打印机:

template<typename T>
std::string prettyPrinter(const T& t);

template<>
std::string prettyPrinter(const std::string& s)
{
    return "The string id: " + s;
}

template<>
std::string prettyPrinter(const int& i)
{
    return "The int id: " + std::to_string(i);
}

我通过将 prettyPrinter 绑定到 boost::phoenix 演员扩展了上一个问题的解决方案:

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/find.hpp>
#include <boost/phoenix/fusion/at.hpp>
#include <boost/phoenix.hpp>
#include <boost/mpl/range_c.hpp>

#include <iostream>

struct Vertex {
    std::string id;
};

struct Edge {
    int id;
};

BOOST_FUSION_ADAPT_STRUCT(Vertex, id)
BOOST_FUSION_ADAPT_STRUCT(Edge, id)

template <typename Tag, typename T_Graph>
void member_iterator(boost::dynamic_properties& dp, T_Graph& g)
{
    using namespace boost;

    using Bundle = typename property_map<T_Graph, Tag>::type;
    using T_Seq  = typename property_traits<Bundle>::value_type;

    using Indices = mpl::range_c<unsigned, 0, fusion::result_of::size<T_Seq>::value>;

    fusion::for_each(
        Indices{},
        [&, bundle=get(Tag{}, g)](auto i) {
            auto name = fusion::extension::struct_member_name<T_Seq, i>::call();
            using TempType = typename fusion::result_of::value_at<T_Seq, decltype(i)>::type;

            //
            // Interesting part starts here:
            //
            dp.property(
                name,
                make_transform_value_property_map(
                    phoenix::bind(
                        prettyPrinter<TempType>,
                        phoenix::at_c<i>(phoenix::arg_names::arg1)
                    ),
                    bundle
                )
            );
        }
    );
}

using MyGraph = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, Vertex, Edge>;

int main()
{
    MyGraph g;
    boost::dynamic_properties dp;

    member_iterator<boost::vertex_bundle_t>(dp, g);
    member_iterator<boost::edge_bundle_t>(dp, g);
}

我现在正在寻找一种更优雅的解决方案的可能性,因为@sehe 在评论中指出,使用phoenix::bind 可能不是这里的最佳选择。

【问题讨论】:

    标签: c++ boost metaprogramming boost-fusion boost-phoenix


    【解决方案1】:

    您显示的代码中有一些随机错误。 prettyPrinter 重载都不会编译。没有Seq 这样的东西。您为不存在的成员调整结构。

    抛开所有这些事情不谈,您在这里的表现并不理想:函数模板和专业化不能很好地融合在一起 (Why not specialize function templates, [HSutter,2001]¹)。

    您似乎有意硬编码您的类型以及漂亮的打印逻辑。

    咒语

    类型推断和 ADL 是可扩展机制的朋友。

    我会写漂亮的打印界面大致如下:

    #include <string>
    
    namespace pretty_printing
    {
        namespace default_impl {
            std::string do_pretty_print(const std::string& s) {
                return "The string id: " + s;
            }
    
            std::string do_pretty_print(const int i) {
                return "The int id: " + std::to_string(i);
            }
        }
    
        struct pretty_print_f {
            using result_type = std::string;
    
            template <typename T> result_type operator()(T&& v) const { 
                using namespace default_impl; // enable ADL
                return do_pretty_print(std::forward<T>(v));
            }
        };
    }
    

    您现在可以重载do_pretty_print

    • 以任何你想要的方式(包括通过提供通用重载)
    • 在安全的地方,例如作为类型的friend 成员,或作为类型的关联命名空间中的(模板)函数

    他们将在POI“神奇地”被选中。


    现在,我建议使用boost::phoenix::function&lt;&gt; 而不是boost::phoenix::bind 来到达更清洁的呼叫站点:

    auto name = fusion::extension::struct_member_name<T_Seq, i>::call();
    px::function<pretty_printing::pretty_print_f> pretty_print;
    
    dp.property(
        name,
        make_transform_value_property_map(pretty_print(px::at_c<i>(arg1)), bundle)
    );
    

    简化的关键是让编译器做它最擅长的事情:使用推导的参数类型进行重载解析。

    完整演示

    Live On Coliru

    #include <string>
    
    namespace pretty_printing
    {
        namespace default_impl {
            std::string do_pretty_print(const std::string& s) {
                return "The string id: " + s;
            }
    
            std::string do_pretty_print(const int i) {
                return "The int id: " + std::to_string(i);
            }
        }
    
        struct pretty_print_f {
            using result_type = std::string;
    
            template <typename T> result_type operator()(T&& v) const { 
                using namespace default_impl;
                return do_pretty_print(std::forward<T>(v));
            }
        };
    }
    
    #include <boost/graph/adjacency_list.hpp>
    #include <boost/graph/graphviz.hpp>
    #include <boost/fusion/include/for_each.hpp>
    #include <boost/fusion/include/adapt_struct.hpp>
    #include <boost/fusion/include/find.hpp>
    #include <boost/phoenix/fusion/at.hpp>
    #include <boost/phoenix.hpp>
    #include <boost/mpl/range_c.hpp>
    
    #include <iostream>
    
    struct Vertex {
        std::string id;
        int numeric_value;
    };
    
    struct Edge {
        int more;
        std::string awesome_sauce;
    };
    
    BOOST_FUSION_ADAPT_STRUCT(Vertex, id, numeric_value)
    BOOST_FUSION_ADAPT_STRUCT(Edge, more, awesome_sauce)
    
    template <typename Tag, typename T_Graph>
    void member_iterator(boost::dynamic_properties& dp, T_Graph& g)
    {
        using namespace boost;
        namespace px = boost::phoenix;
        using namespace px::arg_names;
    
        using Bundle = typename property_map<T_Graph, Tag>::type;
        using T_Seq  = typename property_traits<Bundle>::value_type;
    
        using Indices = mpl::range_c<unsigned, 0, fusion::result_of::size<T_Seq>::value>;
    
        fusion::for_each(
            Indices{},
            [&, bundle=get(Tag{}, g)](auto i) {
    
                auto name = fusion::extension::struct_member_name<T_Seq, i>::call();
                px::function<pretty_printing::pretty_print_f> pretty_print;
    
                dp.property(
                    name,
                    make_transform_value_property_map(pretty_print(px::at_c<i>(arg1)), bundle)
                );
            }
        );
    }
    
    using MyGraph = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, Vertex, Edge>;
    
    int main()
    {
        MyGraph g;
        boost::dynamic_properties dp;
    
        member_iterator<boost::vertex_bundle_t>(dp, g);
        member_iterator<boost::edge_bundle_t>(dp, g);
    }
    

    ¹ 另请参阅 GotW#49 http://www.gotw.ca/gotw/049.htmTemplate Specialization VS Function Overloading,例如

    【讨论】:

    • 这大大改进了我的实际代码!我一定会阅读您的资料,谢谢!
    猜你喜欢
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    • 2012-10-17
    • 2016-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-07
    相关资源
    最近更新 更多