May 1996
Paul DiLascia is a freelance software consultant specializing in training and software development in C++ and Windows. He is the author of Windows++: Writing Reusable Code in C++ (Addison-Wesley, 1992).
Click to open or copy the SCBMED1 project files.
Click to open or copy the SCBMED2 project files.
Click to open or copy the SCBSAMP project files.
class CObject {
.
.
.
virtual void AssertValid() const;
};
The problem is that I'm using an in-house class library that has non-const functions, which I want to call from my AssertValid function. Here's a simplification of my problem:
class CMyObject : public CObject {
CTable m_table; // CTable is from library
virtual void AssertValid() const;
.
.
.
};
void CMyObject::AssertValid() const
{
.
.
.
ASSERT(m_table.Lookup("foo") == TRUE);
}
CTable::Lookup is not declared const (even though it really is), so the compiler complains when I try to call it from AssertValid. I know the right way to fix it would be to declare CTable::Lookup const, but when I tried this, it created other problems in the library, so I decided to leave the library alone and just forget about the Assert check. Isn't there some way I can fool the compiler into thinking my Lookup function is const? I tried different kinds of casts but none of them worked.
Andrew Filbrook
AWell, you must not have tried the right one! It's not obvious, I admit, but one simple cast will remove all your const-ernation. But first, let me give you a two-bit spiel on const-ness.
While const can be a useful tool-the kind of thing that helps you find bugs at compilation time, which is always a good thing-const always sneaks up and bites you when you least expect it. The problem is that const works well only when every function in your C++ universe uses it consistently. As soon as some function or group of functions-like your in-house library-fails to follow the rules of const-ness, everything breaks down, as you found out. I'll bet, when you tried to make Lookup const, you found yourself making all the functions it calls const, and the functions they call, and on and on. const has a way of rippling throughout your entire system.
I can think of at least two common situations where you need to overcome the const-ness of a member function by casting it away. The first is when you have a member function like Lookup that really is const, but someone just forgot to declare it that way. The second situation is when you really want to let a const member function change the object, but you don't want it to "count" as a change.
Suppose you have a Get function that counts how many times someone calls it.
FOO* CMyObject::GetFoo(String s) const
{
m_refCount++; // Oops: not allowed in const fn
return m_pFoo;
}
The ref count is used purely for record keeping-say, to charge applications for calling your function. It doesn't "change" the object conceptually, but strictly speaking it does, which const-itutes a violation of const-ness. Another example is when a Get function caches a value to speed up subsequent Get operations.
FOO* CMyObject::GetFoo(String s) const
{
if (!m_pFoo)
m_pFoo = ComputeTheRealFoo();
return m_pFoo;
}
ComputeTheRealFoo presumably is a const function that comes up with the FOO pointer. GetFoo stores this in m_pFoo as a performance feature, so it won't have to call ComputeTheRealFoo the next time someone wants to GetFoo. Once again, GetFoo doesn't change the object conceptually, even though it does modify m_pFoo. In Figure 1, the Width and Height functions show a real example of this.
Whether you are merely working around someone else's stupid oversight (YOU would never forget to declare a const function const), or you want to change an object without "changing" it (which makes you too clever for your own good), the question remains: how do you get around the compiler's obsessive insistence that you obey the rules of const-ness? By casting, of course.
To figure out the right cast, you just have to understand how the compiler does const. When you declare a function const, the compiler compiles the function as if the "this" pointer were declared const.
void* CMyObject::GetFoo(String s) const
const CMyObject* this; // pseudo make-believe
{
this->m_refCount++; // Oops: modifies this
return foo;
}
Knowing this, you can probably guess how to subvert the compiler and overcome the second const problem-the ref count problem-by casting away the const-ness of "this".
void* CMyObject::GetFoo(String s) const
{
// Nyah nyah, defeat the compiler...
((CMyObject*)this)->m_refCount++;
return foo;
}
It works for calling any non-const member function.
((CMyObject*)this)->SomeNonConstFunction();
A similar trick will work to solve your CTable Lookup problem, where you want to call m_table.Lookup inside AssertValid (which is const), even though Lookup is non-const.
void CMyObject::AssertValid() const
{
.
.
.
// Oops: Lookup is non-const
ASSERT(m_table.Lookup("foo") == TRUE);
}
The compiler handles this situation by treating all the class members that const-itute CMyObject as if they were const. Thus, inside CMyObject::AssertValid, it's as if m_table were declared
const CTable m_table;
All you have to do get rid of the const-ness is cast it away.
ASSERT( ((CTable*)&m_table) Lookup("foo") == TRUE );
If you really want to show off, it can be written slightly more succinctly as
ASSERT( ((CTable&)m_table).Lookup("foo") == TRUE );
which casts m_table to a (non-const) CTable reference. Both forms are equivalent and generate exactly the same code. Indeed, const-ness almost never affects the generated code; it only generates compiler errors. The exception is when you have const and non-const overloads of the same function. In that case the compiler obviously generates a call to whichever function is appropriate for the const-ness of the object.
So the moral is, while one function with the wrong const declaration can sometimes trigger a const-ellation of difficulties, you should not const-rue const-ness as unworkably const-rictive. One cast is all it takes to escape const-raint and avoid all const-ernation. Sheesh.
QI'm writing a form-based application with Visual C++Ò 4.0 and MFC. My app uses the document/view model to fill out the form and save the data in my document object. Currently I have a number of sample documents that come with the app, which I use in help and "tutorial" mode to provide examples of how to fill out the forms in various circumstances. The actual data is not important except to show an example. For example, the name in one of the forms is "John Doe" and the social security number is "123-45-6789".
Currently, I store these documents on disk as separate document files that can be opened like any other document, but I would prefer to store them in the executable itself, along with the dialogs and other resources, so they can't get separated from the program. I gather I can put any file I want into my resource file, but how can I read the data using Serialize and the normal document/view stuff? I was able to get it to work by copying the resource data to a temporary file on disk, then opening this file the usual way. Is there some way I can trick MFC into reading the "file" from the resource data directly?
AYes. MFC provides a class that does just what you need: CMemFile. This class lets you treat any array of bytes in memory as a file. The basic solution to your problem is to read the resource data into memory, set up CMemFile for this buffer, and let the normal MFC archiving mechanism read the bytes from CMemFile. To show all the gory details, I modified the SCRIBBLE program-from the Visual C++ tutorial-by adding a File New Sample command. It works like File New, except that instead of creating an empty document, it creates one that's initialized with resource data. Figure 2 shows the code. Since Scribble is too long (and boringly familiar) to fit in the magazine, I'm showing only my modifications, not the entire program.
The sample data shown in Figure 3 is just an ordinary Scribble document, Sample.scb, that's compiled into Scribble.exe. To compile any disk file into your executable as resource data, all you have to do is add a line to your resource file-in this case Scribble.rc.
IDR_MAINFRAME SCRIBDOC MOVEABLE PURE "res\\sample.scb"
As with any other resource, IDR_MAINFRAME is the resource ID used to identify the sample document. SCRIBDOC is a resource type I made up.
Figure 3 Sample.SCB
Normally, you would use a predefined type like MENU or ICON here, but you can use any name you want and it will compile. Whatever string you use as the resource type must be the same string you use to load the resource.
// Load resource type "SCRIBDOC"
HRSRC hRes = FindResource(hInst,
(LPCSTR)IDR_MAINFRAME, "SCRIBDOC");
So much for getting Sample.scb compiled into Scribble. The next step is to load it from within your program. To do this, I wrote an overloaded version of CScribbleDoc::OnOpenDocument that takes an ID instead of a pathname (it's part of ScribDoc.cpp). This function uses ::FindResource and ::LoadResource to load the resource into memory. Once the resource is in memory, OnOpenDocument sets up a CMemFile for it, then does the normal archiving thing using the CMemFile as the file (see Figure 4). (The real code has TRY/CATCH blocks that I've stripped to highlight the main points.)
The beauty of this is that the loading still goes through the normal mechanism, through CScribbleDoc::Serialize, because the CMemFile looks just like an ordinary CFile disk file. All it does is fetch bytes from memory instead of from disk. By using CMemFile, I've assumed that the entire resource can fit in memory at once, which is usually a safe assumption. If not, you could implement your own CMemFile-derivative that reads resource data in pieces.
Once serialization is complete, OnOpenDocument frees the resource from memory and terminates. By the way, you may have noticed that I don't use Lock/UnlockResource anywhere. That's because locking/unlocking is a thing of the past, and these functions are implemented in Win32Ò as no-ops for backward compatibility only.
If you're wondering how my overloaded OnOpenDocument gets called, it happens in the command handler for File New Sample.
void CScribbleApp::OnFileSample()
{
OnFileNew(); // create new (empty) doc
CScribbleDoc* pDoc = (CScribbleDoc*)
((CFrameWnd*)m_pMainWnd)->
GetActiveFrame()->GetActiveDocument();
ASSERT_VALID(pDoc);
ASSERT_KINDOF(CScribbleDoc, pDoc);
// load resource "file" IDR_MAINFRAME
VERIFY(pDoc->OnOpenDocument(IDR_MAINFRAME));
// give it a name
pDoc->SetPathName(_T("Sample.SCB"));
}
I should point out that CMemFile has nothing to do with Win32 memory-mapped files, which is a mechanism that lets two or more Win32 processes share data by creating a "file" in memory. CMemFile is an MFC thing that lets you read and write data to a memory block as if it were a file.
QI'm implementing an app with MFC 4.0 and I have a status line indicator that displays "MOD" if the current document is modified. I use the normal ON_UPDATE_COMMAND_UI mechanism to do it.
BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
.
.
.
ON_UPDATE_COMMAND_UI(ID_INDICATOR_MOD,
OnUpdateModifiedIndicator)
END_MESSAGE_MAP()
// In my frame class implementation
void CMyFrameWnd::OnUpdateModifiedIndicator(
CCmdUI* pCmdUI)
{
CDocument* pDoc = GetActiveDocument();
pCmdUI->Enable(pDoc && pDoc->IsModified());
}
This works fine, but I also want to display an asterisk next to the file name in the title bar if the document has been modified. This is so users can see if the document is modified even when the status bar is turned off. Is there something like ON_UPDATE_COMMAND_UI for the title bar, or should I change the title whenever the document's modified status changes?
Phillipe Bernous
AWhen I first saw this question, one solution came to mind, then another, and then yet another. I'll show you the first two now and save the surprise ending for next month.
There's nothing like ON_UPDATE_COMMAND_UI for window titles, but adding an asterisk in the title bar is fairly easy. The first solution I'll show you implements the asterisk solely within the document class by overriding CDocument::SetModifiedFlag. The second solution does it within the child frame window by handling WM_IDLEUPDATEUI. I implemented these solutions as SCBMOD1 and SCBMOD2, modifications to the MFC Scribble tutorial program.
You may not know it, but documents already have titles. CDocument::GetTitle and CDocument::SetTitle let you get and set a document's title, which is what MFC displays in the frame window title bar by default. If the document has a file name, the title is just the file name minus the directory. If the document has no file name-if the user just created it with File New and hasn't saved it yet-MFC makes up a name like Scrib1 or Scrib2, as determined from the document type name you specify in the IDR_MYDOCTYPE resource string. The simplest way to add an asterisk is to change the document title whenever someone changes the document's modified flag. The easiest way to do that is to override CDocument::SetModifiedFlag.
Figure 5 shows my SCBMOD1 modifications to CScribbleDoc. The entire feature is implemented in one function, CScribbleDoc::SetModifiedFlag, which adds or removes the asterisk depending on the modified flag. The implementation is pretty straightforward. CDocument::SetTitle immediately updates the document's frame's title bar by calling an undocumented MFC function, CDocument::UpdateFrameCounts. (You can call this function any time you want to update the title bar for a frame associated with a document.)
The second approach is entirely different. It implements the asterisk in CChildFrame by handling WM_IDLEUPDATEUI. This MFC-private message is defined in <afxpriv.h>. Technically, it's a private message, but you can use it as it's not likely to disappear soon. MFC broadcasts WM_IDLEUPDATEUI to the main frame and all its descendants as part of its normal idle duties. (See "Meandering Through the Maze of MFC Message and Command Routing," MSJ, July 1995). The default handler sends CN_UPDATE_COMMAND_UI messages to update all your UI components, like menu items, toolbar buttons, and status bar panes, which is the perfect time to update the title bar as well. CChildFrame::OnIdleUpdateCmdUI in Figure 6 shows how.
The basic idea is to override CFrameWnd::OnUpdateFrameTitle to append an asterisk to the title if the active document is modified. MFC calls this virtual function whenever it needs to update a frame's title. (For example, when the user opens a new document or when you call CDocument::SetTitle.) Implementing OnUpdateFrameTitle is sort of like writing an ON_UPDATE_COMMAND_UI for the title bar, but there's one very important difference: MFC doesn't call OnUpdateFrameTitle continuously during idle processing like it does to update other UI components. You have to do it yourself. That's where WM_IDLEUPDATEUI comes in.
There're only two tricks that deserve further explanation. First, instead of calling OnUpdateFrameTitle directly, my OnIdleUpdateCmdUI handler sets an undocumented flag, idleTitle, in CFrameWind::m_nIdleFlags. If this flag is set, CFrameWnd updates the title in its default OnIdleUpdateCmdUI handler.
// in CFrameWnd::OnIdleUpdateCmdUI
if (m_nIdleFlags & idleTitle)
OnUpdateFrameTitle(TRUE);
If you don't like using idleTitle this way (because it's undocumented), you can always call OnUpdateFrameTitle directly. Just replace the line
m_nIdleFlags |= idleTitle;
with
OnUpdateFrameTitle(TRUE);
The second trick is that I save the previous state of the modified flag, so I update the title only if the document's modified flag has changed since the last time WM_IDLEUPDATEUI was sent. Otherwise, I'd be setting the title (to the same value) on every WM_IDLEUPDATEUI cycle and the title would flicker noticeably. (Trust me, I found out the hard way.) You might try using another undocumented function, AfxSetWindowText, which sets the window text only if it's actually different from the current text-but that won't work here because I use CMDIChildWnd::OnUpdateFrameTitle to first compute the "normal" title, then I append the asterisk. If I used AfxSetWindowText, I would end up continually removing my asterisk from the title bar and then adding it back again, so the title would still flicker. (I found that out the hard way, too.)
What MFC really needs here is some hook to let you modify the title before OnUpdateFrameTitle sets it, or else a separate function that computes the title without setting it in the window. Barring these, the best you can do would be to copy the MFC code from CMDIChildWnd::OnUpdateFrameTitle and then modify it. Unfortunately, you're out of luck if a new release of MFC changes this function. I opted to go ahead and set the title twice-once to compute the "base" title, and again to append the asterisk-and then do the flag thing with m_bModLast to cure the flicker problem. It's only two or three lines of code.
I implemented the change in CChildFrame and not CMainFrame because Scribble is an MDI app, so CChildFrame is the one that contains the document/view. One consequence is that the asterisk appears in the child frame only, not the main frame, unless the child is maximized (see Figure 7). With my first solution (the document approach), both frames get the asterisk since it's part of the document title. You could of course fix this by changing CMainFrame similarly.
Figure 7 Asterisk appears in ChildFrame
Now that you've seen the two solutions, the natural question is which one is better? Well, that depends on whether you consider the asterisk a property of the document or the frame! Personally, it seems more like a property of the frame to me, so I favor the WM_IDLEUPDATEUI approach. The first solution is certainly simpler, but consider this: what if your document is embedded in an OLE In-place frame? If you use the document solution, the IP frame gets the asterisk feature. Users might not like asterisks appearing in their title bar, or maybe they will and then wonder why other embedded document objects don't have the same neat feature. Either way, you'll be inconsistent.
So there you have it; one feature, two implementation philosophies. But the story doesn't end here! At the outset, you asked whether there was a way to change the title with ON_UPDATE_COMMAND_UI. In a way, my second solution is a bit like ON_UPDATE_COMMAND_UI, since WM_IDLEUPDATEUI is closely related to ON_UPDATE_
COMMAND_UI-it's the place from which CN_UPDATE_COMMAND_UI is sent, and all your ON_UPDATE_COMMAND_UI handlers get called. So why not go all the way? Why not implement a solution that does let you use ON_UPDATE_COMMAND_UI to change the title? This would be really neat, because it would let you add not just an asterisk to show the document is modified, but any kind of title bar "indicator" you want, just like the indicator panes in the status bar. You should be able to show the document's time and date of creation or the name of the current selected object in the title bar. Next month I'll show you how.
Have a question about programming in C or C++? You can mail it directly to C/C++ Q&A, Microsoft Systems Journal, 825 Eighth Avenue, 18th Floor, New York, New York 10019, or send it to MSJ (re: C/C++ Q&A) via:
Internet: | Paul DiLascia Eric Maffei |