
Why doesn't the following compile? I get the error "foo: function does not take 1 arguments" on VC8. #include <boost/call_traits.hpp> template < typename P1 > void foo ( typename ::boost::call_traits < P1 >::param_type p1 ) { } int main ( ) { foo ( 42 ); // error here return 0; } (Background info: I would like to use this technique to generate optimized forwarding functions that do not know anything about the function being forwarded to. Overloads taking any number of params would be generated with the boost preprocessor library.) -Jason

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Jason Hise Sent: Sunday, 25 September 2005 2:11 p.m. To: boost@lists.boost.org Subject: [boost] call_traits
Why doesn't the following compile? I get the error "foo: function does not take 1 arguments" on VC8.
#include <boost/call_traits.hpp>
template < typename P1 > void foo ( typename ::boost::call_traits < P1 >::param_type p1 ) { }
int main ( ) { foo ( 42 ); // error here return 0; }
This is the infamous non-deduced context. A nested name specifier (call_traits) is a non-deduced context so P1 cannot be deduced. You have to write foo<int>(42). This is like an invisible cast, so you have to remember to change all the explicit type specifiers if you change the type of an argument being passed in such a function.
(Background info: I would like to use this technique to generate optimized forwarding functions that do not know anything about the function being forwarded to. Overloads taking any number of params would be generated with the boost preprocessor library.)
-Jason
It might be better to write specializations for foo rather than specify the types at the point of call. Graeme

Graeme Prentice wrote: <snipped big header>
#include <boost/call_traits.hpp>
template < typename P1 > void foo ( typename ::boost::call_traits < P1 >::param_type p1 ) { }
int main ( ) { foo ( 42 ); // error here return 0; }
This is the infamous non-deduced context. A nested name specifier (call_traits) is a non-deduced context so P1 cannot be deduced. You have to write foo<int>(42). This is like an invisible cast, so you have to remember to change all the explicit type specifiers if you change the type of an argument being passed in such a function.
You mean: template <class T> void foo( typename bar<T>::baz froble) {...} is fine, but: template <class T> void foo( typename bling::bar<T>::baz froble) {...} isn't? That sounds like a language flaw!
(Background info: I would like to use this technique to generate optimized forwarding functions that do not know anything about the function being forwarded to. Overloads taking any number of params would be generated with the boost preprocessor library.)
-Jason
It might be better to write specializations for foo rather than specify the types at the point of call.
Graeme
Doesn't that defeat the point of a forwarding function? I agree that it's better than most of the alternatives, though.

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Simon Buchan
Graeme Prentice wrote: <snipped big header>
Six lines is big?
#include <boost/call_traits.hpp>
template < typename P1 > void foo ( typename ::boost::call_traits < P1 >::param_type p1 ) { }
int main ( ) { foo ( 42 ); // error here return 0; }
This is the infamous non-deduced context. A nested name specifier (call_traits) is a non-deduced context so P1 cannot be deduced. You have to write foo<int>(42). This is like an invisible cast, so you have to remember to change all the explicit type specifiers if you change the type of an argument being passed in such a function.
You mean: template <class T> void foo( typename bar<T>::baz froble) {...} is fine, but: template <class T> void foo( typename bling::bar<T>::baz froble) {...} isn't? That sounds like a language flaw!
No, neither is fine. bar<T> is a non-deduced context in both cases. What makes you think the first case is fine? There's a good reason for this restriction even though it's a surprise when you first meet it.
(Background info: I would like to use this technique to generate optimized forwarding functions that do not know anything about the function being forwarded to. Overloads taking any number of params would be generated with the boost preprocessor library.)
-Jason
It might be better to write specializations for foo rather than specify the types at the point of call.
Graeme
Doesn't that defeat the point of a forwarding function? I agree that it's better than most of the alternatives, though.
How does it defeat the point of a forwarding function? Do you mean it contradicts the requirement of the forwarding function not knowing anything about the function being forwarded to? If the specializations (or overloads) provided are the same as the call_traits ones, then the forwarding function still doesn't know anything about the function being forwarded to. Chances are the forwarding function will be inlined and the parameter types of the forwarding function won't matter so a single forwarding function that passes by value might do. Using call_traits might even be less efficient. Graeme

Graeme Prentice wrote:
-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Simon Buchan
Graeme Prentice wrote: <snipped big header>
Six lines is big?
Yep. For a header. Consider how big the headers for a quote nested 3 or 4 levels deep would get. <snip>
You mean: template <class T> void foo( typename bar<T>::baz froble) {...} is fine, but: template <class T> void foo( typename bling::bar<T>::baz froble) {...} isn't? That sounds like a language flaw!
No, neither is fine. bar<T> is a non-deduced context in both cases. What makes you think the first case is fine? There's a good reason for this restriction even though it's a surprise when you first meet it.
Huh. Was I thinking of bar<T> and bar<T>::baz, then? (I have no idea what I was thinking, I wrote that at something like 4AM :D)
(Background info: I would like to use this technique to generate optimized forwarding functions that do not know anything about the function being forwarded to. Overloads taking any number of params would be generated with the boost preprocessor library.)
-Jason
It might be better to write specializations for foo rather
than specify the
types at the point of call.
Graeme
Doesn't that defeat the point of a forwarding function? I agree that it's better than most of the alternatives, though.
How does it defeat the point of a forwarding function?
Do you mean it contradicts the requirement of the forwarding function not knowing anything about the function being forwarded to? If the specializations (or overloads) provided are the same as the call_traits ones, then the forwarding function still doesn't know anything about the function being forwarded to. Chances are the forwarding function will be inlined and the parameter types of the forwarding function won't matter so a single forwarding function that passes by value might do. Using call_traits might even be less efficient.
*shrug*, I guess. I probably don't quite understand what he's trying to do.

Jason Hise wrote:
Simon Buchan wrote:
[...]
*shrug*, I guess. I probably don't quite understand what he's trying to do.
I am trying to provide a create member function of singleton_ptr which forwards any parameters passed to it to the singleton's constructor.
Hmm, that is tricky. Do you really think you could PP generate the overloads? (I haven't seen it in the Boost functional headers, but I didn't get too deep in them) The boost headers just use P1& (they call it A1) directly, but you could try something like: template <class P1> void create( P1 & p1 ) { do_create<call_traits<P1>::param_type>(p1); } template <class P1> void create( P1 const & p1 ) // for rval arguments { do_create<call_traits<P1>::param_type>(p1); } template <class P1> void do_create( P1 p1 ) { } (Disclaimer: not tested) Of course, this means for 9 arguments, you have 512 overloads to write, and that's not counting the extra do_create overloads! (Is this where PP magic could help?)

Simon Buchan wrote:
Jason Hise wrote:
I am trying to provide a create member function of singleton_ptr which forwards any parameters passed to it to the singleton's constructor.
Hmm, that is tricky. Do you really think you could PP generate the overloads? (I haven't seen it in the Boost functional headers, but I didn't get too deep in them) The boost headers just use P1& (they call it A1) directly, but you could try something like:
template <class P1> void create( P1 & p1 ) { do_create<call_traits<P1>::param_type>(p1); }
template <class P1> void create( P1 const & p1 ) // for rval arguments { do_create<call_traits<P1>::param_type>(p1); }
template <class P1> void do_create( P1 p1 ) { }
(Disclaimer: not tested) Of course, this means for 9 arguments, you have 512 overloads to write, and that's not counting the extra do_create overloads! (Is this where PP magic could help?)
Creative approach, but unfortunately it defeats the purpose of the call_traits optimization. I suppose I could just remove call traits completely and always pass by const reference instead... Btw, if you are interested this is what said code looks like in one part of my singleton implementation: // generate create functions taking any number of params that are // forwarded to factory create function #define BOOST_PP_LOCAL_LIMITS (1, \ BOOST_SINGLETON_PTR_MAX_CONSTRUCTOR_PARAMS) #define BOOST_PP_LOCAL_MACRO(n) \ template < BOOST_PP_ENUM_PARAMS(n, typename P) > \ void create ( BOOST_PP_ENUM_BINARY_PARAMS(n, const P, & p) ) \ { \ if ( ptr ) return; \ ptr = factory.create ( BOOST_PP_ENUM_PARAMS(n, p) ); \ } #include BOOST_PP_LOCAL_ITERATE() -Jason

Jason Hise wrote:
Simon Buchan wrote:
Jason Hise wrote:
I am trying to provide a create member function of singleton_ptr which forwards any parameters passed to it to the singleton's constructor.
Hmm, that is tricky. Do you really think you could PP generate the overloads? (I haven't seen it in the Boost functional headers, but I didn't get too deep in them) The boost headers just use P1& (they call it A1) directly, but you could try something like:
template <class P1> void create( P1 & p1 ) { do_create<call_traits<P1>::param_type>(p1); }
template <class P1> void create( P1 const & p1 ) // for rval arguments { do_create<call_traits<P1>::param_type>(p1); }
template <class P1> void do_create( P1 p1 ) { }
(Disclaimer: not tested) Of course, this means for 9 arguments, you have 512 overloads to write, and that's not counting the extra do_create overloads! (Is this where PP magic could help?)
Creative approach, but unfortunately it defeats the purpose of the call_traits optimization. I suppose I could just remove call traits completely and always pass by const reference instead...
Actually, the compiler will inline out 'create', and do_create will get the right parameter types. (byval if not big, etc...) if do_create just took [const] reference, and the compiler didn't know anything about the c'tor (quite possible), it would be forced to implement every parameter by reference. You have to ask yourself if this optimisation is really worth it, though, at most you get a few more pointer dereferrencing levels than you wanted.
Btw, if you are interested this is what said code looks like in one part of my singleton implementation:
// generate create functions taking any number of params that are // forwarded to factory create function #define BOOST_PP_LOCAL_LIMITS (1, \ BOOST_SINGLETON_PTR_MAX_CONSTRUCTOR_PARAMS) #define BOOST_PP_LOCAL_MACRO(n) \ template < BOOST_PP_ENUM_PARAMS(n, typename P) > \ void create ( BOOST_PP_ENUM_BINARY_PARAMS(n, const P, & p) ) \ { \ if ( ptr ) return; \ ptr = factory.create ( BOOST_PP_ENUM_PARAMS(n, p) ); \ } #include BOOST_PP_LOCAL_ITERATE()
Wow... Even after reading the docs I can't understand that :D I assume it expands to: template < P1, P2, .. Pn > void create ( const P1& p1, const P2& p2, ... const Pn& pn ) { if ( ptr ) return; ptr = factory.create ( p1, p2, ... pn ); } right? What about nullary c'tors? Is there any way to do O(n^2) declarations like T const& and T& overloads for all parameters?

Simon Buchan wrote:
Jason Hise wrote:
Btw, if you are interested this is what said code looks like in one part of my singleton implementation:
// generate create functions taking any number of params that are // forwarded to factory create function #define BOOST_PP_LOCAL_LIMITS (1, \ BOOST_SINGLETON_PTR_MAX_CONSTRUCTOR_PARAMS) #define BOOST_PP_LOCAL_MACRO(n) \ template < BOOST_PP_ENUM_PARAMS(n, typename P) > \ void create ( BOOST_PP_ENUM_BINARY_PARAMS(n, const P, & p) ) \ { \ if ( ptr ) return; \ ptr = factory.create ( BOOST_PP_ENUM_PARAMS(n, p) ); \ } #include BOOST_PP_LOCAL_ITERATE()
Wow... Even after reading the docs I can't understand that :D I assume it expands to: template < P1, P2, .. Pn > void create ( const P1& p1, const P2& p2, ... const Pn& pn ) { if ( ptr ) return; ptr = factory.create ( p1, p2, ... pn ); } right? What about nullary c'tors? Is there any way to do O(n^2) declarations like T const& and T& overloads for all parameters?
Assuming BOOST_SINGLETON_PTR_MAX_CONSTRUCTOR_PARAMS has a value of 8, this small block actually expands to: template < typename P1 > void create ( const P1 & p1 ) { if ( ptr ) return; ptr = factory.create ( p1 ); } template < typename P1, typename P2 > void create ( const P1 & p1, const P2 & p2 ) { if ( ptr ) return; ptr = factory.create ( p1, p2 ); } /*...*/ template < typename P1, typename P2, /*...*/ typename P8 > void create ( const P1 & p1, const P2 & p2, /*...*/ const P8 & p8 ) { if ( ptr ) return; ptr = factory.create ( p1, p2, /*...*/ p8 ); } and I do provide the untemplated overload as well, separately: void create ( ) { if ( ptr ) return; ptr = factory.create ( ); } I am actually doing even more fancy stuff, in that I have additional overloads which take a 'dynamic_tag_type < Type >' as a first parameter, which tells the factory to call Type's constructor instead of SingletonType's constructor, and pass the remaining parameters to its constructor. This gives client code the ability to create the singleton as a derived type. If you really want to see the implementation of that craziness the first beta of the singleton_ptr library should be available very soon. Oh, and kudos to Pavel, since he clued me in to using BOOST_PP for this in the first place. :) -Jason
participants (3)
-
Graeme Prentice
-
Jason Hise
-
Simon Buchan