🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

enumdevicescallback part of class?

Started by
12 comments, last by AlexM 24 years, 7 months ago
If you're going to make it part of the class, I'd suggest doing it like this. In the header file, define the function as:

static LPD3DENUMDEVICESCALLBACK EnumDevicesCallback(...);

Then when you're using it, do this:

if(FAILED(lpd3d->EnumDevices(SD3d::EnumDevicesCallback, NULL));

I think that'll work, but I haven't checked so I'm not entirely sure...

Jonathan

Advertisement
but i'm calling EnumDevices inside another method, so i have to use "this":
SD3d::Setup(...)
{
lpd3d->EnumDevices(this->EnumDevicesCallback,NULL);
}

thanks for the reply, though.
Alex.

For one thing, you can't make a callback function part of the class (e.g. functions like WindowProc, DialogProc).

A callback function, as you probably know, is called by another program outside of your program - in this case, part of DirectX.

Callback functions cannot access the this pointer because they must use the "__stdcall" calling convention. (A calling convention determines how variables are transferred (what order), who cleans up the stack, and how return values are passed back, among other things.) Only class member functions use the "thiscall" calling convention, which (among other things) adds the this pointer as a hidden last argument. That's how you can access the class's member variables from any of the class's member functions.

Just make it a global and everything should be fine. (Global functions are __stdcall by default.)

Even though you wouldn't need the this pointer if you made the callback function a static member function, it would still use the "thiscall" calling convention, and something technical called "Name mangling." (Name mangling has to do with how C++ manipulates function names to provide function overloading - or using the same name but different arguments. There is no __stdcall equivalent name for class member functions.)

Nothing prevents you from having a callback function call your class's functions - as long as it has a valid pointer. So you can probably just pass the callback function's parameters to a class function and have it process the data. Make the return values the same, so the class's member function controls that, too.

For instance:

LRESULT CALLBACK MyCallback(int x, int y, void* pData)
{
// Call through a global pointer - already created.
return pMyClass->MyCallback(x, y, pData);
}

For more information, lookup "Calling Conventions" in the Visual C++ help file's index. (Most of it is rather cryptic!)

Calling conventions are great things, though. They let you call functions in programs written in different languages, too. For example, you could create a superfast graphics library in assembly, wrap the functions in C or C++, and then call them from a Visual Basic program!

Good Luck.

Well, I personally disagree with global functions that are obviously part of a distinct class. It doesn't take much more work to incorporate a callback into a class.

This is because most Windows callback functions (DirectX especially) are smart enough to allow you to pass some constant data long for the ride on every call. So, what you do is:

In your class, we need to define two functions. First, define a static member function that follows the format of the callback. Then, define a nicer non-static member function that contains the data from the original callback function that you need.

In the static version, simply extract the pointer data passed, cast it to your class, and call the non-static function with the appropriate data.

Then, when you call the DirectInput EnumDevices function (for example), you will do something like this:

lpdi->EnumDevices(0, MyClass::StaticEnumCallback, myClass, DIEDFL_ALLDEVICES);

where MyClass is the name of your class and myClass is a pointer to an instance of that class and is the data that is used in the static callback function.

It may seem complicated, but for me it serves to further abstract away Windows and encapsulate the real functionality of the callback in a standard (read: portable) member function.

- Splat

I stand corrected. Thanks Splat!

I didn't realize it could be done. After that, I remembered some things that actually make it simpler to have a static member function work as a callback.

The static member function will always have public access to the class's data members, as long as it has a way to access that object. A global callback function that operates on a class must be declared a friend to access protected (or private) members. (I should have realized this before!)

Thanks for the somewhat mild correction (not at all like - "look at that idiot! - he thinks he knows everything").

Needless to say, I'll be rewriting some of my classes - it really does make the code more readable!

Happy Coding!

I see Splat already beat me to helping you answer. I've done this in the past with a DirectDraw devices and modes too:

hr = DirectDrawEnumerate((LPDDENUMCALLBACK)this->EnumDevCallback, this);
...

BOOL WINAPI CDisplay::EnumDevCallback(GUID FAR* guidPtr, char* description, char* name, void* user);
CDisplay* ThisObj = (CDisplay*)user;


It really works nicely if you're a class nut like me.

------------------------
E.N.D. - http://listen.to/evil
I really am a "class nut" but didn't know enough about static member functions

Splat: Many callbacks can be put inside classes (like you said). But can WinMain be put inside a class like that? Visual C++ allows you to redefine the entry point. Just wondering if it could be done.

Now that would be great!!

Happy Coding!

I don't think you can put WinMain *inside* of a class; even MFC still has a seperate WinMain buried in one of its files.

I suppose you could minimize the size of WinMain by immediately declaring an application object and jumping into it's initialization though.

-Evil

[This message has been edited by Evil (edited November 15, 1999).]

------------------------
E.N.D. - http://listen.to/evil
I am a major class nut. If it belongs in a class, by God!, I will make it work! Actually, I'm not that radical, but often making it work like a class practically turns code into English.

null_pointer: WinMain can NOT be in a class, at least in my experience.

Evil: Bingo! My WinMain is 8 lines long. One to create the GameEngine (my own class that encapulates EVERYTHING except 3D), 1 to pass the instance handle, 1 to set the name of the game, 1 to Initialize() the game engine, 1 to create the logic class (it plugs into the GameEngine and controls it - this is where you define the actual "game"), 1 to plug it in, and one to Start() the game engine.

And one to return 0

Very easy, keeps my non-portable code files to a minimum. And very nice looking too

"Ahh, the joys of encapsulation!"

- Splat

[This message has been edited by Splat (edited November 15, 1999).]

HA! HA! HA! HA! HA! HA! It's alive!!

(Forgive the madness, I always wanted to say that.)

Splat and Evil: I did it!! No global functions, my program compiles correctly, runs straight into the static member function of CGame::WinMain()! Whoo-Hoo!

Bet you'll be wanting the source code now. Heh heh. Well, here it is:

// Changed these to quotes so they'd appear
// with the rest of the text. Change them
// back when you copy the program.
#include "windows.h"

class CGame
{
public:
static int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
};

int WINAPI CGame::WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
return 0;
}

I'm happy today!!

If you make the static member function "WinMain", then you shouldn't have any problems .

The purpose of static member functions is to enable you to replace ALL global functions. The real entry-point symbol for every EXE, DLL, and Console EXE is found in the C runtime library. WinMain is merely a function that is called by the C runtime library's startup routine - it isn't the actual entry point! So a static member function called "WinMain" would work perfectly fine here.

You would use this static function like this:
declare a global CGame object, and [gasp] take for granted that it's in the program. Then call it's functions.

This is completely safe, because if you forgot to declare a global object of your CGame, the linker would give you an error when your code compiled. Not at runtime.

HAH! Finally put something over on MFC!

(Please forgive my outbursts, but I've been toying with this idea for a LONG time! )

[This message has been edited by null_pointer (edited November 16, 1999).]

This topic is closed to new replies.

Advertisement