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
Adapting the
Item
struct to a fusion sequence: (with or withoutattr_cast<>
)item
rule itself consume a fusion sequence directlyTo 'precook' an actor for use in the semantic action, either
Bake custom
phoenix::function<>
actors forgetId()
andgetValues()
(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: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
rule itself consume a fusion sequence directly instead of anItem
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):
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
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:
3. Using
phoenix::function
to wrap deferred callablesFinally 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:This, you can then employ simply like this:
Full sample code