Thread: Class function pointers

  1. #1
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607

    Class function pointers

    Is there a way to create a function pointer in one class and have it point to a function in another class?

    I keep getting the error that the right side of the equality is (__cdecl)<func_name>(params)


    For instance:

    Code:
    class A
    {
      public:
        void Func(int x) {};
    };
    
    class Object
    {
       A AInstance;
       public:
         Object(void);
         void (A::*Func)(int x);
    };
    
    Object::Object(void)
    {
       Func=AInstance.Func;
    }
    This is a stupid example but what I have is a sound engine that is attached to the main CD3DApp object via a pointer. Instead of creating a function in CD3DApp for the corresponding function in CDXSoundEmitter, I just want to setup function pointers. Otherwise my functions in CD3DApp will just look like this:

    Code:
    void CD3DApp::PlaySound(DWORD dwID)
    {
       //SoundFX is a valid CDXSoundEmitter object
       SoundFX.PlaySound(dwID);
    }
    A lot of calling for no reason. I want to attach the function pointer in CD3DApp to the function in CDXSoundEmitter.

    But my problem is that the declaration inside of CD3DApp is preceded with __thiscall instead of (__cdecl *). And I cannot declare a function pointer with call type (__cdecl).

    Sorry if this confuses you, but I know of no other way to explain it.
    I'm not trying to override C++ protection mechanisms with this, I'm trying to use one function call to call into CDXSoundEmitter from CD3DApp instead of two.


    This is an example of the error I'm getting - obviously lvalue does not equal rvalue, but how do I fix?

    D:\VC6Projects\Zelda\CD3DApp.cpp(26) : error C2440: '=' : cannot convert from 'unsigned int (__thiscall CDXSoundEmitter::*)(unsigned short *)' to 'unsigned int (__cdecl *)(unsigned short *)'
    There is no context in which this conversion is possible
    I know this is possible because in Windows you can register your WndProc with Windows and the WndProc can be a member of a class. So Windows calls the WndProc in the class instead of one sitting in the global namespace.

    This will also allow me to control all access to CDXSoundEmitter and only allow access to the functions necessary for the game. All other creation functions will not be accessible and you will never ever have access to the core DirectMusicSoundSegment8, DirectMusicPerformance8, or DirectMusicLoader8 objects.
    Exactly what I want. This means that by not returning a reference to the interface pointer, you cannot mistakenly call release and/or AddRef() on the object. I don't want to grant access to the actual object. Besides it makes sense that since the sound engine is part of the bigger engine that you should have to go through CD3DApp to play sounds.

    CD3DApp.h
    Code:
    #ifndef CD3DApplication
    #define CD3DApplication
    
    #include <d3dx9.h>
    #include <string>
    #include <limits>
    
    #include "CDXInput.h"
    #include "CMouse.h"
    #include "CKeyboard.h"
    #include "CDXAudio.h"
    
    
    
    const float INFINITY = FLT_MAX;
    const float EPSILON  = 0.001f;
    
    LRESULT CALLBACK      g_WndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
    
    #define CDX_AUDIO_SOUNDFX   0x01
    #define CDX_AUDIO_MUSIC     0x03
    
    
    class CD3DApp
    {
      
      protected:
    
        //...
        //<snip>
        //...
        
        CDXAudio                    Audio;
        CDXSoundEmitter       *SoundFX;
        CDXSoundEmitter       Music;
    
    
    
      public:
    
        int EnterMsgLoop(bool (*DisplayFunc)(float timeDelta));
    
       //
       //<snip>    
       //
    
      //Sound function pointers
      unsigned int (CDXSoundEmitter::*LoadSound)(WCHAR *Filename);
      
    };
    
    #endif
    CD3DApp.cpp
    Code:
    //...
    //<snip>
    //....
    
    bool CD3DApp::InitAudio(DWORD dwNumPChannels,DWORD dwMSLatency)
    {
      Audio.Create(dwNumPChannels);
      Audio.SetLatency(dwMSLatency);
    
      SoundFX=new CDXSoundEmitter();
    
      SoundFX->Create(Audio.GetLoader(),Audio.GetPerformance());
      //Music.Create(Audio.GetLoader(),Audio.GetPerformance());
    
      LoadSound=SoundFX->LoadSound;
      //Play=SoundFX.Play;
      //PlayWithCheck=&SoundFX.PlayWithCheck;
      //GetPlaying=&SoundFX.GetPlaying;
      //SetLooping=&SoundFX.SetLooping;
      //SetVolume=NULL;
      //Stop=&SoundFX.Stop;
      
      return true;
    }
    
    //...
    //<snip>
    //...
    Amd some of you might say I should just return a copy of the interface pointer back to the user, but that entails a lot of COM problems just waiting to happen. That means that the user MUST call Release() on their copy of the interface or you have a serious problem.
    Last edited by VirtualAce; 02-13-2005 at 03:03 PM.

  2. #2
    Registered User Micko's Avatar
    Join Date
    Nov 2003
    Posts
    715
    Try something like this:
    Code:
    class A
    {
      public:
        void Func(int x) {};
    };
    
    typedef void (A::*F)(int) ;
    
    class Object
    {
       A AInstance;
       public:
         Object(void);
    	 F func;
    };
    
    Object::Object(void)
    {
    	func = &A::Func;
    }
    I'm not sure this will help but...

  3. #3
    erstwhile
    Join Date
    Jan 2002
    Posts
    2,227
    I think in your "stupid example" the syntax should be more like:
    Code:
    class A
    {
      public:
        void Func(int x) {};
    };
    
    class Object
    {
       A AInstance;
       public:
         Object(void);
         void (A::*Func)(int x);
    };
    
    Object::Object(void)
    {
       Func=&A::Func;
      //call the function
      AInstance.Func(10);
    }
    >>I know this is possible because in Windows you can register your WndProc with Windows and the WndProc can be a member of a class. So Windows calls the WndProc in the class instead of one sitting in the global namespace.<<

    A typical approach with this is to use a static member function as the window procedure but I suspect you're aware of this already.

    edit: biffed by Micko
    CProgramming FAQ
    Caution: this person may be a carrier of the misinformation virus.

  4. #4
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    How would you use the Func member in main()?

  5. #5
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Worked like a charm. Thanks all. My mistake was the address of operator - I used it before but tried to take the address of the wrong thing so I thought that it wouldn't work.

    This helps me tremendously.

    Ok it compiles but now I cannot call the functions.

    If I have a valid CD3DApp object I cannot do this:


    Code:
    unsigned int ID=ValidCD3DAppObject.LoadSound(Filename);
    It is saying that the term does not evaluate to a function. So since they are function pointers...how do I call them from outside of CD3DApp?

    I will probably have to overload an operator so that it operates on the correct type.
    Last edited by VirtualAce; 02-14-2005 at 01:04 AM.

  6. #6
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    Quote Originally Posted by Bubba
    Worked like a charm. Thanks all.
    How do you use the pointer? It doesn't seem like you can ever use it because you need an Object object to get the pointer, but you need an A object to call the function on.

  7. #7
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    The instance is inside of the class so the function pointer points to a valid instance of the object as long as the parent class is valid. But it seems that these are not function pointers because the compiler is acting like I'm trying to call a non-function.

  8. #8
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    lol. Now I'm really confused. I just can't figure out any syntax that will let you call the function on AInstance in main(). Even if AInstance were public, if you do this:

    Object o;
    o.AInstance.??

    you have to continue with a member function or variable of AInstance. And, if you do this:

    Object o;
    o.pfunc.???

    what can you continue with?
    Last edited by 7stud; 02-14-2005 at 01:36 AM.

  9. #9
    Registered User Micko's Avatar
    Join Date
    Nov 2003
    Posts
    715
    Try this:
    Code:
    class A
    {
      public:
        void Func(int x) {};
    };
    
    typedef void (A::*F)(int) ;
    
    class Object
    {
       A AInstance;
       public:
         Object(void);
         F func;
    	 void foo();
    };
    
    Object::Object(void)
    {
        func = &A::Func;
    	(AInstance.*func)(10);
    
    }
    
    void Object::foo()
    {
    	(AInstance.*func)(10);
    }

  10. #10
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Still doesn't work.

    Code:
    void Setup(void)
    {
      ...
      (g_ZeldaApp.*LoadSound)(L"Test.wav");
      ...
    }
    error C2065: 'LoadSound' : undeclared identifier
    error C2297: '.*' : illegal, right operand has type 'int

  11. #11
    Registered User
    Join Date
    Apr 2003
    Posts
    2,663
    What's the benefit of doing that over this:
    Code:
    class A
    {
    public:
    	void Func()
    	{
    		cout<<"Func executed"<<endl;
    	}
    };
    
    
    class Object
    {
    	A AInstance;
    
    public:
    	Object(void);
    };
    
    Object::Object(void)
    {
    	AInstance.Func();
    }
    
    
    int main()
    {
    	Object o;
    	
    
    	return 0;
    }

  12. #12
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    7Stud if AInstance has a public member function called Test() and I have AInstance as a public member of class Parent like this:

    Code:
    class A
    {
      public:
         void Test(void) {}
    };
    
    class Parent
    {
      public:
        A AInstance;  
        Parent(void) {}
    };
    Then I can do this:

    Parent MyParent;
    MyParent.AInstance.Test();

    or if I code Parent like this:
    Code:
    class Parent
    {
      private:
        A AInstance
      public:
        Parent(void) {}
        A GetAInstance(void) {return AInstance;};
    };
    Then I can do this:

    Code:
    Parent MyParent;
    MyParent.GetAInstance().Test();
    So if I have an instance of A then I can make function pointers in Parent that point to either the instance of A inside of Parent or to the offset of the function in all instances of A.

    But so far pointer to member functions is not working as I thought it should.

    The benefit is this:

    This is for a sound engine that is attached to a 3D graphics engine. Since the sound and graphics are two separate entities I want to separate them. I also do not want to give access to the DirectMusic interfaces created inside of my CDXSoundEmitter class. The class sets all the sound, graphics, and input up as soon as you instantiate it. Everything is valid. All the programmer has to do is call two functions.

    InitInput();
    InitAudio(DWORD dwNumPChannels,DWORD dwMSLatency);

    This creates valid CDXSoundEmitter, CMouse, and CKeyboard objects.

    Now since each of these classes have functions to access the mouse, sound, keyboard, etc., it makes no sense to put a corresponding function inside of CD3DApp for every function in the above classes. For instance let's say you want to update the keyboard with new data.

    Code:
    CD3DApp MyApp;
    MyApp.InitInput();
    MyApp.UpdateKeyboard();
    Really all UpdateKeyboard will do is this:

    Code:
    bool CD3DApp::UpdateKeyboard(void)
    {
       if (Keyboard)
       {
          Keyboard->Update();
          return true;
       } else return false;
    }
    Keyboard is a valid CKeyboard pointer/object that already resides inside of CD3DApp.
    The only other way to gain access to CKeyboard's functions is to return a pointer to the CKeyboard object within CD3DApp. However this is not good since it is possible for someone to misuse this object - as in calling Keyboard.ShutDown(). This will screw everything up. If they call ShutDown() by mistake, when my code shuts down it also calls ShutDown(). This will cause the DirectInput interface to be released twice and will cause major problems.

    The only other way to do this that I know of is to derive CD3DApp from CKeyboard, CMouse, and CDXSoundEmitter or CDXAudio. But I don't want to do this.

    Here is my setup so far:
    • CDXAudio - Handles the main IDirectMusic objects (Loader, Performance, etc) - not accessible by the programmer.
    • CDXSoundSegment - Handles one IDirectMusicSegment8 object - not accessible by the programmer.
    • CDXSoundEmitter - encapsulates an entire sound emitting object. Controls all access and creation of CDXSoundSegment objects. Sounds are added to a vector at setup time and played later based on their unique ID. The internal workings of CDXSoundSegment are totally transparent to the programmer. All the programmer has to do is call:

      CDXSoundEmitter::LoadSound(WCHAR *Filename)

      This function returns a unique ID for the sound - this ID in turn is used to gain all future access to the sound. Sounds are not accessed by pointers - they are all accessed by ID numbers.
    • CDXInput - handles the main IDirectInput8 object.
    • CKeyboard - handles all keyboard setup/shutdown and tracking functions. Requires CDXInput to create the object -CDXInput::GetInterface() is called in CD3DApp::InitInput() thereby hiding the entire interface setup from the programmer.
    • CMouse -> handles all mouse setup/shutdown and tracking functions. Requires a few more parameters than CKeyboard but for the most part is identical in operation to CKeyboard.



    As you can see this is quite complex but it works like a charm. I'm trying to limit the work required to setup DirectX and limit a lot of the nitty gritty of playing sounds, tracking keyboard and mouse, etc.

    Once you setup the engine....everything is ready to rock. To setup my engine takes about 4 lines of code. After that everything is valid and you can start drawing your scene, playing sounds, tracking mouse, keyboard, and/or joystick input.

    Later CD3DApp will also consist of scripting classes, multiplayer classes, etc. Functionality is just one pointer away.
    Last edited by VirtualAce; 02-14-2005 at 02:19 AM.

  13. #13
    Registered User Micko's Avatar
    Join Date
    Nov 2003
    Posts
    715
    Pay attention on that LoadSound
    consider this:
    Code:
    class A
    {
      public:
        void Func(int x) {};
    };
    
    typedef void (A::*F)(int) ;
    
    class Object
    {
       A AInstance;
       public:
         Object(void);
         F func;
         void foo();
    };
    
    Object::Object(void)
    {
        func = &A::Func;
        (AInstance.*func)(10);
    
    }
    
    void Object::foo()
    {
        (AInstance.*Func)(10);//pay attention, your error
        (AInstance.*func)(10);//OK
    }
    Line
    Code:
    (AInstance.*Func)(10);
    will produce this:
    'Func' : undeclared identifier
    error C2297: '.*' : illegal, right operand has type ''unknown-type''

    Think what is the difference?

    Got it?
    Last edited by Micko; 02-14-2005 at 02:06 AM.

  14. #14
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    No. I'm calling it LoadSound() in both classes. So LoadSound() is the correct name.

    The function pointer looks like this in CD3DApp:
    Code:
    unsigned int (CDXSoundEmitter::*LoadSound)(WCHAR *Filename);
    I assign it in CD3DApp::InitAudio like this:
    Code:
    LoadSound=&CDXSoundEmitter::LoadSound;
    But I cannot call it like this:

    Code:
    CD3DApp g_TheApp;
    
    //Init audio and assign pointers
    g_TheApp.InitAudio();
    
    //Load sound
    (g_TheApp.*LoadSound)(L"Test.wav");    //fails - term does not evaluate to a function
    If you are thinking I'm trying to call the function in CDXSoundEmitter using it's name instead of the function pointer name in CD3DApp you are mistaken. It just so happens that I gave them the same name.
    Last edited by VirtualAce; 02-14-2005 at 02:22 AM.

  15. #15
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Why don't you just use the nifty static ability that lets you use an object's functions without having an instance of that object?

    Quzah.
    Hope is the first step on the road to disappointment.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Getting an error with OpenGL: collect2: ld returned 1 exit status
    By Lorgon Jortle in forum C++ Programming
    Replies: 6
    Last Post: 05-08-2009, 08:18 PM
  2. deriving classes
    By l2u in forum C++ Programming
    Replies: 12
    Last Post: 01-15-2007, 05:01 PM
  3. Replies: 28
    Last Post: 07-16-2006, 11:35 PM
  4. Problem with function pointers
    By vNvNation in forum C++ Programming
    Replies: 4
    Last Post: 06-13-2004, 06:49 AM
  5. Warnings, warnings, warnings?
    By spentdome in forum C Programming
    Replies: 25
    Last Post: 05-27-2002, 06:49 PM