[fusion] Trouble using BOOST_FUSION_ADAPT_ADT with boost::fusion::operator<

Hi all, I'm attempting to adapt a third-party struct into a fusion sequence using BOOST_FUSION_ADAPT_ADT to get access to a nice operator<. Unfortunately compilation fails and I'm not sure if it's a bug or me using the library incorrectly. As best I can read the error message, it looks like the code is attempting to compare adt_attribute_proxy structs instead of the values represented by these. Below is some code that demonstrates the problem. Can anybody help me out? Thanks, Taylor #include <boost/fusion/adapted/adt/adapt_adt.hpp> #include <boost/fusion/adapted/struct/define_struct.hpp> #include <boost/fusion/adapted/struct/adapt_struct.hpp> #include <set> #include <string> #include <boost/fusion/sequence/comparison/less.hpp> struct test1 { std::string s; const std::string & getS() const { return s; } void setS( const std::string & s_) { s = s_; } }; BOOST_FUSION_ADAPT_ADT( test1, (const std::string &, const std::string &, obj.getS(), obj.setS(val)) ) BOOST_FUSION_DEFINE_STRUCT( , test2, (std::string, s) (int, a) ) struct test3 { std::string s; }; BOOST_FUSION_ADAPT_STRUCT( test3, (std::string, s) ) using boost::fusion::operator<; int main( int argc, char * argv[]) { //Fails /*std::set<test1> destinations1; destinations1.insert(test1());*/ std::set<test2> destinations2; destinations2.insert(test2()); std::set<test3> destinations3; destinations3.insert(test3()); return 0; }

On 1/18/2012 2:28 AM, GOODHART, TAYLOR wrote:
Hi all,
I'm attempting to adapt a third-party struct into a fusion sequence using BOOST_FUSION_ADAPT_ADT to get access to a nice operator<. Unfortunately compilation fails and I'm not sure if it's a bug or me using the library incorrectly. As best I can read the error message, it looks like the code is attempting to compare adt_attribute_proxy structs instead of the values represented by these. Below is some code that demonstrates the problem. Can anybody help me out?
You've bumped into a limitation of proxies. Fusion adapted ADTs do not have access to the actual L-values of the class members, but only through get and set. As such, it actually exposes proxy elements (adt_attribute_proxy) instead of the actual elements. The only thing you can do with these proxies is either to get the values or set the values: http://tinyurl.com/7t53h8f Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

I'm attempting to adapt a third-party struct into a fusion sequence using BOOST_FUSION_ADAPT_ADT to get access to a nice operator<. Unfortunately compilation fails and I'm not sure if it's a bug or me using the library incorrectly. As best I can read the error message, it looks like the code is attempting to compare adt_attribute_proxy structs instead of the values represented by these. Below is some code that demonstrates the problem. Can anybody help me out?
You've bumped into a limitation of proxies. Fusion adapted ADTs do not have access to the actual L-values of the class members, but only through get and set. As such, it actually exposes proxy elements (adt_attribute_proxy) instead of the actual elements. The only thing you can do with these proxies is either to get the values or set the values:
Can't Fusion provide overloads for relational operators that compare adt_attribute_proxy objects by comparing their values? Regards, Nate

On 1/18/2012 9:35 AM, Nathan Ridge wrote:
I'm attempting to adapt a third-party struct into a fusion sequence using BOOST_FUSION_ADAPT_ADT to get access to a nice operator<. Unfortunately compilation fails and I'm not sure if it's a bug or me using the library incorrectly. As best I can read the error message, it looks like the code is attempting to compare adt_attribute_proxy structs instead of the values represented by these. Below is some code that demonstrates the problem. Can anybody help me out?
You've bumped into a limitation of proxies. Fusion adapted ADTs do not have access to the actual L-values of the class members, but only through get and set. As such, it actually exposes proxy elements (adt_attribute_proxy) instead of the actual elements. The only thing you can do with these proxies is either to get the values or set the values:
Can't Fusion provide overloads for relational operators that compare adt_attribute_proxy objects by comparing their values?
Yes, that is a reasonable solution. I'd welcome a patch if anyone wants to add such operators. I'm a bit worried about the behavior of such operators, however. It needs to deduce the return types of the actual (non-proxy) overloads. It can be done, but is rather tricky without C++11. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

I'm attempting to adapt a third-party struct into a fusion sequence using BOOST_FUSION_ADAPT_ADT to get access to a nice operator<. Unfortunately compilation fails and I'm not sure if it's a bug or me using the library incorrectly. As best I can read the error message, it looks like the code is attempting to compare adt_attribute_proxy structs instead of the values represented by these. Below is some code that demonstrates the problem. Can anybody help me out?
You've bumped into a limitation of proxies. Fusion adapted ADTs do not have access to the actual L-values of the class members, but only through get and set. As such, it actually exposes proxy elements (adt_attribute_proxy) instead of the actual elements. The only thing you can do with these proxies is either to get the values or set the values:
Can't Fusion provide overloads for relational operators that compare adt_attribute_proxy objects by comparing their values?
Yes, that is a reasonable solution. I'd welcome a patch if anyone wants to add such operators. I'm a bit worried about the behavior of such operators, however. It needs to deduce the return types of the actual (non-proxy) overloads. It can be done, but is rather tricky without C++11.
A slightly different idea: rather than provide relational operators for adt_attribute_proxy itself, add support for it to the relational opperators of Fusion sequences. For example, for 'less than', where you have the following in <boost/fusion/sequence/comparison/detail/less.hpp>: template <typename I1, typename I2> static bool call(I1 const& a, I2 const& b, mpl::false_) { return *a < *b || (!(*b < *a) && call(fusion::next(a), fusion::next(b))); } you could instead do (untested): template <typename I1, typename I2> static bool call(I1 const& a, I2 const& b, mpl::false_) { return call_on_values(*a, *b) || (!call_on_values(*b, *a) && call(fusion::next(a), fusion::next(b))); } // call_on_values() for two non-proxies template <typename E1, typename E2> static bool call_on_values(E1 const& a, E2 const& b) { return a < b; } // call_on_values() for a non-proxy and a proxy template <typename E1, typename Seq, int Index, bool Const> static bool call_on_values(E1 const& a, adt_attribute_proxy<Seq, Index, Const> const& b) { return a < b.get(); } // similarly, versions for a proxy and a non-proxy, and two proxies Now the relational operators will work for ADT sequences themselves. The user will still have to call get() explicitly when comparing individual elements of the ADT sequences, but that should be rare. What do you think of this approach? It does not seem to require deducing the type of anything. If you're interested I can try this out and submit a patch. Regards, Nate

On 1/18/2012 12:33 PM, Nathan Ridge wrote:
I'm attempting to adapt a third-party struct into a fusion sequence using BOOST_FUSION_ADAPT_ADT to get access to a nice operator<. Unfortunately compilation fails and I'm not sure if it's a bug or me using the library incorrectly. As best I can read the error message, it looks like the code is attempting to compare adt_attribute_proxy structs instead of the values represented by these. Below is some code that demonstrates the problem. Can anybody help me out?
You've bumped into a limitation of proxies. Fusion adapted ADTs do not have access to the actual L-values of the class members, but only through get and set. As such, it actually exposes proxy elements (adt_attribute_proxy) instead of the actual elements. The only thing you can do with these proxies is either to get the values or set the values:
Can't Fusion provide overloads for relational operators that compare adt_attribute_proxy objects by comparing their values?
Yes, that is a reasonable solution. I'd welcome a patch if anyone wants to add such operators. I'm a bit worried about the behavior of such operators, however. It needs to deduce the return types of the actual (non-proxy) overloads. It can be done, but is rather tricky without C++11.
A slightly different idea: rather than provide relational operators for adt_attribute_proxy itself, add support for it to the relational opperators of Fusion sequences.
[...]
Now the relational operators will work for ADT sequences themselves. The user will still have to call get() explicitly when comparing individual elements of the ADT sequences, but that should be rare.
What do you think of this approach? It does not seem to require deducing the type of anything.
If you're interested I can try this out and submit a patch.
Clever! I like it. We can probably put the customization points (e.g. call_on_values <but better names please>) in the lower level "support" module in "extension" namespace. Then the "adapted" module can simply specialize the CPs for adt_attribute_proxy. Yeah, nice idea! Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

I'm attempting to adapt a third-party struct into a fusion sequence using BOOST_FUSION_ADAPT_ADT to get access to a nice operator<. Unfortunately compilation fails and I'm not sure if it's a bug or me using the library incorrectly. As best I can read the error message, it looks like the code is attempting to compare adt_attribute_proxy structs instead of the values represented by these. Below is some code that demonstrates the problem. Can anybody help me out?
You've bumped into a limitation of proxies. Fusion adapted ADTs do not have access to the actual L-values of the class members, but only through get and set. As such, it actually exposes proxy elements (adt_attribute_proxy) instead of the actual elements. The only thing you can do with these proxies is either to get the values or set the values:
Can't Fusion provide overloads for relational operators that compare adt_attribute_proxy objects by comparing their values?
Yes, that is a reasonable solution. I'd welcome a patch if anyone wants to add such operators. I'm a bit worried about the behavior of such operators, however. It needs to deduce the return types of the actual (non-proxy) overloads. It can be done, but is rather tricky without C++11.
A slightly different idea: rather than provide relational operators for adt_attribute_proxy itself, add support for it to the relational opperators of Fusion sequences.
[...]
Now the relational operators will work for ADT sequences themselves. The user will still have to call get() explicitly when comparing individual elements of the ADT sequences, but that should be rare.
What do you think of this approach? It does not seem to require deducing the type of anything.
If you're interested I can try this out and submit a patch.
Clever! I like it. We can probably put the customization points (e.g. call_on_values <but better names please>) in the lower level "support" module in "extension" namespace. Then the "adapted" module can simply specialize the CPs for adt_attribute_proxy.
Yeah, nice idea!
I implemented a slight variation of this that's a bit more general. I added a new function extension::as_readonly() in the support module, which unwraps wrappers like adt_attribute_proxy to get at the underlying value. The main template is just the identity function, but the adapted/adt module overloads it for adt_attribute_proxy to return proxy.get(). I then changed the implementations of the relational operators to wrap accesses to the sequence elements in as_readonly() before comparing. There may be other parts of the library where it's useful to wrap readonly accesses of sequence elements in as_readonly() - this mechanism can be re-used in those parts. Users can also overload/specialize as_readonly() for their own wrapper classes if they find it convenient. Patch attached. Tests pass with patch. (If you have a better idea for the name of this function that 'as_readonly', I am happy to change it.) Regards, Nate

On 5/16/2012 10:44 AM, Nathan Ridge wrote:
> I'm attempting to adapt a third-party struct into a fusion sequence > using BOOST_FUSION_ADAPT_ADT to get access to a nice operator<. > Unfortunately compilation fails and I'm not sure if it's a bug or me > using the library incorrectly. As best I can read the error message, it > looks like the code is attempting to compare adt_attribute_proxy structs > instead of the values represented by these. Below is some code that > demonstrates the problem. Can anybody help me out? >
You've bumped into a limitation of proxies. Fusion adapted ADTs do not have access to the actual L-values of the class members, but only through get and set. As such, it actually exposes proxy elements (adt_attribute_proxy) instead of the actual elements. The only thing you can do with these proxies is either to get the values or set the values:
Can't Fusion provide overloads for relational operators that compare adt_attribute_proxy objects by comparing their values?
Yes, that is a reasonable solution. I'd welcome a patch if anyone wants to add such operators. I'm a bit worried about the behavior of such operators, however. It needs to deduce the return types of the actual (non-proxy) overloads. It can be done, but is rather tricky without C++11.
A slightly different idea: rather than provide relational operators for adt_attribute_proxy itself, add support for it to the relational opperators of Fusion sequences.
[...]
Now the relational operators will work for ADT sequences themselves. The user will still have to call get() explicitly when comparing individual elements of the ADT sequences, but that should be rare.
What do you think of this approach? It does not seem to require deducing the type of anything.
If you're interested I can try this out and submit a patch.
Clever! I like it. We can probably put the customization points (e.g. call_on_values <but better names please>) in the lower level "support" module in "extension" namespace. Then the "adapted" module can simply specialize the CPs for adt_attribute_proxy.
Yeah, nice idea!
I implemented a slight variation of this that's a bit more general.
I added a new function extension::as_readonly() in the support module, which unwraps wrappers like adt_attribute_proxy to get at the underlying value. The main template is just the identity function, but the adapted/adt module overloads it for adt_attribute_proxy to return proxy.get().
I then changed the implementations of the relational operators to wrap accesses to the sequence elements in as_readonly() before comparing.
There may be other parts of the library where it's useful to wrap readonly accesses of sequence elements in as_readonly() - this mechanism can be re-used in those parts.
Users can also overload/specialize as_readonly() for their own wrapper classes if they find it convenient.
Patch attached. Tests pass with patch.
(If you have a better idea for the name of this function that 'as_readonly', I am happy to change it.)
The idea looks sound. I am applying the patch now. I took the liberty to rename as_readonly to as_const. What's lacking are tests. Could you please also provide some? Thanks a lot! Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com
participants (3)
-
GOODHART, TAYLOR
-
Joel de Guzman
-
Nathan Ridge