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

LVL 46
Kent OlsenData Warehouse Architect / DBAAsked:
Who is Participating?
 
jkrConnect With a Mentor Commented:
BTW, to elaborate:

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

in fact is missing the relevant part - in your prev. examples these lines introduced 'Handler' as a variable

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

would syntactically be correct for accessing it inside the TRecord - yet dereferencing makes it 'unknown'. Now, the above lie still is missing teh instance related part to make the call, thus

   (KeyStrings[0].Report->*KeyStrings[0].Report->Handler)("Text");
0
 
imladrisCommented:
 (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?
0
 
Kent OlsenData Warehouse Architect / DBAAuthor Commented:

>>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
0
Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

 
jkrCommented:
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

0
 
jkrCommented:
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

0
 
Kent OlsenData Warehouse Architect / DBAAuthor Commented:
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
0
 
jkrCommented:
>>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?

0
 
jkrCommented:
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

0
 
Kent OlsenData Warehouse Architect / DBAAuthor Commented:
That's correct.

The line that you'd offered:

           Handler = TReport::Handler;

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


0
 
Kent OlsenData Warehouse Architect / DBAAuthor Commented:

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

0
 
jkrCommented:
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

0
 
Kent OlsenData Warehouse Architect / DBAAuthor Commented:
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.


0
 
jkrCommented:
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");
0
 
Kent OlsenData Warehouse Architect / DBAAuthor Commented:
??  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
0
 
Kent OlsenData Warehouse Architect / DBAAuthor Commented:
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

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

   (KeyStrings[0].Report->*KeyStrings[0].Report->Handler)("Text");
0
 
Kent OlsenData Warehouse Architect / DBAAuthor Commented:
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
0
 
jkrCommented:
Thanks, will try my best regarding the weekend ;o)

It already started with Friday being a holiday here...
0
 
Infinity08Commented:
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");
0
 
Kent OlsenData Warehouse Architect / DBAAuthor Commented:
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.   :)

0
 
Infinity08Commented:
You should as it's weekend :) Just spent a day breaking down walls in my sister's house ... Now that's fun lol.
0
 
Kent OlsenData Warehouse Architect / DBAAuthor Commented:
>> 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
0
 
Infinity08Commented:
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 ?
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.