boost::iterator_range does not work in boost::variant in boost 1.55

If I try to put a boost::iterator_range into a boost::variant, it breaks the variant, because something in boost::iterator_range doesn't allow variant to use SFINAE deduction. My best guess at the problem is the declarations in boost/range/iterator_range_core.hpp that have a 'class ForwardRange' template argument are insufficiently constrained and accept almost anything as a possible ForwardRange type. I'm guessing that putting the correct enable_if<> template constraints on the templates that use 'ForwardRange' will fix the issue, but I'm having trouble identifing the correct metafunction test to use, since concept FowardRange concept doesn't have too many good targets. I'd like to test a candidate FowardRange type to see if boost::begin() and boost::end() can be successfully called on it, but I haven't figured out how to do that yet. Any suggestions? Known fixes or workarounds? I know about the has_range_iterator metafunction, but it can't be used here due to circular definition issues... Exmaple: #include <boost/variant.hpp> #include <boost/range.hpp> #include <boost/mpl/vector.hpp> using namespace boost; enum E { e1, e2, e3 }; typedef mpl::vector<E, std::string > args; typedef boost::make_variant_over<args>::type OkVariant; typedef mpl::vector<boost::iterator_range<std::string::iterator>, args> args1; typedef boost::make_variant_over<args1>::type BrokenVariant; int main(int argc, char **argv) { OkVariant ok; ok = e1; ok = ""; BrokenVariant broken; std::string s; broken = boost::iterator_range<std::string::iterator>(s.begin(), s.end()); broken = e2; broken = ""; } Yields errors on the last two assignments to 'broken' in g++4.8.2: test.cpp:27:12: required from here ./boost/mpl/eval_if.hpp:60:31: error: no type named 'type' in 'boost::mpl::eval_if_c<true, boost::range_const_iterator<E>, boost::range_mutable_iterator<const E> >::f_ {aka struct boost::range_const_iterator<E>}' typedef typename f_::type type; I've attached the full error log and my project-config.jam. -- Jon Biggar jon@floorboard.com jon@biggar.org jonbiggar@gmail.com

On Sun, Mar 2, 2014 at 3:24 AM, Jon Biggar <jon@biggar.org> wrote:
I'm guessing that putting the correct enable_if<> template constraints on the templates that use 'ForwardRange' will fix the issue, but I'm having trouble identifing the correct metafunction test to use, since concept FowardRange concept doesn't have too many good targets.
Making the constructor overloads less greedy is definitely an improvement. This has been implemented and pushed to the "develop" branch.
I'd like to test a candidate FowardRange type to see if boost::begin() and boost::end() can be successfully called on it, but I haven't figured out how to do that yet.
Any suggestions? Known fixes or workarounds? I know about the has_range_iterator metafunction, but it can't be used here due to circular definition issues...
I has to modify your test case in a number of ways to be able to reproduce the problem accurately without artificial breakage. Hopefully my changes to make your test-case work did not remove any of the conditions you were attempting to demonstrate.
Exmaple:
#include <boost/variant.hpp> #include <boost/range.hpp> #include <boost/mpl/vector.hpp>
using namespace boost;
enum E { e1, e2, e3 };
typedef mpl::vector<E, std::string > args; typedef boost::make_variant_over<args>::type OkVariant;
typedef mpl::vector<boost::iterator_range<std::string::iterator>, args> args1;
args1 is not what you want for passing into the make_variant_over function since it is not an extended sequence as I think you intended. You need to do this: typedef boost::mpl::push_back< args, boost::iterator_range<std::string::iterator> >::type args1;
typedef boost::make_variant_over<args1>::type BrokenVariant;
int main(int argc, char **argv) { OkVariant ok; ok = e1; ok = "";
Your expression "" is of type const char[1]. Depending on the compiler this might match overloads for char* due to old rules allowing interopability between string literals and mutable char pointers and arrays. It is valid for a standard library implementation to implement the std::string::iterator as char*. If all these factors conspire against you then even with the updates in the "develop" branch you will suffer ambiguous function calls. This therefore probably now works with "develop", but it is not going to be super-portable. If old compilers are not important to you it would be entirely reasonable to ignore this consideration.
-- Jon Biggar jon@floorboard.com jon@biggar.org jonbiggar@gmail.com
If you have time to try out the "develop" branch then please let me know how you get on with it. Thanks for reporting the issue. I hope these changes help. Regards, Neil Groves

On 3/2/14, 3:01 PM, Neil Groves wrote:
Making the constructor overloads less greedy is definitely an improvement. This has been implemented and pushed to the "develop" branch.
Good, I did it also in my own copy, using the SInglePassRangeConcept and WriteableRangeConcept from boost/range/concept.hpp, for both the constructors and the assignment operators. It seems to help a lot.
I has to modify your test case in a number of ways to be able to reproduce the problem accurately without artificial breakage. Hopefully my changes to make your test-case work did not remove any of the conditions you were attempting to demonstrate.
I expected that. :) I'm still pretty new to mpl.
Exmaple:
#include <boost/variant.hpp> #include <boost/range.hpp> #include <boost/mpl/vector.hpp>
using namespace boost;
enum E { e1, e2, e3 };
typedef mpl::vector<E, std::string > args; typedef boost::make_variant_over<args>::type OkVariant;
typedef mpl::vector<boost::iterator_range<std::string::iterator>, args> args1;
args1 is not what you want for passing into the make_variant_over function since it is not an extended sequence as I think you intended. You need to do this:
typedef boost::mpl::push_back< args, boost::iterator_range<std::string::iterator> >::type args1;
Yup, discovered that a day or two after posting, thanks.
typedef boost::make_variant_over<args1>::type BrokenVariant;
int main(int argc, char **argv) { OkVariant ok; ok = e1; ok = "";
Your expression "" is of type const char[1]. Depending on the compiler this might match overloads for char* due to old rules allowing interopability between string literals and mutable char pointers and arrays. It is valid for a standard library implementation to implement the std::string::iterator as char*. If all these factors conspire against you then even with the updates in the "develop" branch you will suffer ambiguous function calls.
Ah, true. I've fallen into the problem that template argument deduction doesn't work like function overloading resolution...there's no use or ranking of conversions. I've learned a lot in the last week or too, and moved past this trouble. Thanks for your help. -- Jon Biggar jon@floorboard.com jon@biggar.org jonbiggar@gmail.com
participants (2)
-
Jon Biggar
-
Neil Groves