Kent Olsen
asked on
Function Pointer dereferencing
The chasm between knowing how to do something and knowing why it's done a particular way is the difference between being a coder and a designer. With that in mind, I came upon a situation a short time back that I can certainly code, but I must confess that the subtlety of "why" has escaped me. Here's the story -- let's see if anyone can explain the detail that I'm missing. (please?) :)
I've got my hands into an old application and trying to marry "the old and the new". The code contains a table (of structures) with two key elements -- a C string and a class pointer.
The key elements of the structures are in the snippets below.
My question regards the dereferencing of the Handler pointer in the TReport object in the KeyStrings table.
Intuitively, I want to do something like this:
(KeyStrings[MessageType].R eport->*Ha ndler) ("Text");
But C++ gives the error: Undefined Symbol 'Handler'. (BTW, 'Handler' is declared *public*.)
I can certainly call the function if the statement is recoded into discrete steps.
void __fastcall (TReport::*Handler) (char *);
TReport *Report;
Report = KeyStrings[MessageType].Re port;
Handler = KeyStrings[MessageType].Re port->Hand ler;
(Report->*Handler) ("Text");
So what did I miss?
Thanks,
Kent
I've got my hands into an old application and trying to marry "the old and the new". The code contains a table (of structures) with two key elements -- a C string and a class pointer.
The key elements of the structures are in the snippets below.
My question regards the dereferencing of the Handler pointer in the TReport object in the KeyStrings table.
Intuitively, I want to do something like this:
(KeyStrings[MessageType].R
But C++ gives the error: Undefined Symbol 'Handler'. (BTW, 'Handler' is declared *public*.)
I can certainly call the function if the statement is recoded into discrete steps.
void __fastcall (TReport::*Handler) (char *);
TReport *Report;
Report = KeyStrings[MessageType].Re
Handler = KeyStrings[MessageType].Re
(Report->*Handler) ("Text");
So what did I miss?
Thanks,
Kent
// The TReport class contains the function processors for all of the handled strings.
typedef void __fastcall (TReport::*handler_t) (char *);
class TReport
{
public:
void __fastcall ReportxxHandler (char *String);
void __fastcall Report00Handler (char *String);
...
void __fastcall Report20Handler (char *String);
handler_t Handler;
};
// The processor_t structure links a string with the report object that will process it.
typedef struct
{
char *String;
TReport *Report;
} processor_t;
// Define the handled strings and processors.
processor_t KeyStrings[] =
{
"String1", 0, // The TReport object is created at run time.
"String2", 0,
..
"Stringn", 0,
0, 0
};
ASKER
>>Or am I missing the point?
:) I'm certainly missing it.
I've tried several variations of encapsulation with parenthesis, to no avail.
I'll try your suggestion when I get back to the office.
Kent
IMO that should be
void __fastcall (TReport::*Handler) (char *);
TReport *Report;
Report = KeyStrings[MessageType].Report;
Handler = TReport::Handler;
(Report->*Handler) ("Text");
See also the attached snippet and be sure to check out http://www.newty.de/fpt/index.html
#include <iostream>
using namespace std;
class B; // fwd. decl.
class A {
public:
void DoSomething();
void SetFunctionPointer(void (B::*ptr)()) { m_pFunctionPointer = ptr; }
void (B::*m_pFunctionPointer)();
};
class B {
public:
B();
A* m_p;
void SayHello() { cout << "Hello" << endl; }
};
void A::DoSomething()
{
B b;
(b.*m_pFunctionPointer)();
}
B::B()
{
m_p->SetFunctionPointer(B::SayHello);
}
void main () {
A a;
a.DoSomething();
}
ASKER
Hi jkr,
Handler is a function pointer, not a method, so line 4 of your first example won't work. But then mine doesn't either, as a memory access violation occurs at my line 4, using either:
void __fastcall (TReport::*Handler) (char *);
TReport *Report;
Report = KeyStrings[MessageType].Re port;
Handler = KeyStrings[MessageType].Re port->Hand ler;
or
void __fastcall (TReport::*Handler) (char *);
TReport *Report;
Report = KeyStrings[MessageType].Re port;
Handler = Report->Handler;
I know it's a Friday, but I've gone absolutely catatonic. I look at this and see nothing but blank.....
Kent
Handler is a function pointer, not a method, so line 4 of your first example won't work. But then mine doesn't either, as a memory access violation occurs at my line 4, using either:
void __fastcall (TReport::*Handler) (char *);
TReport *Report;
Report = KeyStrings[MessageType].Re
Handler = KeyStrings[MessageType].Re
or
void __fastcall (TReport::*Handler) (char *);
TReport *Report;
Report = KeyStrings[MessageType].Re
Handler = Report->Handler;
I know it's a Friday, but I've gone absolutely catatonic. I look at this and see nothing but blank.....
Kent
>>Handler is a function pointer, not a method
Err, this statement
void __fastcall (TReport::*Handler) (char *);
declares a pointer to a method of 'TReport' that takes a 'char*' as an argument - or am I missing something?
Err, this statement
void __fastcall (TReport::*Handler) (char *);
declares a pointer to a method of 'TReport' that takes a 'char*' as an argument - or am I missing something?
Um, wait a minute - from your more complete snippet, I think I got it. Shouldn't the following work?
handler_t Handler;
TReport *Report;
Report = KeyStrings[MessageType].Report;
Handler = Report->Handler;
(Report->*Handler)("Text");
ASKER
That's correct.
The line that you'd offered:
Handler = TReport::Handler;
There is no TReport::Handler method. There is a TReport.Handler property.
The line that you'd offered:
Handler = TReport::Handler;
There is no TReport::Handler method. There is a TReport.Handler property.
ASKER
Compilation errors
Cannot convert 'void (_fastcall TReport::*)(char *)' to 'void (_fastcall TReport::* *) (char *);
The handler_t type is a pointer to a TReport:: method, so declaring 'TReport Report' instead (shown below), moves the problem down 1 line:
Cannot convert 'TReport *' to 'TReport' (While setting Report)
handler_t Handler;
TReport Report;
Report = KeyStrings[MessageType].Report;
Handler = Report->Handler;
(Report->*Handler)("Text");
That is more than odd, since both are of the same type - 'handler_t'. BTW, the following compiles fine for me:
C:\tmp\cc>cl kdo.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.762 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
kdo.cpp
Microsoft (R) Incremental Linker Version 8.00.50727.762
Copyright (C) Microsoft Corporation. All rights reserved.
/out:kdo.exe
kdo.obj
C:\tmp\cc>cl kdo.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.762 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
kdo.cpp
Microsoft (R) Incremental Linker Version 8.00.50727.762
Copyright (C) Microsoft Corporation. All rights reserved.
/out:kdo.exe
kdo.obj
// The TReport class contains the function processors for all of the handled strings.
#define __fastcall
class TReport;
typedef void __fastcall (TReport::*handler_t) (char *);
class TReport
{
public:
void __fastcall ReportxxHandler (char *String);
void __fastcall Report00Handler (char *String);
//...
void __fastcall Report20Handler (char *String);
handler_t Handler;
};
// The processor_t structure links a string with the report object that will process it.
typedef struct
{
char *String;
TReport *Report;
} processor_t;
// Define the handled strings and processors.
processor_t KeyStrings[] =
{
"String1", 0, // The TReport object is created at run time.
"String2", 0,
"Stringn", 0,
0, 0
};
int main () {
handler_t Handler;
TReport *Report;
Report = KeyStrings[0].Report;
Handler = Report->Handler;
(Report->*Handler)("Text");
return 0;
}
ASKER
Ok. Let's back up to where I was just confused, and not completely wrong.
The code compiles just fine and the correct function processor is being called. This little compilation issue was me just seeing what wasn't there. Correcting the source does wonders.... :) (Damned bifocals.)
What I don't understand is what I originally don't understand. What's wrong with the statement:
(KeyStrings[MessageType].R eport->*Ha ndler) (Cell->GetBuffer ());
Handler is an 'undefined symbol'. Clearly in this context, the compiler does not recognize 'Handler' as I am expecting. Modifying the statement slightly to this:
(KeyStrings[MessageType].R eport->Han dler) (Cell->GetBuffer ());
cleary is not legal C++, but the error message becomes 'call of a nonfunction'. Either the parser now recognizes Handler as an entity within the class, or the 'call of a nonfunction' error takes precedence.
The code compiles just fine and the correct function processor is being called. This little compilation issue was me just seeing what wasn't there. Correcting the source does wonders.... :) (Damned bifocals.)
What I don't understand is what I originally don't understand. What's wrong with the statement:
(KeyStrings[MessageType].R
Handler is an 'undefined symbol'. Clearly in this context, the compiler does not recognize 'Handler' as I am expecting. Modifying the statement slightly to this:
(KeyStrings[MessageType].R
cleary is not legal C++, but the error message becomes 'call of a nonfunction'. Either the parser now recognizes Handler as an entity within the class, or the 'call of a nonfunction' error takes precedence.
Any chance that this is a compiler bug? Even
(KeyStrings[MessageType].R eport->*Ha ndler) (Cell->GetBuffer ());
works fine for me when 'adapting' that to simpler terms. My original line reads
(KeyStrings[0].Report->*Ha ndler)("Te xt");
(KeyStrings[MessageType].R
works fine for me when 'adapting' that to simpler terms. My original line reads
(KeyStrings[0].Report->*Ha
ASKER
?? Any chance that this is a compiler bug?
I've wondered, but having been on several O/S and compiler development teams I've learned that by far the vast majority of "compiler bugs" are improper use.
>> (KeyStrings[MessageType].R eport->*Ha ndler) (Cell->GetBuffer ());
>>
>>works fine for me when 'adapting' that to simpler terms.
That works for me as long as it is preceded by the lines:
handler_t Handler;
TReport Report;
Report = KeyStrings[MessageType].Re port;
Handler = Report->Handler;
It would appear that the parser isn't treating Handler as a class member in that statement.
Kent
I've wondered, but having been on several O/S and compiler development teams I've learned that by far the vast majority of "compiler bugs" are improper use.
>> (KeyStrings[MessageType].R
>>
>>works fine for me when 'adapting' that to simpler terms.
That works for me as long as it is preceded by the lines:
handler_t Handler;
TReport Report;
Report = KeyStrings[MessageType].Re
Handler = Report->Handler;
It would appear that the parser isn't treating Handler as a class member in that statement.
Kent
ASKER
Can you try compling the code below?
Thanks,
Thanks,
// The TReport class contains the function processors for all of the handled strings.
#define __fastcall
class TReport;
typedef void __fastcall (TReport::*handler_t) (char *);
class TReport
{
public:
void __fastcall ReportxxHandler (char *String);
void __fastcall Report00Handler (char *String);
//...
void __fastcall Report20Handler (char *String);
handler_t Handler;
};
// The processor_t structure links a string with the report object that will process it.
typedef struct
{
char *String;
TReport *Report;
} processor_t;
// Define the handled strings and processors.
processor_t KeyStrings[] =
{
"String1", 0, // The TReport object is created at run time.
"String2", 0,
"Stringn", 0,
0, 0
};
int main () {
(KeyStrings[0].Report->*Handler)("Text");
return 0;
}
Gotcha, name and scope confusion - it works if you make that
(KeyStrings[0].Report->*Ke yStrings[0 ].Report-> Handler)(" Text");
(KeyStrings[0].Report->*Ke
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Jeepers...
Of course.... "this->*this->F()" is pretty common, but I didn't make the leap to the array context.
I learned something today so this has been a pretty good day.
But I am soooooo ready for happy hour...
Thanks jkr. Have a great weekend.
Kent
Of course.... "this->*this->F()" is pretty common, but I didn't make the leap to the array context.
I learned something today so this has been a pretty good day.
But I am soooooo ready for happy hour...
Thanks jkr. Have a great weekend.
Kent
Thanks, will try my best regarding the weekend ;o)
It already started with Friday being a holiday here...
It already started with Friday being a holiday here...
It's nice to have a little macro to avoid confusions like this (imo) :
#define CALLMEMFUN(obj, memptr) ((obj).*(memptr))
So :
(KeyStrings[0].Report->*Ke yStrings[0 ].Report-> Handler)(" Text");
becomes :
CALLMEMFUN(KeyStrings[0].R eport, KeyStrings[0].Report->Hand ler)("Text ");
#define CALLMEMFUN(obj, memptr) ((obj).*(memptr))
So :
(KeyStrings[0].Report->*Ke
becomes :
CALLMEMFUN(KeyStrings[0].R
ASKER
That would have helped if my mental block would have let me get to the necessary object pointer.
Every now and then we just have to pick fun at ourselves -- I guess that it's my turn.
I was stepping through the debugger and even saw the pointer address as (0x01020304, 0x05060708:0x090a0b0c) and STILL blocked out the fact that I needed an object pointer.
But I'm feeling much better now. :)
Every now and then we just have to pick fun at ourselves -- I guess that it's my turn.
I was stepping through the debugger and even saw the pointer address as (0x01020304, 0x05060708:0x090a0b0c) and STILL blocked out the fact that I needed an object pointer.
But I'm feeling much better now. :)
You should as it's weekend :) Just spent a day breaking down walls in my sister's house ... Now that's fun lol.
ASKER
>> Just spent a day breaking down walls in my sister's house
And no one called the authorities? Cool.... :)
Actually, that's what I do when I'm not being a techie. My wife and I buy distressed properties in decent neighborhoods, fix them up, and rent them. We got started by buying an absolute disaster that we fixed up and now call home.
It's amazing what a few hundred thousand will do for a place. :S
Kent
And no one called the authorities? Cool.... :)
Actually, that's what I do when I'm not being a techie. My wife and I buy distressed properties in decent neighborhoods, fix them up, and rent them. We got started by buying an absolute disaster that we fixed up and now call home.
It's amazing what a few hundred thousand will do for a place. :S
Kent
That's definitely a good investment, especially these days.
In this case, it was my sister that bought a house for her and her husband. The location is nice, and they have a huge garden, but other than that, it's like a war zone ... The garden is a jungle/dump with even a rusted car wreck in it, the windows need to be replaced, electricity and heating needs to be re-done, the kitchen and bathrooms are unusable, etc. etc.
We're gonna have fun making it livable by the end of the month (which is their goal).
Btw, with a few hundred thousand, I assume that includes buying of the property ?
In this case, it was my sister that bought a house for her and her husband. The location is nice, and they have a huge garden, but other than that, it's like a war zone ... The garden is a jungle/dump with even a rusted car wreck in it, the windows need to be replaced, electricity and heating needs to be re-done, the kitchen and bathrooms are unusable, etc. etc.
We're gonna have fun making it livable by the end of the month (which is their goal).
Btw, with a few hundred thousand, I assume that includes buying of the property ?
Wouldn't the compiler have trouble recognizing Handler as part of the Report structure in that syntax? You can do "structptr->field", but I've never seen "structptr->*field". It seems to be a alternate way of finding the field in the structure.
would "(*(KeyStrings[MessageType
Or am I missing the point?