如何在使用boost :: spirit :: karma生成时避免boost :: phoenix [英] How to avoid boost::phoenix when generating with boost::spirit::karma

查看:133
本文介绍了如何在使用boost :: spirit :: karma生成时避免boost :: phoenix的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是错误"LNK1179:文件无效或损坏:COMDAT重复"的受害者 和这些 我之前的问题的跟踪.) 我想用其他东西代替boost::phoenix.也许boost::bind,但我不知道如何授予它对karma::_val的访问权限.

以下代码无法在

的VC9上编译

错误C2825:"F":后跟"::"必须是类或名称空间

#include <boost/config/warning_disable.hpp>

#include <boost/foreach.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>

#include <boost/bind.hpp>

#include <iostream>
#include <string>
#include <list>


namespace karma = boost::spirit::karma;
namespace spirit = boost::spirit;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;


class Item
{
public:
    typedef std::vector<int> Values;

    Item(const std::string  & i, const Values & v) : m_id(i), m_values(v) {}
    std::string getId() const { return m_id; }
    const Values & getValues() const { return m_values; }

private:
    std::string m_id;
    Values m_values;
};

class ItemList
{
public:
    typedef std::map<std::string, Item> Items;

    ItemList() {}
    ItemList(const Items & s, const Items & o) : m_some(s), m_other(o) {}
    const Items getSome() const { return m_some; }
    const Items getOther() const { return m_other; }

private:
    Items m_some;;
    Items m_other;
};

template <typename Iterator>
struct list_generator : karma::grammar<Iterator, ItemList()>
{
    list_generator(const ItemList & i)
        : list_generator::base_type(start)
{
    using karma::int_;
    using karma::_1;
    using karma::lit;
    using karma::_val;

    // using phoenix causes: fatal error LNK1179: invalid or corrupt file: duplicate COMDAT '?value@?$result_@U?$member_variable@$$A6E?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZP8Item@@AE?AV12@XZ@detail@phoenix@boost@@@?$is_mem_fun_pointer_select@$0A@@detail@boost@@2_NB'
    // this is probably because the symbol names are too long.


    // Convert maps into lists containing only the values
    const Items some  = boost::copy_range<Items>(i.getSome() | boost::adaptors::map_values);
    const Items other = boost::copy_range<Items>(i.getOther() | boost::adaptors::map_values);

    id =
        lit("<id>")
        << karma::string
        << lit("</id>");

    values =
        lit("<values>") 
        << (int_ % ';') 
        << lit("</values>");

    item =
        lit("<item>")
        //<< id[_1 = phoenix::bind(&Item::getId, _val)]
        << id[boost::bind(&Item::getId, &_val, _1)] // !! error C2825 !!
        << values[_1 = phoenix::bind(&Item::getValues, _val)]
        << lit("</item>");

    start =
        lit("<some>")     << (*item)[_1 = some] << lit("</some>")
        << lit("<other>")  << (*item)[_1 = other] << lit("</other>");
}

typedef std::vector<Item> Items;
karma::rule<Iterator, std::string()> id;
karma::rule<Iterator, Item::Values()> values;
karma::rule<Iterator, Item()> item;
karma::rule<Iterator, ItemList()> start;
};

int main()
{
    const Item::Values values = boost::assign::list_of(1)(2)(3);
    const Item a("a", values);
    const Item b("b", values);

    ItemList::Items some, other;
    some.insert(std::make_pair(a.getId(), a));
    other.insert(std::make_pair(b.getId(), b));
    const ItemList items(some, ItemList::Items());

    typedef std::back_insert_iterator<std::string> Iter;
    typedef list_generator<Iter> Generator;

    Generator grammar(items);

    std::string generated;
    Iter sink(generated);
    if (!karma::generate(sink, grammar))
    {
        std::cout << "Generating failed\n";
    }
    else
    {
        std::cout << "Generated: " << generated << "\n";
    }

    return 0;
}

完整的错误是这样的:

error C2825: 'F': must be a class or namespace when followed by '::'
1>        c:\path\to\boost\boost/bind/bind_template.hpp(15) : see reference to class template instantiation 'boost::_bi::result_traits<R,F>' being compiled
1>        with
1>        [
1>            R=boost::_bi::unspecified,
1>            F=std::basic_string<char,std::char_traits<char>,std::allocator<char>> (__thiscall Item::* )(void) const
1>        ]
1>        .\spiritTest.cpp(85) : see reference to class template instantiation 'boost::_bi::bind_t<R,F,L>' being compiled
1>        with
1>        [
1>            R=boost::_bi::unspecified,
1>            F=std::string (__thiscall Item::* )(void) const,
1>            L=boost::_bi::list2<boost::_bi::value<boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::reference_eval,boost::fusion::vector<boost::spirit::attribute<0>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>>>>,boost::_bi::value<boost::spirit::_1_type>>
1>        ]
1>        .\spiritTest.cpp(57) : while compiling class template member function 'list_generator<Iterator>::list_generator(const ItemList &)'
1>        with
1>        [
1>            Iterator=Iter
1>        ]
1>        .\spiritTest.cpp(116) : see reference to class template instantiation 'list_generator<Iterator>' being compiled
1>        with
1>        [
1>            Iterator=Iter
1>        ]

解决方案

您不能将boost::bind表达式用作凤凰演员.

对于长整齐的名称,如果使用其他大量TMP繁重的库做出了很大的贡献(例如Boost Range,它拥有自己的模板实例森林,从适配器返回视图等),我不会感到惊讶./p>

您可以尝试

  1. 使Item结构适应融合序​​列 :(带有或不带有attr_cast<>)

    • 沿着相似的几行,您可以使item规则本身直接消耗融合序​​列
  2. 要预煮"一个在语义动作中使用的演员,

    • 使用多态函数对象(也称为延迟可调用对象)
    • 或使用免费功能
  3. getId()getValues()烘焙自定义phoenix::function<> actor(出于完整性)

在下面,我演示了所有方法,最后给出了包含所有这些选项的完整示例程序,并在Windows(Cygwin)上使用gcc 4.5.3进行了编译.


1.使Item结构适应融合序​​列:

BOOST_FUSION_ADAPT_ADT(Item,
        (std::string,         std::string,         obj.getId(),     (void)val)
        (Item::Values const&, Item::Values const&, obj.getValues(), (void)val)
    )

// the rule becomes simply
item =
    lit("<item>")
    << id      // yay for fusion magic!
    << values
    << lit("</item>");

这实际上可能使内部情况同样糟糕-从我的角度来看很难说.如果是这样,您可以尝试撬开parse表达式中使用属性的阶段和评估fusion adapter proxies的阶段:

item =
    lit("<item>")
    << karma::attr_cast<boost::fusion::vector<std::string, Item::Values> > // cast up front
    (
           id     // directly
        << values // 
    )
    << lit("</item>");

  • 沿着相似的几行,您可以使item规则本身代替Item实例直接使用融合序​​列. 确保可以从解析器表达式中删除所有的bind/proxy复杂性,但是需要更多的工作才能使其余生成器建立连接.

2. 预煮"一个演员以用于语义动作,要么是

  • 使用多态函数对象(又称​​延迟可调用对象):

    struct deferredGetId
    {
        template<typename,typename,typename> struct result { typedef void type; };
    
        template<typename Attr, typename Ctx, typename Bool>
        void operator()(Attr& attribute, Ctx const& context, Bool& flag) const
        {
            attribute = boost::fusion::at_c<0>(context.attributes).getId();
            flag = true;
        }
    };
    

  • 或使用免费功能.然后,这需要对Context的模板参数进行硬编码.有关精神情境在业力规则中的作用的有用说明,可以在这里找到: boost精神语义动作参数

    // non-template free function
    void hardcodedGetId(std::string& attribute, 
            boost::spirit::context<boost::fusion::cons<const Item&, boost::fusion::nil>, boost::fusion::vector0<> > const& context, 
            bool& flag)
    {
        attribute = boost::fusion::at_c<0>(context.attributes).getId();
        flag = true;
    }
    

  • 请注意,还有 BOOST_PHOENIX_ADAPT_FUNCTION ,可让您直接使用功能模板,但真正要做的就是将功能模板包装在新的多态功能对象类型中,如上所述.这不会给你带来任何好处.

现在您可以在规则中使用它们了:

 item =
     lit("<item>")
     << id [_1 = phoenix::bind(&Item::getId, _val)] // works fine on GCC :)
     << id [deferredGetId()] // approach #1 (a)
     << id [hardcodedGetId]  // approach #1 (b)
     << values[_1 = phoenix::bind(&Item::getValues, _val)]
     << lit("</item>");

3.使用phoenix::function封装延迟的可调用项

最后,可以选择使用phoenix::function将延迟的可调用对象直接包装为(一元)actor.我不相信这实际上会对您有帮助,除非发生某种我不知道的删除. 但是最终结果非常优雅,即使出于完整性考虑,这本身也是一个很好的理由:

struct GetId
{
    template<typename> struct result { typedef std::string type; };
    template<typename Item>
    std::string operator()(Item const& item) const { return item.getId(); }
};

struct GetValues
{
    template<typename> struct result { typedef Item::Values type; };
    template<typename Item>
    typename Item::Values const& operator()(Item const& item) const { return item.getValues(); }
};

boost::phoenix::function<GetId>     phx_getId;
boost::phoenix::function<GetValues> phx_getValues;

这样,您就可以像这样简单地聘用:

item =
    lit("<item>")
    << id[_1 = phx_getId(_val)]
    << values[_1 = phx_getValues(_val)]
    << lit("</item>");

完整的示例代码

#include <boost/config/warning_disable.hpp>

#include <boost/foreach.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>

#include <boost/bind.hpp>

#include <iostream>
#include <string>
#include <list>

#include <boost/fusion/adapted.hpp>

namespace karma = boost::spirit::karma;
namespace spirit = boost::spirit;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;

class Item
{
public:
    typedef std::vector<int> Values;

    Item(const std::string  & i, const Values & v) : m_id(i), m_values(v) {}
    std::string getId() const { return m_id; }
    const Values & getValues() const { return m_values; }

private:
    std::string m_id;
    Values m_values;
};

class ItemList
{
public:
    typedef std::map<std::string, Item> Items;

    ItemList() {}
    ItemList(const Items & s, const Items & o) : m_some(s), m_other(o) {}
    const Items getSome() const { return m_some; }
    const Items getOther() const { return m_other; }

private:
    Items m_some;;
    Items m_other;
};

/////////////////////////////////////////////
// 1. Adapting the `Item` struct

BOOST_FUSION_ADAPT_ADT(Item,
        (std::string,         std::string,         obj.getId(),     (void)val)
        (Item::Values const&, Item::Values const&, obj.getValues(), (void)val)
    )

/////////////////////////////////////////////
// 2. Precooking Actors

struct deferredGetId
{
    template<typename,typename,typename> struct result { typedef void type; };

    template<typename Attr, typename Ctx, typename Bool>
    void operator()(Attr& attribute, Ctx const& context, Bool& flag) const
    {
        attribute = boost::fusion::at_c<0>(context.attributes).getId();
        flag = true;
    }
};

// non-template free function
void hardcodedGetId(std::string& attribute, 
        boost::spirit::context<boost::fusion::cons<const Item&, boost::fusion::nil>, boost::fusion::vector0<> > const& context, 
        bool& flag)
{
    attribute = boost::fusion::at_c<0>(context.attributes).getId();
    flag = true;
}

/////////////////////////////////////////////
// 3. phoenix::function

struct GetId
{
    template<typename> struct result { typedef std::string type; };
    template<typename Item>
    std::string operator()(Item const& item) const { return item.getId(); }
};

struct GetValues
{
    template<typename> struct result { typedef Item::Values type; };
    template<typename Item>
    typename Item::Values const& operator()(Item const& item) const { return item.getValues(); }
};

boost::phoenix::function<GetId>     phx_getId;
boost::phoenix::function<GetValues> phx_getValues;

template <typename Iterator>
struct list_generator : karma::grammar<Iterator, ItemList()>
{
    list_generator(const ItemList & i)
        : list_generator::base_type(start)
{
    using karma::int_;
    using karma::_1;
    using karma::lit;
    using karma::_val;

    // using phoenix causes: fatal error LNK1179: invalid or corrupt file:
    // duplicate COMDAT
    // '?value@?$result_@U?$member_variable@$$A6E?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZP8Item@@AE?AV12@XZ@detail@phoenix@boost@@@?$is_mem_fun_pointer_select@$0A@@detail@boost@@2_NB'
    // this is probably because the symbol names are too long.

    // Convert maps into lists containing only the values
    const Items some  = boost::copy_range<Items>(i.getSome() | boost::adaptors::map_values);
    const Items other = boost::copy_range<Items>(i.getOther() | boost::adaptors::map_values);

    id =
        lit("<id>")
        << karma::string
        << lit("</id>");

    values =
        lit("<values>") 
        << (int_ % ';') 
        << lit("</values>");

    item =
        lit("<item>")
     //
        << id[_1 = phoenix::bind(&Item::getId, _val)] // works fine on GCC :)
        << id [deferredGetId()]     // approach #2 (a)
        << id [hardcodedGetId]      // approach #2 (b)
        << id [_1= phx_getId(_val)] // approach #3
     //
        << values[_1 = phoenix::bind(&Item::getValues, _val)]
        << values[_1 = phx_getValues(_val)]
        << lit("</item>");

    item =
        lit("<item>")
        << id     // approach #1: using BOOST_FUSION_ADAPT_ADT
        << values // approach #1: using BOOST_FUSION_ADAPT_ADT
        << lit("</item>");

    // approach #2 _with_ attr_cast:
    item =
        lit("<item>")
        << karma::attr_cast<boost::fusion::vector<std::string, Item::Values> >
        (
               id     // 'native' fusion sequence access
            << values // 'native' fusion sequence access
        )
        << lit("</item>");

    start =
        lit("<some>")     << (*item)[_1 = some] << lit("</some>")
        << lit("<other>") << (*item)[_1 = other] << lit("</other>");
}

typedef std::vector<Item> Items;
karma::rule<Iterator, std::string()> id;
karma::rule<Iterator, Item::Values()> values;
karma::rule<Iterator, Item()> item;
karma::rule<Iterator, ItemList()> start;
};

int main()
{
    const Item::Values values = boost::assign::list_of(1)(2)(3);
    const Item a("a", values);
    const Item b("b", values);

    ItemList::Items some, other;
    some.insert(std::make_pair(a.getId(), a));
    other.insert(std::make_pair(b.getId(), b));
    const ItemList items(some, ItemList::Items());

    typedef std::back_insert_iterator<std::string> Iter;
    typedef list_generator<Iter> Generator;

    Generator grammar(items);

    std::string generated;
    Iter sink(generated);
    if (!karma::generate(sink, grammar))
    {
        std::cout << "Generating failed\n";
    }
    else
    {
        std::cout << "Generated: " << generated << "\n";
    }

    return 0;
}

I'm a victim of error "LNK1179: invalid or corrupt file: duplicate COMDAT" and these sources lead me to believe that by not using phoenix I could avoid this error.

(This is a follow-up to my previous question.) I want to replace boost::phoenix with something else. Maybe boost::bind but I don't see how I can give it access to karma::_val.

The following code fails to compile on VC9 with

error C2825: 'F': must be a class or namespace when followed by '::'

#include <boost/config/warning_disable.hpp>

#include <boost/foreach.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>

#include <boost/bind.hpp>

#include <iostream>
#include <string>
#include <list>


namespace karma = boost::spirit::karma;
namespace spirit = boost::spirit;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;


class Item
{
public:
    typedef std::vector<int> Values;

    Item(const std::string  & i, const Values & v) : m_id(i), m_values(v) {}
    std::string getId() const { return m_id; }
    const Values & getValues() const { return m_values; }

private:
    std::string m_id;
    Values m_values;
};

class ItemList
{
public:
    typedef std::map<std::string, Item> Items;

    ItemList() {}
    ItemList(const Items & s, const Items & o) : m_some(s), m_other(o) {}
    const Items getSome() const { return m_some; }
    const Items getOther() const { return m_other; }

private:
    Items m_some;;
    Items m_other;
};

template <typename Iterator>
struct list_generator : karma::grammar<Iterator, ItemList()>
{
    list_generator(const ItemList & i)
        : list_generator::base_type(start)
{
    using karma::int_;
    using karma::_1;
    using karma::lit;
    using karma::_val;

    // using phoenix causes: fatal error LNK1179: invalid or corrupt file: duplicate COMDAT '?value@?$result_@U?$member_variable@$$A6E?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZP8Item@@AE?AV12@XZ@detail@phoenix@boost@@@?$is_mem_fun_pointer_select@$0A@@detail@boost@@2_NB'
    // this is probably because the symbol names are too long.


    // Convert maps into lists containing only the values
    const Items some  = boost::copy_range<Items>(i.getSome() | boost::adaptors::map_values);
    const Items other = boost::copy_range<Items>(i.getOther() | boost::adaptors::map_values);

    id =
        lit("<id>")
        << karma::string
        << lit("</id>");

    values =
        lit("<values>") 
        << (int_ % ';') 
        << lit("</values>");

    item =
        lit("<item>")
        //<< id[_1 = phoenix::bind(&Item::getId, _val)]
        << id[boost::bind(&Item::getId, &_val, _1)] // !! error C2825 !!
        << values[_1 = phoenix::bind(&Item::getValues, _val)]
        << lit("</item>");

    start =
        lit("<some>")     << (*item)[_1 = some] << lit("</some>")
        << lit("<other>")  << (*item)[_1 = other] << lit("</other>");
}

typedef std::vector<Item> Items;
karma::rule<Iterator, std::string()> id;
karma::rule<Iterator, Item::Values()> values;
karma::rule<Iterator, Item()> item;
karma::rule<Iterator, ItemList()> start;
};

int main()
{
    const Item::Values values = boost::assign::list_of(1)(2)(3);
    const Item a("a", values);
    const Item b("b", values);

    ItemList::Items some, other;
    some.insert(std::make_pair(a.getId(), a));
    other.insert(std::make_pair(b.getId(), b));
    const ItemList items(some, ItemList::Items());

    typedef std::back_insert_iterator<std::string> Iter;
    typedef list_generator<Iter> Generator;

    Generator grammar(items);

    std::string generated;
    Iter sink(generated);
    if (!karma::generate(sink, grammar))
    {
        std::cout << "Generating failed\n";
    }
    else
    {
        std::cout << "Generated: " << generated << "\n";
    }

    return 0;
}

The full error is this:

error C2825: 'F': must be a class or namespace when followed by '::'
1>        c:\path\to\boost\boost/bind/bind_template.hpp(15) : see reference to class template instantiation 'boost::_bi::result_traits<R,F>' being compiled
1>        with
1>        [
1>            R=boost::_bi::unspecified,
1>            F=std::basic_string<char,std::char_traits<char>,std::allocator<char>> (__thiscall Item::* )(void) const
1>        ]
1>        .\spiritTest.cpp(85) : see reference to class template instantiation 'boost::_bi::bind_t<R,F,L>' being compiled
1>        with
1>        [
1>            R=boost::_bi::unspecified,
1>            F=std::string (__thiscall Item::* )(void) const,
1>            L=boost::_bi::list2<boost::_bi::value<boost::phoenix::actor<boost::phoenix::composite<boost::phoenix::reference_eval,boost::fusion::vector<boost::spirit::attribute<0>,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_,boost::fusion::void_>>>>,boost::_bi::value<boost::spirit::_1_type>>
1>        ]
1>        .\spiritTest.cpp(57) : while compiling class template member function 'list_generator<Iterator>::list_generator(const ItemList &)'
1>        with
1>        [
1>            Iterator=Iter
1>        ]
1>        .\spiritTest.cpp(116) : see reference to class template instantiation 'list_generator<Iterator>' being compiled
1>        with
1>        [
1>            Iterator=Iter
1>        ]

解决方案

You can't use boost::bind expressions as a phoenix actor.

With regards to the long mangled names, I wouldn't be surprised if the use of other TMP-heavy libraries contribute significantly (e.g. Boost Range, which has it's own forest of template instantiations returning views from adaptors etc.).

You could try

  1. Adapting the Item struct to a fusion sequence: (with or without attr_cast<>)

    • Along similar same lines, you could make the item rule itself consume a fusion sequence directly
  2. To 'precook' an actor for use in the semantic action, either

    • using a Polymorphic Function Object (a.k.a. deferred calleable object)
    • or using a free function
  3. Bake custom phoenix::function<> actors for getId() and getValues() (for completeness)

In the following, I demonstrate all of the approaches, concluding with a full sample program that includes all of these options, and was compiled with gcc 4.5.3 on windows (Cygwin).


1. Adapting the Item struct to a fusion sequence:

BOOST_FUSION_ADAPT_ADT(Item,
        (std::string,         std::string,         obj.getId(),     (void)val)
        (Item::Values const&, Item::Values const&, obj.getValues(), (void)val)
    )

// the rule becomes simply
item =
    lit("<item>")
    << id      // yay for fusion magic!
    << values
    << lit("</item>");

This might actually make things equally bad internally - it's hard to tell from my vantage point. If it does, you could try to pry apart the phase where the attributes are consumed in the parse-expressions and the place where the fusion adapter proxies are evaluated:

item =
    lit("<item>")
    << karma::attr_cast<boost::fusion::vector<std::string, Item::Values> > // cast up front
    (
           id     // directly
        << values // 
    )
    << lit("</item>");

  • Along similar same lines, you could make the item rule itself consume a fusion sequence directly instead of an Item instance. This is sure to remove all of the bind/proxy complexity from the parser expressions, but it requires more work to get the rest of the generators to connect.

2. to 'precook' an actor for use in the semantic action, either

  • using a Polymorphic Function Object (a.k.a. deferred calleable object):

    struct deferredGetId
    {
        template<typename,typename,typename> struct result { typedef void type; };
    
        template<typename Attr, typename Ctx, typename Bool>
        void operator()(Attr& attribute, Ctx const& context, Bool& flag) const
        {
            attribute = boost::fusion::at_c<0>(context.attributes).getId();
            flag = true;
        }
    };
    

  • or using a free function. This then requires hard-coding the template argument for Context. A helpful explanation of the role of Spirit Context in karma rules may be found here: boost spirit semantic action parameters

    // non-template free function
    void hardcodedGetId(std::string& attribute, 
            boost::spirit::context<boost::fusion::cons<const Item&, boost::fusion::nil>, boost::fusion::vector0<> > const& context, 
            bool& flag)
    {
        attribute = boost::fusion::at_c<0>(context.attributes).getId();
        flag = true;
    }
    

  • Note that there is also BOOST_PHOENIX_ADAPT_FUNCTION that allows you to use a function template directly, but all that really does is wrap the function template inside a new Polymorphic Function Object type like above, so this will not gain you anything.

Now you could use those in your rules:

 item =
     lit("<item>")
     << id [_1 = phoenix::bind(&Item::getId, _val)] // works fine on GCC :)
     << id [deferredGetId()] // approach #1 (a)
     << id [hardcodedGetId]  // approach #1 (b)
     << values[_1 = phoenix::bind(&Item::getValues, _val)]
     << lit("</item>");

3. Using phoenix::function to wrap deferred callables

Finally there is the option to use phoenix::function to wrap deferred callables directly as a (unary) actor. I'm not convinced it will actually help you, unless there is some kind of erasure going on that I'm not aware of. But the end result is pretty elegant, which in itself is a good reason to mention it, if only for completeness:

struct GetId
{
    template<typename> struct result { typedef std::string type; };
    template<typename Item>
    std::string operator()(Item const& item) const { return item.getId(); }
};

struct GetValues
{
    template<typename> struct result { typedef Item::Values type; };
    template<typename Item>
    typename Item::Values const& operator()(Item const& item) const { return item.getValues(); }
};

boost::phoenix::function<GetId>     phx_getId;
boost::phoenix::function<GetValues> phx_getValues;

This, you can then employ simply like this:

item =
    lit("<item>")
    << id[_1 = phx_getId(_val)]
    << values[_1 = phx_getValues(_val)]
    << lit("</item>");

Full sample code

#include <boost/config/warning_disable.hpp>

#include <boost/foreach.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>

#include <boost/bind.hpp>

#include <iostream>
#include <string>
#include <list>

#include <boost/fusion/adapted.hpp>

namespace karma = boost::spirit::karma;
namespace spirit = boost::spirit;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;

class Item
{
public:
    typedef std::vector<int> Values;

    Item(const std::string  & i, const Values & v) : m_id(i), m_values(v) {}
    std::string getId() const { return m_id; }
    const Values & getValues() const { return m_values; }

private:
    std::string m_id;
    Values m_values;
};

class ItemList
{
public:
    typedef std::map<std::string, Item> Items;

    ItemList() {}
    ItemList(const Items & s, const Items & o) : m_some(s), m_other(o) {}
    const Items getSome() const { return m_some; }
    const Items getOther() const { return m_other; }

private:
    Items m_some;;
    Items m_other;
};

/////////////////////////////////////////////
// 1. Adapting the `Item` struct

BOOST_FUSION_ADAPT_ADT(Item,
        (std::string,         std::string,         obj.getId(),     (void)val)
        (Item::Values const&, Item::Values const&, obj.getValues(), (void)val)
    )

/////////////////////////////////////////////
// 2. Precooking Actors

struct deferredGetId
{
    template<typename,typename,typename> struct result { typedef void type; };

    template<typename Attr, typename Ctx, typename Bool>
    void operator()(Attr& attribute, Ctx const& context, Bool& flag) const
    {
        attribute = boost::fusion::at_c<0>(context.attributes).getId();
        flag = true;
    }
};

// non-template free function
void hardcodedGetId(std::string& attribute, 
        boost::spirit::context<boost::fusion::cons<const Item&, boost::fusion::nil>, boost::fusion::vector0<> > const& context, 
        bool& flag)
{
    attribute = boost::fusion::at_c<0>(context.attributes).getId();
    flag = true;
}

/////////////////////////////////////////////
// 3. phoenix::function

struct GetId
{
    template<typename> struct result { typedef std::string type; };
    template<typename Item>
    std::string operator()(Item const& item) const { return item.getId(); }
};

struct GetValues
{
    template<typename> struct result { typedef Item::Values type; };
    template<typename Item>
    typename Item::Values const& operator()(Item const& item) const { return item.getValues(); }
};

boost::phoenix::function<GetId>     phx_getId;
boost::phoenix::function<GetValues> phx_getValues;

template <typename Iterator>
struct list_generator : karma::grammar<Iterator, ItemList()>
{
    list_generator(const ItemList & i)
        : list_generator::base_type(start)
{
    using karma::int_;
    using karma::_1;
    using karma::lit;
    using karma::_val;

    // using phoenix causes: fatal error LNK1179: invalid or corrupt file:
    // duplicate COMDAT
    // '?value@?$result_@U?$member_variable@$$A6E?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZP8Item@@AE?AV12@XZ@detail@phoenix@boost@@@?$is_mem_fun_pointer_select@$0A@@detail@boost@@2_NB'
    // this is probably because the symbol names are too long.

    // Convert maps into lists containing only the values
    const Items some  = boost::copy_range<Items>(i.getSome() | boost::adaptors::map_values);
    const Items other = boost::copy_range<Items>(i.getOther() | boost::adaptors::map_values);

    id =
        lit("<id>")
        << karma::string
        << lit("</id>");

    values =
        lit("<values>") 
        << (int_ % ';') 
        << lit("</values>");

    item =
        lit("<item>")
     //
        << id[_1 = phoenix::bind(&Item::getId, _val)] // works fine on GCC :)
        << id [deferredGetId()]     // approach #2 (a)
        << id [hardcodedGetId]      // approach #2 (b)
        << id [_1= phx_getId(_val)] // approach #3
     //
        << values[_1 = phoenix::bind(&Item::getValues, _val)]
        << values[_1 = phx_getValues(_val)]
        << lit("</item>");

    item =
        lit("<item>")
        << id     // approach #1: using BOOST_FUSION_ADAPT_ADT
        << values // approach #1: using BOOST_FUSION_ADAPT_ADT
        << lit("</item>");

    // approach #2 _with_ attr_cast:
    item =
        lit("<item>")
        << karma::attr_cast<boost::fusion::vector<std::string, Item::Values> >
        (
               id     // 'native' fusion sequence access
            << values // 'native' fusion sequence access
        )
        << lit("</item>");

    start =
        lit("<some>")     << (*item)[_1 = some] << lit("</some>")
        << lit("<other>") << (*item)[_1 = other] << lit("</other>");
}

typedef std::vector<Item> Items;
karma::rule<Iterator, std::string()> id;
karma::rule<Iterator, Item::Values()> values;
karma::rule<Iterator, Item()> item;
karma::rule<Iterator, ItemList()> start;
};

int main()
{
    const Item::Values values = boost::assign::list_of(1)(2)(3);
    const Item a("a", values);
    const Item b("b", values);

    ItemList::Items some, other;
    some.insert(std::make_pair(a.getId(), a));
    other.insert(std::make_pair(b.getId(), b));
    const ItemList items(some, ItemList::Items());

    typedef std::back_insert_iterator<std::string> Iter;
    typedef list_generator<Iter> Generator;

    Generator grammar(items);

    std::string generated;
    Iter sink(generated);
    if (!karma::generate(sink, grammar))
    {
        std::cout << "Generating failed\n";
    }
    else
    {
        std::cout << "Generated: " << generated << "\n";
    }

    return 0;
}

这篇关于如何在使用boost :: spirit :: karma生成时避免boost :: phoenix的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆