Member function pointers and high-performance C ++ delegation (Part 1)

Source: Internet
Author: User

Member function pointers and high-performance

C ++ delegation (Part 1)

Member function pointers and the fastest possible C ++ delegates

Author:

Don Clugston

Translation: Zhou Xiang

Introduction

Standard

C ++ does not have a real Object-Oriented function pointer. This is unfortunate for C ++, because object-oriented pointers (also called "closure" or "delegate )") some languages have proved its valuable value. In Delphi (Object Pascal), object-oriented function pointers are the basis for Borland visual build Library (VCL, visual component library. At present, the concept of C # "delegation" is becoming increasingly popular, which also shows the success of C. In many applications, "delegation" simplifies the design pattern of loosely coupled objects [gof]. This feature will undoubtedly play a major role in standard C ++.

Sorry,

C ++ does not have a "delegate", it only providesMember function pointer (Member function pointers ). Many programmers never use function pointers. This is a specific reason. Because function pointers have many strange syntax rules (such as "-> *" and ". * "operator), and it is difficult to find the exact meaning of them, and you will find a better way to avoid using function pointers. More ironic: in fact, it is much easier for compilers to implement "delegate" than to implement member function pointers!

In this article, I will unveil the "mysterious lid" of the member function pointer ". After briefly repeat the syntax and features of the member function pointer, I will explain to the reader how the member function pointer is implemented in some common compilers, then I will show you how the compiler can effectively implement "delegation ". Finally, I will use these profound knowledge to show you

The C ++ compiler implements an optimized and reliable "delegate" technology. For example, in Visual C ++ (6.0,. net, and. NET 2003), the compiler generates only two lines of assembly code for a single-target delegate!

Function pointer

Next, let's review the function pointer. In

In C and C ++, a function pointer named my_func_ptr points to a function with an int and a char * as parameters. This function returns a floating point value and the declaration is as follows:

Float (* my_func_ptr) (int, char *);

//

For ease of understanding, I strongly recommend that you use the typedef keyword.

//

If this is not the case, when the function pointer is passed as a function parameter,

//

The program will become obscure.

//

In this case, the statement should be as follows:

Typedef float (* MyFuncPtrType) (int, char *);

MyFuncPtrType my_func_ptr;

It should be noted that the types of function pointers should be different for Parameter Combinations of each function. In

In Microsoft Visual C ++ (MSVC), there are three different call Methods: __cdecl, _ stdcall, and _ fastcall. If your function pointer points to a function of Type float some_func (int, char *), you can do this:

My_func_ptr = some_func;

When you want to call the function to which it points, you can write as follows:

(* My_func_ptr) (7, "Arbitrary String ");

You can convert one type of function pointer to another type of function pointer, but you cannot direct a function pointer to one

Void * type data pointer. Other conversion operations are not described in detail. A function pointer can be set to 0 to indicate that it is a null pointer. All comparison operators (= ,! =, <,>, <=, >=) Can all be used, you can use "= 0" or an explicit Boolean conversion to test whether the pointer is null ).

In

In C language, function pointers are usually used to take functions as parameters like qsort, or as Callback functions for Windows functions. Function pointers also have many other applications. The implementation of function pointers is simple: they are just "code Pointers", which are embodied in the first address used to save the subroutine code in assembly language. This function pointer only exists to ensure correct call specifications are used.

Member function pointer

In

In C ++ programs, many functions are member functions, that is, these functions are part of a class. You cannot point to a member function as a normal function pointer. The correct way is to use a member function pointer. The pointer of a member function points to a member function in the class and has the same parameters as before. The declaration is as follows:

Float (SomeClass: * my_memfunc_ptr) (int, char *);

// For member functions modified using the const keyword, the Declaration is as follows:

Float (SomeClass: * my_const_memfunc_ptr) (int, char *) const;

Note that special operators (

: *), And "SomeClass" is part of the Declaration. Member function pointers have a terrible restriction: they can only point to member functions in a specific class. For the combination of each parameter, different member function pointer types are required. For each function that uses const modifier and function in different classes, different function pointer types are also required. In MSVC, there is a different call type for the following four call Methods: __cdecl, _ stdcall, _ fastcall, and _ thiscall. (_ Thiscall is the default method. It is interesting that there is no detailed description of the _ thiscall keyword in any official documents, but it often appears in the error message. If you use it explicitly, you will see an error message "it is reserved for future use .) If you use a member function pointer, you 'd better use typedef to prevent confusion.

Point the function pointer to a type such

Float SomeClass: some_member_func (int, char *) function, you can write as follows:

My_memfunc_ptr = & SomeClass: some_member_func;

Many compilers (such

MSVC) will let you remove "&", while some other compilers (such as gnu g ++) need to add "&", so I suggest you add it when writing a program. To call a member function pointer, you must first create an instance of SomeClass and use the special operator "-> *". This operator has a low priority, you need to place it in parentheses as appropriate.

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 -- it looks like

The designers of C ++ have sincere feelings for punctuation! C ++ adds three Special operators compared with C to support member pointers. ": *" Is used to declare the pointer, and "-> *" and ". *" are used to call the function pointed to by the pointer. It seems that excessive attention to the vague and rarely used part of a language is unnecessary. (Of course you can reload the "-> *" operators, but this is not the scope involved in this article .)

A member function pointer can be set

0, and can use "=" and "! = "Comparison operator, but this comparison can only be performed between pointers of member functions in the same class. Any member function pointer can be compared with 0 to determine whether it is null. Unlike function pointers, unequal operators (<,>,<=, >=) are not available for member function pointers.

What's strange about member function pointers

Member function pointers sometimes look strange. First, you cannot use a member function pointer to point to a static member function. You must use a common function pointer, it should actually be a "non-static member function pointer ). Second, some strange situations may occur when class inheritance is used. For example, the following code

Msvc will be compiled successfully (note the code comment ):

# 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 pin the comments 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 (SomeClass: * SomeClassMFP) (int, char *);

SomeClassMFP my_memfunc_ptr;

My_memfunc_ptr = & DerivedClass: some_member_func; // ---- line (*)

Return 0;

}

The strange thing is,

& DerivedClass: some_member_func is a member function pointer of the SomeClass class, rather than 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 undefined .) However, if the some_member_func function is rewritten in the DerivedClass class (override), the Code cannot be compiled because the current & DerivedClass: some_member_func has become a member function pointer in the DerivedClass!

The type conversion between member function pointers is a very vague topic. In

During the standardization process of C ++, when the member function pointer of the inherited class is involvedConverting a member function pointer to a member function pointer of the base class is still a problem of converting it to a member function pointer of the subclass.AndWhether a member function pointer of a class can be converted into a member function pointer of another unrelated classPeople have had a heated debate. Unfortunately, before the Standards Board makes a decision, different compiler manufacturers have implemented their compilers based on their own different answers to these questions. According to the standard (section 5.2.10/9), you can useReinterpret_castSaves a member function that is unrelated to the original class in a member function pointer. The final result of member function pointer conversion is not fixed. What you can do now is convert the member function pointer to the member function pointer of this class as before. I will continue to discuss this issue later in the article, because this is a topic in which compilers have no consensus on such a standard.

In some compilers, conversions between member function pointers of the base class and subclass often occur. When multiple inheritance is involved

Reinterpret_castWhen a subclass is converted to a base class, it may be compiled by a specific compiler, but it may not be able to compile,This depends on the order of the base classes in the base class list of the subclass!The following is an example:

Class Derived: public Base1, public Base2 //

Case ()

Class Derived2: public Base2, public Base1 //

Case (B)

Typedef void (Derived: * Derived_mfp )();

Typedef void (Derived2: * Derived2_mfp )();

Typedef void (Base1: * Base1mfp )();

Typedef void (Base2: * Base2mfp )();

Derived_mfp x;

ForSituation

(A), static_cast <base1mfp> (X) Is legal, andStatic_cast <base2mfp> (X) It is incorrect. HoweverSituation(B) the opposite. You can only safely convert the member function pointer of the subclass to the member function pointer of the first base class! If you want to perform an experiment, MSVC will issue the C4407 warning, while Digital Mars C ++ will encounter a compilation error. If you useReinterpret_castReplaceStatic_cast,Both compilers have errors, but the two compilers have different causes. However, some compilers ignore this details and everyone should be careful!

Another interesting rule in Standard C ++ is:You can declare its member function pointer before the class definition.This will have unpredictable side effects on Some compilers. I will discuss this issue later. Now you only need to know how to avoid this situation as much as possible.

It is worth noting that, just like the member function pointer, the standard C ++ also provides the Member Data Pointer (Member Data Pointer ). They have the same operators and some implementation principles are the same. They are used in some implementations of STL: stable_sort, and I will not mention many other applications.

Use of member function pointers

Now you may think the member function pointer is a bit strange. But what can it do? I did a very extensive survey on the Internet. Finally, I summarized the two reasons for using the member function pointer:

  • For example

C ++ beginners can help them learn the syntax. or

  • To implement (
  • Delegate )"!

    Member function pointer in

    The use of the one-line function adapter in STL and boost libraries is insignificant and allows you to mix member functions with standard algorithms. However, their most important applications are in different types of application frameworks. For example, they form the core of the MFC message system.

    When you use

    When the message ing macro (such as on_command) of MFC is used, you will assemble a sequence containing the Message ID and the member function pointer (type: csf-target: * member function pointer. This is one of the reasons why the MFC class must inherit csf-target to process messages. However, different message processing functions have different parameter lists (for example, the first parameter type of ondraw processing function is CDC *), therefore, the sequence must contain various types of member function pointers. How does MFC achieve this?MFC exploits a terrible Compiler Vulnerability (hack), which puts all possible member function pointers in a large union, this avoids the C ++ type matching check.(Take A Look At the Union in afximpl. h and cmdtarg. cpp named messagemapfunctions, and you will find this horrible fact .) Because MFC has such an important part of the code, the fact is that all compilers have given a green light for this vulnerability. (However, we will see it later,If multiple inheritance is used for some classes, this vulnerability occurs inMSVC does not work, which is why only a single inheritance is required when you use MFC .)

    In

    Boost: function has similar vulnerabilities (but not too serious ). It seems that if you want to do anything interesting about member function pointers, you must be prepared to challenge the vulnerabilities in this language. If you want to deny the flawed design of the member function pointers of C ++, it seems very difficult.

    In this article, I need to specify the following:"Allow conversion between member function pointers (

    Cast), rather than calling the function after the conversion is complete. It is ridiculous to include this rule into the C ++ standard. First,ManyPopular compilers do not support such conversions (therefore, conversions are standard but not portable ). Second,AllIf the conversion is successful, you can still implement the expected function when calling the converted member function pointer: the compiler does not have the so-called "undefined behavior (undefined behavior) "This type of error is necessary (Invocation), but this is not a standard !). Third, it is completely useless to allow conversion rather than calling. Only when both conversion and calling are feasible can the delegation be conveniently and effectively implemented, thus benefiting the language.

    To convince you of this controversial argument, consider that there is only one piece of code in a file, which is legal:

    Class SomeClass;

    Typedef void (SomeClass: * SomeClassFunction) (void );

    Void Invoke (SomeClass * pClass, SomeClassFunction funcptr ){

    (PClass-> * funcptr )();};

    Note that the compiler must generate assembly code to call the member function pointer.

    The someclass class knows nothing about it. Obviously, unless the linker performs some extremely fine-grained optimization measures, the code will ignore the actual definition of the class and be able to run correctly. The direct consequence of this is that you can "Safely" Call the member function pointer converted from a completely different class.

    To explain the other half of my assertion-the conversion does not follow the standard approach, I need to discuss in detail how the compiler implements the member function pointer. I will also explain why rules using member function pointers have such strict restrictions. It is not easy to obtain a document describing member function pointers in detail, and everyone is familiar with the incorrect comments. Therefore, I carefully checked the compilation code generated by a series of compilers ......

     

    (To be continued)

    Related Article

    Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.