c++ - 在使用 boost::fusion 迭代时将 pretty-print 绑定(bind)到 boost::phoenix actors

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

此问题是 Pointers to class members when iterating with boost::fusion 的后续问题,其中接受的解决方案有效。

现在,我不仅要将(原始)值添加到属性映射中,还要使用 pretty-print 来改进值的显示方式。如果打印的值不是微不足道的,也将使用此机制。

所以,有一些 pretty-print 是这样的:

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 绑定(bind)到 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);
}

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

最佳答案

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

抛开所有这些因素,您在这里的路线并不理想:函数模板和特化不能很好地结合(Why not specialize function templates, [HSutter,2001] ¹)。

您似乎有意硬编码您的类型以及 pretty-print 逻辑。

咒语:

Type deduction and ADL are your friends for extensible mechanisms.

我会大致编写 pretty-print 界面:

#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<>而不是 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例如

关于c++ - 在使用 boost::fusion 迭代时将 pretty-print 绑定(bind)到 boost::phoenix actors,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35919228/

相关文章:

C++ mysql setString 不替换准备好的语句中的占位符

c++ - `g++ -E file.cxx` 的 Visual Studio 2010 模拟是什么?

c++ - 在 C++ 中编译时提供库路径的更好方法

ruby - 在运行时在 ruby​​ 中从模板定义方法

c++ - 发送数据lwip STM32 PC

c++ - 使用 NMAKE 将所有源代码制作在一个目录中?

c++ - 为什么函数不是对象?

c++ - 为什么这个 boost::spirit::qi 规则与输入不匹配?

ruby - 这个例子中 lambda 的目的是什么?

c++ 容器容器的通用 `flatten`