Solved

Function Pointer dereferencing

Posted on 2008-10-03
23
951 Views
Last Modified: 2008-10-06
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

0
Comment
Question by:Kdo
  • 10
  • 9
  • 3
  • +1
23 Comments
 
LVL 16

Expert Comment

by:imladris
ID: 22635244
 (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
 
LVL 45

Author Comment

by:Kdo
ID: 22635332

>>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
 
LVL 86

Expert Comment

by:jkr
ID: 22635527
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
 
LVL 86

Expert Comment

by:jkr
ID: 22635537
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
 
LVL 45

Author Comment

by:Kdo
ID: 22636933
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
 
LVL 86

Expert Comment

by:jkr
ID: 22636975
>>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
 
LVL 86

Expert Comment

by:jkr
ID: 22637016
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
 
LVL 45

Author Comment

by:Kdo
ID: 22637024
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
 
LVL 45

Author Comment

by:Kdo
ID: 22637131

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
 
LVL 86

Expert Comment

by:jkr
ID: 22637226
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
 
LVL 45

Author Comment

by:Kdo
ID: 22637358
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
Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

 
LVL 86

Expert Comment

by:jkr
ID: 22637464
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
 
LVL 45

Author Comment

by:Kdo
ID: 22637691
??  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
 
LVL 45

Author Comment

by:Kdo
ID: 22637719
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
 
LVL 86

Expert Comment

by:jkr
ID: 22637746
Gotcha, name and scope confusion - it works if you make that

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

Accepted Solution

by:
jkr earned 500 total points
ID: 22637845
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
 
LVL 45

Author Comment

by:Kdo
ID: 22637859
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
 
LVL 86

Expert Comment

by:jkr
ID: 22637993
Thanks, will try my best regarding the weekend ;o)

It already started with Friday being a holiday here...
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 22640116
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
 
LVL 45

Author Comment

by:Kdo
ID: 22640904
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
 
LVL 53

Expert Comment

by:Infinity08
ID: 22642162
You should as it's weekend :) Just spent a day breaking down walls in my sister's house ... Now that's fun lol.
0
 
LVL 45

Author Comment

by:Kdo
ID: 22649050
>> 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
 
LVL 53

Expert Comment

by:Infinity08
ID: 22649270
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

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Errors will happen. It is a fact of life for the programmer. How and when errors are detected have a great impact on quality and cost of a product. It is better to detect errors at compile time, when possible and practical. Errors that make their wa…
C++ Properties One feature missing from standard C++ that you will find in many other Object Oriented Programming languages is something called a Property (http://www.experts-exchange.com/Programming/Languages/CPP/A_3912-Object-Properties-in-C.ht…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now