Link to home
Start Free TrialLog in
Avatar of Kent Olsen
Kent OlsenFlag for United States of America

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].Report->*Handler) ("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].Report;
           Handler = KeyStrings[MessageType].Report->Handler;
           (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
};

Open in new window

Avatar of imladris
imladris
Flag of Canada image

 (KeyStrings[MessageType].Report->*Handler) ("Text");

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].Report->Handler)))("Text");" work?

Or am I missing the point?
Avatar of Kent Olsen

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");

Open in new window

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();
 
}

Open in new window

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].Report;
  Handler = KeyStrings[MessageType].Report->Handler;

or

  void __fastcall (TReport::*Handler) (char *);
  TReport *Report;
  Report = KeyStrings[MessageType].Report;
  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?

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");

Open in new window

That's correct.

The line that you'd offered:

           Handler = TReport::Handler;

There is no TReport::Handler method.  There is a TReport.Handler property.



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");

Open in new window

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
//  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;
}

Open in new window

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].Report->*Handler) (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].Report->Handler) (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.


Any chance that this is a compiler bug? Even

   (KeyStrings[MessageType].Report->*Handler) (Cell->GetBuffer ());

works fine for me when 'adapting' that to simpler terms. My original line reads

   (KeyStrings[0].Report->*Handler)("Text");
??  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].Report->*Handler) (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].Report;
           Handler = Report->Handler;


It would appear that the parser isn't treating Handler as a class member in that statement.


Kent
Can you try compling the code below?


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;
}

Open in new window

Gotcha, name and scope confusion - it works if you make that

   (KeyStrings[0].Report->*KeyStrings[0].Report->Handler)("Text");
ASKER CERTIFIED SOLUTION
Avatar of jkr
jkr
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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
Thanks, will try my best regarding the weekend ;o)

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->*KeyStrings[0].Report->Handler)("Text");

becomes :

        CALLMEMFUN(KeyStrings[0].Report, KeyStrings[0].Report->Handler)("Text");
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.   :)

You should as it's weekend :) Just spent a day breaking down walls in my sister's house ... Now that's fun lol.
>> 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
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 ?