|  | Detecting memfun staticness |  | |
| | | Joaquín M López Muñoz |  |
| Posted: Thu Aug 28, 2008 8:34 pm Post subject: Detecting memfun staticness |  |
| |  | |
Say I've got the following template function
template<typename T> void call_f(int x) { T t; t.f(x); }
whose purpose is to invoke t.f(x) on an object of type T:
struct foo { void f(int){} };
struct bar { static void f(int){}; };
...
call_f<foo>(5); call_f<bar>(5);
And now I'd like to optimize call_f's implementation by avoiding the creation of the T object when it is not necessary (as in bar, whose memfun f is static). The following will do for simple cases like foo and bar above:
template<typename T,typename R,typename A> void call_f_impl(int x,R(*)(A)) { T::f(x); }
template<typename T,typename Q> void call_f_impl(int x,Q) { T t; t.f(x); }
template<typename T> void call_f(int x) { call_f_impl<T>(x,&T::f); }
but fails when f is a memfun template:
struct baz { template<typename T> void f(T){} };
...
call_f<baz>(5);
For instance, Comeau says:
"ComeauTest.c", line 17: error: no instance of overloaded function "call_f_impl" matches the argument list The argument types that you used are: (int, <unknown- type>) call_f_impl<T>(x,&T::f); ^ detected during instantiation of "void call_f<T>(int) [with T=baz]" at line 42
1 error detected in the compilation of "ComeauTest.c".
Any idea about how to solve this in a more comprehensive way?
Thank you in advance,
Joaquín M López Muñoz Telefónica, Investigación y Desarrollo
-- [ See LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
| | | Roman.Perepelitsa@gmail.c |  |
| Posted: Sat Aug 30, 2008 2:40 pm Post subject: Re: Detecting memfun staticness |  |
| |  | |
On 28 Aug, 22:34, Joaquín M López Muñoz <joaq...@tid.es> wrote:
| Quote: | Say I've got the following template function
template<typename T void call_f(int x) { T t; t.f(x); }
whose purpose is to invoke t.f(x) on an object of type T:
struct foo { void f(int){} };
struct bar { static void f(int){}; };
...
call_f<foo>(5); call_f<bar>(5);
And now I'd like to optimize call_f's implementation by avoiding the creation of the T object when it is not necessary (as in bar, whose memfun f is static).
|
Example of usage:
struct non_static_int { void f(int); };
struct static_int { static void f(int); };
struct non_static_short { void f(short); };
struct static_short { static void f(short); };
struct non_static_tmpl { template <typename T> void f(T); };
struct static_tmpl { template <typename T> static void f(T); };
struct non_static_int_returns_int { int f(int); };
struct static_int_returns_int { static int f(int); };
#define STATIC_ASSERT(expr) \ do { char static_checker[(expr) ? 1 : -1]; } while (false)
#define CHECK_TRUE(type) \ STATIC_ASSERT((is_static_call_possible<type, void(int)>::value))
#define CHECK_FALSE(type) \ STATIC_ASSERT((!is_static_call_possible<type, void(int)>::value))
int main() { CHECK_TRUE(static_int); CHECK_TRUE(static_short); CHECK_TRUE(static_tmpl); CHECK_TRUE(static_int_returns_int);
CHECK_FALSE(non_static_int); CHECK_FALSE(non_static_short); CHECK_FALSE(non_static_tmpl); CHECK_FALSE(non_static_int_returns_int); }
So basically is_static_call_possible<type, r(arg1)>::result is true if and only if expression type::f(*(arg1)0) is well formed and its result can be implicitly converted to type r.
Implementation (based on LINK):
template <typename type> class void_exp_result {};
template <typename type, typename U> U const& operator,(U const&, void_exp_result<type>);
template <typename type, typename U> U& operator,(U&, void_exp_result<type>);
template <typename type, typename call_details> struct is_static_call_possible { private: class yes {}; class no { yes m[2]; };
struct derived : public type { using type::f; static no f(...); };
template <typename T, typename due_type> struct return_value_check { static yes deduce(due_type); static no deduce(...); static no deduce(no); static no deduce(void_exp_result<type>); };
template <typename T> struct return_value_check<T, void> { static yes deduce(...); static no deduce(no); };
template <typename F> struct impl;
template <typename arg1, typename r> struct impl<r(arg1)> { static const bool value = sizeof( return_value_check<type, r>::deduce( (derived::f(*(arg1*)0), void_exp_result<type>())) ) == sizeof(yes); };
// Add specializations for different number of args.
public: static const bool value = impl<call_details>::value; };
Roman Perepelitsa.
-- [ See LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
| | | Daniel Krügler |  |
| Posted: Sat Aug 30, 2008 3:28 pm Post subject: Re: Detecting memfun staticness |  |
| |  | |
On 28 Aug., 22:34, Joaquín M López Muñoz <joaq...@tid.es> wrote:
| Quote: | Say I've got the following template function
template<typename T void call_f(int x) { T t; t.f(x); }
whose purpose is to invoke t.f(x) on an object of type T:
struct foo { void f(int){} };
struct bar { static void f(int){}; };
...
call_f<foo>(5); call_f<bar>(5);
And now I'd like to optimize call_f's implementation by avoiding the creation of the T object when it is not necessary (as in bar, whose memfun f is static). The following will do for simple cases like foo and bar above:
template<typename T,typename R,typename A void call_f_impl(int x,R(*)(A)) { T::f(x); }
template<typename T,typename Q void call_f_impl(int x,Q) { T t; t.f(x); }
template<typename T void call_f(int x) { call_f_impl<T>(x,&T::f); }
but fails when f is a memfun template:
struct baz { template<typename T void f(T){} };
...
call_f<baz>(5);
For instance, Comeau says:
"ComeauTest.c", line 17: error: no instance of overloaded function "call_f_impl" matches the argument list The argument types that you used are: (int, <unknown- type>) call_f_impl<T>(x,&T::f); ^ detected during instantiation of "void call_f<T>(int) [with T=baz]" at line 42
1 error detected in the compilation of "ComeauTest.c".
|
Yes, the compiler is correct to diagnose this, because your deduction case is explicitly described in [temp.deduct.type]/16:
"A template-argument can be deduced from a pointer to function or pointer to member function argument if the set of overloaded functions does not contain function templates and at most one of a set of overloaded functions provides a unique match."
This wording has been drastically shortened in the current draft, but the essence is the same: Type deduction fails if the argument is itself a template.
| Quote: | Any idea about how to solve this in a more comprehensive way?
|
Not a perfect one, unfortunately :-(
A perfect one would probably try to apply SFINAE to differentiate cases, where the expression T::f(a) is either valid or invalid (with a of int type). I could not come up with any example that fulfills silent deduction failure. Just as an example, one might consider to try
--------------------------------- #include <stddef.h> #include <typeinfo> #include <boost/type_traits/remove_reference.hpp>
typedef char Yes; typedef char (&No)[2];
template<typename T> struct type_wrapper {};
template<size_t N> struct size_wrapper {};
template<typename T, typename Arg> struct IsStaticMemFunF1 { private: static typename boost::remove_reference<Arg>::type& arg(); template<typename U> static Yes check(type_wrapper<U>*, size_wrapper<sizeof(typeid(U::f(arg())))>* = 0); static No check(...); public: static const bool value = sizeof(check(static_cast<type_wrapper<T>*>(0))) == sizeof(Yes); };
struct foo { void f(int){} };
struct bar { static void f(int){} };
struct baz { template<typename T> void f(T){} };
struct kuz { template<typename T> static void f(T){} };
typedef bool ok1 [!IsStaticMemFunF1<foo, int>::value ? 1 : -1]; typedef bool ok2 [IsStaticMemFunF1<bar, int>::value ? 1 : -1]; typedef bool ok3 [!IsStaticMemFunF1<baz, int>::value ? 1 : -1]; typedef bool ok4 [IsStaticMemFunF1<kuz, int>::value ? 1 : -1];
int main() { } ---------------------------------
but this is not covered by the standard and will not work. The upcoming standard is going to extend these cases and given that extension I think above example should work. But let's speak of todays compiler techno- logy (In C++0x you would use a simple concept and be ready):
I think you can solve your problem in C++03, if you accept the restriction that static function call mechanism is only invoked, when the actual function is convertible to void(*)(int). This applies for all your examples, but I don't know whether this is an acceptable way to go for you, because it means that non-void return types would be ignored for the static route, similar ignorance would apply for arguments not identical to int. If you allow slight deviations (e.g. void(*)(const int&)) this could be simply added to the here proposed very simple SFINAE deduction:
----------------------------- template<bool E, typename T = void> struct enable_if{};
template<typename T> struct enable_if<true, T>{ typedef T type; };
typedef char Yes; typedef char (&No)[2];
template<typename T> struct type_wrapper {};
template<typename T, typename R, typename Arg> struct ConvertibleFunPtr { private: template<R(*)(Arg)> struct func_wrapper {}; template<typename U> static Yes check(type_wrapper<U>*, func_wrapper<&U::f>* = 0); static No check(...); public: static const bool value = sizeof(check(static_cast<type_wrapper<T>*>(0))) == sizeof(Yes); };
template<typename T> typename enable_if<ConvertibleFunPtr<T, void, int>::value>::type call_f_impl(int x) { T::f(x); }
template<typename T> typename enable_if<!ConvertibleFunPtr<T, void, int>::value>::type call_f_impl(int x) { T t; t.f(x); }
template<typename T> void call_f(int x) { call_f_impl<T>(x); }
struct foo { void f(int){} };
struct bar { static void f(int){} };
struct baz { template<typename T> void f(T){} };
struct kuz { template<typename T> static void f(T){} };
typedef bool ok1 [!ConvertibleFunPtr<foo, void, int>::value ? 1 : -1]; typedef bool ok2 [ConvertibleFunPtr<bar, void, int>::value ? 1 : -1]; typedef bool ok3 [!ConvertibleFunPtr<baz, void, int>::value ? 1 : -1]; typedef bool ok4 [ConvertibleFunPtr<kuz, void, int>::value ? 1 : -1];
int main() { call_f<foo>(5); call_f<bar>(5); call_f<baz>(5); call_f<kuz>(5); } ----------------------------------------------------
HTH & Greetings from Bremen,
Daniel
-- [ See LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
| | | Joaquín M López Muñoz |  |
| Posted: Mon Sep 01, 2008 2:04 am Post subject: Re: Detecting memfun staticness |  |
| |  | |
Hi Roman,
Your technique is very ingenious, but I've got some doubts regarding the validity of the assumptions it relies on. If I'm understanding the code, it all revolves around struct derived:
| Quote: | struct derived : public type { using type::f; static no f(...); };
|
and an assumed property that overload resolution of
derived::f(*(arg*)0)
will prefer "static no f(...)" over other overloads of f in type if these overloads happen not to be static. I have been reading the standard and cannot find any base for this assumption (although admittedly I'm no standard guru). Do you have a clue about this?
FWIW, the code you provide does not work under MSVC++ 8.0. Errors like the following show:
error C2352: 'non_static_int::f' : illegal call of non-static member function[...]
which suggests that the property stated above (ruling out non-static candidates) is not observed by this compiler. GCC, on the other hand, accepts your code fine.
I've investigated this issue separately from my original problem and will post a specific post now to clc++m. Maybe you can rejoin the discussion there.
Thank you,
Joaquín M López Muñoz Telefónica, Investigación y Desarrollo
-- [ See LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
| | | Roman.Perepelitsa@gmail.c |  |
| Posted: Mon Sep 01, 2008 10:58 am Post subject: Re: Detecting memfun staticness |  |
| |  | |
On 1 Sep, 04:04, Joaquín M López Muñoz <joaq...@tid.es> wrote:
| Quote: | Hi Roman,
Your technique is very ingenious, but I've got some doubts regarding the validity of the assumptions it relies on. If I'm understanding the code, it all revolves around struct derived:
struct derived : public type { using type::f; static no f(...); };
and an assumed property that overload resolution of
derived::f(*(arg*)0)
will prefer "static no f(...)" over other overloads of f in type if these overloads happen not to be static. I have been reading the standard and cannot find any base for this assumption (although admittedly I'm no standard guru). Do you have a clue about this?
FWIW, the code you provide does not work under MSVC++ 8.0. Errors like the following show:
error C2352: 'non_static_int::f' : illegal call of non-static member function[...]
which suggests that the property stated above (ruling out non-static candidates) is not observed by this compiler. GCC, on the other hand, accepts your code fine.
I've investigated this issue separately from my original problem and will post a specific post now to clc++m. Maybe you can rejoin the discussion there.
Thank you,
Joaquín M López Muñoz Telefónica, Investigación y Desarrollo
|
I believe my code is indeed based on gcc bug (sorry, I should've tried it with other compilers before posting). I can't find relevant text in the standard but I have a gut feeling that it is the case.
Leaving theoretical problems aside, there is a practical workaround that might work for you. You can change the logic of dispatching function to the following: - if it is possible to call nonstatic function, then call it - otherwise try to call static function For that you need "is_nonstatic_call_possible", which you can find here: LINK
Roman Perepelitsa.
-- [ See LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
| | | Greg Herlihy |  |
| Posted: Mon Sep 01, 2008 7:39 pm Post subject: Re: Detecting memfun staticness |  |
On Aug 28, 1:34 pm, Joaquín M López Muñoz <joaq...@tid.es> wrote:
| Quote: | Say I've got the following template function
template<typename T void call_f(int x) { T t; t.f(x); }
whose purpose is to invoke t.f(x) on an object of type T:
struct foo { void f(int){} };
struct bar { static void f(int){}; }; ... call_f<foo>(5); call_f<bar>(5);
And now I'd like to optimize call_f's implementation by avoiding the creation of the T object when it is not necessary (as in bar, whose memfun f is static).
|
To avoid the overhead of initializing a "T" object each time call_f<T>() is invoked, simply have the T object declared inside call_f() - be static as well:
template <class T> void call_f(int x) { static T t;
t.f(x); }
Greg
-- [ See LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
| | | Joaquín M López Muñoz |  |
| Posted: Mon Sep 01, 2008 7:40 pm Post subject: Re: Detecting memfun staticness |  |
| |  | |
On 1 sep, 12:58, "Roman.Perepeli...@gmail.com" <Roman.Perepeli...@gmail.com> wrote:
| Quote: | On 1 Sep, 04:04, Joaquín M López Muñoz <joaq...@tid.es> wrote: [...] Leaving theoretical problems aside, there is a practical workaround that might work for you. You can change the logic of dispatching function to the following: - if it is possible to call nonstatic function, then call it - otherwise try to call static function For that you need "is_nonstatic_call_possible", which you can find here:http://preview.tinyurl.com/5dkg9v
|
I'm afraid this won't work either: is_nonstatic_call_possible (named is_call_possible in the original source) only determines whether a call of the form p->f(arg) is valid, but alas static member functions are also covered by this call syntax:
struct foo { static void f(){} };
int main() { foo x; x.f(); // OK, despite foo::f being static }
So we're back to square one :(
Joaquín M López Muñoz Telefónica, Investigación y Desarrollo
-- [ See LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
| | | Joaquín M López Muñoz |  |
| Posted: Mon Sep 01, 2008 10:04 pm Post subject: Re: Detecting memfun staticness |  |
| |  | |
On 1 sep, 21:39, Greg Herlihy <gre...@mac.com> wrote:
| Quote: | On Aug 28, 1:34 pm, Joaquín M López Muñoz <joaq...@tid.es> wrote:
Say I've got the following template function
template<typename T void call_f(int x) { T t; t.f(x); } [...] And now I'd like to optimize call_f's implementation by avoiding the creation of the T object when it is not necessary (as in bar, whose memfun f is static).
To avoid the overhead of initializing a "T" object each time call_f<T>() is invoked, simply have the T object declared inside call_f() - be static as well:
template <class T void call_f(int x) { static T t;
t.f(x); }
|
Well, my probem statement is a simplification of the real scenario; actually, I'm not creating t, but rather retrieving a reference to it through a Meyers singleton, something like this:
template<class T> struct singleton { static T& get() { static T t; return t; } };
template <class T> void call_f(int x) { singleton<T>::get().f(x); }
The problem is that the compiler does not optimize the singleton<T>::get() call away when T::f is static because obtaining the singleton reference has side effects, namely the creation of t the first time get() is called. The translation of your proposal to this scenario:
template <class T> void call_f(int x) { T& t=singleton<T>::get(); t.f(x); }
is also suboptimal because the *presence* of a static local incurs an execution overhead every time call_f is invoked, precisely for the same reason as above, first-time-creation code must be executed on every call_f invocation.
Joaquín M López Muñoz Telefónica, Investigación y Desarrollo
-- [ See LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
| | | Greg Herlihy |  |
| Posted: Wed Sep 03, 2008 8:36 am Post subject: Re: Detecting memfun staticness |  |
| |  | |
On Sep 1, 3:04 pm, Joaquín M López Muñoz <joaq...@tid.es> wrote:
| Quote: | On 1 sep, 21:39, Greg Herlihy <gre...@mac.com> wrote:
To avoid the overhead of initializing a "T" object each time call_f<T>() is invoked, simply have the T object declared inside call_f() - be static as well:
template <class T void call_f(int x) { static T t;
t.f(x); }
The problem is that the compiler does not optimize the singleton<T>::get() call away when T::f is static because obtaining the singleton reference has side effects, namely the creation of t the first time get() is called. The translation of your proposal to this scenario:
template <class T void call_f(int x) { T& t=singleton<T>::get(); t.f(x); }
is also suboptimal because the *presence* of a static local incurs an execution overhead every time call_f is invoked, precisely for the same reason as above, first-time-creation code must be executed on every call_f invocation.
|
The call to singleton<T>::get() will be executed only once - if the T& reference variable inside call_f() is declared static as well:
template <class T> void call_f(int x) { static T& t = singleton<T>::get();
t.f(x); }
Greg
-- [ See LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
| | | Joaquín M López Muñoz |  |
| Posted: Wed Sep 03, 2008 8:58 pm Post subject: Re: Detecting memfun staticness |  |
On 3 sep, 10:36, Greg Herlihy <gre...@mac.com> wrote:
| Quote: | On Sep 1, 3:04 pm, Joaquín M López Muñoz <joaq...@tid.es> wrote:
The call to singleton<T>::get() will be executed only once - if the T& reference variable inside call_f() is declared static as well:
template <class T void call_f(int x) { static T& t = singleton<T>::get();
t.f(x); }
|
Yep, but we incur the first-time-creation code penalty again, this time associated with the static local t :(
Joaquín M López Muñoz Telefónica, Investigación y Desarrollo
-- [ See LINK for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
| |
|
|