【摘录】Something about Unreferenced Parameters

Q I've seen C++ code that uses UNREFERENCED_PARAMETER for parameters that aren't used. For example:
int SomeFunction(int arg1, int arg2)
{
UNREFERENCED_PARAMETER(arg2)
...
}
But I also see code like this
int SomeFunction(int arg1, int /* arg2 */)
{
...
}
Can you explain what the difference is and which is better?
Q I've seen C++ code that uses UNREFERENCED_PARAMETER for parameters that aren't used. For example:
int SomeFunction(int arg1, int arg2)
{
UNREFERENCED_PARAMETER(arg2)
...
}
But I also see code like this
int SomeFunction(int arg1, int /* arg2 */)
{
...
}
Can you explain what the difference is and which is better?
Judy McGeough

A Why surely. Let's start with UNREFERENCED_PARAMETER. This macro is defined in winnt.h, like so:
#define UNREFERENCED_PARAMETER(P) (P)
In other words, UNREFERENCED_PARAMETER expands to the parameter or expression passed. Its purpose is to avoid compiler warnings about unreferenced parameters. Many programmers, including yours truly, like to compile with the highest warning level, Level 4 (/W4). Level 4 warnings fall into the category of "things that can be safely ignored." Little infelicities that won't break your code, though they might make you look bad. For example, you might have some line of code in your program like this
int x=1;
but you never use x. Perhaps this line is left over from a time when you did use x, but then you removed the code and forgot to remove the variable. Warning Level 4 can find these minor mishaps. So why not let the compiler help you achieve the highest level of professionalism possible? Compiling with Level 4 is a way to show pride in your work. Level 4 is de rigueur if you're writing a library for public consumption. You don't want to force your developers to use a lower level to compile their code cleanly.
A Why surely. Let's start with UNREFERENCED_PARAMETER. This macro is defined in winnt.h, like so:
#define UNREFERENCED_PARAMETER(P) (P)
In other words, UNREFERENCED_PARAMETER expands to the parameter or expression passed. Its purpose is to avoid compiler warnings about unreferenced parameters. Many programmers, including yours truly, like to compile with the highest warning level, Level 4 (/W4). Level 4 warnings fall into the category of "things that can be safely ignored." Little infelicities that won't break your code, though they might make you look bad. For example, you might have some line of code in your program like this
int x=1;
but you never use x. Perhaps this line is left over from a time when you did use x, but then you removed the code and forgot to remove the variable. Warning Level 4 can find these minor mishaps. So why not let the compiler help you achieve the highest level of professionalism possible? Compiling with Level 4 is a way to show pride in your work. Level 4 is de rigueur if you're writing a library for public consumption. You don't want to force your developers to use a lower level to compile their code cleanly.
The problem is, Level 4 is really fussy. At Level 4, the compiler complains about such harmless things as—well, unreferenced parameters (unless, of course, you really did mean to use the parameter, in which case it's not so harmless). Say you have a function with two arguments, but you only use one:
int SomeFunction(int arg1, int arg2)
{
return arg1+5;
}
With /W4, the compiler complains: "warning C4100: 'arg2' : unreferenced formal parameter." To fool the compiler, you can add UNREFERENCED_PARAMETER(arg2). Now your function references arg2 so the compiler will shut up. And since the statement
arg2;
doesn't actually do anything, the compiler doesn't generate any code for it, so there's no loss of either space or efficiency.
Sharp minds might wonder: if you don't use arg2, why declare it in the first place? Usually it's because you're implementing a function to meet some API signature ordained from the heavens. For example, MFC's OnSize handler must have the following signature:
void OnSize(UINT nType, int cx, int cy);
Here cx/cy are the window's new width/height and nType is a code like SIZE_MAXIMIZED if the window is being maximized or SIZE_RESTORED for normal sizing. Usually you don't care about nType; you only care about cx and cy. So you need UNREFERENCED_PARAMETER(nType) if you want to compile with /W4. OnSize is just one function among thousands in MFC and Windows®. It's hardly possible to write a Windows-based program without unreferenced parameters.
So much for UNREFERENCED_PARAMETER. As Judy notes in her question, another trick C++ programmers often use to achieve the same result is to comment the parameter name out of the function signature:
void CMyWnd::OnSize(UINT /* nType */, 
int cx, int cy)
{
}
Now nType is an unnamed parameter, the same as if you'd typed OnSize(UINT, int cx, int cy). So now the $64,000 question is: which method should you use—unnamed parameters or UNREFERENCED_PARAMETER?
Most of the time it makes no difference, it's purely a style thing. (Do you like your java black or with cream?) But I can think of at least one situation where UNREFERENCED_PARAMETER is required. Suppose you've decided to disallow maximizing your window. You disable the Maximize button, remove Maximize from the system menu, and block every other place the user could maximize. Because you're paranoid (and most good programmers are paranoid), you add an ASSERT to ensure that your code is working as you intended:
void CMyWnd::OnSize(UINT nType, int cx, int cy)
{
ASSERT(nType != SIZE_MAXIMIZE);
... // use cx, cy
}
The QA team runs your program 87 ways and the ASSERT never bombs, so you figure it's safe to compile a Release build. But now without _DEBUG defined, ASSERT(nType != SIZE_MAXIMIZE) expands to ((void)0) and suddenly nType becomes an unreferenced parameter! There goes your clean compile. You can't comment nType out of the parameter list because you need it for the ASSERT. So in this situation—where the only place you use a parameter is within an ASSERT or other _DEBUG-conditional code—only UNREFERENCED_PARAMETER will keep the compiler happy in both Debug and Release builds. Got it?
Before closing, I'd be remiss not to mention that you can suppress individual compiler warnings using a pragma like so:
#pragma warning( disable : 4100 )
4100 is the error code for unreferenced parameter. The pragma suppresses the warning for the rest of the file/module. You can reenable the warning like so:
#pragma warning( default : 4100 )
However, a better approach is to store all of the warning states before disabling the specific warning, and then go back to that configuration when you're done. That way, you get back to the state you were at previously and not just the compiler default.
So you could suppress unreferenced parameter warnings for a single function by surrounding it with pragmas like this:
#pragma warning( push ) 
#pragma warning( disable : 4100 )
void SomeFunction(...)
{
}
#pragma warning( pop )
Of course, that's way too verbose for unreferenced parameters, but possibly necessary for other kinds of warnings. Library builders use #pragma warning all the time to block warnings so their code can compile cleanly with /W4. MFC is full of such pragmas. There're even more #pragma warning options I haven't mentioned. Check 'em out in the documentation.

Q I've noticed that some apps have special commands when you right-click on their minimized button in the task bar. For example, WinAmp (a popular media player program) has an added "WinAmp" menu item with WinAmp commands. How can I add my own items to my app's task bar button?
Q I've noticed that some apps have special commands when you right-click on their minimized button in the task bar. For example, WinAmp (a popular media player program) has an added "WinAmp" menu item with WinAmp commands. How can I add my own items to my app's task bar button?
Jirair Osygian

Q I've created a simple MFC single document interface (SDI) application with a form view to display a counter. I want to be able to start and stop the counter by right-clicking on the minimized application down on the task bar. The start and stop functions work fine as buttons on my form, and I was able to add the start/stop commands to the system menu. But when I click on them in the system menu, nothing happens. How can I handle these messages from my modified system menu?
Q I've created a simple MFC single document interface (SDI) application with a form view to display a counter. I want to be able to start and stop the counter by right-clicking on the minimized application down on the task bar. The start and stop functions work fine as buttons on my form, and I was able to add the start/stop commands to the system menu. But when I click on them in the system menu, nothing happens. How can I handle these messages from my modified system menu?
Monicque Sharman

A I'll answer both questions in one fell swoop. The answer to Jirair's question is simple: the menu the user sees when you right-click on an application's minimized task bar button is the same as the menu displayed if she clicks on the application icon in the top-left corner of the caption bar or presses Alt+Space. Figure 1 shows what I mean. This menu is called the system menu and has commands like Restore, Minimize, Maximize, and Close.
A I'll answer both questions in one fell swoop. The answer to Jirair's question is simple: the menu the user sees when you right-click on an application's minimized task bar button is the same as the menu displayed if she clicks on the application icon in the top-left corner of the caption bar or presses Alt+Space. Figure 1 shows what I mean. This menu is called the system menu and has commands like Restore, Minimize, Maximize, and Close.
Figure 1  Sys Menu 
You can call ::GetSystemMenu to get the system menu, then modify it by adding, deleting, or changing items. You can even suppress the system menu entirely by turning off the WS_SYSMENU style in your window creation flags or virtual PreCreateWindow function. But whatever you do, the system menu is also the one that's displayed when users right-click on your application's minimized button in the task bar.Origins of Ctrl+Alt+Del
In my January column, I asked if anyone knew the origins of Ctrl+Alt+Del. Apparently several readers know how to use Google, because they sent me links to the same USA Today article I discovered before posing my challenge (see Thank this guy for 'control-alt-delete'). Ctrl+Alt+Del was invented by a man named David J. Bradley who worked at IBM.
IBM decided it would be good to have a way to reset their new PC without turning off the power. And why those particular keys, Ctrl+Alt+Del? For technical reasons, David needed to use two modifier keys. He also wanted a combination no one was likely to type by accident. So he chose Ctrl+Alt as the modifier keys (less common than Shift) and Delete, which is on the other side of the keyboard, so typing Ctrl+Alt+Del required two hands. At least it did in the old days.
Modern keyboards now have Ctrl and Alt on the right side as well. The reset feature was originally intended to be a secret escape hatch for IBMers, but inevitably the cat got out of the bag. Once developers learned about it, they started telling customers to use it as a last resort when their machines were hung. The rest is history. Ctrl+Alt+Del is affectionately known as the "three finger salute," and survives in Windows even today, where it invokes the Task Manager so you can kill hung tasks or shut your system down (you can learn more about Secure Attention Sequences like Ctrl+Alt+Del in this month's Security Briefs column). And what if Ctrl+Alt+Del fails? Why, just hold the power button down for five seconds, please.
David Bradley was one of the original 12 engineers who built the IBM Personal Computer. He wrote the ROM BIOS. For a brief bio on David, see David J. Bradley.

Which leads to Monicque's question: if you add your own commands to the system menu, how do you handle them in MFC? If you do the normal thing—write an ON_COMMAND handler somewhere and add it to one of your message maps, you'll discover your handler never gets called. How come?
It's because Windows and MFC handle system commands differently from ordinary menu commands. When the user invokes a normal menu command or button in your form, Windows sends your main window a WM_COMMAND message. If you're using MFC, MFC's command-routing mechanism catches this message and routes it through the system to any object in the command route that has an ON_COMMAND handler for that command. (For details on MFC command routing, see my article " Meandering Through the Maze of MFC Message and Command Routing" in the July 1995 MSJ.)
But system commands don't come via WM_COMMAND. They come via a different message called—what else?—WM_SYSCOMMAND. This is true whether the command ID is one of the true system commands like SC_MINIMIZE and SC_CLOSE, or some other command ID you've added. To handle system menu commands, you have to handle WM_SYSCOMMAND explicitly and check for your own command IDs. This requires adding ON_WM_SYSCOMMAND to your main window's message map, with a handler function like this:
CMainFrame::OnSysCommand(UINT nID, LPARAM lp)
{
if (nID==ID_MY_COMMAND) {
... // handle it
return 0;
}
// pass to base class: important!
return CFrameWnd::OnSysCommand(nID, lp);
}
If the command isn't yours, don't forget to pass it to your base class—which is typically CFrameWnd or CMDIFrameWnd. Otherwise, Windows won't get the message and you'll break the built-in commands.
Handling WM_SYSCOMMAND in your main frame certainly works, but it feels kludgy. Why use a special mechanism to handle commands just because they come via the system menu? What if you want to handle a system command in some other object like your view or document? One common command to put in your system menu is About (ID_APP_ABOUT), and most MFC programs handle ID_APP_ABOUT in the application object:
void CMyApp::OnAppAbout()
{
static CAboutDialog dlg;
dlg.DoModal();
}
One of the really cool features of MFC is its command-routing system, which lets non-window objects like CMyApp handle menu commands. Many programmers don't even realize how out of the ordinary this is. If you already handle ID_APP_ABOUT in your application object, why should you have to implement a separate mechanism if you add ID_APP_ABOUT to your system menu?
A better and more MFC-like way to handle add-on system commands would be to pass them through the normal command-routing mechanism. Then you could handle system commands the normal MFC way, by writing ON_COMMAND handlers. You could even use ON_UPDATE_COMMAND_UI to update your system menu items, for example to disable an item or display a checkmark next to it.
Figure 2 shows a little class I wrote, CSysCmdRouter, that turns system commands into ordinary commands. To use it, all you have to do is instantiate CSysCmdRouter in your main frame and call its Init method from OnCreate:
int CMainFrame::OnCreate(...)
{
// Add my items to system menu
CMenu* pMenu = GetSystemMenu(FALSE);
pMenu->AppendMenu(..ID_MYCMD1..);
pMenu->AppendMenu(..ID_MYCMD2..);
// Route sys commands through MFC
m_sysCmdHook.Init(this);
return 0;
}
 Figure 2 CSysCmdRouter

// MSDN Magazine May 2005
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
#include "Subclass.h"

//
// Class to route user-defined WM_SYSCOMMAND messages through the normal
// MFC command routing system, so you can handle them the normal MFC way
// with ON_COMMAND and ON_UPDATE_COMMAND_UI handlers. The simplest way to
// achieve this is to translate the system commands to ordinary WM_COMMAND
// messages.
//
// To use: instantiate in your CMainFrame and call Init from OnCreate.
//
// You must also link Subclass.cpp in your app.
//
class CSysCmdRouter : public CSubclassWnd {
protected:
CWnd* m_pMainWnd; // main window hooked
public:
CSysCmdRouter() { }
virtual ~CSysCmdRouter() { }

// Initialize: hook the main window
BOOL Init(CWnd* pMainWnd) {
ASSERT(pMainWnd);
m_pMainWnd = pMainWnd;
return HookWindow(pMainWnd);
}

// Terminate: unhook. No need to call unless you want to stop hooking
// for some reason; CSubclassWnd will automatically unhook itself when
// the hooked window is destroyed.
void Term() {
Unhook();
}

protected:

// virtual WndProc handler: convert WM_SYSCOMMAND to WM_COMMAND if the
// command is not a system command: That is, if the command ID is less
// than SC_SIZE = 0xFFFF.
//
virtual LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
if (msg==WM_SYSCOMMAND && wp<SC_SIZE) {
return m_pMainWnd->SendMessage(WM_COMMAND, wp, NULL);
} else if (msg==WM_INITMENUPOPUP) {
// Hide system menu flag (= HIWORD(lp)) so even system menu can
// be initialized through MFC. This is somewhat dangerous
// because you lose the ability to distinguish between the
// system menu and other menus—but if you're adding your own
// commands it shouldn't matter which menu(s) they come from!
// Just make sure your command IDs don't conflict with the
// built-in commands like SC_SIZE,and so on, which start at
// 0xF000. By default, MFC command IDs start at 0x8000 so you'll
// be safe if you follow MFC.
lp = LOWORD(lp);
}
return CSubclassWnd::WindowProc(msg, wp, lp); // pass along
//-important!
}
};
Once you call CSysCmdRouter::Init, you can process ID_MYCMD1 and ID_MYCMD2 the normal way, by writing ON_COMMAND handlers for any object in the MFC command-routing superhighway—view, document, frame, app, or any other command target you've added by overriding OnCmdMsg. CSysCmdRouter also lets you update your system menu using ON_UPDATE_COMMAND_UI handlers. The only caveat is to make sure your command IDs don't conflict with any other menu commands (unless they truly represent the same command) or any of the built-in system commands, which begin at SC_SIZE = 0xF000. Visual Studio® .NET assigns command IDs starting at 0x8000 = 32768, so if you let Visual Studio assign the IDs, you'll be OK as long as you don't have more than 0xF000-0x8000 = 0x7000 commands. That's 28,672 in base 10. If your app has more than 28,000 commands, you need to consult a programming psychiatrist.
How does CSysCmdRouter perform its magic? Simple: it uses the ubiquitous CSubclassWnd from my previous columns. CSubclassWnd lets you subclass MFC window objects without deriving from them. CSysCmdRouter derives from CSubclassWnd and uses it to subclass the main frame. Specifically, it intercepts WM_SYSCOMMAND messages sent to the frame. If the command ID is in the system command range (greater than SC_SIZE = 0xF000), CSysCmdRouter passes it along to Windows; otherwise it eats the WM_SYSCOMMAND and resends it as a WM_COMMAND, whereupon MFC follows its normal routing procedures, calling your ON_COMMAND handlers. Pretty clever, eh?
And what about ON_UPDATE_COMMAND_UI handlers? How does CSysCmdRouter make them work for system menu commands? Simple. Just before Windows displays a menu, it sends your main window a WM_INITMENUPOPUP message. This is your big chance to update the menu items—enable or disable them, add checkmarks, and so on. MFC catches WM_INITMENUPOPUP in CFrameWnd::OnInitMenuPopup and performs its UI-update magic. MFC creates a CCmdUI object for each menu item and passes it to the appropriate ON_UPDATE_COMMAND_UI handlers in your message maps. The MFC function that takes care of this is CFrameWnd::OnInitMenuPopup, which begins like so:
void CFrameWnd::OnInitMenuPopup(CMenu* pMenu, 
UINT nIndex, BOOL bSysMenu)
{
if (bSysMenu)
return; // don't support system menu
...
}
MFC doesn't do anything to initialize the system menu. Why not? Why should it care? What if somehow you could make bSysMenu FALSE, even for the system menu? That's exactly what CSysCmdRouter does. It intercepts WM_INITMENUPOPUP and clears the bSysMenu flag, which is the HIWORD of LPARAM:
if (msg==WM_INITMENUPOPUP) {
lp = LOWORD(lp); // (set HIWORD = 0)
}
Now when MFC gets WM_INITMENUPOPUP, it thinks the menu is a normal menu. This all works fine as long as your command IDs don't conflict with true system commands. The only thing you lose is the ability to distinguish the system menu from the main window menu if you override OnInitMenuPopup. Hey, you can't have everything! You can always handle WM_INITMENUPOPUP by overriding CWnd::WindowProc, or compare HMENUs if you need to distinguish. But really, you shouldn't care where the command came from.
Figure 3  Task Bar Menu 
To show how all this works in practice, I wrote a little test program called TBMenu. Figure 3 shows the menu displayed when you right-click on TBMenu's minimized button in the task bar. You can see the two extra commands at the bottom of the menu. Figure 4 shows the code for TBMenu's CMainFrame. You can see where it adds the commands in OnCreate and handles them with ON_COMMAND and ON_UPDATE_COMMAND_UI handlers in CMainFrame's message map. TBMenu handles ID_APP_ABOUT within its application class (not shown). CSysCmdRouter makes system commands work like any other commands. Speaking of commands, see the fascinating sidebar "Origins of Ctrl+Alt+Del".
 Figure 4 MinFrm.cpp
// MSDN Magazine May 2005  
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
#include "StdAfx.h"
#include "TbMenu.h"
#include "MainFrm.h"
...
IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_CREATE()
ON_COMMAND(ID_CHECKME, OnCheckMe)
ON_UPDATE_COMMAND_UI(ID_CHECKME, OnUpdateCheckMe)
END_MESSAGE_MAP()

CMainFrame::CMainFrame() : m_bChecked(0)
{
}

CMainFrame::~CMainFrame()
{
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
... // normal MFC stuff: call base class OnCreate,
... // create toolbar and status bar (not shown here)

// Append my own items to the system menu
CMenu* pMenu = GetSystemMenu(FALSE);
ASSERT(pMenu!=NULL);
pMenu->AppendMenu(MF_BYPOSITION|MF_SEPARATOR);
pMenu->AppendMenu(MF_STRING,(UINT_PTR)ID_CHECKME, _T("Chec&k Me"));
pMenu->AppendMenu(MF_STRING,(UINT_PTR)ID_APP_ABOUT,
_T("&About TBMenu"));

// Hook system commands so they're routed through MFC
// command routing to ON_COMMAND handlers.
m_sysCmdHook.Init(this);

return 0;
}

//
// Handle "Check Me" command
//
void CMainFrame::OnCheckMe()
{
m_bChecked = !m_bChecked;
MessageBox(m_bChecked ? _T("Checked") : _T("Unchecked"), _T("TBMenu"));
}

//
// Set checkmark next to "Check Me" command (or not)
//
void CMainFrame::OnUpdateCheckMe(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_bChecked);
}
Happy programming!

转载于:https://www.cnblogs.com/IamEasy_Man/archive/2010/12/03/1895563.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值