Member function pointer and high performance
C
Entrusted (superior)
MEMBER FUNCTION POINTERS AND THE FASTEST POSSIBLE C Delegates
Written: Don Clugston
Translation: Zhou Xiang
Primer
There is no real object-oriented function pointer in standard C . This is unfortunate for C because it has proved its valuable value in some languages because of an object-oriented pointer (also called "closure" or "delegate"). In Delphi (Object Pascal), object-oriented function pointers are the basis of the Borland visualization set library (VCL, Visual Component Library). At present, C # makes the concept of "delegate" becomes more popular, which is also showing the success of C # this language. In many applications, "delegate" simplifies the design pattern of loosely coupled objects [GOF]. This feature undoubtedly produces a lot of role in standard C .
Unfortunately, there is no "delegate" in C , which only provides member function pointers. Many programmers have never used a function pointer, which is reasonable. Because the function pointer itself has a lot of strange syntax rules (such as "-> * "and". * "Operator, and it is difficult to find their accurate meaning, and you will find a better way to avoid using function pointers. More ironic is:
In this article, I want to uncover the member function pointer and the "mysterious cover". After stating the grammatical and characteristics of the member function pointer, I will explain the member function pointer in some common compilers in some common compilers to achieve "delegate" to everyone. Finally, I will use these exquisite knowledge to show you a technique that implements optimization and reliable "commission" on the C compiler. For example, in Visual C (6.0, .NET, And .NET 2003), the compiler only generates two lines of assembly code!
Function pointer
Let's review the function pointer. In C and C languages, a function pointer named my_func_ptr pointing to a function that is parameter with an int and a char *, which returns a floating point value, as follows:
Float (* my_func_ptr) (int, char *);
// For the sake of understanding, I highly recommend you to use the Typedef keyword.
// If this is not this, when the function pointer is passed as a function of the parameter,
// The program will become obscured.
// This kind, the statement should be as follows:
Typedef float (int, char *); myfuncptype my_func_ptr;
It should be noted that the type of function pointer to each function should be different. In Microsoft Visual C (hereinafter referred to as MSVC), there are different types of different call modes: __ cdecl, __stdcall, and __fastcall. If your function pointer points to a function of a float some_func (int, char *), you can do: my_func_ptr = some_func;
When you want to call the function you point to, you can write:
(* my_func_ptr) (7, "Arbitrary String");
You can convert a type of function pointer into another function pointer type, but you can't point a function pointer to a Void * type data pointer. Other conversion operations are not detailed. A function pointer can be set to 0 to indicate that it is an empty pointer. All comparison operators (==,! =, <,>, <=,> =) Can be used, can use "== 0" or pass a explicit Boolean conversion to test the pointer is empty (NULL) .
In the C language, the function pointer is usually used as a parameter like QSORT, or as a callback function of the Windows system function. There are many other applications in the function pointer. The implementation of the function pointer is simple: they are just "code pointer", which is reflected in the assembly language is the first address used to hold the subroutine code. The presence of this function pointer is just to ensure the correct invoking specification.
Member function pointer
In a C program, many functions are members functions, that is, these functions are part of a certain class. You can't point to a member function like a normal function pointer, the correct approach should be, you must use a member function pointer. The pointer to a member function points to a member function in the class and has the same parameters as before, and the declaration is as follows:
FLOAT (SomeClass :: * my_Memfunc_ptr) (int, char *);
/ / Declare as follows: for member functions modified using const keyword:
FLOAT (SomeClass :: * my_const_memfunc_ptr) (int, char *) const;
Note The special operator (:: *) is used, and "someclass" is part of the declaration. Member function pointer has a terrible restriction: they can only point to member functions in a particular class. For each parameter, there is a need for different member function pointer types, and there are different function pointer types for each function and different classes using const modified functions and different classes. In MSVC, there is a different call type for the following four calls: __ cdecl, __stddcall, __fastcall, and __thiscall. (_Thiscall is the default way, interesting is that there is no detailed description of the __thiscall keyword in any official document, but it often appears in the error message. If you explicitly use it, you will see " It is retained an error prompt for IT IS Reserved for Future Use.) If you use a member function pointer, you'd better use TypeDef to prevent confusion.
Write the function pointer to the function of Float SomeClass :: Some_member_func (int, char *), you can write:
MY_MEMFUNC_PTR = & SomeClass :: Some_member_func; Many compilers (such as MSVC) will let you go to "&", and other compilers (such as GNU G ) need to add "&", so I suggest add it when handwriting. . To call a member function pointer, you need to build an instance of SomeClass and use special operators "-> *", which is lower than the priority of this operator, you need to put it in parentheses.
Someclass * x = new someclass;
(x -> * my_memfunc_ptr) (6, "Another Arbitrary Parameter);
// If the class is on the stack, you can also use the ". *" Operator.
SomeClass Y;
(y. * my_memfunc_ptr) (15, "DiffERENT Parameters this Time);
Don't blame me for using such a strange syntax - looks that C designers have a sincere feelings for punctuation! C increases three special operators to support member pointers relative to C. ":: *" is used to declare the pointer, and "-> *" and ". *" Are used to call the functionality points to the pointer. It looks that the excessive concern of some of the part of a language blurred and fewer use is excessive. (You can of course be overloaded "-> *", but this is not the range to be involved in this article.)
A member function pointer can be set to 0, and can use the "==" and "! =" Comparison operators, but it can only be limited between the pointers of the member functions in the same class. Comparison. Any member function pointer can be compared to 0 to determine if it is empty. Unlike function pointers, unequal operators (<,>, <=,> =) are not available to member function pointers.
Member function pointer weird
Member function pointer sometimes is very strange. First, you can't use a member function to point to a static member function, you must use a normal function pointer ("Member Function Pointer", which should actually be "non-static member function pointer" Correct). Second, when using the inheritance of the class, there will be some strange situations. For example, the following code will compile success in MSVC (note code notes):
#include "stdio.h" Class SomeClass {public: Virtual void Some_member_func (int x, char * p) {Printf ("in someclass");};}; class derivedclass: public someclass {public:
// If you sell the comment of the next line, the line with line (*) will have an error.
// Virtual void Some_member_func (int x, char * p) {printf ("in derivedclass");};}; int main () {
// Declare the member function pointer of someClass
Typedef void (INT, CHAR *); SomeClassmfp my_memfunc_ptr; my_memfunc_ptr = & derivedclass :: some_member_func; // ---- line (*) returnograph;
}
Surprisingly, & derivedclass :: some_member_func is a member function pointer for a SomeClass class, not a member function pointer of the DeriveDClass class! (Some compilers are slightly different: For example, for Digital Mars C , in the above example, & derivedClass :: some_member_func will be considered unfained.) However, if you override (override) in the DeriveDClass class, the code is Unable to compile because the current & derivedClass :: Some_member_func has become a member function pointer in the DeriveDClass class! The type conversion between member function pointers is a very vague topic discussed. During the standardization of C , when the member function pointer involving the inherited class, for a member function pointer that converts the member function pointer to the base class or whether a member of the subclass member function pointer can be used and a class member The function pointer translates into another member function of the unrelated class, and people have had a very intense debate. Unfortunately, the different compiler producers have implemented their compilers according to their different answers to these issues before they make a decision. Based on standards (Section 5.2.2.2.2/9), you can use ReinterPret_CAST to save a member function of classes that are not related to the category of categories in a member function pointer. The final result of the issue of the member function pointer conversion is not determined. What you can do now is like previously - convert the member function pointer into a pointer to the member function of this class. Behind the article I will continue to discuss this problem, because this is a topic that each compiler has not reached a consensus on such a standard.
In some compilers, there is often a strange thing between the transition between the base class and subclauses. When involving multiple inheritance, when using ReinterPret_CAST converts subclasses into base classes, it is possible to compile a particular compiler, and may also be compiled, depending on the list of base classs in the subclass. The order of the base class! Here is an example:
Class Derived: Public Base1, Public Base2 // Case (a)
Class Derived2: Public Base2, Public Base1 // Situation (B)
TypedEf void (Derived :: * Derived_mfp) (); typedef void (Derived2 :: * Derived2_mfp) (); typedef void (base1 :: * base1mfp) (); type2 :: * base2mfp) (); Derived_mfp x ;
For the situation (a), static_cast
(x)
Is legal, but static_cast
(x)
It is wrong. However, the situation (b) is opposite to it. You can only transfer the members of the subclass into the member function pointer of the first base class! If you have to experiment, MSVC will issue a Warning C4407, while Digital Mars C will compile errors. If you replace Static_cast_cast, both compilers have errors, but both compilers have different reasons. But some compilers are ignored by this detail, everyone can be careful!
The other interesting rules in standard C are: You can declare its member function pointer before class definition. This will have some compiler that there will be some unpredictable side effects. I will discuss this problem later, now you know how to avoid this as much as possible.
It is worth noting that the Member Data Pointer is also provided like a member function pointer. They have the same operator, and some implementation principles are also the same. They are used in some implementations in Stl :: Stable_Sort, and I will no longer mention many other applications. Member function pointer
Now you may think that the member function pointer is a bit of strange. But what can it be used? I have made a very broad survey on this online. Finally, I summarize two reasons for the use of member function pointers:
Used to do an example to see the C beginners, help them learn grammar; or to achieve "delegate"!
The use of the member function pointer in the single-line function adaptor in the STL and Boost libraries is negligible and allows you to use the member function and the standard algorithm. But their most important applications are in different types of application frameworks, such as their core of the MFC message system.
When you use the MFC message mapping macro (such as on_command), you will assemble a sequence containing a message ID and a member function pointer (type: ccmdtarget :: * member function pointer). This is one of the reasons why the MFC class must inherit ccmdTarget can handle the message. However, a variety of different message processing functions have different parameter lists (such as the type CDC * of the first parameter of the onDRAW processing function, so the sequence must contain various types of member function pointers. How does the MFC do this? MFC uses a terrible compiler vulnerability (HACK) that puts all possible members function pointers in a huge union (Union), avoiding a C type matching check that is often required. (Look at the union of the MersSageMapFunctions in AfxImpl.h and cmdtarg.cpp, you will find this horror fact.) Because the MFC has such an important part of the code, the truth is that all the compilers open for this vision. Green light. (However, we will see later, if some classes use multiple inheritance, this vulnerability does not work in the MSVC, which is only the reason for the use of single inheritage when using the MFC.)
There are similar vulnerabilities in Boost :: function (but not too serious). It seems that if you want to do anything more interesting about the member function pointer, you must prepare for challenges with this language vulnerability. If you want to deny the member function of C , it is difficult to design a defective point of view.
In this article, I have something to indicate: "Allow conversion between member function pointers, not allowing the function to call after the conversion is completed,", incorporating this rule into C is a ridiculous . First, a lot of popular compilers do not support this conversion (so conversion is standard requirements, but not portable). Second, all the compilers, if the conversion is successful, you can still achieve your expected function when calling the converted member function pointer: The compiler does not have the necessary "undefined behavior". " (Invocation) is feasible, but this is not a standard!). Third, allowing conversion without allowing calls to be completely useless, only converts and calls are feasible, to facilitate entrustment, so that this language benefits. In order to let you believe this controversial argument, consider only the following code in one file, this code is legal:
Class SomeClass; TypeDef void (someclass :: * pclass, someclassFunction funcptr) {(PCLASS -> * Funcptr) ();
Note that the compiler must generate assembly code to call member function pointers, and the compiler does not know about the SomeClass class. Obviously, unless the linker performs some extreme fine optimization measures, the code will ignore the actual definition of the class and can operate correctly. And the direct consequences of this is that you can "safely" call the member function pointer converted from a completely different class.
In order to explain the other half of my assertion - the conversion cannot be carried out in the way, I need to discuss how the compiler implements the member function pointer on the details. I will explain why the rules of the member function pointer have such a strict limit. Detailed discussion of the document of the member function pointer is not too easy, and everyone has learned the wrong speech, so I carefully check the assembly code generated by a series of compilers ...? (To be continued)