473,396 Members | 1,599 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

Why pointer to member function?

Ben
Hi, there.

Recently I was working on a problem where we want to save generic
closures in a data structure (a vector). The closure should work for
any data type and any method with pre-defined signature.

When developing this lib, I figured that the
pointer-to-member-function, although seemingly an attractive solution,
does not work well for us.

The size and the memory model of a generic PTMF is not guaranteed,
therefore, cannot be saved in a heterogeneous container.

My solution turned out to be a C callback function pointer.

That is, instead of using a "int T::(*f)()", use a "int (*f)(T*)".

Although a wrapper function that does the forward call is needed, it
solved our problem.
For instance:

instead of calling

add_closure(&obj, &MyClass::f);

I do
int mywrapper(*MyClass pobj){
return pobj->f();
}
add_closure(&obj, &mywrapper);

Here, 2 assumptions were made:
1. all pointers have the same size as void*
2. all function pointers have the same size.

Then, the annoying wrapper function makes me think:

Why PTMF in the first place?
What if the language generates the wrapper function implicitly and
makes MyClass::f a regular function pointer of type int (*)
(MyClass*)?

At least it can make my life much easier. And I wonder whose life will
it make harder?

Dislike the ptmf(pobj) syntax? Still like the pobj->ptmf()syntax? No
problem, the language can still do that, as long as it guarantees the
binary memory model of a PTMF is a function pointer. I don't mind
doing a reinterpret_cast here.

Well, just when I'm about to finish, my gut's feeling starts yelling
hard: "No! It can't be right. This must have been thought over 1000
times! You must be missing something here."

"All right, sir. so what am I missing here?"
Jul 22 '05 #1
37 4928
"Ben" <be****@asc.aon.com> wrote in message
news:ff*************************@posting.google.co m...
Hi,
Recently I was working on a problem where we want to save generic
closures in a data structure (a vector). The closure should work for
any data type and any method with pre-defined signature.

[....]

I recommend you take a look at boost::function:
http://www.boost.org/doc/html/function.html

An equivalent class is expected to be included in the next
revision of the C++ standard. See:
http://anubis.dkuug.dk/jtc1/sc22/wg2...2003/n1540.pdf
Cheers,
Ivan
--
http://ivan.vecerina.com/contact/?subject=NG_POST <- e-mail contact form
Brainbench MVP for C++ <> http://www.brainbench.com
Jul 22 '05 #2

"Ben" <be****@asc.aon.com> wrote in message
news:ff*************************@posting.google.co m...
Hi, there.

Recently I was working on a problem where we want to save generic
closures in a data structure (a vector). The closure should work for
any data type and any method with pre-defined signature.

When developing this lib, I figured that the
pointer-to-member-function, although seemingly an attractive solution,
does not work well for us.

The size and the memory model of a generic PTMF is not guaranteed,
therefore, cannot be saved in a heterogeneous container.

My solution turned out to be a C callback function pointer.

That is, instead of using a "int T::(*f)()", use a "int (*f)(T*)".

Although a wrapper function that does the forward call is needed, it
solved our problem.
Have you seen http://www.boost.org/libs/libraries....ction-objects?
Which does all of the above and more. And boost::function at least is in the
upcoming C++ TR1.

typedef boost::function< int() > tFnc;
typedef std::vector< tFnc > tFncs;

tFncs.push_back( boost::bind( &MyClass::f, pobj ) );

int somevalue = (tFncs.front())(); // = pobj->f();

Or use boost::signals:

boost::signal< int() > mysig;

mysig.connect( boost::bind( &MyClass::f, pobj1 ) );
mysig.connect( boost::bind( &MyClass::f, pobj2 ) );

mysig(); // executes both of the above

You can even specify combiners that do something with the return values from
each f() call.

Jeff F
For instance:

instead of calling

add_closure(&obj, &MyClass::f);

I do
int mywrapper(*MyClass pobj){
return pobj->f();
}
add_closure(&obj, &mywrapper);

Here, 2 assumptions were made:
1. all pointers have the same size as void*
2. all function pointers have the same size.

Then, the annoying wrapper function makes me think:

Why PTMF in the first place?
What if the language generates the wrapper function implicitly and
makes MyClass::f a regular function pointer of type int (*)
(MyClass*)?

At least it can make my life much easier. And I wonder whose life will
it make harder?

Dislike the ptmf(pobj) syntax? Still like the pobj->ptmf()syntax? No
problem, the language can still do that, as long as it guarantees the
binary memory model of a PTMF is a function pointer. I don't mind
doing a reinterpret_cast here.

Well, just when I'm about to finish, my gut's feeling starts yelling
hard: "No! It can't be right. This must have been thought over 1000
times! You must be missing something here."

"All right, sir. so what am I missing here?"

Jul 22 '05 #3
Ben wrote:
...
What if the language generates the wrapper function implicitly and
makes MyClass::f a regular function pointer of type int (*)
(MyClass*)?

At least it can make my life much easier. And I wonder whose life will
it make harder?

Dislike the ptmf(pobj) syntax? Still like the pobj->ptmf()syntax? No
problem, the language can still do that, as long as it guarantees the
binary memory model of a PTMF is a function pointer. I don't mind
doing a reinterpret_cast here.

Well, just when I'm about to finish, my gut's feeling starts yelling
hard: "No! It can't be right. This must have been thought over 1000
times! You must be missing something here."
...


You are missing the fact that in general case the situation with member
function calls is much more complex than in case of ordinary function
calls. Consider the following example

struct A { int a; };

struct B {
int b;
void bar() { b = 1; }
};

struct C : A, B {
int c;
void baz() { c = 2; }
};

void foo(B* pb, void (B::*pbm)()) {
(pb->*pbm)();
}

int main()
{
B b;
foo(&b, &B::bar);

C c;
foo(&c, static_cast<void (B::*)()>(&C::baz));
}

Both calls to 'foo' and consequent indirect calls to member functions
'bar' and 'baz' are valid in C++. Note though, that in order to perform
the member call inside 'foo' correctly the compiler shall be able to
produce the correct 'this' pointer to pass to the member function being
called. In general case, the compiled code will have to perform
adjustment of object pointer value before the actual call. The nature of
the correction depends on the memory layout of the class (which is
implementation-dependent). In the above example there's no direct need
for any correction inside the first call to 'foo', but it might be
necessary to adjust 'pb's value inside the second call to 'foo' (since
we need a pointer to the entire 'C' object, not to a 'B' subobject). In
general case, the information required for this adjustment is generated
by 'static_cast' and stored in the pointer itself, along with the
address of the member function's entry point. For this reason, member
function pointers are usually bigger than ordinary function pointers and
there's no way to replace the former with the latter (unless you are
willing to increase the size of the latter, which in some applications
will result in waste of memory).

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #4
Ben
Jeff,
Thanks for refering me to this nice article.

boost function does look clean and powerful. I have no doubt about the
usefulness of this lib.

However, it does not answer my question.

Let me rephrase my question and reasoning:

I want a simple,clean,cheap way that can save the member function
closure into a vector, which implies that the element size has to be
fixed for any type of closure.

And because of the following 2 reasons, it is not possible to save a
ptm INLINE in a vector:

1. pointers to member function don't have the same size and the
maximum size cannot be even predictable.

2. don't want to save a pointer to a pointer to a member function
because a pointer to a member function is normally a constant.

the boost lib does prove that I'm not wrong on this. It uses "new" to
allocate space for the closure in heap! That gets around the problem.

Although the interface of boost looks very pleasant and all the memory
management are hidden under the hood, the fact that "new" has to be
used for what should have been a vanilla thing makes me feel sad.

In my specific case, I don't need all the rich features such as
bindXXX, all I need is to pass a member function for a hook callback.
And calling a "new" for every closure where I don't have to is a bit
of a overkill to me. I don't quite like to pay that price.

My final question about "why pointer to member function at all?" came
from this thought:
What if, let's just say "what if", there's no "PTM" at all, MyClass::f
simply yields a function pointer of type "int (*)(MyClass*)"?

Isn't that simpler, faster and has better compatibility with the
already-existing function pointer? And, more importantly, does it
work?
The "D&E" book justifies PTM as:

"... This only worked with a liberal sprinkling of explicit casts that
never ought to have worked in the first place. It also relied on the
assumption that a member function is passed its object pointer as the
first argument in the way Cfront implements it"

My arguments are:
1. It can be type safe to make T::*f a function pointer of
"int(*)(T*)", if the language wants to do so. Since I can create the
wrapper function manually, there's no reason the compiler cannot
implicitly create it.

2. Making "MyClass::f" a pointer of type "int(*)(T*)" does NOT rely on
an implementation where the "this pointer" is passed as the first
argument.

Evidence? Well, look at the wrapper function again.

Whatever the implementation is, you can pass it as the first param,
the second, or weirdly enough - from a global variable, the function
pointer "MyClass::f" simply points to a function from which you can
eventually invoke the real underlying member function.

There's no promise made that this function must have the same address
of the real "member function". It may be the same address of the real
function, may be an address of a proxy function, all up to the
implementation.

3. Even if we do want a PTM where we can enjoy the favorable syntax
"p->ptm(...)", the implementation of PTM can still be such function
pointer.
Jul 22 '05 #5
Ben wrote:
...
I want a simple,clean,cheap way that can save the member function
closure into a vector, which implies that the element size has to be
fixed for any type of closure.
...
Unfortunately in ?++ there's no portable way to implement the kind of
closure that would produce an ordinary function pointer (if that's what
you are looking for), although I've seen many platform-specific solutions.

If you are looking for a portable solution, there's no other choice but
to store both the object reference and the member function pointer. You
can do it yourself (as you described in your original message) or you
can a solution provided by some library, you'll still get a _functional_
_object_ as the result of the closure, never a function pointer. In one
way or another you'll have to manage memory occupied by that object.
And because of the following 2 reasons, it is not possible to save a
ptm INLINE in a vector:

1. pointers to member function don't have the same size and the
maximum size cannot be even predictable.
Actually in practical implementations they normally do have the same
size. It is just not the same as ordinary function pointers.
...
My final question about "why pointer to member function at all?" came
from this thought:
What if, let's just say "what if", there's no "PTM" at all, MyClass::f
simply yields a function pointer of type "int (*)(MyClass*)"?

Isn't that simpler, faster and has better compatibility with the
already-existing function pointer? And, more importantly, does it
work?
It doesn't work in general case. I explained the problem in my previous
message.

The "D&E" book justifies PTM as:

"... This only worked with a liberal sprinkling of explicit casts that
never ought to have worked in the first place. It also relied on the
assumption that a member function is passed its object pointer as the
first argument in the way Cfront implements it"

My arguments are:
1. It can be type safe to make T::*f a function pointer of
"int(*)(T*)", if the language wants to do so. Since I can create the
wrapper function manually, there's no reason the compiler cannot
implicitly create it.

2. Making "MyClass::f" a pointer of type "int(*)(T*)" does NOT rely on
an implementation where the "this pointer" is passed as the first
argument.

Evidence? Well, look at the wrapper function again.

Whatever the implementation is, you can pass it as the first param,
the second, or weirdly enough - from a global variable, the function
pointer "MyClass::f" simply points to a function from which you can
eventually invoke the real underlying member function.

There's no promise made that this function must have the same address
of the real "member function". It may be the same address of the real
function, may be an address of a proxy function, all up to the
implementation.


Your wrapper function has significantly narrower functionality than PMFs
in the language. It always calls a function with pre-defined name. Take
a look at my other message. The functionality presented by an example
there cannot be implemented by such wrapper function.

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #6
"Ben" <be****@asc.aon.com> wrote in message
The size and the memory model of a generic PTMF is not guaranteed,
therefore, cannot be saved in a heterogeneous container.


It is garaunteed, I think. On my platform sizeof(member function pointer) =
8 bytes, whereas sizeof(non-member function pointer) = 4 bytes.
Jul 22 '05 #7
Andrey Tarasevich wrote:
...
Your wrapper function has significantly narrower functionality than PMFs
in the language. It always calls a function with pre-defined name.
...


I admit that saying that "It always calls a function with pre-defined
name" is not very precise description of the problem with your
wrapper-based approach. Let me make another, more organized attempt at
the explanation.

There is an important change that took place between the first and the
second version on draft C++ specification (and made it into the final
standard) when it comes to PMFs. If you care to see it for yourself, I
suggest you compare 5.2.9/9 ('Static cast') in these versions of the
document and also note the addition of 5.5/4 ('Pointer to member
operators') in the latter. Essentially this change boils down to one
thing: new version allows upcasts for member pointers (including PMFs)
with 'static_cast'. It was illegal in the first draft. This seemingly
innocent change has very serious consequences. Using PMFs, it became
possible to call member function of a derived class with an object
expression of base class type (the example in my first message
demonstrates such a call). The obvious problem here is that in order to
perform such a call the program has to be able to obtain the correct
'this' pointer for the derived class' member function. All it has at the
moment of the call is two pointers: a pointer to some base class
subobject and a PMF.

In a single inheritance context the problem has trivial solution. Simply
organize class memory layouts in such a way that all base class
subobjects start at the same address as the entire object. In this case
a pointer to a base class subobject can be immediately used as a pointer
to the derived object. No adjustments required.

But once we get into the realm of multiple inheritance, things get more
complicated. It is no longer possible to keep all base class subobjects
aligned at the origin of the entire object. Some base class subobjects
will have to reside at certain offset from the origin. This immediately
means that in order to perform the correct member function invocation
through a PMF the code might be required to "convert" base class pointer
to derived class pointer by adding certain non-zero offset to the
former. Note that the concrete derived type is not known at compile
time, which means that this functionality will require certain amount of
additional run-time information in order to be able to calculate the
offset in question. This additional information (most often - the offset
itself) is usually stored inside the PMF. In a traditional
implementation a PMF contains two pieces of information: the target
address (function entry point) and the offset (adjustment value for
object pointer). That's what turns PMFs into beast of completely
different nature from ordinary function pointers.

Now, your wrapper-based approach is not capable of supporting this
particular functionality of PMFs (calling derived class' member function
with an object expression of base class type). Your approach is more or
less equivalent to PMF functionality described in the first draft of C++
specification. Moreover, it is safe to say that if that first version
made it into the final standard, we would be working today with PMFs
that have the same size as ordinary function pointers and your wrappers
would not be necessary (at least for your purposes). But that's not the
case.

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #8
be****@asc.aon.com (Ben) wrote in message news:<ff*************************@posting.google.c om>...
Hi, there.

Recently I was working on a problem where we want to save generic
closures in a data structure (a vector). The closure should work for
any data type and any method with pre-defined signature.

When developing this lib, I figured that the
pointer-to-member-function, although seemingly an attractive solution,
does not work well for us.

The size and the memory model of a generic PTMF is not guaranteed,
therefore, cannot be saved in a heterogeneous container.


Wrong. The basic assumptions are right, but the logic is flawed. Any
particular PTMF can be saved. Other PTMFs can be cast to that single
type, and be cast back where used.

E.g.
class InternalHelperClass;
typedef (void InternalHelperClass::* PTMF)();

PTMF genPtr = reinterpret_cast<PTMF>( &MyClass::MyMethod );

PTMF may have a different implementation than &MyClass::MyMethod,
which means that genPtr can't be used directly. However, if it
is casted to the correct MyClass::* type, the resulting value
cab be used. At the point of call, you have all arguments for
MyMethod, so logiaclly you do know the type you must cast to.

(rest snipped, followed from flawed logic)

Regards,
Michiel Salters
Jul 22 '05 #9
Ben
Andrey,
Thanks for the attention and the detailed explanation.

Your example clarifies the difficulty to represent a PTM at runtime,
which I totally agree.

However, do we really need a PTM at runtime? Is it necessary? We don't
have any pointer arithmetic for PTM that makes a PTM variable really
meaningful, do we?

In most cases, where people do "&T::foo", the PTM value is known at
compile time and can be safely transformed to a function pointer.

In my wrapper function approach, I don't attempt to pass the PTM to
the function at all.

Here's my solution to the example:

void foo_bar(B* pb) {
pb->bar();
}
void foo_baz(C* pc){
pc->baz();
}
int main()
{
B b;
foo_bar(&b); //correspond to B::bar
C c;
foo_baz(&c); //correspond to C::baz
}

What I'm hoping is that the compiler can implicitly generate foo_bar
and foo_baz for me. That saves a lot of keystrokes.
As a bonus,such compiler generated wrapper functions should take
advantage of the tail-call and makes minimal overhead for extra
function-call and no overhead or side-effect for extra parameter
passing and return value copying. In other words, RVO is guaranteed
here.
Jul 22 '05 #10
Ben
Siemel,

Although some implementations may use the same size, it is not stated
in the standard that they will be of the same size.
As far as I know, VC uses different sizes for classes with and without
virtual functions and multi-inheritance.
"Siemel Naran" <Si*********@REMOVE.att.net> wrote in message news:<1C*******************@bgtnsc04-news.ops.worldnet.att.net>...
"Ben" <be****@asc.aon.com> wrote in message
The size and the memory model of a generic PTMF is not guaranteed,
therefore, cannot be saved in a heterogeneous container.


It is garaunteed, I think. On my platform sizeof(member function pointer) =
8 bytes, whereas sizeof(non-member function pointer) = 4 bytes.

Jul 22 '05 #11
On 11 Jun 2004 04:14:22 -0700, Mi*************@logicacmg.com (Michiel
Salters) wrote:

[snip]
class InternalHelperClass;
typedef (void InternalHelperClass::* PTMF)();


Doesn't InternalHelperClass need to be a complete type here?

[snip]
--
Bob Hairgrove
No**********@Home.com
Jul 22 '05 #12
Ben
Ok. I think I misunderstood you.

So you are saying that it is the upcast from (C::*)() to (B::*)() that
kills the implementation of a function pointer.

Yes. That does explain the problem. Although I'd rather call it
"downcast" because it is not type-safe.

Well. Thanks a lot, Andrey. You clarified my question. Although I
really hope the PTM upcast for Multi-inheritance and virtual functions
(where subclass has virtual and super does not) had never been
approved. :-)
Jul 22 '05 #13
Ben
Andrey,
yes, yes. it's me again. And yes, I do understand the function pointer
does not work as an implementation for the current PTM spec and it is
the upcast that kills it.
However, I found my original question still not answered.
"why PTM in the first place"?
What if, there's not an animal called "pointer to member function" at
all? What if MyClass::foo only evaluates to a function pointer of type
"void(*)(MyClass*)"?

In that way, B::bar is of type "void(*)(B*)" and C::baz is of type
"void(*)(C*)".

Nothing's special here, nothing needs special spec. Programmer already
know not to cast "void(*)(C*)" to "void(*)(B*)" for case of "C:public
A, public B".
Isn't that simpler? We will be still living in the familiar farm with
sheeps and cows like "function pointer". No unicorn from the forest
such as PTM to disturb the peace.
Jul 22 '05 #14
Ben wrote:
...
However, I found my original question still not answered.

"why PTM in the first place"?

What if, there's not an animal called "pointer to member function" at
all? What if MyClass::foo only evaluates to a function pointer of type
"void(*)(MyClass*)"?

In that way, B::bar is of type "void(*)(B*)" and C::baz is of type
"void(*)(C*)".

Nothing's special here, nothing needs special spec. Programmer already
know not to cast "void(*)(C*)" to "void(*)(B*)" for case of "C:public
A, public B".

Isn't that simpler? We will be still living in the familiar farm with
sheeps and cows like "function pointer". No unicorn from the forest
such as PTM to disturb the peace.


Hmm... As I said in my previous message, the extra functionality (the
permission to call 'void C::baz()' through a 'void (B::*)()') was added
to PMFs in the second draft of C++ specification. The reason it was
added is that someone in the standard committee thought that this
behavior is needed and others agreed.

You are saying that "programmer already know not to cast 'void(*)(C*)'
to 'void(*)(B*)' for case of 'C:public A, public B'" I don't exactly
understand what you are trying to say. Casting 'void (C::*)()' to 'void
(B::*)()' is perfectly legal in C++ and required to work correctly (as
was described before). I.e. programmer already knows that he _can_ do it
and that it will work. The approach that you propose kills that
functionality. In that respect your approach is not equivalent to what
we have in C++ today.

So, the answer to your question is that we need PTM as a beast of
completely different nature (from ordinary function pointer) because we
need to be able to implement some PTM-related functionality, which is
not implementable with ordinary function pointers.

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #15
Ben wrote:
...
However, do we really need a PTM at runtime? Is it necessary? We don't
have any pointer arithmetic for PTM that makes a PTM variable really
meaningful, do we?
Actually, applying 'static_cast' to a PFM is a form of "pointer
arithmetic" in some general meaning of the word. In a traditional
implementation 'static_cast' applied to PFM will change the PFM (or,
more precisely, some portion of PFM's internal representation). That's
why we need PFM as runtime variables.
In most cases, where people do "&T::foo", the PTM value is known at
compile time and can be safely transformed to a function pointer.
In most cases. But not in general case.
In my wrapper function approach, I don't attempt to pass the PTM to
the function at all.

Here's my solution to the example:

void foo_bar(B* pb) {
pb->bar();
}
void foo_baz(C* pc){
pc->baz();
}
int main()
{
B b;
foo_bar(&b); //correspond to B::bar
C c;
foo_baz(&c); //correspond to C::baz
}

What I'm hoping is that the compiler can implicitly generate foo_bar
and foo_baz for me. That saves a lot of keystrokes.
As a bonus,such compiler generated wrapper functions should take
advantage of the tail-call and makes minimal overhead for extra
function-call and no overhead or side-effect for extra parameter
passing and return value copying. In other words, RVO is guaranteed
here.


This solution will not do. You see, in my original example function
'foo' was the actual algorithm that we were trying to implement. You
should not split 'foo' into several functions because that defeats the
purpose of using function pointers. Moreover, the only reason you were
able to do it so easily is because in my original example function 'foo'
was very short an simple (it contained only one PMF call). But what
would you do if 'foo' looked as follows

void foo_3(B* pb, void (B::*pbm[3])()) {
(pb->*pbm[0])();
(pb->*pbm[1])();
(pb->*pbm[2])();
}

In order to split this function, you'll have to generate relatively
large number of variants of 'foo_3' (all possible combinations). But it
is still doable. But look at this

void foo_n(B* pb, void (B::*pbm[])(), unsigned n) {
for (unsigned i = 0; i < n; ++i)
(pb->*pbm[i])();
}

Not splitting 'foo_n' into all possible variants is not an option for
obvious reasons. What are going to do in this last case?

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #16
Ben
> Wrong. The basic assumptions are right, but the logic is flawed. Any
particular PTMF can be saved. Other PTMFs can be cast to that single
type, and be cast back where used.

E.g.
class InternalHelperClass;
typedef (void InternalHelperClass::* PTMF)();

PTMF genPtr = reinterpret_cast<PTMF>( &MyClass::MyMethod );

PTMF may have a different implementation than &MyClass::MyMethod,
which means that genPtr can't be used directly. However, if it
is casted to the correct MyClass::* type, the resulting value
cab be used. At the point of call, you have all arguments for
MyMethod, so logiaclly you do know the type you must cast to.


That actually sounds a good news to me, if true.

Actually that was the first attempt I was trying to make: use a void
PTM and cast it back to what it was.
However, I just could not find a statement saying that this is safe.

From what I read in this group and other articles on the internet, it
is only guaranteed that you can upcast and downcast between super
classes and subclasses.

Could you point me to the source stating that cross-casting between
any two PTM is safe and portable?
Jul 22 '05 #17
Ben
Let me try to summarize a little bit.

1. function pointer could have been a more efficient and simpler PTM.
2. function pointer won't be able to support certain castings when
Multi Inheritance or virtual functions are involved.

So the decision was made in favor of the "castings in Multi
inheritance" and in sacrifice of simplicity and efficiency.

In the current design of PTM, people like me, who want to get a
fixed-size PTM or want to get a function pointer compatible PTM
suffer. We have to use functor objects, wrapper functions or run-time
cost to get around the problems.
Whereas if the decision had been made differently, people who want a
fixed size PTM or a function pointer compatible PTM would be happy,
while people who try to do casting with "multi-inheritance" present
will suffer. They would have to write functor objects, wrapper
functions or other techniques to get around.
The question is trade-off. If someone has to pay the price, who should
be the victim?
From where I'm standing, certainly I don't want to be among the people
who suffer. I'm just trying to do vanilla code, why force me to do all
that hassel?

Actually I would wonder "why people want castings among PTM and
Multi-inheritance at the same time?"
And "Even if they do want it, what's the big deal of writing the
functor objects as we are currently forced to do? They choose
Multi-inheritance, they choose casting, which means they choose the
complexity in the first place anyway."
Best Regards,

Ben. Y.
Jul 22 '05 #18
Ben wrote:
...
Could you point me to the source stating that cross-casting between
any two PTM is safe and portable?
...


You can read about it in 5.2.10/9. But note that that's not
"cross-casting between any two PTM". That would be neither safe nor
portable. Safe and portable is the round-trip 'renterpret_cast'.
"Round-trip" is this case means that you may cast one PMF type to
another PMF type, but you have to cast it back to the original PMF type
before attempting to use it as PMF.

(Wasn't it already mentioned in this discussion before? OR am I thinking
about some other discussion?)

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #19
Ben
Yes, "Casting 'void (C::*)()' to 'void (B::*)()' is perfectly legal in
C++".

I agree with that.

What I was talking about was casting 'void (*)(C*)' to 'void (*)(B*)'.

As long as function pointer is still a valid citizen in the language,
casting between two function pointers is also valid citizen in the
language.

And we programmers know it is not safe to cast from 'void (*)(C*)' to
'void (*)(B*)' in case of "C::A,B", don't we?

Even with PTM today, it is still possible that I write this code:
void f1(B*){...}
void f2(C*){...}
void(*p1)(B*) = (void(*)(B*))f2;

The existence of PTM does not prevent us from doing this. What saves
us from making this mistake is knowing that "this is not safe" and
contientiously staying away from it.
So, if we had no PTM at all, if PTM was just an alias of function
pointers, would it surprise anybody that we cannot do this cast? I
think not.


In my other post I concluded my understanding of this entire issue. It
is all about trade-off. Somebody in the committee thought it is more
important to have a handy way to do the cast, while somebody like me
is complaining we paid too much for this convenience that is rarely
needed.
Jul 22 '05 #20
Ben
>> programmer already knows that he _can_ do it and that it will work

Let's make sure what the "already" means.

If we are talking about "why PTM in the first place", then we
programmers DID not already know that we can do it at the time before
the 2nd draft of PTM was given birth.

And we did know that casting function pointers from 'void(*)(C*)' to
'void(*)(B*)' is not gonna work.
But all these "what-if" are none-sense anyway. The creature has been
released and killing it will result in broken code (if not the end of
the world).

I just feel pity that I have to pay price for something that I would
never want to use. Does it contradict with the slogan: no use, no
overhead?
Thanks again for the accurate and detailed explanation.

And have a nice weekend!
Jul 22 '05 #21
Ben
> You can read about it in 5.2.10/9. But note that that's not
"cross-casting between any two PTM". That would be neither safe nor
portable. Safe and portable is the round-trip 'renterpret_cast'.
"Round-trip" is this case means that you may cast one PMF type to
another PMF type, but you have to cast it back to the original PMF type
before attempting to use it as PMF.

(Wasn't it already mentioned in this discussion before? OR am I thinking
about some other discussion?)


That is exactly what I want. I actually don't really need to cast PTM
a to PTM b and use it as PTM b. If the roundtrip from any PTM to void
PTM and back to that PTM is safe, I'm happy.

However, I just can't see how this can be true. If PTM a and PTM b has
different size, it means that one way in the roundtrip is losing
information. How can the roundtrip be safe?

Does the standard say that roundtrip between ANY two PTMs is safe? Or
it is only between sub/super classes?
Jul 22 '05 #22
Ben wrote:
You can read about it in 5.2.10/9. But note that that's not
"cross-casting between any two PTM". That would be neither safe nor
portable. Safe and portable is the round-trip 'renterpret_cast'.
"Round-trip" is this case means that you may cast one PMF type to
another PMF type, but you have to cast it back to the original PMF type
before attempting to use it as PMF.

(Wasn't it already mentioned in this discussion before? OR am I thinking
about some other discussion?)
That is exactly what I want. I actually don't really need to cast PTM
a to PTM b and use it as PTM b. If the roundtrip from any PTM to void
PTM and back to that PTM is safe, I'm happy.


Yes, it is safe, assuming that by "void PMF type" you mean something
like 'void (SomeClass::*)()', not 'void (*)()' or 'void*'.
However, I just can't see how this can be true. If PTM a and PTM b has
different size, it means that one way in the roundtrip is losing
information. How can the roundtrip be safe?
The implementation is required not to loose any information in the
round-trip 'reinterpert_cast' conversion. What steps the implementation
takes to satisfy that requirement is its internal business. One is not
supposed to worry about it. In practice that normally means that all
PMFs have the same size, even though it isn't formally required by the
language specification.
Does the standard say that roundtrip between ANY two PTMs is safe? Or
it is only between sub/super classes?


It is safe between any two _PMFs_ (pointer-to-member-function) types. As
it is safe between any two pointer-to-ordinary-function types.

With object pointers (both regular pointers and
pointers-to-data-members) alignment requirements come into play and
situation becomes slightly different. Original pointer value is
guaranteed to be preserved in a round-trip 'reinterpert_cast' conversion
only if alignment requirements of the second type are no stricter than
those of the original type.

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #23
Ben
> It is safe between any two _PMFs_ (pointer-to-member-function) types. As
it is safe between any two pointer-to-ordinary-function types.


This is the test code I did in VC7.0:

struct Dummy{};
typedef void(Dummy::*VoidPtm)();

struct A{
void f(int i){}
};
struct B{
int f(){return 1;}
};
struct C: public A, public B{
int g(int i,int j){
cout <<"C::g"<<endl;
return i;
}
};

void test(){
C c;
VoidPtm p = reinterpret_cast<VoidPtm>(A::f);
p = reinterpret_cast<VoidPtm>(B::f);
p = reinterpret_cast<VoidPtm>(C::g);//DOES NOT COMPILE!
}

And if I comment out "public B" from class C, it compiles.

Do you think VC7 is just not conformant to the standard?
Jul 22 '05 #24
On 12 Jun 2004 23:33:54 -0700, be****@combined.com (Ben) wrote:
It is safe between any two _PMFs_ (pointer-to-member-function) types. As
it is safe between any two pointer-to-ordinary-function types.

This is the test code I did in VC7.0:


I made some minor changes and it compiled OK with the Borland 5.5.1
free command line compiler:

===============
#include <iostream>
#include <ostream>
struct Dummy{};
typedef void(Dummy::*VoidPtm)();

struct A{
void f(int i){}
};
struct B{
int f(){return 1;}
};
struct C: public A, public B{
int g(int i,int j){
// cout <<"C::g"<<endl; std::cout <<"C::g"<< std::endl; return i;
}
};

void test(){
C c;
VoidPtm p = reinterpret_cast<VoidPtm>(A::f);
p = reinterpret_cast<VoidPtm>(B::f);
p = reinterpret_cast<VoidPtm>(C::g);//compiles OK now.
}


================

Results:

bcc32 -c ptmf_test.cpp
Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland
ptmf_test.cpp:
Warning W8004 ptmf_test.cpp 29: 'p' is assigned a value that is never
used in function test()
--
Bob Hairgrove
No**********@Home.com
Jul 22 '05 #25
Ben
> Results:

bcc32 -c ptmf_test.cpp
Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland
ptmf_test.cpp:
Warning W8004 ptmf_test.cpp 29: 'p' is assigned a value that is never
used in function test()


The question is: does this have a standard? or it is all
implementation dependent?

From what VC is doing: using differnet size for MI, it makes sense to
disallow this.

And if bcc uses same size, it certainly can allow this.

If standard does not make a guarantee here, I'll have to assume that
it is NOT safe for a roundtrip between any two PTMs, generally
speaking.
Jul 22 '05 #26
I read this whole thread and feel this is a good place to drop in.

I have an issue that is similar to Ben's.

My problem are my serializer methods. Since I want the everyday
programming in my class library that may grow as simple as possible, I
want only two member functions:
Object::Serialize( IFormatter& f );
Object::DeSerialize( IFormatter& f );

they contain some code to write to or read Data from formatter.

They also invoke the serialization of the base class. But any managemant
and tracking of object sized in the stream is done in the formatter.
That means, the base class serializer cannot be called directly. It
would just put its data into the stream without marking the end of the
base. This will result in the fact that I cannot add serialized
veriables to the base after the release of the program, or I would have
to write converter to read and write to documents from an older version.
And the older version cannot read docs from the newer one.
So what I want to do is passing a member function pointer into the
stream that points to the serializer of the base class.
Hmm, sounds weird? Ideas are welcome.

So, my problem regarding the function pointer is, that the functions are
virtual. They have to be virtual, because if the object is serialized
through a base class (this)pointer, I want the complete object to be
serialized.
If the formatter is calling the serializer function through the function
pointer it needs an object pointer of the type of the common base class
for all objects, pointing to the object to serialize. And it needs a
function pointer to the serializer. But due to the virtual function it
calls always the function of the complete type, which would rather
result in an endless loop, than in what I want.
So, what I need is to have a pointer that points to a function rather to
( how I understand it ) the entry in the vftable.
So, is it true that this last point is the same as the issue with the
first and the second draft of the standard? Or am I wrong. Or is there
another way to have the kind of pointer that I want.
I thought about using a static wrapper function, but it would also call
the function of the complete type.
The only way I know to make it work is having the static for the
function pointer and a virtual member function that calls the static.
But it is still more arkward than I wanted.

So, can you spread some light in the dark for me?

Ingo
Jul 22 '05 #27
Ben wrote:
It is safe between any two _PMFs_ (pointer-to-member-function) types. As
it is safe between any two pointer-to-ordinary-function types.


This is the test code I did in VC7.0:

struct Dummy{};
typedef void(Dummy::*VoidPtm)();

struct A{
void f(int i){}
};
struct B{
int f(){return 1;}
};
struct C: public A, public B{
int g(int i,int j){
cout <<"C::g"<<endl;
return i;
}
};

void test(){
C c;
VoidPtm p = reinterpret_cast<VoidPtm>(A::f);
p = reinterpret_cast<VoidPtm>(B::f);
p = reinterpret_cast<VoidPtm>(C::g);//DOES NOT COMPILE!
}

And if I comment out "public B" from class C, it compiles.

Do you think VC7 is just not conformant to the standard?


My experience with VC is limited to VC6 and I don't know how much it is
different from VC7 in this respect. VC6 happens to support several PMF
models. The default one is called "Best-case always", which means that
VC6 is trying to optimize the size of PMFs by analyzing the class
hierarchy and choosing the size of the PMF accordingly. The logic it
uses is simple: if there's no multiple inheritance in the given class
and any of its ancestors, then the only thing PMFs to member functions
of this class need to store is the function's entry point (4 bytes
long). No additional information is needed. Otherwise (i.e. there's
multiple inheritance somewhere above in the hierarchy) VC6 would use
fully functional PMFs (8 bytes long).

It is easy to demonstrate that this VC6's logic is flawed and will lead
to non-standard conforming behavior in certain contexts (my original
example is one of such contexts, 'reinterpret_cast' round-trip is
another, just to name a few). However, there are a couple of compiler
options that will force VC6 to always use
multiple-inheritance-compatible PMFs regardless of the inheritance
structure (see '/vmg', '/vmm').

I'm pretty sure (although I could be wrong) that what you observe in
your experiment with VC7 happens for similar reasons. The source and
destination pointer types are so different internally that even
'reinterpret_cast' refuses to convert one into another. Maybe you should
check the compiler's documentation and see whether if describes some
options similar to '/vm...' in VC6.

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #28
Ben wrote:
...
void test(){
C c;
VoidPtm p = reinterpret_cast<VoidPtm>(A::f);
p = reinterpret_cast<VoidPtm>(B::f);
p = reinterpret_cast<VoidPtm>(C::g);//DOES NOT COMPILE!
}
...


BTW, the explicit application of 'address-of' operator is required in
this context

VoidPtm p = reinterpret_cast<VoidPtm>(&A::f);
p = reinterpret_cast<VoidPtm>(&B::f);
p = reinterpret_cast<VoidPtm>(&C::g);

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #29

"Ingo Nolden" <nu************@SPAMgmx.de> wrote in message
news:Xn***************************@62.245.150.226. ..
I read this whole thread and feel this is a good place to drop in.

I have an issue that is similar to Ben's.

My problem are my serializer methods. Since I want the everyday
programming in my class library that may grow as simple as possible, I
want only two member functions:
Object::Serialize( IFormatter& f );
Object::DeSerialize( IFormatter& f );
There is a serialization library soon to be part of boost at www.boost.org.
The pre-release version can be found at www.rrsd.com. It melds the
(De)Serialize function pair into a single templated serialize function. So
you add a single template method to you class like:

template< class Archive > void serialize( Archive& ar, const int
version )
{
ar & mData1;
ar & mData2;

...
}

The method is then instantiated for input/output archives, which by the way
can be text,binary,xml or you can create your own custom archive class.
they contain some code to write to or read Data from formatter.

They also invoke the serialization of the base class. But any managemant
and tracking of object sized in the stream is done in the formatter.
That means, the base class serializer cannot be called directly. It
would just put its data into the stream without marking the end of the
base. This will result in the fact that I cannot add serialized
veriables to the base after the release of the program, or I would have
to write converter to read and write to documents from an older version.
And the older version cannot read docs from the newer one.
boost::serialization provides versioning. Just add to the above method:

if( version > 1 )
{
ar & mDataVersion2;

...
}

So what I want to do is passing a member function pointer into the
stream that points to the serializer of the base class.
Hmm, sounds weird? Ideas are welcome.
Prepend the member serialization with:

ar & boost::serialization::base_object<base_class_of_T> (*this);
So, my problem regarding the function pointer is, that the functions are
virtual. They have to be virtual, because if the object is serialized
through a base class (this)pointer, I want the complete object to be
serialized.


boost::serialization does not use/require a common base class for
serialization, thereby avoids this problem.
Jeff Flinn
Jul 22 '05 #30
Ben wrote:
...
If standard does not make a guarantee here, I'll have to assume that
it is NOT safe for a roundtrip between any two PTMs, generally
speaking.


The standard _does_ make a guarantee here. The explicit guarantee is
given, once again, in 5.2.10/9.

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #31
Andrey Tarasevich <an**************@hotmail.com> wrote in message news:<MY********************@comcast.com>...
Ben wrote:
It is safe between any two _PMFs_ (pointer-to-member-function) types. As
it is safe between any two pointer-to-ordinary-function types.

<snip>
My experience with VC is limited to VC6 and I don't know how much it is
different from VC7 in this respect. VC6 happens to support several PMF
models. The default one is called "Best-case always", which means that
VC6 is trying to optimize the size of PMFs by analyzing the class
hierarchy and choosing the size of the PMF accordingly. The logic it
uses is simple: if there's no multiple inheritance in the given class
and any of its ancestors, then the only thing PMFs to member functions
of this class need to store is the function's entry point (4 bytes
long). No additional information is needed. Otherwise (i.e. there's
multiple inheritance somewhere above in the hierarchy) VC6 would use
fully functional PMFs (8 bytes long).

It is easy to demonstrate that this VC6's logic is flawed and will lead
to non-standard conforming behavior in certain contexts (my original
example is one of such contexts, 'reinterpret_cast' round-trip is
another, just to name a few). However, there are a couple of compiler
options that will force VC6 to always use
multiple-inheritance-compatible PMFs regardless of the inheritance
structure (see '/vmg', '/vmm').

I'm pretty sure (although I could be wrong) that what you observe in
your experiment with VC7 happens for similar reasons. The source and
destination pointer types are so different internally that even
'reinterpret_cast' refuses to convert one into another. Maybe you should
check the compiler's documentation and see whether if describes some
options similar to '/vm...' in VC6.


I have recently written an article on this topic. There have been many
attempts to 'hack' closure pointers into C++; most fail because of an
inadequate understanding of the implementation of member function
pointers.
My article documents exactly what popular compilers are doing. I also
provide an implementation that satisfies Ben's original goal: closure
pointers without the use of new. I also provide operator == and !=, a
significant limitation of boost::function.

http://www.codeproject.com/cpp/FastDelegate.asp

Unfortunately, for MSVC and Intel C++, it's only possible with a nasty
hack. The portability is quite good: it works on MSVC 6, 7, 7.1, Gnu
3.2, and the latest versions of Intel C++ for Windows, Intel C++ for
Itanium, Digital Mars C++, Metrowerks (x86). It's also been compiled
on Comeau C++. Recently I've also got it working on BCB5.5, but that
version is not yet uploaded to the website.

There are a few errors in the article which I need to correct, but I
think you'll still find it an interesting read. It has *much* more
information than I've seen anywhere else.

Incidentally, if you use the /vmg option on VC6 (without specifying
/vmm or /vms), the compiler generates incorrect code!! In the virtual
inheritance case, it can end up calling the wrong function... Only
happens in fairly obscure circumstances, but it's an appalling bug.
They fixed this for VC7, but I haven't seen it mentioned anywhere.

-Don.
Jul 22 '05 #32
On 15 Jun 2004 00:44:44 -0700, da*****@yahoo.com.au (Don Clugston)
wrote:
http://www.codeproject.com/cpp/FastDelegate.asp


Interesting article! I haven't actually finished reading it, but found
this sentence:

"You're even allowed to overload the ->* operator, though why you want
to do such a thing is beyond my comprehension."

FWIW, Scott Meyers wrote a good article about overloading ->* in "Dr.
Dobb's Journal" of October 1999. You can find the beginning of it
here:

http://www.ddj.com/documents/s=898/ddj9910b/
--
Bob Hairgrove
No**********@Home.com
Jul 22 '05 #33
On 15 Jun 2004 00:44:44 -0700, da*****@yahoo.com.au (Don Clugston)
wrote:

[snip]

http://www.codeproject.com/cpp/FastDelegate.asp

[snip]

PS - the link to the article by Jan Gray referenced by your article
doesn't seem to work for me.
--
Bob Hairgrove
No**********@Home.com
Jul 22 '05 #34
Am Mon, 14 Jun 2004 08:57:36 -0400 schrieb Jeff Flinn <NO****@nowhere.com>:

Thank you for responding. I like your proposal. There are things similar
to what I began. Others are less important. But I also have some questions:

To 3: My serialization can load newer files in older versions. I made bad
experience with serialization that could do only the other way.
I am doing this by saving the size of the object ( I do this for
inheritance levels also, so I can add members to a base class also. If an
inheritance Level is small I can still omit this. ) When deserializing a
newer file, any data after the members known in the old versions are
omitted and the formatter jumps to the point after the current object.
Deserializing an older file, standard values replace any missing data,
when the end of an onject is reached in the stream but still values are
demanded.

The syntax in your terms:

ar & SObject( pmData1, "mData1", pData1default );

I also thought about joining de- and serialization methods. I was just a
matter of blindness :-)

To 4: I don't want to save objects twice, so if I am the owner of an
object I do the above, but when I just point to an object owned by someone
else I do:

ar & SRef( pmData1, "mData1", pData1default );

The base should be done like:

ar & SBase( ... ) <- here I was passing a function pointer, now it is open
what I'll do, I also want to be able to save the base as "inline" and omit
the size of the base and therefore I cannot change the base class'
serialization later. Maybe good for small bases.

Also important for me is references to objects that are not yet serialized
when serializing or not yet deserialized when deserializing. I keep a hash
map of pointers and stream positions to resolve them at the end of the
serialization.

Oh, I jast saw that Point 5 is probably addressing this issue.

To 11:

how is this useful manner of xml serialization. I have two requirements
only: I want to control wether something is a node or an attribute. And I
want to control the name for the attributes and nodes. I cannot see this
in your code examples in your posting, but I assume that I will find it in
the library docs that I am going to read this evening.

template< class Archive > void serialize( Archive& ar, const int
version )
{
ar & mData1;
ar & mData2;

...
}

Prepend the member serialization with:

ar & boost::serialization::base_object<base_class_of_T> (*this);
So, my problem regarding the function pointer is, that the functions are
virtual. They have to be virtual, because if the object is serialized
through a base class (this)pointer, I want the complete object to be
serialized.


boost::serialization does not use/require a common base class for
serialization, thereby avoids this problem.


Hmm, there`s something I did not understand. I guess the serialization
method can be virtual. Otherwise I couldn't pass an arbitrary Object, that
I point to via ObjectBase*, to the serializing mechanism. If the method is
virtual, do you make sure, when calling the serialize method, that the
version is called that belongs to the base class and not to the complete
object's class?

It seems this library is cloase to what I have/what I want. If it fits and
works well I will definitely need it. So, thank you for pointing me to it.

cheers

Ingo
--
Erstellt mit M2, Operas revolutionärem E-Mail-Modul:
http://www.opera.com/m2/
~ Samba, more than a low cost File and Printer server ~
-----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
-----== Over 100,000 Newsgroups - 19 Different Servers! =-----
Jul 22 '05 #35

"Ingo Nolden" <in**********@SPAMrecurdyn.org> wrote in message
news:op**************@news.univ-angers.fr...
Am Mon, 14 Jun 2004 08:57:36 -0400 schrieb Jeff Flinn <NO****@nowhere.com>:
Thank you for responding. I like your proposal. There are things similar
Woe, It's Robert Ramey's proposal not mine. I just use it( with great
pleasure).
to what I began. Others are less important. But I also have some questions:
To 3: My serialization can load newer files in older versions. I made bad
I've never come across a product that is forward-compatible before, or at
least was visibly so. When you operate on the data loaded with an older
version of the software, and then serialize, don't you lose information?
experience with serialization that could do only the other way.
I am doing this by saving the size of the object ( I do this for
inheritance levels also, so I can add members to a base class also. If an
inheritance Level is small I can still omit this. ) When deserializing a
newer file, any data after the members known in the old versions are
omitted and the formatter jumps to the point after the current object.
You'd have to bring this up with Robert. You can join the boost devel
mailing list - via Yahoo Groups. I suppose you could derive your own archive
class that could accomodate this requirement.
Deserializing an older file, standard values replace any missing data,
when the end of an onject is reached in the stream but still values are
demanded.
This is how boost::serialization works but it's explicit with the version
info stored in the file.

The syntax in your terms:

ar & SObject( pmData1, "mData1", pData1default );

I also thought about joining de- and serialization methods. I was just a
matter of blindness :-)

To 4: I don't want to save objects twice, so if I am the owner of an
object I do the above, but when I just point to an object owned by someone
else I do:

ar & SRef( pmData1, "mData1", pData1default );

The base should be done like:

ar & SBase( ... ) <- here I was passing a function pointer, now it is open
what I'll do, I also want to be able to save the base as "inline" and omit
the size of the base and therefore I cannot change the base class'
serialization later. Maybe good for small bases.

Also important for me is references to objects that are not yet serialized
when serializing or not yet deserialized when deserializing. I keep a hash
map of pointers and stream positions to resolve them at the end of the
serialization.

Oh, I jast saw that Point 5 is probably addressing this issue.
I think so.
To 11:

how is this useful manner of xml serialization. I have two requirements
only: I want to control wether something is a node or an attribute. And I
want to control the name for the attributes and nodes. I cannot see this
in your code examples in your posting, but I assume that I will find it in
the library docs that I am going to read this evening.

template< class Archive > void serialize( Archive& ar, const int
version )
{
ar & mData1;
ar & mData2;

...
}

Prepend the member serialization with:

ar & boost::serialization::base_object<base_class_of_T> (*this);
So, my problem regarding the function pointer is, that the functions are virtual. They have to be virtual, because if the object is serialized
through a base class (this)pointer, I want the complete object to be
serialized.
boost::serialization does not use/require a common base class for
serialization, thereby avoids this problem.


Hmm, there`s something I did not understand. I guess the serialization
method can be virtual. Otherwise I couldn't pass an arbitrary Object, that


No ClassBeingSerialized::serialize is not virtual.
I point to via ObjectBase*, to the serializing mechanism. If the method is
virtual, do you make sure, when calling the serialize method, that the
version is called that belongs to the base class and not to the complete
object's class?
See "Pointers to Objects of derived Classes" under "Special Considerations"
in the documentation. Generally the library handles these issues
automatically. In cases not automaticlaly handled, you register your classes
using:

#include <boost/serialization/export.hpp>
...
BOOST_CLASS_EXPORT_GUID(derived_one, "derived_one")
BOOST_CLASS_EXPORT_GUID(derived_two, "derived_two")
It seems this library is cloase to what I have/what I want. If it fits and
works well I will definitely need it. So, thank you for pointing me to it.


Your welcome, I hope it helps.

Jeff Flinn
Jul 22 '05 #36
Ben
> I have recently written an article on this topic. There have been many
attempts to 'hack' closure pointers into C++; most fail because of an
inadequate understanding of the implementation of member function
pointers.

-Don.


Don, I did not go through the gory details of the implementation, but
I love this article. Will recommend it to my friends.

I totally agree with you in that 'delegate' is a much more
preferable,type-safer and simpler solution than PTM. And it is more
OO.

One question about Ryazanov's code (that was my another solution tried
before posting here, very appealing indeed.), is it standard C++ to
have PTM as a template parameter? I'm not a language lawyer, but after
searching the internet the only thing found that can be used as
non-type parameter was "integral type". And don't think PTM is
integral type, is it?

Finally, I may not dare to use your code in production. I tend to
avoid all gray areas whenever I can (the standard but non-portable
reinterpret_cast of PMF is another gray area that I will stay away
from).

My current solution would be: either my plain wrapper function where I
gain efficiency, or boost:function where I pay some runtime overhead
but gain functor support.
Just hope as you said, 'delegate' can be included in the next release
of the language.
Best Regards,
Jul 22 '05 #37
be****@combined.com (Ben) wrote in message news:<24**************************@posting.google. com>...
I have recently written an article on this topic. There have been many
attempts to 'hack' closure pointers into C++; most fail because of an
inadequate understanding of the implementation of member function
pointers.

-Don.
Don, I did not go through the gory details of the implementation, but
I love this article. Will recommend it to my friends.

I totally agree with you in that 'delegate' is a much more
preferable,type-safer and simpler solution than PTM. And it is more
OO.


I think that PTM is horribly broken. Allowing you to specify an
overridden function from the derived class instead of through the base
class was IMHO a tragic mistake. That tiny bit of functionality came
at an enormous cost.
One question about Ryazanov's code (that was my another solution tried
before posting here, very appealing indeed.), is it standard C++ to
have PTM as a template parameter? I'm not a language lawyer, but after
searching the internet the only thing found that can be used as
non-type parameter was "integral type". And don't think PTM is
integral type, is it?
It is standard, but a bit wierd. PTM is not integral type. Instead,
it's a *name-based* template parameter, quite distinct from normal
parameters and from integral non-type parameters. The syntax is ugly,
so I'm not sure what theyintened it to be used for. Compiler support
for it is patchy, but improving. I would class it as a light gray
area.
Unfortunately, because of its behaviour with inline functions, you
can't use operator == which is a real shame.
Finally, I may not dare to use your code in production. I tend to
avoid all gray areas whenever I can (the standard but non-portable
reinterpret_cast of PMF is another gray area that I will stay away
from).
The relationship between "standard" and "portable" is interesting...
the surpising thing about my code is that it's the allegedly standard
part that requires a hack; the non-standard part (invoking the
function once it's been cast) is no problem at all.

My current solution would be: either my plain wrapper function where I
gain efficiency, or boost:function where I pay some runtime overhead
but gain functor support.
Just hope as you said, 'delegate' can be included in the next release
of the language.


I'm a little concerned that since they've included boost::function,
they may think they've answered the need for delegates/closures. But
really, boost::function generates slow, complicated code for such a
simple concept. I find it very disappointing. And the absence of
operator == is a real limitation which convolutes a lot of code.

-Don.
Jul 22 '05 #38

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

9
by: David Hill | last post by:
Hello - I am using a library that takes a function pointer as an argument. Is the code below not possible? int library_func(void (*func)(int, short, void *)); I am trying to do this... ...
5
by: Newsgroup - Ann | last post by:
Gurus, I have the following implementation of a member function: class A { // ... virtual double func(double v); void caller(int i, int j, double (* callee)(double)); void foo() {caller(1,...
7
by: jon wayne | last post by:
Hi I'm a little confused here about the lifetime of a static pointer to member function, Say, I declare,define & initialize a static ptr to mem function in the header file of a class(the class...
6
by: keepyourstupidspam | last post by:
Hi, I want to pass a function pointer that is a class member. This is the fn I want to pass the function pointer into: int Scheduler::Add(const unsigned long timeout, void* pFunction, void*...
7
by: WaterWalk | last post by:
Hello. I thought I understood member function pointers, but in fact I don't. Consider the following example: class Base { public: virtual ~Base() {} }; class Derived : public Base {
5
by: Tim Frink | last post by:
Hi, I'm experimenting with function pointers and found two questions. Let's assume this code: 1 #include <iostream> 2 class A; 3 4 //////////////////////////////////////////// 5 class B
5
by: Immortal Nephi | last post by:
I would like to design an object using class. How can this class contain 10 member functions. Put 10 member functions into member function pointer array. One member function uses switch to call...
4
by: Immortal_Nephi | last post by:
I had a lot of research to see how function pointer works. Sometimes, programmers choose switch keyword and function in each case block can be called. Sometimes, they choose ordinary function...
7
by: ghulands | last post by:
I am having trouble implementing some function pointer stuff in c++ An object can register itself for many events void addEventListener(CFObject *target, CFEventHandler callback, uint8_t...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.