referencing problem in C++

I have the following code in an existing C++ program with no classes:

//***********************************************************************
void add_font_to_list(char *facename, uint charset, u8 pitch, u8 family)
{
// do stuff
}

//***********************************************************************
static int CALLBACK EnumFontFamiliesExProc(ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *lpntme,
int FontType, LPARAM lParam )
{
LOGFONT *lfptr = &lpelfe->elfLogFont ;
u8 paf = (u8) lfptr->lfPitchAndFamily ;
u8 pitch = paf & 0x03 ;
u8 family = paf & 0xFC;
add_font_to_list((char *) lpelfe->elfFullName, lfptr->lfCharSet, pitch, family) ;
return 1;
} //lint !e715

//***********************************************************************
void build_font_list(void)
{
HDC hDC = GetDC( NULL );
LOGFONT lf = { 0, 0, 0, 0, 0, 0, 0, 0,
DEFAULT_CHARSET, 0, 0, 0,
DEFAULT_PITCH, ""};
EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC) EnumFontFamiliesExProc, 0, 0 );
ReleaseDC( NULL, hDC );
}

//***********************************************************************
This all works fine. Now, I'm trying to move all this into a font-handling class, but I have a problem with EnumFontFamiliesExProc() ...
I moved all three functions into the class declaration, but the call to EnumFontFamiliesEx() fails because EnumFontFamiliesExProc() isn't of the right type for passing to EnumFontFamiliesEx().

If I change EnumFontFamiliesExProc() to be a static function, but *not* part of the class, then it cannot call add_font_to_list().

What is the proper way to solve this problem?? I've been generally making my internal functions (not intended for external use) private functions in the class declaration; I *think* my core problem is just the problem of how to properly pass the function pointer to EnumFontFamiliesEx(), but perhaps I have a more general failure to understand how to do this, which I'm hoping someone can clarify here.
Most Windows callbacks allow you to pass in a context pointer. Your code shoudl look something like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class SomeClass
{
    void add_font_to_list(char *facename, uint charset, u8 pitch, u8 family)
    {
        // do stuff
    }

    static int CALLBACK EnumFontFamiliesExProc(
        ENUMLOGFONTEX *lpelfe,
        NEWTIMXTMETRICEX *plntme,
        DWORD FontType,
        LPARAM lParam)
    {
        SomeClass* pThis = (SomeClass*)(void*)lParam;

        LOGFONT *lfptr = &lpelfe->elfLogFont;
        u8 paf = (u8) lfptr->lfPitchAndFamily;
        u8 pitch = paf & 0x03;
        u8 family = paf & 0xFC;
        pThis->add_font_to_list((char *)lpelfe->elfFullName, lfptr->lfCharSet, pitch, family);
        return 1;
    }

    build_font_list()
    {
        HDC hDC = GetDC(NULL);
        LOGFONT lf = { 0, 0, 0, 0, 0, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, DEFAULT_PITCH, ""};
        EnumFontFamiliesEx(hDC, &lf, EnumFontFamiliesExProc, (LPARAM)(void*)&this, 0 );
        ReleaseDC(NULL, hDC);
    }
    //...
};
Last edited on
Okay, with a couple of mods, I got this to work (as in, both compile *and* run properly!)... Thanks for the help. I'm new to C++ (though I've been using C for decades) and the "this" pointer is still a little obtuse to me. I'll need to study this code for awhile before I will entirely understand it!

In order to get your example to work, I had to include add_font_to_list() to the class, but the callback function EnumFontFamiliesExProc() had to be *not* included in the class (i.e., not included in the class declaration). That explains why I had to pass (this) in to it, because it doesn't otherwise know about my CFontList class members.

Ironically, however, I had to make add_font_to_list() a public member of the class, so this non-class function could access it. However, in reality, all of these functions are internal operations of the class, I don't really want them to be public...

In fact, I *really* want the callback function to be a private member of the class as well; the only function that should be accessed by a class user is build_font_list(), the rest is internal implementation details. However, if I make EnumFontFamiliesExProc() also a member of the class, the call to the Windows function:
EnumFontFamiliesEx(hDC, &lf, (FONTENUMPROC) EnumFontFamiliesExProc, (LPARAM) (void*) this, 0 );
fails with:
font_list.cpp:237: error: argument of type
`int (CFontList::)(ENUMLOGFONTEX*, NEWTEXTMETRICEX*, int, LPARAM)'
does not match
`int (*)(const LOGFONTA*, const TEXTMETRICA*, DWORD, LPARAM)'

It appears that my cast of the callback function to (FONTENUMPROC) is ignored... I don't understand why the cast fails, and I don't understand why it works differently if the callback is *not* included in the class...





Last edited on
EnumFontFamiliesExProc should be a static member function. Being static, it doesn't take a this pointer. It's syntactically identical to an external EnumFontFamiliesExProc. Making it a static member just restricts it naming and visibility as it can be made private.

If EnumFontFamiliesExProc is a member of the class, then add_font_to_list() can be private.

In fact, I *really* want the callback function to be a private member of the class as well
I didn't compile the code I posted, I just wrote it from your code. But if done properly, all those functions should be members, with the callback being static.

The syntax error you listed is because EnumFontFamiliesExProc should be a static member of the class. I realise I'm repeating myself, but it's an important point.
Okay, with further study, I have resolved this issue... in order to make all these functions private class members, as I wished, I had to:

1. Use: reinterpret_cast<FONTENUMPROC>(EnumFontFamiliesExProc)
instead of (FONTENUMPROC) EnumFontFamiliesExProc, to cast the pointer.
Shame on me for using C syntax in a C++ program.

2. I had to make the callback function static, otherwise I got an utterly inscrutable compiler error on the call that passed the pointer.

3. I *still* need to pass (this) to the callback function, because apparently static members cannot call non-static members in a class...

Now all my internal functions *are* private, everything compiles and runs, all I need to do is study the language and figure out why (2) and (3) are necessary...
It's a common pattern to handle C callbacks this way. So I sort of cheated because I know the pattern.

The callback has to be static because that's what the Windows API (in this case) demands. Databases also use the same callback mechanism. As I recall, Microsoft insisted that Sybase add a void* parameter the dbLibrary callback so they could pass the this pointer in exactly this way, back when they licenced it. You even get the same thing in thread libraries. Why does pthread_create take a void*? So you can pass in a context pointer.

As your callback is going to call member functions on the class, it needs a this pointer so it knows which instance to call. And where does it get the this pointer from? It has to be passed in as a parameter. And who passes in the correct parameter? The person who called the called the thing that does the callback.

In your case, build_font_list passes in this, which is passed into EnumFontFamiliesEx, which is passed into your callback EnumFontFamiliesExProc, which is used to call add_font_to_list.

Ooo, that's begining to sound complicated, but once you understand it, you use it all over the place. I even used it last week with SQLite3, wrapping a query callback.
Last edited on
I'm going to save this thread so I can refer to it in the future; alot of good information about callbacks and other C++ issues. Maybe after awhile I'll actually feel competent in this language...

I'm also marking the issue as solved, which I think it is. Thank you very much, kbw, for all of your assistance and explanations!
Topic archived. No new replies allowed.