 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
GianGuz Guest
|
Posted: Wed Jan 26, 2005 7:25 pm Post subject: Pointers morphing from base to derived classes - what's the |
|
|
I want a generic base class able to guarantee access to any method
in its direct derived class. I found two possible solution:
#include <iostream>
using namespace std;
template<class DERIVED>
class A1 {
public:
A1() { }
virtual long test(const long x) { return -1; }
virtual DERIVED* morph() = 0;
~A1() { }
};
template<class DERIVED>
class A2 {
public:
A2() { }
virtual long test(const long x) { return -1; }
DERIVED* morph() { return dynamic_cast<DERIVED*>(this); }
~A2() { }
};
class B1 : public A1<B1> {
public:
B1() { }
long onlybtest() { return test(0); }
long test(const long x) { return x; }
B1* morph() { return this; }
~B1() { }
};
class B2 : public A2<B2> {
public:
B2() { }
long onlybtest() { return test(0); }
long test(const long x) { return x; }
~B2() { }
};
int main() {
A1<B1>* a1 = new B1;
A2<B2>* a2 = new B2;
//a1->morph() becomes a B1 pointer via virtual function overriding
cout << "a1->morph()->onlybtest() " << a1->morph()->onlybtest() <<
endl;
//a2->morph() becomes a B2 pointer via dynamic_cast from the base
class method
cout << "a2->morph()->onlybtest() " << a2->morph()->onlybtest() <<
endl;
}
Anyone knows about pitfall in one or both of them?!
They should be considered almost the same thing,
or can also be sensible differences
(performance, type checking, ...)?
Thanks!
Gianguglielmo
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
M Jared Finder Guest
|
Posted: Thu Jan 27, 2005 1:06 pm Post subject: Re: Pointers morphing from base to derived classes - what's |
|
|
GianGuz wrote:
| Quote: | I want a generic base class able to guarantee access to any method
in its direct derived class. I found two possible solution:
#include <iostream
using namespace std;
template
class A1 {
public:
A1() { }
virtual long test(const long x) { return -1; }
virtual DERIVED* morph() = 0;
~A1() { }
};
template
class A2 {
public:
A2() { }
virtual long test(const long x) { return -1; }
DERIVED* morph() { return dynamic_cast
~A2() { }
};
class B1 : public A1<B1> {
public:
B1() { }
long onlybtest() { return test(0); }
long test(const long x) { return x; }
B1* morph() { return this; }
~B1() { }
};
class B2 : public A2<B2> {
public:
B2() { }
long onlybtest() { return test(0); }
long test(const long x) { return x; }
~B2() { }
};
int main() {
A1<B1>* a1 = new B1;
A2<B2>* a2 = new B2;
//a1->morph() becomes a B1 pointer via virtual function overriding
cout << "a1->morph()->onlybtest() " << a1->morph()->onlybtest()
endl;
//a2->morph() becomes a B2 pointer via dynamic_cast from the base
class method
cout << "a2->morph()->onlybtest() " << a2->morph()->onlybtest()
endl;
}
Anyone knows about pitfall in one or both of them?!
They should be considered almost the same thing,
or can also be sensible differences
(performance, type checking, ...)?
|
Neither of these solutions work because of the way templating works. If
you create a new class:
class B3 : A2<B2> {}
then the dynamic_cast will fail. If you however create the class:
class B4 : A2<B4> {}
then B2 and B4 have different base classes (B2 has A2<B2> while B4 has
A2<B4>). The fundamental problem here is that there is no way to
guarantee that you have only one derived class and that every instance
of the base class actually refers to a member of the derived class.
If you want such a guarantee, you should combine the classes into one
class, since you don't want polymorphic behavior.
-- MJF
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Maxim Yegorushkin Guest
|
Posted: Thu Jan 27, 2005 8:52 pm Post subject: Re: Pointers morphing from base to derived classes - what's |
|
|
GianGuz wrote:
| Quote: | I want a generic base class able to guarantee access to any method
in its direct derived class. I found two possible solution:
|
[]
Why do you need it? I mean, why factoring out an interface and then using
it in concerned functions does not work for you? What are your goals, what
are your constraints?
--
Maxim Yegorushkin
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Daniel Krügler (ne Spange Guest
|
Posted: Thu Jan 27, 2005 9:17 pm Post subject: Re: Pointers morphing from base to derived classes - what's |
|
|
Hello!
GianGuz schrieb:
| Quote: | I want a generic base class able to guarantee access to any method
in its direct derived class. I found two possible solution:
#include <iostream
using namespace std;
template
class A1 {
public:
A1() { }
virtual long test(const long x) { return -1; }
virtual DERIVED* morph() = 0;
~A1() { }
You really should have declared the destructor as virtual (I expect, |
that you wouldn't omit this
in production code, but we both know, that this news group is also
studied by less experienced
persons, which take this kind of code as reasonable example).
I propose to use a combination of A1 with an additional helper class
template:
class Morphable { // Abstract interface (corresponds to A1, but is **no** template)
public:
virtual Morphable* morph() = 0;
virtual ~Morphable() { }
};
// Final wrapper template, providing the implemenation of morph +
possible further
// constructors (not shown)
template
class FinalMorphable : public Base {
public:
// By ommitting the virtual keyword, you can even control whether
// this function should produce static or polymorphic behaviour:
FinalMorphable* morph() { return this; }
};
// Concrete but still abstract classes, e.g.
class B1Abstract : public Morphable {
// Further stuff, but still abstract, because you **don't** override morph!!
};
typedef FinalMorphable<B1Abstract> B1; // Concrete and final class
This might be an interesting alternative. (Note that this is an
analogous solution as that provided by
Maxim Yegorushkin concerning a similar posting concerning a single,
virtual clone implementation,
search for "Implement Clone via a template").
Hope that helps,
Daniel
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
GianGuz Guest
|
Posted: Sat Jan 29, 2005 4:27 am Post subject: Re: Pointers morphing from base to derived classes - what's |
|
|
Daniel Krügler (ne Spangenberg) wrote:
| Quote: | Hello!
GianGuz schrieb:
I want a generic base class able to guarantee access to any method
in its direct derived class. I found two possible solution:
#include <iostream
using namespace std;
template
class A1 {
public:
A1() { }
virtual long test(const long x) { return -1; }
virtual DERIVED* morph() = 0;
~A1() { }
You really should have declared the destructor as virtual (I expect,
that you wouldn't omit this
in production code, but we both know, that this news group is also
studied by less experienced
persons, which take this kind of code as reasonable example).
|
You are absolutely right! I forgot it but I made a mistake in that
sense because is a group writer duty to provide always well formed and
unmisleading examples!
| Quote: |
I propose to use a combination of A1 with an additional helper class
template:
....
This might be an interesting alternative. (Note that this is an
analogous solution as that provided by
Maxim Yegorushkin concerning a similar posting concerning a single,
virtual clone implementation,
search for "Implement Clone via a template").
|
I understand the solution. At first glance seems to me perfect.
But I tried it and seems that the compiler (GCC 3.3.4) is not able
to figure out that the virtual overrided morph() return type should
be FinalMorphable and not Morphable as in the signature.
Let me show you the modified example:
#include
using namespace std;
class Morphable {
// Abstract interface (corresponds to A1, but is **no** template)
public:
virtual Morphable* morph() = 0;
virtual ~Morphable() { }
};
// Final wrapper template, providing the implemenation of morph +
possible further constructors (not shown)
template <typename Base>
class FinalMorphable : public Base {
public:
// By ommitting the virtual keyword, you can even control whether
// this function should produce static or polymorphic behaviour:
FinalMorphable* morph() { return this; }
};
// Concrete but still abstract classes, e.g.
class B1Abstract : public Morphable {
// Further stuff, but still abstract, because you **don't** override
morph!!
public:
int test() { return 1; }
};
typedef FinalMorphable<B1Abstract> B1; // Concrete and final class
int main() {
Morphable* b = new B1;
//Here is the problem!!!
//b->morph() should be recognized as
//a FinalMorphable<B1Abstract> pointer
//that has int test() has inherited member
//but it recognize it has a Morphable
//pointer so test result undeclared!
cout << b->morph()->test() << endl;
}
I also want to clear to Yegorushkin my constraints so it could
be able to idenify different approaches.
I have an Object Factory shared across various Thread-encapsulated
classess. Each Class can refer to the global factory to create
a new object of its own type. The factory stores pointers to a base
morpheable (polimorphic) object. The creation of each instance is
managed with a static template function in the factory.
Thread-encapsulated classes belongs to separated compilation units.
So we have:
factory (instance, global shared and access serialized)
class A1 in Thread 1
class A2 in Thread 2
....
class An in Thread n
* A1 want to create an instance of an object of type B1
* An want to refer and use the B1 instance created by A1
so it calls a factory.getObject(...) method that return a pointer
to B1 instance. As a developer of class A1 that want to create
B1 I would only write something like:
//file B1.h
class B1 : public Morpheable {
//all my stuff i.e. int test() { return 1; }
};
//file A1.cpp
#include "B1.h"
factory.createObject
As a developer of class An that uses B1 I would like to write
something like:
//file An.cpp
#include "B1.h"
factory.getObject(my_id)->test();
Hope to be clearer than the previous post!;)
Gianguglielmo
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Daniel Krügler (ne Spange Guest
|
Posted: Tue Feb 01, 2005 8:00 pm Post subject: Re: Pointers morphing from base to derived classes - what's |
|
|
Hello Gianguglielmo,
GianGuz schrieb:
[snip]
| Quote: | typedef FinalMorphable<B1Abstract> B1; // Concrete and final class
int main() {
Morphable* b = new B1;
//Here is the problem!!!
//b->morph() should be recognized as
//a FinalMorphable<B1Abstract> pointer
//that has int test() has inherited member
//but it recognize it has a Morphable
//pointer so test result undeclared!
cout << b->morph()->test() << endl;
}
Its seems that you want a combination of static and dynamic dispatch, |
which is
impossible to realize. If you prefer the alternative of writing
b->morph<B1>()->test()
then the morph member function template can't be virtual. Even it could,
that would be
of no much help (that is the phenomenon that MJF did describe), because
it seems that
you **want** static resolution.
| Quote: | So we have:
factory (instance, global shared and access serialized)
class A1 in Thread 1
class A2 in Thread 2
...
class An in Thread n
* A1 want to create an instance of an object of type B1
* An want to refer and use the B1 instance created by A1
so it calls a factory.getObject(...) method that return a pointer
to B1 instance. As a developer of class A1 that want to create
B1 I would only write something like:
//file B1.h
class B1 : public Morpheable {
//all my stuff i.e. int test() { return 1; }
};
//file A1.cpp
#include "B1.h"
factory.createObject<B1>(my_id);
As a developer of class An that uses B1 I would like to write
something like:
//file An.cpp
#include "B1.h"
factory.getObject(my_id)->test();
Hope to be clearer than the previous post!;)
Actually you have the same problem here. I'm not sure whether I |
completely understand
your requirements on the factory, but if you create objects via
factory.createObject<B1>(my_id);
the logical consequence of accessing this object is
factory.getObject<B1>(my_id)->test();
So, if you can solve createFactory<>, you should also be able to solve
getObject<>.
The main problem I have with your object accessibility is the following:
Do you want static type deduction or dynamic behaviour due to polymorphism?
Actually it seems, that Morphable is not very useful at all, because it
seems, that you either have to use dynamic_cast<WhatEver>(getObject(id)).test()
or getObject<WhatEver>(id).test(). The first ansatz uses dynamic behaviour and
test is part of the virtual base interface, the second one remains pure
statically resolved and so does not need any Morphable at all.
Greetings from Bremen,
Daniel
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
GianGuz Guest
|
Posted: Thu Feb 03, 2005 9:41 pm Post subject: Re: Pointers morphing from base to derived classes - what's |
|
|
Daniel Krügler (ne Spangenberg) wrote:
| Quote: | Its seems that you want a combination of static and dynamic dispatch,
which is
impossible to realize. If you prefer the alternative of writing
b->morph<B1>()->test()
then the morph member function template can't be virtual. Even it
could,
that would be
of no much help (that is the phenomenon that MJF did describe),
because
it seems that
you **want** static resolution.
|
Well, you exactly understand what I mean!;)
| Quote: | Actually you have the same problem here. I'm not sure whether I
completely understand
your requirements on the factory, but if you create objects via
factory.createObject<B1>(my_id);
the logical consequence of accessing this object is
factory.getObject<B1>(my_id)->test();
|
Yes. That was what I tried at first. It works via static resolution
but I've to take care of something like :
factory.getObject<C1>(my_id)->test();
where C1 is another class with also test() method!
In that case I've to avoid such reference to an allocated
object of type B1 "reinterpreted as" type C1.
| Quote: | So, if you can solve createFactory<>, you should also be able to
solve
getObject<>.
The main problem I have with your object accessibility is the
following:
Do you want static type deduction or dynamic behaviour due to
polymorphism?
Actually it seems, that Morphable is not very useful at all, because
it
seems, that you either have to use
dynamic_cast<WhatEver>(getObject(id)).test()
or getObject<WhatEver>(id).test(). The first ansatz uses dynamic
behaviour and
test is part of the virtual base interface, the second one remains
pure
statically resolved and so does not need any Morphable at all.
|
That is what I would like to do if it is possible.
Problems are :
- FACTORY is in a component that (as usual) DOESN'T KNOW anything
about object types that it have to create and return.
- CREATOR of an object IS in another module and thread and KNOWS
exactly what it is creating.
- OBJECT USER/REFERRER CAN BE in in another module and thread and
KNOWS about the interface of possible objects it can refer to, but
I would like to automatize the recognition of the object type
via factory.getObject(my_id)->test() that should be able to resolve
dynamically the fact that the object instance in the factory with my_id
is a B1 (as in the example) object with a test() method.
Probably the right way,as you said, is to follow a whole static (or
dynamic, but I prefer static) approach in which
factory.getObject<B1>(my_id)->test() specifies what is
getting and the factory ensure via static check that objects size
and hierarchy are compatible. I'm wrong about that?
| Quote: | Greetings from Bremen,
Daniel
|
Greetings from Rome and thanks for a so clear and useful response!
Gianguglielmo
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Daniel Krügler (ne Spange Guest
|
Posted: Sat Feb 05, 2005 10:36 am Post subject: Re: Pointers morphing from base to derived classes - what's |
|
|
Hello Gianguglielmo,
GianGuz schrieb:
| Quote: | Actually you have the same problem here. I'm not sure whether I
completely understand
your requirements on the factory, but if you create objects via
factory.createObject<B1>(my_id);
the logical consequence of accessing this object is
factory.getObject<B1>(my_id)->test();
Yes. That was what I tried at first. It works via static resolution
but I've to take care of something like :
factory.getObject<C1>(my_id)->test();
where C1 is another class with also test() method!
In that case I've to avoid such reference to an allocated
object of type B1 "reinterpreted as" type C1.
Errors might happen  |
Honestly, I don't think that you can prevent this kind of error as
compile-time checks, because
obviously the access to C1 is legal, because C1::test() exists and is
accessible. If it is not (e.g.
if my_id is incompatible for a C1 instance), then getObject<> should
throw a corresponding
exception (or return a NULL, if you don't like exceptions here).
| Quote: | That is what I would like to do if it is possible.
Problems are :
- FACTORY is in a component that (as usual) DOESN'T KNOW anything
about object types that it have to create and return.
- CREATOR of an object IS in another module and thread and KNOWS
exactly what it is creating.
- OBJECT USER/REFERRER CAN BE in in another module and thread and
KNOWS about the interface of possible objects it can refer to, but
I would like to automatize the recognition of the object type
via factory.getObject(my_id)->test() that should be able to resolve
dynamically the fact that the object instance in the factory with my_id
is a B1 (as in the example) object with a test() method.
Probably the right way,as you said, is to follow a whole static (or
dynamic, but I prefer static) approach in which
factory.getObject<B1>(my_id)->test() specifies what is
getting and the factory ensure via static check that objects size
and hierarchy are compatible. I'm wrong about that?
I would consider this direction. Note that the advantage is, that |
Factory at least has the possibility
to ensure proper consistency between registered objects and accessed
objects (even if it does not
know the types itself). So it *can* check, whether any id-driven access
was valid during runtime.
Of course, depending on your implementation it might **not** be able to
check, whether the
registrator him/herself made an error, but than you are lost anyway....
| Quote: | Greetings from Rome and thanks for a so clear and useful response!
Thanks, you are welcome! Btw.: How is the current wheather in Rome? |
Bremen: ~8°C, very cloudy....
Greetings,
Daniel
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|