1

我是错误“LNK1179:无效或损坏的文件:重复的 COMDAT”的受害者,这些 消息来源让我相信,不使用phoenix我可以避免这个错误。

(这是我上一个问题的后续。)我想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>        ]
4

1 回答 1

5

boost::bind作为凤凰演员,你不能用表情。

关于冗长的名称,如果使用其他重 TMP 库有显着贡献(例如 Boost Range,它有自己的模板实例林,从适配器返回视图等),我不会感到惊讶。

你可以试试

  1. Item使结构适应融合序列:(有或没有attr_cast<>

    • 沿着类似的思路,您可以使item规则本身直接使用融合序列
  2. 要“预煮”一个演员以用于语义动作,要么

    • 使用多态函数对象(又名延迟可调用对象
    • 或使用免费功能
  3. 为和(为了完整性)烘焙自定义phoenix::function<>演员getId()getValues()

在下文中,我将演示所有方法,最后是一个包含所有这些选项的完整示例程序,并在 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>");

这实际上可能会使事情在内部变得同样糟糕——从我的角度很难判断。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实例。这肯定会从解析器表达式中消除所有绑定/代理的复杂性,需要更多的工作才能让其余的生成器连接起来。

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 的模板参数。可以在这里找到关于精神语境在业力规则中的作用的有用解释:提升精神语义动作参数

    // 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;
}
于 2013-05-17T13:02:52.180 回答