MSDN Magazine > Issues and Downloads > 2000 > June >  C++ Q&A: Inline Virtual Functions, AVI Files in...
This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.
MSDN Magazine
C++ Q&A
Inline Virtual Functions, AVI Files in EXEs, and the DynPrompt Sample App
Paul DiLascia

Code for this article: CQA0600.exe (47KB)


Q
How does C++ handle inline virtual functions? When a function is inline and virtual, will code substitution take place or is the call resolved using the vtable?
G.N. Rajagopal
A
The answer is, it depends. To see why, let's consider each caseâ€"inline and virtualâ€"separately. Normally, an inline function is expanded, well, inline.


class CFoo {
private:
    int val;
public:
    int GetVal()      { return val; }
    int SetVal(int v) { return val=v; }
}; 
In this situation, if you write


CFoo x; 
x.SetVal(17); 
int y = x.GetVal(); 
then the compiler generates the code as if you'd written:


CFoo x; 
x.val = 17; 
int y = x.val; 
Of course you can't do this because val is private. Inline functions give you the advantage of data hiding without paying the price of a function call. So much for inline functions.
      Virtual functions give you polymorphism, which means derived classes can implement the same function differently. Suppose GetVal is now declared virtual and you have a second class, CFoo2, with a different implementation:


class CFoo2 : public CFoo { 
public: 
    // virtual in base class too!
    virtual int CFoo2::GetVal() { 
        return someOtherVal; 
    } 
}; 
If pFoo is a pointer to a CFoo or CFoo2, then pFoo->GetVal will call the member function for whichever class pFoo really points toâ€"CFoo or CFoo2. This is C++ 101, and you should know it like the back of your hand.
      But what happens if a function is both virtual and inline? Remember, there are two ways to make a function inline: by using the inline keyword in the function definition, as in


inline CFoo::GetVal() { return val; } 
or by coding the function body inline within the class declaration, as in the previous CFoo2::GetVal example. So if you include the body of your virtual function within the class declaration


class CFoo { 
public:
    virtual int GetVal() { return val; } 
}; 
then you are telling the compiler you want GetVal to be both inline and virtual. This doesn't seem to make sense, for how can polymorphism work if the function is expanded inline? The answer, of course, is that it can't. Or can it?
      The first rule the compiler follows when it encounters such a beast is this: whatever happens, polymorphism must work. If you have a pointer to a CFoo object, then pFoo->GetVal is guaranteed to call the right function. In general, this means the GetVal functions will be instantiated as real (noninline) functions, with vtable entries pointing to them. But that doesn't mean the function can't ever be expanded! Consider this code again:


CFoo x; 
x.SetVal(17); 
int y = x.GetVal(); 
      The compiler knows that x is really a CFoo, not a CFoo2, since the stack object is explicitly declared. There's no way x could really be a CFoo2. So it's safe to expand SetVal/GetVal inline. If you write this more complex code


CFoo x; 
CFoo* pfoo=&x; 
pfoo->SetVal(17); 
int y = pfoo->GetVal(); 
���
CFoo2 x2; 
pfoo = &x2; 
pfoo->SetVal(17); //etc. 
the compiler knows that pfoo points to x the first time and x2 the second time, so again it's safe to expand the virtual functions. You can dream up ever more complex examples, where the type of object pfoo points to is always known, but most compilers won't do any heavy analysis. Even in the preceding example, some compilers will do the safe thing, which is to instantiate the function and call through a vtable. Indeed, the compiler is free to ignore the inline requirement and always use the vtable. The only absolute rule is that the code must work; that is, virtual functions must behave polymorphically.
      In general, inlineâ€"whether explicit or implicitâ€"is a hint, not a mandate, just like register. (Does anyone remember register? It asks the compiler to use a machine register for a variable if it can.) The compiler can refuse to expand even a nonvirtual inline function if it wants to, and the first C++ compilers often complained, "inline abortedâ€"function too big." Certainly if an inline function calls itself, or if you pass its address somewhere, the compiler must generate a normal (outline?) function. And, of course, inline functions are not expanded in debug builds, or if you set a compiler option preventing it.
      The only way to really know what your compiler is doing is to look at the code it generates. For the Microsoft® compiler, you can compile with -FA to generate an assembly listing. You don't need to be an assembler jock to know what's going on. I encourage you to perform this experiment; it's good for the soul to see what the machine is actually doing, and you can learn a lot poking around assembly listings. For now, I'll spare you that agony.
      The topic of inline functions is more complex than you might think at first. There are many circumstances that force the compiler to generate a normal function: recursion, taking the address of your function, functions that are too big, and virtual functions. Here's another consideration: if the compiler decides to instantiate your inline function, where does it put the function? Which module does it go into?
      Usually, classes are declared in header (.h) files. So if mumble.cpp includes foo.h and the compiler decides it has to instantiate CFoo:: GetVal, it will instantiate it as a static function in mumble.cpp. If 10 modules include foo.h, the compiler could generate up to 10 copies of your virtual function. In fact, you could end up with objects of the same type with vtables pointing to different copies of GetVal. Yuk! Some linkers are smart enough to eliminate the redundancies at link time, but in general you can't be sure.
      So the bottom line is: it's best not to use inline virtual functions, since they're almost never expanded anyway. Even if your function is just one line, you're better off putting it in the module (.cpp file) along with the other class functions. Of course, programmers often put short virtual functions in the class declarationâ€"not because they expect the function to be expanded inline, but because it's more convenient and readable.
Q
CAnimateCtrl::Open(filename) is working just great. But why should I have to carry around an external AVI file with my app? What's the trick to obtaining the AVI from resources? Open seems only to work with strings, not resource IDs.
Bob Logan
A
Indeed, why should you have to carry an external file? How terribly gauche that is, and prone to breakage if someone moves the file orâ€"heaven forbidâ€"deletes it. Much better to store your AVI in the EXE itself as a resource. Fortunately, there's an overloaded CAnimateCtrl::Open that takes a UINT, the resource ID of your AVI resource.
      But how do you get the AVI file into your EXE? For that, you have to know the secret voodoo. The animation control looks for a resource type called AVI, so all you have to do is code your resource (.rc) file like so:


IDR_MYANIMATION AVI DISCARDABLE "res\\myanim.avi" 
This assumes your IDR_MYANIMATION is #defined to an integer value (you can't use a string name for the resource) and the AVI file is in the RES directory under your main project. The resource compiler copies myanim.avi verbatim into your .exe, where the animation control will find it. The trick is knowing to use the name AVI in the resource file.
      Incidentally, you can use this trick to put any file you like as a resource in your EXE. For example, say your app uses FOO files (Funky Omnigenous Object) and you have some sample FOO files you want to include in your app. You can add them as resources by writing:


IDR_MYFOOFILE FOO DISCARDABLE "res\\myanim.foo" 
      Once you have the FOO file in your EXE, how do you access it? That requires writing a little code. First, you have to get a resource handle by calling FindResource.


HINSTANCE hInst = AfxGetResourceHandle(); 
HRSRC hRsrc = ::FindResource(hInst,
    MAKEINTRESOURCE(IDR_MYFOOFILE), // ID
    "FOO");                         // type 
ASSERT(hRsrc); 
Assuming the hRsrc is not null, you'll call a couple of functions to get the size of the resource (FOO file) and the data itself.


DWORD len = SizeofResource(hInst, hRsrc); 
BYTE* lpRsrc = (BYTE*)LoadResource(hInst, hRsrc); 
ASSERT(lpRsrc); 
Now lpRsrc points to the FOO file in memory. If you want to access the memory as if it were an ordinary disk file, you can use MFC's CMemFile, which makes a block of storage look like a CFile.


CMemFile file(lpRsrc, len); 
file.Read(...); // etc. 
      You can even make CDocument open a resource as if it were a disk file by overriding OnOpenDocument. For details, see my column in the May 1996 issue of MSJ. Also of related interest is my February 1998 column, where I presented a little utility that finds and extracts AVI and WAV resources from any EXE or DLL.
Q
I have an app that has a dynamically changing menu item. I can use CCmdUI::SetText("Menu Text") to change the menu text, but how do I dynamically change the tooltip/status bar text?
Andrew Akins
A
There are several strategies. Avoidance, deception, confrontation.... First, avoidance: why do you want a dynamically changing menu item in the first place? Generally speaking, it's a bad idea. Dynamic menus are confusing. I'm using your product and all of a sudden the menu items are different. Why? Whenever I see an app with changing menus, I think: they need a UI designer.
      But, of course, there's an exception to every rule and there are many good examples where dynamic menu items are cool: for example, the most recently used (MRU) file list at the bottom of the File menu in most document-oriented apps. But here, as a user, I don't think about or even notice that the items are changing; it all seems totally obvious and intuitive. I would elevate this to a design principle: if the user notices the menu items are changing, it's a bad design. If the user doesn't notice them changing, it's OK.
      But what about the status bar prompt? In the case of the MRU menu, the prompt is something generic like "Open the selected document," which works for all the menu items, whatever the file name is. This is another avoidance strategy. Only an exceptionally egotistical programmer would bother to implement a dynamically changing prompt just so you can say "Open the file mumble.txt" when "Open this document" is perfectly fine and more efficient. But you could do it. If you keep reading, I'll show you how.
      Another situation in which dynamic menus are useful is when you want to toggle some Boolean state. For example, a command that hides or shows the toolbar might be called Hide Toolbar when the toolbar is visible, and Show Toolbar when it's not. A more common way to do this is to have a single command, View Toolbar, with a checkmark that appears when the toolbar is visible (see Figure 1). GUI gurus could debate for hours about which approach is better. Probably it doesn't make much difference. But even if you decide to go dynamic (like Hide Toolbar/Show Toolbar), you can use a single command ID, ID_VIEW_ TOOLBAR, and a single prompt, "Hide or show the toolbar." Again, it's not worth the bother to implement a dynamic prompt.
Figure 1 Dynamic Menu Control
Figure 1 Dynamic Menu Control

      All of this suggests that the first thing you should do is seriously reconsider your user interface. Do you really need a dynamic menu item? And do you really need a dynamic prompt to go with it? If the answer is yes to both questions (perhaps you're building an app that reflects the internal mental state of a psychotic person) then read on. Otherwise you can stop here and feel good about how much work I saved you.
      As Andrew points out, changing the menu text is easy. All you have to do is implement an ON_UPDATE_COMMAND_UI handler and call CCmdUI::SetText like so:



void CFrameWnd::OnUpdateToolbar(CCmdUI* pCmdUI) 
{
    BOOL bVisible = IsToolbarVisible(...);
    // Note same mnemonic (&T) for both cmds!
    pCmdUI->SetText(bVisible ?
       "Hide &Toolbar" : "Show &Toolbar"); 
} 
      So much for changing the menu text. Next, the prompt. When you create a menu command, you give it an ID. MFC uses this ID to get the command prompt by looking for a resource string with the same ID. For example:


STRINGTABLE DISCARDABLE  
BEGIN
    ID_VIEW_TOOLBAR "Show or hide the toolbar\nToggle ToolBar" 
END 
MFC uses the text following the newline (\n) as the tooltip text, in case your command also appears as a toolbar button. Since MFC allows only one string for each command ID, how can you change the prompt dynamically? The simplest way is to finesse the problem by writing a prompt that works in either case, such as the hide or show toolbar example discussed earlier. But your app will look silly if the prompt says "Open the file, make the font blue, or exitâ€"take your pick."
      One way to get a dynamic prompt is to break your command into several commandsâ€"for example, ID_HIDE_ TOOLBAR and ID_SHOW_TOOLBAR. This is the deception strategy. The last thing the command handler for each of these commands would do is change the menu item ID to that of the other command. Problem solved. I'll leave you to work out the details.
      Using two IDs may be a simple solution, but it won't work in all cases. For example, in the MRU file menu, you would need a different ID for every possible file name. Since there are more possible file names than there are atoms in the universe you'll run out of IDs, even on a 64-bit machine!
Figure 2 Dynamic Prompt in Action
Figure 2 Dynamic Prompt in Action

      I wrote a little app, DynPrompt, that finally confronts the problem of dynamic prompts. Figure 2 shows it in action and Figure 3 shows the source. To understand how DynPrompt works, you have to probe a little deeper into how MFC does menu prompts. When the user moves the mouse over a menu item, Windows® sends WM_MENUSELECT with the ID of the item. MFC's CFrameWnd handles it like so:



// much simplified 
void CFrameWnd::OnMenuSelect(UINT nItemID,
    UINT nFlags, HMENU hSysMenu)
{
   SendMessage(WM_SETMESSAGESTRING, nItemID); 
} 
      I've simplified things a bit; the actual function is more than 60 lines of code! But the basic idea is that the frame sends a WM_SETMESSAGESTRING message to itself, with the command ID as WPARAM. WM_SETMESSAGESTRING is an MFC-private message defined in afxpriv.h. It sets the text in the status bar pane. You can pass a resource string ID as WPARAM or an actual string as LPARAM.


// resource string ID 
SendMessage(WM_SETMESSAGESTRING, ID_MYSTRING);
 
// string 
SendMessage(WM_SETMESSAGESTRING, 0, (LPARAM)_T("Hello, world")); 
      So if you want to implement a dynamically changing menu prompt, all you have to do is override CFrameWnd::OnMenuSelect and send a WM_SETMESSAGESTRING with whatever string you want.


void CMainFrame::OnMenuSelect(UINT nItemID, UINT nFlags,
                              HMENU hSysMenu) 
{ 
    if (/* nItemID has a dynamic prompt */) {
        CString sPrompt = // whatever you want 
        SendMessage(WM_SETMESSAGESTRING, 0, 
      (LPARAM)(LPCTSTR)sPrompt); 
          m_nIDTracking = nItemID; 
      } else { 
          CFrameWnd::OnMenuSelect(nItemID,   
              nFlags, hSysMenu); 
      } 
  } 
      The actual OnMenuSelect handler in MainFrm.cpp (see Figure 3) calls a bunch of menu functions to extract the file name from the MRU menu item and build a prompt of the form "Open the file file name." If you try this at home, make sure you call the base class CFrameWnd::OnMenuSelect for any command whose prompt you don't handle; otherwise, all the other prompts will vanish into the invisible bit bucket.
      Finally, what can you do about changing the tooltip text dynamically? CFrameWnd::OnToolTipText is the MFC function that handles tooltip notifications. The standard implementation loads the resource string with the matching command ID, extracts the text following the newline (\n), and copies it into the caller's TOOLTIPTEXT struct. All you have to do is override this handler and do your own thing. I leave the details as an exercise. Hey, I can't do everything!

Paul DiLascia is the author of Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992) and a freelance consultant and writer-at-large. He can be reached at cppqa@microsoft.com or http://www.dilascia.com.

From the June 2000 issue of MSDN Magazine.

Page view tracker