|
|
 |
 |
 |
 |
Accessing virtuals in base class
Hi, Is there a generic way to access the (virtual) functions in the base class of a class? Like in: if (!this->Foo(something)) return(((Base*)this)->Foo(something)); // for a normal member function if (!this->Foo(something)) return(this->Base::Foo(something)); // for a virtual member function I would like to do this in a generic way, thus without having to put this line in every class specifying the proper base class Cheers, Jo
Jo wrote: > I would like to do this in a generic way, thus without having to put > this line in every class specifying the proper base class
How could the compiler know *which* version of the function you want to call if you don't specify it? Just call: Base::Foo(something);
Juha Nieminen wrote: >Jo wrote: >>I would like to do this in a generic way, thus without having to put >>this line in every class specifying the proper base class > How could the compiler know *which* version of the function you >want to call if you don't specify it? > Just call: Base::Foo(something);
I mean the immediate base class "one up" in the inheritance tree. That should be no problem for the compiler.
Jo wrote: > Juha Nieminen wrote: >> Jo wrote: >>> I would like to do this in a generic way, thus without having to put >>> this line in every class specifying the proper base class >> How could the compiler know *which* version of the function you >> want to call if you don't specify it? >> Just call: Base::Foo(something); > I mean the immediate base class "one up" in the inheritance tree. > That should be no problem for the compiler.
no? what about this: class A { public: virtual ~A() {} virtual void foo() { std::cout << "A::foo\n"; } };
class B { public: virtual ~B() {} virtual void foo() { std::cout << "B::foo\n"; } };
class C : public A, public B { public: virtual ~C() {} virtual void foo() { std::cout << "C::foo\n"; } };
what function should call C, given that inherits from two different classes that define the same virtual function? On the other side, I don't see what kind of problem do you have in specifying the base class, when it's needed. Regards, Zeppe
Zeppe wrote: > Jo wrote: >> Juha Nieminen wrote: >>> Jo wrote: >>>> I would like to do this in a generic way, thus without having to put >>>> this line in every class specifying the proper base class >>> How could the compiler know *which* version of the function you >>> want to call if you don't specify it? >>> Just call: Base::Foo(something); >> I mean the immediate base class "one up" in the inheritance tree. >> That should be no problem for the compiler. > no? what about this: > class A > { > public: > virtual ~A() {} > virtual void foo() { std::cout << "A::foo\n"; } > }; > class B > { > public: > virtual ~B() {} > virtual void foo() { std::cout << "B::foo\n"; } > }; > class C > : public A, public B > { > public: > virtual ~C() {} > virtual void foo() { std::cout << "C::foo\n"; } > }; > what function should call C, given that inherits from two different > classes that define the same virtual function? On the other side, I > don't see what kind of problem do you have in specifying the base > class, when it's needed.
Agreed, in this case the compiler could warn that there is ambiguity of course. Just like the compiler does warn about ambiguity in some other cases. But in many cases the corresponding direct base class function should be very definable. But reading your answers i suppose there is no such syntax to do so..? (though i thought to remember that i saw that somewhere)
In article <owx9i.217137$aC3.2017@phobos.telenet-ops.be>, j@mutools.com says... [ ... ] > I mean the immediate base class "one up" in the inheritance tree. > That should be no problem for the compiler.
...except when it is, such as when you've used multiple inheritance: struct B1 { virtual int foo(); };
struct B2 { virtual inf foo(); };
struct D : B1, B2 { void bar() { // if we invoke foo() here, should it mean B1::foo() // or B2::foo()? } };
I'm less concerned over the mechanism than your design -- why do you want to do this in the first place? If you're doing it so often that you need/want a way to do it conveniently/generically, that sounds to me like you're fairly routinely creating derived classes that aren't really substitutable for the base and overriding virtual functions in a way that they aren't really polymorphic. -- Later, Jerry. The universe is a figment of its own imagination.
Jo wrote:
> Agreed, in this case the compiler could warn that there is ambiguity of course. Just like the compiler does warn about ambiguity in some other cases. > > But in many cases the corresponding direct base class function should be very definable. it's the concept itself that is ambiguous... why for example the previous class in the hierarchy and not the first one? And I still don't understand why you may need this kind of syntax... > But reading your answers i suppose there is no such syntax to do so..? no, there isn't AFAIK. It would have been a specific extension for a specific situation that is not even a problem given that there is no situation in which you don't have a valid alternative. > (though i thought to remember that i saw that somewhere) > I don't think so, maybe I'm wrong... Regards, Zeppe
Jo wrote: :: Zeppe wrote:
:: ::: Jo wrote:
::: :::: Juha Nieminen wrote:
:::: ::::: Jo wrote:
::::: ::::: :::::: I would like to do this in a generic way, thus without having :::::: to put this line in every class specifying the proper base :::::: class :::::: ::::: ::::: ::::: How could the compiler know *which* version of the function ::::: you want to call if you don't specify it? ::::: ::::: Just call: Base::Foo(something); :::: :::: :::: I mean the immediate base class "one up" in the inheritance tree. :::: :::: That should be no problem for the compiler. ::: ::: ::: no? what about this: ::: ::: class A ::: { ::: public: ::: virtual ~A() {} ::: virtual void foo() { std::cout << "A::foo\n"; } ::: }; ::: ::: class B ::: { ::: public: ::: virtual ~B() {} ::: virtual void foo() { std::cout << "B::foo\n"; } ::: }; ::: ::: class C ::: : public A, public B ::: { ::: public: ::: virtual ~C() {} ::: virtual void foo() { std::cout << "C::foo\n"; } ::: }; ::: ::: what function should call C, given that inherits from two ::: different classes that define the same virtual function? On the ::: other side, I don't see what kind of problem do you have in ::: specifying the base class, when it's needed. ::: :: :: Agreed, in this case the compiler could warn that there is :: ambiguity of course. Just like the compiler does warn about :: ambiguity in some other cases. :: :: But in many cases the corresponding direct base class function :: should be very definable. If you need to refer to the direct base class, you can add a typedef to you class. class C : public A, public B { typedef A direct_base_class; // select the one you want public: virtual void foo() { direct_base_class::foo(); // do someting more } };
:: :: But reading your answers i suppose there is no such syntax to do :: so..? No, but you can easily use existing features when needed. Bo Persson
Zeppe wrote: > Jo wrote: > > Agreed, in this case the compiler could warn that there is ambiguity > of course. Just like the compiler does warn about ambiguity in some > other cases. > > But in many cases the corresponding direct base class function > should be very definable. > it's the concept itself that is ambiguous... why for example the > previous class in the hierarchy and not the first one? And I still > don't understand why you may need this kind of syntax... > > But reading your answers i suppose there is no such syntax to do so..? > no, there isn't AFAIK. It would have been a specific extension for a > specific situation that is not even a problem given that there is no > situation in which you don't have a valid alternative. > > (though i thought to remember that i saw that somewhere) > I don't think so, maybe I'm wrong...
OK, what i want to do is having a kind of run time type information in a basic way: (see the BASE word, that's what i need) class Object { // the very base class virtual long GetObjectType() { return(' obj'); } bool Is(long object_type) { return(object_type==GetObjectType()); } virtual bool IsInSomeWay(long object_type) { return(object_type==GetObjectType()); } };
class Figure : public Object { virtual long GetObjectType() { return('figr'); } virtual bool IsInSomeWay(long object_type) { if (GetObjectType()==object_type) return(true); else return(BASE::IsInSomeWay(object_type)); } };
class Rectangle : public Figure { virtual long GetObjectType() { return('rect'); } };
class Circle : public Figure { virtual long GetObjectType() { return('circ'); } };
class Ellipse : public Figure { virtual long GetObjectType() { return('ellp'); } };
Ellipse *e = new Ellipse; Circle *c = e; c->IsInSomeWay('ellp'); // returns true c->IsInSomeWay('circ'); // returns true c->IsInSomeWay('rect'); // returns false So this way the exact actual object type can be recalled, but also wether it's actually derived from another type!
Jo wrote: > Zeppe wrote: >> Jo wrote: >> > Agreed, in this case the compiler could warn that there is >> ambiguity of course. Just like the compiler does warn about ambiguity >> in some other cases. >> > But in many cases the corresponding direct base class function >> should be very definable. >> it's the concept itself that is ambiguous... why for example the >> previous class in the hierarchy and not the first one? And I still >> don't understand why you may need this kind of syntax... >> > But reading your answers i suppose there is no such syntax to do so..? >> no, there isn't AFAIK. It would have been a specific extension for a >> specific situation that is not even a problem given that there is no >> situation in which you don't have a valid alternative. >> > (though i thought to remember that i saw that somewhere) >> I don't think so, maybe I'm wrong... > OK, what i want to do is having a kind of run time type information in > a basic way: > (see the BASE word, that's what i need) > class Object { // the very base class > virtual long GetObjectType() { return(' obj'); } > bool Is(long object_type) { > return(object_type==GetObjectType()); } > virtual bool IsInSomeWay(long object_type) { > return(object_type==GetObjectType()); } > }; > class Figure : public Object { > virtual long GetObjectType() { return('figr'); } > virtual bool IsInSomeWay(long object_type) { if > (GetObjectType()==object_type) return(true); else > return(BASE::IsInSomeWay(object_type)); } > }; > class Rectangle : public Figure { > virtual long GetObjectType() { return('rect'); } > }; > class Circle : public Figure { > virtual long GetObjectType() { return('circ'); } > }; > class Ellipse : public Figure {
This must be : "class Ellipse : public Circle" of course, sorry for the typo > virtual long GetObjectType() { return('ellp'); } > }; > Ellipse *e = new Ellipse; > Circle *c = e; > c->IsInSomeWay('ellp'); // returns true > c->IsInSomeWay('circ'); // returns true > c->IsInSomeWay('rect'); // returns false > So this way the exact actual object type can be recalled, but also > wether it's actually derived from another type!
Or am i on a totally wrong path here? Maybe i should use RTTI? But isn't that taking a lot of speed/memory resources? It's about a realtime code working on interupt level, so it must be steady and fast code! (e.g. no mem allocs)
Jo wrote: > Jo wrote: >> OK, what i want to do is having a kind of run time type information in >> a basic way: >> [CUT] >> So this way the exact actual object type can be recalled, but also >> wether it's actually derived from another type! > Or am i on a totally wrong path here? > Maybe i should use RTTI? But isn't that taking a lot of speed/memory > resources? > It's about a realtime code working on interupt level, so it must be > steady and fast code! (e.g. no mem allocs)
It's a interesting problem, it makes sense (at compile time you should be able to construct such a function that returns true for a set of values, based on the inheritance of each class) and it shouldn't require memory allocation. I think the solution is based on template, I've tried to sketch a solution that uses an intermediate template class that has as arguments the Actual class (CRTP) and the parent. #include <string> #include <iostream> class Figure { public: virtual ~Figure() {} virtual std::string GetName() const = 0; virtual bool IsType(const std::string name) const { return name == "Figure"; } };
template <class Parent, class Actual> class FigureT : public Parent { public: virtual std::string GetName() const { return "Figure"; } virtual bool IsType(const std::string name) const { return name == static_cast<const Actual*>(this)->Actual::GetName() || Parent::IsType(name); } };
class Square : public FigureT<Figure,Square> { public: virtual std::string GetName() const { return "Square"; } };
class Ellipse : public FigureT<Figure,Ellipse> { public: virtual std::string GetName() const { return "Ellipse"; } };
class Circle : public FigureT<Ellipse,Circle> { public: virtual std::string GetName() const { return "Circle"; } };
int main() { Square sq; std::string type; char* yes = "yes"; char* no = "no"; type = "Square"; std::cout << "is sq a " << type << "? " << (sq.IsType(type) ? yes : no) << "\n"; type = "Figure"; std::cout << "is sq a " << type << "? " << (sq.IsType(type) ? yes : no) << "\n"; type = "Ellipse"; std::cout << "is sq a " << type << "? " << (sq.IsType(type) ? yes : no) << "\n"; Ellipse el; type = "Ellipse"; std::cout << "is el a " << type << "? " << (el.IsType(type) ? yes : no) << "\n"; type = "Figure"; std::cout << "is el a " << type << "? " << (el.IsType(type) ? yes : no) << "\n"; type = "Circle"; std::cout << "is el a " << type << "? " << (el.IsType(type) ? yes : no) << "\n"; Circle ci; type = "Figure"; std::cout << "is ci a " << type << "? " << (ci.IsType(type) ? yes : no) << "\n"; type = "Ellipse"; std::cout << "is ci a " << type << "? " << (ci.IsType(type) ? yes : no) << "\n"; type = "Circle"; std::cout << "is ci a " << type << "? " << (ci.IsType(type) ? yes : no) << "\n"; }
I think you can start from here and improve this solution that is somewhat messy. The concept is to implement some sort of recursion on the function calls based on the templates arguments. With multiple inheritance it won't work due to ambiguities... maybe it can be improved. Regards, Zeppe
Zeppe wrote: > Jo wrote: >> Jo wrote: >>> OK, what i want to do is having a kind of run time type information >>> in a basic way: >>> [CUT] >>> So this way the exact actual object type can be recalled, but also >>> wether it's actually derived from another type! >> Or am i on a totally wrong path here? >> Maybe i should use RTTI? But isn't that taking a lot of speed/memory >> resources? >> It's about a realtime code working on interupt level, so it must be >> steady and fast code! (e.g. no mem allocs) > It's a interesting problem, it makes sense (at compile time you should > be able to construct such a function that returns true for a set of > values, based on the inheritance of each class) and it shouldn't > require memory allocation. I think the solution is based on template, > I've tried to sketch a solution that uses an intermediate template > class that has as arguments the Actual class (CRTP) and the parent. > #include <string> > #include <iostream> > class Figure > { > public: > virtual ~Figure() {} > virtual std::string GetName() const = 0; > virtual bool IsType(const std::string name) const { return name == > "Figure"; } > }; > template <class Parent, class Actual> > class FigureT > : public Parent > { > public: > virtual std::string GetName() const { return "Figure"; } > virtual bool IsType(const std::string name) const { return name == > static_cast<const Actual*>(this)->Actual::GetName() || > Parent::IsType(name); } > }; > class Square > : public FigureT<Figure,Square> > { > public: > virtual std::string GetName() const { return "Square"; } > }; > class Ellipse > : public FigureT<Figure,Ellipse> > { > public: > virtual std::string GetName() const { return "Ellipse"; } > }; > class Circle > : public FigureT<Ellipse,Circle> > { > public: > virtual std::string GetName() const { return "Circle"; } > }; > int main() > { > Square sq; > std::string type; > char* yes = "yes"; > char* no = "no"; > type = "Square"; > std::cout << "is sq a " << type << "? " << (sq.IsType(type) ? yes > : no) << "\n"; > type = "Figure"; > std::cout << "is sq a " << type << "? " << (sq.IsType(type) ? yes > : no) << "\n"; > type = "Ellipse"; > std::cout << "is sq a " << type << "? " << (sq.IsType(type) ? yes > : no) << "\n"; > Ellipse el; > type = "Ellipse"; > std::cout << "is el a " << type << "? " << (el.IsType(type) ? yes > : no) << "\n"; > type = "Figure"; > std::cout << "is el a " << type << "? " << (el.IsType(type) ? yes > : no) << "\n"; > type = "Circle"; > std::cout << "is el a " << type << "? " << (el.IsType(type) ? yes > : no) << "\n"; > Circle ci; > type = "Figure"; > std::cout << "is ci a " << type << "? " << (ci.IsType(type) ? yes > : no) << "\n"; > type = "Ellipse"; > std::cout << "is ci a " << type << "? " << (ci.IsType(type) ? yes > : no) << "\n"; > type = "Circle"; > std::cout << "is ci a " << type << "? " << (ci.IsType(type) ? yes > : no) << "\n"; > } > I think you can start from here and improve this solution that is > somewhat messy. The concept is to implement some sort of recursion on > the function calls based on the templates arguments. With multiple > inheritance it won't work due to ambiguities... maybe it can be improved.
Thanks Zeppe. You're thinking creatively :) But to be honnest, the work that i should do to implement this with templates is much more than the work i just wanted to spare out by "automatically" refering to the direct base class. So, for now, i think i'll stick with explicitly writing the 'BaseClass::' thing all the time... And i'll think your idea over. Cheers, Jo
Jo wrote: > Zeppe wrote: >> Jo wrote: >>> Jo wrote: >>>> OK, what i want to do is having a kind of run time type information >>>> in a basic way: >>>> [CUT] >>>> So this way the exact actual object type can be recalled, but also >>>> wether it's actually derived from another type! >>> Or am i on a totally wrong path here? >>> Maybe i should use RTTI? But isn't that taking a lot of speed/memory >>> resources? >>> It's about a realtime code working on interupt level, so it must be >>> steady and fast code! (e.g. no mem allocs) >> It's a interesting problem, it makes sense (at compile time you >> should be able to construct such a function that returns true for a >> set of values, based on the inheritance of each class) and it >> shouldn't require memory allocation. I think the solution is based on >> template, I've tried to sketch a solution that uses an intermediate >> template class that has as arguments the Actual class (CRTP) and the >> parent. >> #include <string> >> #include <iostream> >> class Figure >> { >> public: >> virtual ~Figure() {} >> virtual std::string GetName() const = 0; >> virtual bool IsType(const std::string name) const { return name >> == "Figure"; } >> }; >> template <class Parent, class Actual> >> class FigureT >> : public Parent >> { >> public: >> virtual std::string GetName() const { return "Figure"; } >> virtual bool IsType(const std::string name) const { return name >> == static_cast<const Actual*>(this)->Actual::GetName() || >> Parent::IsType(name); } >> }; >> class Square >> : public FigureT<Figure,Square> >> { >> public: >> virtual std::string GetName() const { return "Square"; } >> }; >> class Ellipse >> : public FigureT<Figure,Ellipse> >> { >> public: >> virtual std::string GetName() const { return "Ellipse"; } >> }; >> class Circle >> : public FigureT<Ellipse,Circle> >> { >> public: >> virtual std::string GetName() const { return "Circle"; } >> }; >> int main() >> { >> Square sq; >> std::string type; >> char* yes = "yes"; >> char* no = "no"; >> type = "Square"; >> std::cout << "is sq a " << type << "? " << (sq.IsType(type) ? yes >> : no) << "\n"; >> type = "Figure"; >> std::cout << "is sq a " << type << "? " << (sq.IsType(type) ? yes >> : no) << "\n"; >> type = "Ellipse"; >> std::cout << "is sq a " << type << "? " << (sq.IsType(type) ? yes >> : no) << "\n"; >> Ellipse el; >> type = "Ellipse"; >> std::cout << "is el a " << type << "? " << (el.IsType(type) ? yes >> : no) << "\n"; >> type = "Figure"; >> std::cout << "is el a " << type << "? " << (el.IsType(type) ? yes >> : no) << "\n"; >> type = "Circle"; >> std::cout << "is el a " << type << "? " << (el.IsType(type) ? yes >> : no) << "\n"; >> Circle ci; >> type = "Figure"; >> std::cout << "is ci a " << type << "? " << (ci.IsType(type) ? yes >> : no) << "\n"; >> type = "Ellipse"; >> std::cout << "is ci a " << type << "? " << (ci.IsType(type) ? yes >> : no) << "\n"; >> type = "Circle"; >> std::cout << "is ci a " << type << "? " << (ci.IsType(type) ? yes >> : no) << "\n"; >> } >> I think you can start from here and improve this solution that is >> somewhat messy. The concept is to implement some sort of recursion on >> the function calls based on the templates arguments. With multiple >> inheritance it won't work due to ambiguities... maybe it can be >> improved. > Thanks Zeppe. > You're thinking creatively :) > But to be honnest, the work that i should do to implement this with > templates is much more than the work i just wanted to spare out by > "automatically" refering to the direct base class. > So, for now, i think i'll stick with explicitly writing the > 'BaseClass::' thing all the time... > And i'll think your idea over.
To be honnest, the more i think about this, the more i'm surprised that this is not in the C++ standard. Being able to refer to the first parent base class in an abstract way (e.g. this->_bass::foo or ((_base)this)->foo) definitely improves an OO construction! If you change the class hierarchy, now you may have to update all references to the parent base class manually!!
Jo wrote: > To be honnest, the more i think about this, the more i'm surprised that > this is not in the C++ standard. > Being able to refer to the first parent base class in an abstract way > (e.g. this->_bass::foo or ((_base)this)->foo) definitely improves an OO > construction!
As I told you, such a construct would be too specific for a problem, it would create problem with multiple inheritance, and it could be replicated with a little template. In my view, the biggest problem with your construct is that it should refer to the _first_ parent in the hierarchy. Why the first? I think there is no such a strong concept of first parent in c++ that can justify such an operator. > If you change the class hierarchy, now you may have to update all > references to the parent base class manually!!
Not with the method that i suggested you. If you don't want to implement it, it's another story ;) Anyway, probably you won't change the class hierarchy so often... Regards, Zeppe
zeppe wrote: > Jo wrote: >> To be honnest, the more i think about this, the more i'm surprised >> that this is not in the C++ standard. >> Being able to refer to the first parent base class in an abstract way >> (e.g. this->_bass::foo or ((_base)this)->foo) definitely improves an >> OO construction! > As I told you, such a construct would be too specific for a problem, > it would create problem with multiple inheritance, and it could be > replicated with a little template. In my view, the biggest problem > with your construct is that it should refer to the _first_ parent in > the hierarchy. Why the first? I think there is no such a strong > concept of first parent in c++ that can justify such an operator.
Just to be clear: with the first parent class i mean the class of which explicitly is derived from. I don't mean the root base class. The possible ambiguity in certain situations (e.g. multiple inheritance) is not a big problem. It's at the same problematic level as having ambiguous overloaded functions: the compiler warns you, and you resolve it. Not a big problem. I would suggest something analog here. >> If you change the class hierarchy, now you may have to update all >> references to the parent base class manually!! > Not with the method that i suggested you. If you don't want to > implement it, it's another story ;)
Hey Zeppe, i believe it could be a nice technical solution, definitely creative! But the bad thing with templates is that they make the code harder to read, more complex, and that's something i always try to avoid. Keeping things as simple as possible. (but using complexity where really necessary) Anyway, i did not yet decide wether or not to use your method, i'll give it some further thought... > Anyway, probably you won't change the class hierarchy so often...
Mmm, ... not sure about that. And it's also about the OO principle of encapsulation: being able to refer to the immediate base class of a class in a generic way would make things more abstract, while constructively OK. Anyway, if it's not in the syntax, then i'll have to live with it. Nothing is perfect. Thanks again, Jo
|
 |
 |
 |
 |
|