enable_if and constructors

I see examples of enable_if used to specify a return type to a function: template <class T> typename enable_if_c<boost::is_arithmetic<T>::value, T>::type foo(T t) { return t; } Is it possible to use enable_if with a constructor? For example, I'd like to define a constructor that allows only certain values of an enum: #include <boost/utility/enable_if.hpp> class foo { public: enum state { state1, state2, state3 }; // This should compile only when passed (s == state2 or s == state3) template<int ID> foo(boost::enable_if_c<some_boolean_condition, state>::type s) : status_(s) {} private: state status_; }; Even if this turns out to be impossible, I'd like to go on and try to define some_boolean_condition if I may. I see that Aleksey plans a contains_c metafunction for 1.33, http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?MPL_TODO_List Equivalent URL: http://tinyurl.com/4toly so my limited understanding leads me to some_boolean_condition is equivalent to: mpl::contains_c< mpl::vector_c< int, state2, state3 >, ID >::value Does that look correct? In the absence of contains_c, I've been trying to use mpl::find_if but my predicate compiles for both "valid" and "invalid" values, so I must be doing something wrong. I'd be grateful if someone could put me back on track. Regards, Angus #include <boost/type_traits/is_same.hpp> #include <boost/mpl/assert.hpp> #include <boost/mpl/end.hpp> #include <boost/mpl/equal_to.hpp> #include <boost/mpl/find_if.hpp> #include <boost/mpl/lambda.hpp> #include <boost/mpl/integral_c.hpp> #include <boost/mpl/not.hpp> #include <boost/mpl/vector_c.hpp> class foo { public: enum state { state1, state2, state3 }; }; int main() { // Should lead to a compile-time failure. #define TESTED_VALUE foo::state1 // Should compile. // #define TESTED_VALUE foo::state2 namespace mpl = boost::mpl; typedef mpl::vector_c< int, foo::state2, foo::state3 > allowable_values; typedef mpl::find_if< allowable_values, mpl::equal_to< mpl::_1, mpl::integral_c<allowable_values::value_type, TESTED_VALUE> > >::type iter; BOOST_MPL_ASSERT(( mpl::not_< boost::is_same< iter::type, mpl::end<allowable_values>::type > > )); return 0; }

Angus Leeming wrote:
Even if this turns out to be impossible, I'd like to go on and try to define some_boolean_condition if I may. I see that Aleksey plans a contains_c metafunction for 1.33,
Apologies for replying to self, but I have managed to define a contains_c metafunction. (Below). I still don't know how to use it with enable_if, however. This fails to compile: boost::enable_if< mpl::contains_c< mpl::vector_c< int, foo::state2, foo::state3 > , foo::state2 > , foo::state
::type s = foo::state2;
My naïve understanding is that enable_if<...>::type should be foo::state here. Any pointers? Regards, Angus #include <boost/utility/enable_if.hpp> #include <boost/type_traits/is_same.hpp> #include <boost/mpl/assert.hpp> #include <boost/mpl/deref.hpp> #include <boost/mpl/end.hpp> #include <boost/mpl/equal_to.hpp> #include <boost/mpl/find_if.hpp> #include <boost/mpl/lambda.hpp> #include <boost/mpl/integral_c.hpp> #include <boost/mpl/not.hpp> #include <boost/mpl/vector_c.hpp> namespace boost { namespace mpl { template <typename IntegralSequence, int Value> struct contains_c { private: typedef typename find_if< IntegralSequence , equal_to< _1 , integral_c<typename IntegralSequence::value_type, Value> > >::type iter; public: typedef typename not_< is_same< iter , typename end<IntegralSequence>::type > >::type type; typedef typename type::value_type value; }; } // namespace mpl } // namespace boost class foo { public: enum state { state1, state2, state3 }; }; int main() { namespace mpl = boost::mpl; typedef mpl::vector_c< int, foo::state2, foo::state3 > allowable_values; // Fails to compile, as expected. // BOOST_MPL_ASSERT(( mpl::contains_c< allowable_values, foo::state1 > )); // Compiles, as expected. BOOST_MPL_ASSERT(( mpl::contains_c< allowable_values, foo::state2 > )); // Why doesn't this compile? boost::enable_if< mpl::contains_c< allowable_values, foo::state2 > , foo::state >::type s = foo::state2; return 0; }

Angus Leeming <angus.leeming@btopenworld.com> writes:
Is it possible to use enable_if with a constructor?
In principle, yes. You just add a dummy default parameter for the enabler.
For example, I'd like to define a constructor that allows only certain values of an enum:
No chance; that's a runtime test... or you'd have to specify the enum as a template argument to the ctor explicitly... but you can't provide explicit template arguments to ctors. Well, you can do something like: template <class N> foo( N , typename boost::enable_if< typename contains_c< mpl::vector_c<state, state2, state3> , N >::type >::type*=0 ); where template <class S, class N> struct contains_c : mpl::not_< mpl::is_same< typename mpl::find_if<S,mpl::equal_to<N,_> >::type , typename mpl::end<S>::type > > {}; and then a foo is constructed something like this: foo x( mpl::int_<state2>() ); HTH, -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Angus Leeming <angus.leeming@btopenworld.com> writes:
Is it possible to use enable_if with a constructor?
In principle, yes. You just add a dummy default parameter for the enabler.
For example, I'd like to define a constructor that allows only certain values of an enum:
No chance; that's a runtime test... or you'd have to specify the enum as a template argument to the ctor explicitly... but you can't provide explicit template arguments to ctors.
Many, many thanks, Dave, both for the explanations and for the working code. Your code worked perfectly of course, but I decided to use a static "set" member function to return a foo instance: foo x(foo::set<foo::state2>()); I guess that this is semantically equivalent to your foo x(mpl::int_<foo::state2>()); FWIW, I include my working version of this MPL code at the bottom of this mail. However, it all seems like a lot of machinery. In my case, there are only a few "valid" enum values. What are the advantages of the Boost.MPL approach over the functionally identical: #include <iostream> class foo { public: enum state { state1, state2, state3 }; // Only the specializations (below) of this template will compile. template <int N> static foo set() { return invalid_value; } private: foo(state) { std::cout << "foo" << std::endl; } }; template <> foo foo::set<foo::state2>() { return foo::state2; } template <> foo foo::set<foo::state3>() { return foo::state3; } int main() { // Compiles, as expected. foo f1 = foo::set<foo::state2>(); foo f2(foo::set<foo::state2>()); // Fail to compile, as expected. // foo f3 = foo::set<foo::state1>(); // foo f4(foo::set<foo::state1>()); return 0; } Regards, Angus ----------------------------------------------------------------------- My version of Dave Abrahams's working version of my broken original ;-) #include <boost/type_traits/is_integral.hpp> #include <boost/utility/enable_if.hpp> #include <boost/type_traits/is_same.hpp> #include <boost/mpl/assert.hpp> #include <boost/mpl/deref.hpp> #include <boost/mpl/end.hpp> #include <boost/mpl/equal_to.hpp> #include <boost/mpl/find_if.hpp> #include <boost/mpl/lambda.hpp> #include <boost/mpl/integral_c.hpp> #include <boost/mpl/not.hpp> #include <boost/mpl/vector_c.hpp> #include <iostream> namespace boost { namespace mpl { template <class S, int N> struct contains_c : not_< is_same< typename find_if<S, equal_to<_1, integral_c<typename S::value_type, N> > >::type , typename end<S>::type > > {}; } // namespace boost } // namespace mpl class foo { public: enum state { state1, state2, state3 }; // Will compile only for state2 and state3 template <int N> static typename boost::enable_if< typename boost::mpl::contains_c< boost::mpl::vector_c<int, state2, state3> , N > , foo >::type set() { return foo(static_cast<state>(N)); } private: foo(state) { std::cout << "foo" << std::endl; } }; int main() { // Compiles, as expected. foo f1 = foo::set<foo::state2>(); foo f2(foo::set<foo::state2>()); // Fail to compile, as expected. // foo f3 = foo::set<foo::state1>(); // foo f4(foo::set<foo::state1>()); return 0; }

Angus Leeming <angus.leeming@btopenworld.com> writes:
What are the advantages of the Boost.MPL approach over the functionally identical:
#include <iostream>
class foo { public: enum state { state1, state2, state3 };
// Only the specializations (below) of this template will compile. template <int N> static foo set() { return invalid_value; }
Without a definition for invalid_value, this is invalid code, and on a conforming compiler, compilation fails at the point it is parsed.
private: foo(state) { std::cout << "foo" << std::endl; } };
template <> foo foo::set<foo::state2>() { return foo::state2; }
template <> foo foo::set<foo::state3>() { return foo::state3; }
int main() { // Compiles, as expected. foo f1 = foo::set<foo::state2>(); foo f2(foo::set<foo::state2>()); // Fail to compile, as expected. // foo f3 = foo::set<foo::state1>(); // foo f4(foo::set<foo::state1>());
return 0; }
-- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
// Only the specializations (below) of this template will compile. template <int N> static foo set() { return invalid_value; }
Without a definition for invalid_value, this is invalid code, and on a conforming compiler, compilation fails at the point it is parsed.
Thanks again. g++ 3.3 appears to happy differentiate between "valid" and "invalid" enum values with the code below, so allow me to rephrase my earlier question: which of the two techniques we've discussed would you use? I assume that one has technical merits over the other? Regards, Angus #include <boost/static_assert.hpp> #include <iostream> class foo { public: enum state { state1, state2, state3 }; // Only the specializations (below) of this template will compile. template <int N> static foo set() { BOOST_STATIC_ASSERT( N == state2 ); } private: foo(state) { std::cout << "foo" << std::endl; } }; template <> foo foo::set<foo::state2>() { return foo::state2; } template <> foo foo::set<foo::state3>() { return foo::state3; } int main() { // Compiles, as expected. foo f1 = foo::set<foo::state2>(); foo f2(foo::set<foo::state2>()); // Fail to compile, as expected. foo f3 = foo::set<foo::state1>(); // foo f4(foo::set<foo::state1>()); return 0; }

Angus Leeming <angus.leeming@btopenworld.com> writes:
David Abrahams wrote:
// Only the specializations (below) of this template will compile. template <int N> static foo set() { return invalid_value; }
Without a definition for invalid_value, this is invalid code, and on a conforming compiler, compilation fails at the point it is parsed.
Thanks again. g++ 3.3 appears to happy differentiate between "valid" and "invalid" enum values with the code below,
Well *that* code is valid, unlike the code of your previous post.
so allow me to rephrase my earlier question: which of the two techniques we've discussed would you use?
I have no opinion.
I assume that one has technical merits over the other?
I don't know; they're both pretty ugly ;-) -- Dave Abrahams Boost Consulting www.boost-consulting.com
participants (2)
-
Angus Leeming
-
David Abrahams