Prev: Why does inserter need Iterator template parameter
Next: Detecting when a function in a CRTP base class is hidden
From: Johannes Schaub (litb) on 16 Feb 2010 11:05 Cory Nelson wrote: > Hello all, > > I am using CRTP (the Curiously Recurring Template Pattern) like so: > > template<T> > struct base > { > void do_something() > { > prepare(); > static_cast<T*>(this)->hide_me(); > finalize(); > } > > void hide_me() {} > }; > > struct derived : base<derived> > { > void hide_me() { /* do something else */ } > }; > > I have a lot of these hideable functions, and different types of T > need to hide different functions. In this situation, it would be more > efficient to simply not do the prepare() or finalize() stage if these > functions are not implemented in the derived type. > > So I'd like to find a way to detect if a function is hidden or not, in > a way that works at compile-time so that I can use it with templates > to prevent the prepare() or finalize() code from even being > instantiated. Does anyone have any ideas? > Assuming that the derived class implements a fixed interface. The sizeof works at compile time. The below is a simple example that is not entirely compile time, of course: template<typename T> struct base { template<typename F> static identity<char[1]>::type &check(F base::*); template<typename F> static identity<char[2]>::type &check(F T::*); void do_something() { if(sizeof check<void()>(&T::hide_me) == 2) prepare(); static_cast<T*>(this)->hide_me(); if(sizeof check<void()>(&T::hide_me) == 2) finalize(); } void hide_me() { return unimplemented(); } }; struct derived : base<derived> { void hide_me() { /* do something else */ } }; Notice that because of a restriction in the Standard, compilers will reject "void()const". You will have to write "typedef void ft() const; check<ft>(....)" to check a const member function. But even then, at least one compiler (remarkable, Comeau/EDG) rejects it. I think tho that the issue is not all that clear. The Standard forbids a cv- qualifier-seq like "void()const" except when nested in a member pointer, when used for declaring class member functions or in a typedef. In my opinion, the Standard is not sufficiently clear whether this is a lexical rule (i.e that one merely cannot write "void()const"), or a semantical rule (that a type cannot be denoted if not by a typedef-name (in C++03 "F" above will violate such a requirement), or implicitly as part of a member pointer, etc). For the logs: clang accepts the code: It apparently interprets it lexically, while comeau interprets it semantically. In any case, to summarize: It won't work portably with const member functions. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Gert-Jan de Vos on 16 Feb 2010 11:01
On Feb 16, 7:09 am, Cory Nelson <phro...(a)gmail.com> wrote: > Hello all, > > I am using CRTP (the Curiously Recurring Template Pattern) like so: > > template<T> > struct base > { > void do_something() > { > prepare(); > static_cast<T*>(this)->hide_me(); > finalize(); > } > > void hide_me() {} > > }; > > struct derived : base<derived> > { > void hide_me() { /* do something else */ } > > }; > > I have a lot of these hideable functions, and different types of T > need to hide different functions. In this situation, it would be more > efficient to simply not do the prepare() or finalize() stage if these > functions are not implemented in the derived type. > > So I'd like to find a way to detect if a function is hidden or not, in > a way that works at compile-time so that I can use it with templates > to prevent the prepare() or finalize() code from even being > instantiated. Does anyone have any ideas? You can try to match a T::hide_me member function pointer with both void (base::*)() and void (T::*)(). If T::hide_me is not defined, then it will match void (base::*)(). #include <iostream> template <typename T> struct base { void do_something() { do_something(&T::hide_me); } void do_something(void (base::*)()) { // There is no T::hide_me, do nothing.. } void do_something(void (T::*)()) { prepare(); static_cast<T*>(this)->hide_me(); finalize(); } void prepare() { std::cout << "prepare()\n"; } void finalize() { std::cout << "finalize()\n"; } void hide_me() {} }; struct derived1 : base<derived1> { void hide_me() { /* do something else */ } }; struct derived2 : base<derived2> { }; int main() { std::cout << "derived1\n"; derived1 d1; d1.do_something(); std::cout << "derived2\n"; derived2 d2; d2.do_something(); } -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |