[Function] requiring an exact match in function signature?
Hello, I'm wondering if there is a way to force boost::function to only accept functions/functors with the exact same signature. I need this because there are cases in my program where passing a function with a different but convertible signature is almost certainly an error, and it would really be nice if such uses triggered a compiler error. For instance, boost::function will readily convert a mutator function with this signature: T mutate(const T&) to a function with this signature: void mutate(T&) even though the two functions clearly have different semantics. I know I could force the signatures to be the same by simply using a plain function pointer, but I want to give the user the flexibility to pass in function objects (functors) as well. Any suggestions? Cheers, Nate. _________________________________________________________________ Got a phone? Get Hotmail & Messenger for mobile! http://go.microsoft.com/?linkid=9724464
Nathan Ridge a écrit :
Hello,
I'm wondering if there is a way to force boost::function to only accept functions/functors with the exact same signature.
I need this because there are cases in my program where passing a function with a different but convertible signature is almost certainly an error, and it would really be nice if such uses triggered a compiler error.
For instance, boost::function will readily convert a mutator function with this signature:
boost::function doesn't really "convert" anything. The boost::function<sig> objects simply define an operator() with the `sig' signature, which in turns calls the wrapped function object by means of a virtual function call. So if your function is callable from a `sig' context, it works even though that's not its exact signature, because the arguments specified are convertible to the real arguments and the real return type is convertible to the specified return type. It doesn't really make sense to worry about the exact signature in my opinion; Anyway, if you want to test the signature of a function object, just take the address of &T::operator() and check it is what you want. Of course that won't work if it's a template or if it is overloaded.
Anyway, if you want to test the signature of a function object, just take the address of &T::operator() and check it is what you want. Of course that won't work if it's a template or if it is overloaded.
Could you be kind enough to give an example of how I can do such a check? Thanks, Nate. _________________________________________________________________ Hotmail & Messenger are available on your phone. Try now. http://go.microsoft.com/?linkid=9724461
Nathan Ridge a écrit :
Anyway, if you want to test the signature of a function object, just take the address of &T::operator() and check it is what you want. Of course that won't work if it's a template or if it is overloaded.
Could you be kind enough to give an example of how I can do such a check?
This works on GCC, haven't tested with other compilers. #include <boost/mpl/bool.hpp> #include <boost/mpl/push_front.hpp> #include <boost/function_types/result_type.hpp> #include <boost/function_types/parameter_types.hpp> #include <boost/function_types/member_function_pointer.hpp> #include <boost/function_types/property_tags.hpp> template<class X, class Prototype> struct function_to_member { typedef typename boost::function_types::result_type<Prototype>::type result; typedef typename boost::function_types::parameter_types<Prototype>::type args; typedef typename boost::mpl::push_front<args, X*>::type base; typedef typename boost::mpl::push_front<base, result>::type type; }; template<class X, class Prototype> struct build_member_type { typedef typename function_to_member<X,Prototype>::type root; typedef typename boost::function_types::member_function_pointer<root>::type type; }; template<class X, class Prototype> struct build_const_member_type { typedef typename function_to_member<X,Prototype>::type root; typedef typename boost::function_types::member_function_pointer< root, boost::function_types::const_qualified >::type type; }; template<class T, typename Sig> struct has_signature { typedef char not_found; struct found { char x[2]; }; template<typename X, typename build_member_type<X, Sig>::type> struct member {}; template<typename X, typename build_const_member_type<X, Sig>::type> struct const_member {}; template<typename X> static found test(member<X, &X::operator()>*); template<typename X> static found test(const_member<X, &X::operator()>*); template<typename X> static not_found test(...); static const bool value = (sizeof(found) == sizeof(test<T>(0))); typedef boost::mpl::bool_<value> type; }; example of use: struct foo { int operator()(short) const; }; struct bar { int operator()(short); }; struct baz { int operator()(int); }; int main() { std::cout << has_signature<foo, int(short)>::value << std::endl; // 1 std::cout << has_signature<bar, int(short)>::value << std::endl; // 1 std::cout << has_signature<baz, int(short)>::value << std::endl; // 0 }
Anyway, if you want to test the signature of a function object, just take the address of &T::operator() and check it is what you want. Of course that won't work if it's a template or if it is overloaded.
Could you be kind enough to give an example of how I can do such a check?
This works on GCC, haven't tested with other compilers.
[snip]
Thank you so much for this example code. Not only does this allow me to enforce an exact match in the function signature, it also allows me to avoid using boost::function altogether (the only reason I was using it was in an attempt to enforce the signature), thus avoiding the associated performance overhead. I also learned a lot about templates and metaprogramming while trying to understand how it works! I modified the code slightly to allow for both functors and plain functions. As this is my first attempt at nontrivial metaprogramming, I would very much appreciate any comments on my modifications: template<class X, class Prototype> struct function_to_member { typedef typename boost::function_types::result_type<Prototype>::type result; typedef typename boost::function_types::parameter_types<Prototype>::type args; typedef typename boost::mpl::push_front<args, X*>::type base; typedef typename boost::mpl::push_front<base, result>::type type; }; template<class X, class Prototype> struct build_member_type { typedef typename function_to_member<X, Prototype>::type root; typedef typename boost::function_types::member_function_pointer<root>::type type; }; template<class X, class Prototype> struct build_const_member_type { typedef typename function_to_member<X, Prototype>::type root; typedef typename boost::function_types::member_function_pointer<root,boost::function_types::const_qualified>::type type; }; template <typename Prototype> struct build_function { typedef typename boost::function_types::result_type<Prototype>::type result; typedef typename boost::function_types::parameter_types<Prototype>::type args; typedef typename boost::mpl::push_front<args, result>::type type_sequence; typedef typename boost::function_types::function_type<type_sequence>::type function_type; typedef typename boost::function_types::function_pointer<type_sequence>::type function_pointer_type; typedef typename boost::function_types::function_reference<type_sequence>::type function_reference_type; }; template<class T, typename Sig> struct has_signature_functor { typedef char not_found; struct found { char x[2]; }; template<typename X, typename build_member_type<X, Sig>::type V> struct member { }; template<typename X, typename build_const_member_type<X, Sig>::type V> struct const_member { }; template<typename X> static found test(member<X, &X::operator()>*); template<typename X> static found test(const_member<X, &X::operator()>*); template<typename X> static not_found test(...); static const bool value = (sizeof(found) == sizeof(test<T>(0))); typedef boost::mpl::bool_<value> type; }; template <class T, typename Sig> struct has_signature_function { static const bool value = boost::type_traits::ice_or<boost::is_same<T, typename build_function<Sig>::function_type>::value, boost::is_same<T, typename build_function<Sig>::function_pointer_type>::value, boost::is_same<T, typename build_function<Sig>::function_reference_type>::value >::value; }; template <class T, typename Sig> struct has_signature { static const bool value = boost::type_traits::ice_or<has_signature_function<T, Sig>::value, has_signature_functor<T, Sig>::value>::value; }; Thanks again! Nate. _________________________________________________________________ Live connected. Get Hotmail & Messenger on your phone. http://go.microsoft.com/?linkid=9724462
participants (2)
-
Mathias Gaunard
-
Nathan Ridge