Binding a pretty-printer to boost::phoenix actors when iterating with boost::fusion

时间秒杀一切 提交于 2020-01-06 04:09:27

问题


This question is a follow-up to Pointers to class members when iterating with boost::fusion, where the accepted solution works.

Now, I want not only to add the (primitive) values to the property-map, but use a pretty-printer to improve how the values are displayed. This mechanism will also be used in case the values are not trivial to print.

So, there is some pretty-printer like this:

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);
}

and I expanded the solution from the previous question by binding the prettyPrinter to the boost::phoenix actor:

#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);
}

What I am now looking for is the possibility of a more elegant solution, since @sehe pointed out int a comment, that using phoenix::bind might not be the optimal thing to do here.


回答1:


There are some random errors in the code you show. None of the prettyPrinter overloads would compile. There's no such thing as Seq. You adapt the structs for members that don't exist.

All these things aside, you are racing a suboptimal line here: function templates and specializations don't mix well (Why not specialize function templates, [HSutter,2001]¹).

You seem intent on hard-coding your types as well as the pretty-print logic.

Mantra:

Type deduction and ADL are your friends for extensible mechanisms.

I'd write the pretty print interface roughly like:

#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));
        }
    };
}

You can now overload your do_pretty_print

  • in any way you want (including by providing generic overloads)
  • in safe places, like as friend members of your types, or as (template) functions in the associated namespace(s) of your types

They will "magically" be picked at the POI.


Now, I suggest using boost::phoenix::function<> instead of boost::phoenix::bind to arrive at a much cleaner call-site:

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)
);

Key to the simplification is to let the compiler do what it does best: overload resolution with deduced argument types.

Full Demo

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);
}

¹ See also GotW#49 http://www.gotw.ca/gotw/049.htm and Template Specialization VS Function Overloading e.g.



来源:https://stackoverflow.com/questions/35919228/binding-a-pretty-printer-to-boostphoenix-actors-when-iterating-with-boostfus

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!