
I'm using boost 1.41. I use a variant to store a symbolic representation of an algebraic expression, wrapped inside of a master "node" class. Like this: struct expr_node { friend class constant; friend class variable; friend class arithmetic_op; friend class exponent; friend class special_func; friend class negative; public: template<class T> expr_node(const T& expr) : expr_(expr) { } expr_node& operator=(const expr_node& rhs) { expr_ = rhs; return *this; } private: typedef boost::variant<constant, variable, arithmetic_op, exponent, special_func, negative> node_type; node_type expr_; }; Of course, this is a recursive definition, because exponent, arithmetic_op, etc also contain expr_node members. At some point, I flatten the tree structure across either addition or multiplication, the result of which is a std::list<expr_node>. So in this example, if my expression is 2 * x * 2 * x^x * (2+x)^2 * (1 + x) this becomes the list [2, x, 2, x^x, (2+x)^2, 1+x]. I then use std::partition() to organize this list into the desired order according to the node type. This is where the strange behavior comes in. As you can see there are no negatives in this expression, and indeed, I can verify this is indeed the case in my code as well with: bool expr_node::is_negative() const { return boost::get<negative>(&expr_); } ... BOOST_ASSERT(std::find_if(terms.begin(), terms.end(), boost::bind(&expr_node::is_negative, _1)) == terms.end()); This assert passes. However, Immediately executing std::partition(terms.begin(), terms.end(), boost::bind(&expr_node::is_constant, _1)); BOOST_ASSERT(std::find_if(terms.begin(), terms.end(), boost::bind(&expr_node::is_negative, _1)) == terms.end()); triggers the assert. So something is going wrong involving copying the variants. Every class has both a copy constructor and an assignment operator. Examining the exact contents of the list both before and after the partition, the which_ fields are [0,1,0,3,3,2] (before), and [0,5,5,3,3,2] (after). Putting a breakpoint in all of the negative constructors, I can get a callstack for the first time a 'negative' is constructed. The callstack is as follows: negative::negative(const expr_node & arg={...}) Line 747 C++ node_type::convert_construct<expr_node const >(const expr_node & operand={...}, int __formal=1, int __formal=1) Line 1298 + 0xf bytes C++ node_type::node_type<expr_node>(const expr_node & operand={...}) Line 1367 C++ node_type::assign<expr_node>(const expr_node & rhs={...}) Line 1609 + 0xc bytes C++ node_type::operator=<expr_node>(const expr_node & rhs={...}) Line 1620 C++ expr_node::operator=(const expr_node & rhs={...}) Line 171 C++ std::swap<expr_node>(expr_node & _Left={...}, expr_node & _Right={...}) Line 23 C++ std::iter_swap<term_list::_Iterator<0>,term_list::_Iterator<0>
(term_list::_Iterator<0> _Left={...}, term_list::_Iterator<0> _Right={...}) Line 594 + 0x17 bytes C++ std::_Partition<term_list::_Iterator<0>,boost::_bi::bind_t<bool,boost::_mfi::cmf0<bool,expr_node>,boost::_bi::list1<boost::arg<1>
(term_list::_Iterator<0> _First={...}, term_list::_Iterator<0> _Last={...}, boost::_bi::bind_t<bool,boost::_mfi::cmf0<bool,expr_node>,boost::_bi::list1<boost::arg<1> _Pred={...}) Line 1838 + 0x4d bytes C++ std::partition<term_list::_Iterator<1>,boost::_bi::bind_t<bool,boost::_mfi::cmf0<bool,expr_node>,boost::_bi::list1<boost::arg<1> (term_list::_Iterator<1> _First={expr_={...} }, term_list::_Iterator<1> _Last={expr_={...} }, boost::_bi::bind_t<bool,boost::_mfi::cmf0<bool,expr_node>,boost::_bi::list1<boost::arg<1> _Pred={...}) Line 1847 + 0x64 bytes C++ expr_node::organize() Line 213 + 0x77 bytes C++
Perhaps it's related to the implementation of swap() for variants? If a more detailed code sample is needed, I can upload this entire program to a web server for someone to download. Zach

Hmm. As usual, it was user error. Although I'm not quite sure why it didn't work. My operator= was assigning an object of type const expr_node& directly to the variant. Still, I'm not sure why it compiled at all. shouldn't the compiler have reported an error? And even if it were correct to not report an error, shouldn't it have actually worked since the class was pod? I'm not sure what was really going on behind the scenes in the compiler to make it create a junk object.

On 12/10/09 22:11, Zachary Turner wrote:
I'm using boost 1.41. I use a variant to store a symbolic representation of an algebraic expression, wrapped inside of a master "node" class. Like this:
struct expr_node { friend class constant; friend class variable; friend class arithmetic_op; friend class exponent; friend class special_func; friend class negative; public: template<class T> expr_node(const T& expr) : expr_(expr) { }
expr_node& operator=(const expr_node& rhs) { expr_ = rhs; return *this; }
private: typedef boost::variant<constant, variable, arithmetic_op, exponent, special_func, negative> node_type;
I think some of these variant args should have recursive_wrapper wrapping them. See the link in following Dec 3 post to this list: http://sourceforge.net/mailarchive/message.php?msg_name=loom.20091203T005648... However, in that code, I don't think there needs to be a recursive_wrapper<Node> in the variant's components.
node_type expr_; };
Of course, this is a recursive definition, because exponent, arithmetic_op, etc also contain expr_node members.
[snip] HTH. -regards, Larry
participants (2)
-
Larry Evans
-
Zachary Turner