分类: C/C++
2008-06-07 21:24:25
C++的 Function Pointer 使用问题!
侯捷 1998.12.04
最近网路的讨论群上出现 C++的 Function Pointer 使用问题 这个题目。
此题颇为有趣。Frank 网友的疑惑,经过前面数位网友的讨论,我们发现,"强制转型" 表面上可以解决问题,但骨子里问题还是存在如下:
若以 function pointer 做为 class 的一笔 data member,而我们希望把 "argument list 不同" 的各个函式的指标塞给它(指定给上述的 data member),那麽我们该如何解决 "argument list 不同" 的问题?
这很像 MFC 里头的 message map。我想 MFC 的做法很可供叁考。如果各位有 <深入浅出 MFC> 2/e 的话,可看 p580「罗塞达石碑:AfxSig_xx 的奥秘」一节。提要如下:
MFC 设计了一个 struct(C++ 的 struct 与 class 有相同的能力):
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // <--
UINT nCode;
UINT nID;
UINT nLastID;
UINT nSig; // <-- enum AfxSig,例如 AfxSig_vwl, AfxSig_iis, ...
AFX_PMSG pfn; // <--
}
其中的 nMessage 和 pfn 就是 Frank 网友想要的东西。只不过在第一个栏位上,Frank 网友是以 string 表示,此处则以 UINT 表示。
pfn 的型别(AFX_PMSG)定义如下:
typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
显然 AFX_PMSG 是 pointer of CCmdTarget's member function. 上述 typedef 将 AFX_PMSG 定义为无叁数(void)函式的指标。
MFC 又定义了一个 enum AfxSig(《深入浅出 MFC》2/e p581)及一个 union MessageMapFunctions(《深入浅出 MFC》2/e p583),根据前述 nSig 栏位(内容是 Afx_xxx),将「无叁数函式」的指标转换为「有各种叁数之函式」的指标。由於各式各样的函式指标系定义於 union MessageMapFunctions 之中,都是 4 bytes,利用 union 的特性可顺利做转换,而又解决 "argument list 不同" 的问题。换句话说真正的函式指标只是 pfn,但通过 union,它展现了不同的形象。
注:罗塞达碑石(Rosetta Stone),1799 年拿破仑远征埃及时,由一名官员在尼罗河口罗塞达发现,从此揭开古埃及象形文字之谜。石碑是黑色玄武岩,高 114 公分,厚 28 公分,宽 72 公分。经法国学者波伦研究後,世人因得顺利研读古埃及文献。上述的 AfxSig_xxx 使我们揭开了 MFC 的 message map 之谜,因以喻之!
以下是我写的一个小小模拟程式。<多型与虚拟> 改版时 (2/e),我会把这个题目加入第一章中。
#0001 // file : fptr2.cpp (simulate MFC message mapping)
#0002 // author : jjhou
#0003 // date : 1998.12.04
#0004 // build : cl -GX fptrs.cpp
#0005 // bcc32 fptrs.cpp
#0006
#0007 #include模拟不断有 command 进来,不断地处理之
#0008 #include
#0009
#0010 using namespace std;
#0011
#0012 // generic type of function pointer
#0013 typedef void (*PFUNC)(void);
#0014
#0015 // command ID definition
#0016 #define CMD_COPY 0
#0017 #define CMD_PASTE 1
#0018 #define CMD_CUT 2
#0019 #define CMD_NULL 0xFFFF
#0020
#0021 // command function implementation
#0022 int copy(void) { cout << "copy" << endl; return 0; }
#0023 bool paste(string& str) { cout << "paste: " << str << endl; return true; }
#0024 void cut(int i, int j) { cout << "cut: " << i << " " << j << endl; }
#0025
#0026 enum Sig // refp581
#0027 {
#0028 Sig_end = 0, // [marks end of message map]
#0029
#0030 Sig_iv, // int (void)
#0031 Sig_bs, // bool (string&)
#0032 Sig_vii, // void (int, int)
#0033 };
#0034
#0035 union CmdFunctions // refp583
#0036 {
#0037 PFUNC pfn; // generic function pointer
#0038
#0039 // specific type safe variants
#0040 int (*pfn_iv)(void);
#0041 bool (*pfn_bs)(string&);
#0042 void (*pfn_vii)(int, int);
#0043 };
#0044
#0045 struct Command // refp580
#0046 {
#0047 unsigned int nCmd;
#0048 unsigned int nSig;
#0049 PFUNC pfn; // command handler
#0050 };
#0051
#0052 Command commands[] = // refp555
#0053 {
#0054 { CMD_COPY, Sig_iv, (PFUNC)(int (*)(void))copy },
#0055 { CMD_PASTE, Sig_bs, (PFUNC)(bool (*)(string&))paste },
#0056 { CMD_CUT, Sig_vii, (PFUNC)(void (*)(int, int))cut },
#0057 { CMD_NULL, Sig_end, (PFUNC)0 }
#0058 };
#0059
#0060 void main(void)
#0061 {
#0062 unsigned int icmd;
#0063 int iparam1, iparam2;
#0064 string sparam;
#0065 union CmdFunctions cf; // refp581
#0066
#0067 while (1) //
#0068 {
#0069 // 备妥 command
#0070 cout << "Command (0 or 1 or 2, any other will exit) : ";
#0071 cin >> icmd;
#0072
#0073 // 备妥 parameters
#0074 switch (icmd) {
#0075 case 0 :
#0076 break;
#0077
#0078 case 1 :
#0079 cout << "param str = ";
#0080 cin >> sparam;
#0081 break;
#0082
#0083 case 2 :
#0084 cout << "iparam1 = ";
#0085 cin >> iparam1;
#0086 cout << "iparam2 = ";
#0087 cin >> iparam2;
#0088 break;
#0089
#0090 default :
#0091 cout << "command error!" << endl;
#0092 exit(1);
#0093 }
#0094
#0095 // 搜寻适当的 cmd index
#0096 // 本例中,icmd 即为 commands[] 的 index.
#0097 // 其他情况可能未必
#0098 // ...
#0099
#0100 // 呼叫适当的 cmd handler refp581
#0101 cf.pfn = commands[icmd].pfn;
#0102 switch (commands[icmd].nSig) {
#0103 case Sig_iv:
#0104 cout << "result= " << (*cf.pfn_iv)() << endl;
#0105 break;
#0106
#0107 case Sig_bs:
#0108 cout << "result= " << (*cf.pfn_bs)(sparam) << endl;
#0109 break;
#0110
#0111 case Sig_vii:
#0112 (*cf.pfn_vii)(iparam1, iparam2);
#0113 break;
#0114 }
#0115 } // while
#0116 }
#0117
#0118
#0119 /*
#0120 H:\g001p\prog\fptr.01>fptr2
#0121 Command (0 or 1 or 2, any other will exit) : 0
#0122 copy
#0123 result= 0
#0124 Command (0 or 1 or 2, any other will exit) : 1
#0125 param str = helloMFC
#0126 paste: helloMFC
#0127 result= 1
#0128 Command (0 or 1 or 2, any other will exit) : 2
#0129 iparam1 = 839
#0130 iparam2 = 912
#0131 cut: 839 912
#0132 Command (0 or 1 or 2, any other will exit) : 9
#0133 command error!
#0134 */
--- the end