enable_if and multiple partial specializations
Hi, It's late and I might be missing something, but I can't figure out how can I use enable_if to disambiguate 2 partial specializations where one is subcase of another one: #include <boost/utility/enable_if.hpp> #include <boost/type_traits/is_enum.hpp> #include <boost/type_traits/is_scalar.hpp> #include <iostream> template<typename T, typename Enabler=void> struct M { static void foo() { std::cout << "generic" << std::endl; } }; template<typename T> struct M<T, typename boost::enable_if<boost::is_scalar<T> >::type> { static void foo() { std::cout << "scalar" << std::endl; } }; template<typename T> struct M<T,typename boost::enable_if<boost::is_enum<T> >::type> { static void foo() { std::cout << "enum" << std::endl; } }; struct S {}; enum FOO { FOO1, FOO2 }; int main() { M<S>::foo(); M<int>::foo(); M<FOO>::foo(); } --------------- M<FOO>::foo() fails to compile due to ambiguity. Any pointers? Or How to get what I want? Gennadiy
On 08/04/11 10:59, Gennadiy Rozental wrote:
Hi,
It's late and I might be missing something, but I can't figure out how can I use enable_if to disambiguate 2 partial specializations where one is subcase of another one:
#include<boost/utility/enable_if.hpp> #include<boost/type_traits/is_enum.hpp> #include<boost/type_traits/is_scalar.hpp>
#include<iostream>
template<typename T, typename Enabler=void> struct M { static void foo() { std::cout<< "generic"<< std::endl; } };
template<typename T> struct M<T, typename boost::enable_if<boost::is_scalar<T> >::type> { static void foo() { std::cout<< "scalar"<< std::endl; } };
template<typename T> struct M<T,typename boost::enable_if<boost::is_enum<T> >::type> { static void foo() { std::cout<< "enum"<< std::endl; } };
M<FOO>::foo() fails to compile due to ambiguity.
Any pointers? Or How to get what I want?
enum are scalar so : template<class T> struct M<T,typename boost::enable_if< boost::is_enum<T> >::type> { static void foo() { std::cout<< "enum"<< std::endl; } }; template<typename T> struct M<T, typename boost::enable_if_c< boost::is_scalar<T>::value && !boost::is_enum<T>::value >::type> { static void foo() { std::cout<< "scalar"<< std::endl; } };
Joel Falcou <joel.falcou <at> lri.fr> writes:
template<class T> struct M<T,typename boost::enable_if< boost::is_enum<T> >::type> { static void foo() { std::cout<< "enum"<< std::endl; } };
template<typename T> struct M<T, typename boost::enable_if_c< boost::is_scalar<T>::value && !boost::is_enum<T>::value >::type> { static void foo() { std::cout<< "scalar"<< std::endl; } };
This will not really work for me. This was just an artificial example. In reality I have generic header which defines the template, another common header which defines some not very specific specialization and multiple (number changes as project grows) headers where I define more specific specializations. I can't "NOT" all the specific conditions in "scalar" level specialization - It's just not maintainable. Any other options? Thank you, Gennadiy
On 08/04/11 17:05, Gennadiy Rozental wrote:
This will not really work for me. This was just an artificial example. In reality I have generic header which defines the template, another common header which defines some not very specific specialization and multiple (number changes as project grows) headers where I define more specific specializations. I can't "NOT" all the specific conditions in "scalar" level specialization - It's just not maintainable.
Any other options?
- have a first level function enable_if on the highest catching metafunction then do tag dispatching inside to other function enabled_if on specialization of said initial test. - build sensible, non overlapping traits for your cases and use these instead of raw type traits. enable_if cant work with overlapping conditions.
On Fri, Apr 8, 2011 at 11:04 AM, Joel Falcou <joel.falcou@lri.fr> wrote:
On 08/04/11 17:05, Gennadiy Rozental wrote:
This will not really work for me. This was just an artificial example. In reality I have generic header which defines the template, another common header which defines some not very specific specialization and multiple (number changes as project grows) headers where I define more specific specializations. I can't "NOT" all the specific conditions in "scalar" level specialization - It's just not maintainable.
Any other options?
- have a first level function enable_if on the highest catching metafunction then do tag dispatching inside to other function enabled_if on specialization of said initial test.
- build sensible, non overlapping traits for your cases and use these instead of raw type traits.
enable_if cant work with overlapping conditions.
This suggestion really is your best bet. To elaborate a bit further, you don't really even want to use enable_if in this case. You really want to write something like: #include <boost/mpl/if.hpp> #include <boost/type_traits/is_enum.hpp> #include <boost/type_traits/is_scalar.hpp> #include <iostream> struct tag_other{}; struct tag_scalar{}; struct tag_enum : tag_scalar{}; // there are many better ways of writing tag_lookup. // This is just an example. template<typename T> struct tag_lookup { typedef typename boost::mpl::if_< boost::is_scalar<T>, typename boost::mpl::if_< boost::is_enum<T>, tag_enum, tag_scalar >::type, tag_other
::type type; };
template<typename T> struct M_impl; template<> struct M_impl<tag_other> { static void foo() { std::cout << "generic" << std::endl; } }; template<> struct M_impl<tag_scalar> { static void foo() { std::cout << "scalar" << std::endl; } }; template<> struct M_impl<tag_enum> { static void foo() { std::cout << "enum" << std::endl; } }; template<typename T> struct M : M_impl< typename tag_lookup<T>::type > {}; struct S {}; enum FOO { FOO1, FOO2 }; int main() { M<S>::foo(); M<int>::foo(); M<FOO>::foo(); } -- Stirling Westrup Programmer, Entrepreneur. https://www.linkedin.com/e/fpf/77228 http://www.linkedin.com/in/swestrup http://technaut.livejournal.com http://sourceforge.net/users/stirlingwestrup
Stirling Westrup <swestrup <at> gmail.com> writes:
This suggestion really is your best bet. To elaborate a bit further, you don't really even want to use enable_if in this case. You really want to write something like:
#include <boost/mpl/if.hpp> #include <boost/type_traits/is_enum.hpp> #include <boost/type_traits/is_scalar.hpp>
#include <iostream>
struct tag_other{}; struct tag_scalar{}; struct tag_enum : tag_scalar{};
// there are many better ways of writing tag_lookup. // This is just an example. template<typename T> struct tag_lookup { typedef typename boost::mpl::if_< boost::is_scalar<T>, typename boost::mpl::if_< boost::is_enum<T>, tag_enum, tag_scalar >::type, tag_other
::type type; };
template<typename T> struct M_impl;
template<> struct M_impl<tag_other> { static void foo() { std::cout << "generic" << std::endl; } };
template<> struct M_impl<tag_scalar> { static void foo() { std::cout << "scalar" << std::endl; } };
template<> struct M_impl<tag_enum> { static void foo() { std::cout << "enum" << std::endl; } };
This has at least 2 problems: 1. It does not work. I need "scalar" level specialization to be applied if enum level specialization is not present. In your case it will not even compile. Or it will choose incorrect (generic) option if you non specialized version for that. We can try to follow STL's approach of iterator tag dispatching based on hierarhy, but that really cumbersome and require extra runtime wrappers. 2. It's intrusive. Once I introduce new subgroup of types I need to go in common header and update tag selection (and potentially something else to handle relation with other groups) That's said I did not come up with something better yet. Gennadiy
On 09/04/11 05:14, Gennadiy Rozental wrote:
This has at least 2 problems:
1. It does not work. I need "scalar" level specialization to be applied if enum level specialization is not present. In your case it will not even compile. Or it will choose incorrect (generic) option if you non specialized version for that. We can try to follow STL's approach of iterator tag dispatching based on hierarhy, but that really cumbersome and require extra runtime wrappers.
I can be doen using compile-time stuff, no need for runtime.
2. It's intrusive. Once I introduce new subgroup of types I need to go in common header and update tag selection (and potentially something else to handle relation with other groups)
We faced this exact same problem in NT2 and we ended up having a whole component dedicated to complex, multiple arguments, hierarchical traits dispatching. This is a non trivial problem and it gets out of hand fast once you want to add new types familly and still be extensible. Our solution rely on having a "hierarchy" traits defined for types and use operator overload detection meta-function to select the proper hierarchy from : the actual type and the actual existence of an overload. We use this exact system in our SIMD library, so it may be of interest to make it a proper reusable component.
On Fri, Apr 8, 2011 at 9:59 AM, Gennadiy Rozental <rogeeff@gmail.com> wrote:
Hi,
It's late and I might be missing something, but I can't figure out how can I use enable_if to disambiguate 2 partial specializations where one is subcase of another one:
#include <boost/utility/enable_if.hpp> #include <boost/type_traits/is_enum.hpp> #include <boost/type_traits/is_scalar.hpp>
#include <iostream>
template<typename T, typename Enabler=void> struct M { static void foo() { std::cout << "generic" << std::endl; } };
template<typename T> struct M<T, typename boost::enable_if<boost::is_scalar<T> >::type> { static void foo() { std::cout << "scalar" << std::endl; } };
template<typename T> struct M<T,typename boost::enable_if<boost::is_enum<T> >::type> { static void foo() { std::cout << "enum" << std::endl; } };
struct S {}; enum FOO { FOO1, FOO2 };
int main() { M<S>::foo(); M<int>::foo(); M<FOO>::foo(); }
---------------
M<FOO>::foo() fails to compile due to ambiguity.
Any pointers? Or How to get what I want?
I'm assuming the problem is that the scalar case is also true for the enum case? On that basis, could you not us an enabler that asks if it is scalar && ! enum? - Rob. (That's just off the cuff - I 'spect a proper expert will be along soon!)
participants (4)
-
Gennadiy Rozental
-
Joel Falcou
-
Robert Jones
-
Stirling Westrup