Topic: Polymorphic function pointers
Author: remove.haberg@matematik.su.se (Hans Aberg)
Date: Mon, 30 Apr 2001 11:06:51 GMT Raw View
In article <vlvgnnu7yt.fsf@telgt.cs.uu.nl>, Ren van Oostrum
<rene+gnus@cs.uu.nl> wrote:
>You seem to be missing the point. Simplicity has nothing to do with
>it; it's all about type-safety.
...
> class B {void f(); };
...
> class D : public B { void g(); };
...
> void (D::* mfpd)() = &B::f; // OK
> void (B::* mfpb)() = &D::g; // No!
>
>Note that in the second case the conversion can be forced with a
>static_cast, but in that case one has to make sure that the object on
>which the member function is called actually has a member function g:
The C++ standard is so written that one should never under any
circumstances coerce a function pointer to another type of function
pointer unless one before evaluation coerces back to its original type.
(With the exception of the static member functions, because these are
essentially a different name for a global function pointer.) The reason is
the simplicity chosen of the underlying compiler implementation model.
In the examples you gave above, runtime conversions from a class to its
base is easy, because at runtime one mereley uses the offsets of the base,
and it will lok like one of those objects.
But suppose you try to do that with a function pointer: The function
pointer does not know anything about the runtime objects it gets as its
argument. So when the wrong sized objects are piled up on the parameter
stack, the converted function pointer will make the wrong offset
computations.
I tried a lot with different function pointer conversions until a form of
the example I gave was posted in the C++ newsgroup.
My suggestion is _never_ convert a function pointer cast of any sort but
use template variations of that example.
But sure, one can think of different C++ extensions in this respect. But
then open will also have to think a lot on how the compiler should be able
to implement it.
Hans Aberg * Anti-spam: remove "remove." from email address.
* Email: Hans Aberg <remove.haberg@member.ams.org>
* Home Page: <http://www.matematik.su.se/~haberg/>
* AMS member listing: <http://www.ams.org/cml/>
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: remove.haberg@matematik.su.se (Hans Aberg)
Date: Sun, 29 Apr 2001 13:16:32 GMT Raw View
In article <9ccsbm$d1alq$1@ID-3706.news.dfncis.de>, "Markus Schaaf"
<m.schaaf.exp-jun2001@gmx.de> wrote (with subject "conversion: ptr to
member -> ptr to base of member (was: Re: passing member function as a
parameter)"):
>> ... Pointer to derived member is not necessarily
>> convertable to pointer to base member.
>
>That's a shame. It seems to me that this conversion is always
>possible. Am I missing something? Is this simply an omission
>in the standard or are there actually reasons to do so?
Yes, you are missing the facts about how the runtime code works: Typically
one puts the pointer of the function on the parameter stack plus some
arguments, and hands it over to the function evaluator. When the function
has finished evaluating, the top of the stack is now what the function
returns.
It means that on this primitive level, no conversions can take place. If
allowed in the C++ language, something extra must be added for it to work.
This is a deliberate exclusion, in the name of simplicity.
Actually, I intended to write a "feature request" for this feature,
polymorphic function pointers.
The feature can be implemented, as all other polymorphic features, by the
use of the only polymorphic type that currently exists in C++, the
polymorphic function pointer:
Suppose you have class object_root which the others are derived from.
Then, if your functions T is derived from object_root, and the type of
your function is
A (T::*)(B)
write an abstract class
class object_root_method_base {
public:
virtual ~object_root_method_base() {}
virtual object_root_method_base* clone() = 0; // Virtual copy constructor.
virtual A apply(object_root*, B) = 0;
};
Then write
template<class T> // or: <typename T>
class object_root_method : public virtual object_root_method_base {
A (T::*f)(B);
public:
root_method(A (T::*f)(B)) : f(f) { }
root_method* clone() { return new object_root_method<T>(*this); }
virtual A apply(object_root* op, B b) {
T* tp = dynamic_cast<T*>(op);
if (tp != 0) return (tp->*f)();
else // error handling.
}
};
You can create new object_root_method's by the macro
#define new_object_method(V,f) new object_root_method<V>(&V ## :: ## f)
So if you have say
class T : public virtual object_root {
public:
A g(B);
};
you can write
object_method* fp = new_object_method(T, g);
object_root* xp = new ...;
A a;
B b;
a = op->apply(xp, b);
Usually one wraps up the object_root pointers in a class object which
maintains the polymorphic pointer object_root. If you derive
object_root_method_base from the object_root, they too become object. If
you need to copy the object_root_method object, use the clone operator.)
But returning to the original discussion, this shows some problems of
introducing polymorphic function pointers in the C++ language:
In the code part above "error handling", one must figure out a good
replacement. For example, in the code I use, I have a default function to
be executed, so there is no immediate error.
A drawback with the code above is that dynamic allocations are usually
very slow. So that is, I think, a good reason for introducing polymorphic
function pointers into the C++ language.
Hans Aberg * Anti-spam: remove "remove." from email address.
* Email: Hans Aberg <remove.haberg@member.ams.org>
* Home Page: <http://www.matematik.su.se/~haberg/>
* AMS member listing: <http://www.ams.org/cml/>
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]