Member function pointer and high performance C ++ entrustment (Part 2)

zhaozj2021-02-16  69

Member function pointer and high performance C entrustment (Part 2)

MEMBER FUNCTION POINTERS AND THE FASTEST POSSIBLE C Delegates

Written: Don Clugston

Translation: Zhou Xiang (connected)

Delegate

Unlike the member function pointer, you can't find the use of the commission. Most importantly, using the entrustment can easily implement an improved version of the Subject / Observer design mode [GOF, P. 293]. Observer (observers) is obviously there are many applications in GUI, but I found that it also has a big role in the design of the application core. The commission can also be used to implement strategy [GOF, P. 315] and State (state) [GOF, P. 305] mode.

Now, I will illustrate a fact that it is not just easy compared to the entrustment and member function pointer, and it is much more simpler than the member function pointer! Since all the .NET languages ​​have entrustted, you may guess such a high-level concept is not good in assembly code. But the fact is not this: The implementation of the entrusted is indeed a bottom concept, and it is as simple as a normal function call (and very efficient). A C entrustment only needs to include a THIS pointer and a simple function pointer. When you create a delegate, you provide this entrusted a THIS pointer and indicate which function you need to call. The compiler can calculate the offset required to adjust the THIS pointer when establishing a commission. In this way, when you use the commission, you don't have to do anything. This is better that the compiler can complete all of these work when compiling. In this case, the delegated processing can be said to be a compiler. The assembly code that will be delegated under x86 should be as simple as:

MOV ECX, [this]

Call [pfunc]

However, such efficient code cannot be generated in standard C . Borland Added a new keyword (__closure) in its C compiler to resolve the issue of the entrusted, which is used to generate an optimized code through a concise syntax. The GNU compiler also expands the language, but is not compatible with the Borland's compiler. If you use these two language extensions, you will limit yourself using only one manufacturer's compiler. And if you still follow the rules of standard C , you can still achieve delegate, but the entrustment of the implementation will not be so efficient.

Interestingly, in C # and other .NET language, executing a delegate time is 8 times lower than a function call (see http://msdn.microsoft.com/library/en-us/dndotNet/html/fastmanagedcode. ASP). I guess this may be a need for garbage collection and .NET security check. Recently, Microsoft adds "Unified Event Model" to Visual C . With the addition of this model, add __Event, __raise, __ hook, __ unhat, esterace, and event_receiver, and more. Fort-of-white, I am very disgusted on these features, because it is completely unsuitable, these grammar is ugly because they make this C unlike C , and generate a pile of execution efficiency with extremely low code.

Solving the driving force for this problem: Emotional demand for Efficient Entrust (Fast Delegate) has a standard C to achieve an overweening symptom. Most implementation methods use the same idea. The basic view of these methods is to see the member function pointer as a delegate & # 0; but such a pointer can only be used by a separate class. To avoid this limitation, you need to indirectly use another idea: You can use the template to create a member function invoker "" for each class. The delegate contains the THIS pointer and a pointer to the invoker, and needs to be assigned space for the member function caller on the heap.

There are many implementations for this solution, including implementation on CodeProject. Various implementations are on complexity, syntax (such as some and C # syntax are very close), generally different. The most authoritative implementation is boost :: function. Recently, it has been adopted as part of the next released C standard version [SUTTER1]. I hope it can be widely used.

Just like a traditional entrustment implementation method, I also find this method is not very satisfied. Although it provides features expected, it will confuse a potential problem: people lack the construction of the underlying of a language. The "Member Function Call" code is the same for almost all classes, and this situation occurs on all platforms is frustrating. After all, the heap is used. But in some applications, this new approach is still unacceptable.

One project I made is a discrete event simulator. Its core is an event scheduler that is used to call the member function of the analog object. Most member functions are very simple: they only change the internal state of the object, sometimes adding events to occur in the event queue (Event Queue), which is best suited in this case. However, each delegate is only called (Invoked) once. At first, I used Boost :: Function, but I found that the program is running, and the memory space allocated is more than one-third of the entire program space! "I want to be truly entrusted!" I shouted in my heart, "the real commission only needs only two lines of assembly instructions!"

I can't always get what I want, but I am very lucky. I am showing an optimized assembly code in almost all compilation environments. Most importantly, calling a SINGLE-TARGET DELEGATE in a single target is almost as fast as a normal function. It is not to use such a code and is not used. The only regret is that in order to achieve the goal, my code and standard C rules are somewhat deviated. I used some unapplicament knowledge about member function pointers to make it work. If you are very careful, and don't care about some compiler related (Compiler-Specific) code, the high-performance commissioning mechanism is feasible under any C compiler.

窍: Translate any type of member function pointer into a standard form

My code is a class that can convert any classes and any member function pointer to a generic class pointer and a pointer for a general member function. Since C does not have the type of "Generic Member Function", I convert all types of member functions into a member function of the CGenericClass class that is not defined in the code.

Most compilers treat all member function pointers, regardless of which class they belong. So for these compilers, you can use ReinterPret_CAST to convert a specific member function pointer into a general member function pointer. In fact, if the compiler is not, then this compiler is not standard. For some compilers close to the standard (Almost-Compliant), such as DIGITAL MARS, the ReinterPret_cast conversion of the member function pointer generally involves some extra special code, and when there is no association between the class of the transformed member function, the compiler Will go wrong. For these compilers, we use an inline function called Horrible_cast (using a Union in the function to avoid C type check). Using this method seems to be inevitable & # 0; & # 0; boost :: function also used this method. For other compilers (such as Visual C , Intel C and Borland C ), we must convert multiple (virtiple-inheritance and virtual (Virtual-) member function pointers to a single (SINGLE-) inherited function pointer. In order to achieve this, I cleverly use the template and take advantage of a wonderful trick. Note that this trick is used because these compilers are not fully compliant, but use this trick to get the reward: it makes these compilers generate optimized code.

Since we know how the compiler is internally stored member function pointers, and we know how to adjust the THIS pointer for member function pointer in the question, our code can adjust the THIS pointer yourself when setting up. For a single inherited function pointer, no adjustment is required; for multiple inheritance, only one additional addition can be adjusted; for virtual inheritance ... But this is to use, and in most cases, all work is completed when compiling!

This is the last trick. How do we distinguish between different inheritance types? There is no official approach to let us distinguish between a class that is multiple inheritance or other types of inheritance. But there is a clever way, you can view I gave a list in front (see the middle) - the size of the member function pointer generated by each inheritance mode is different. So, we can use the template based on the size of the member function pointer! For example, this is just a simple calculation for multiple inheritance types. Similar computational methods are also used when determining the unknown_inheritance type.

For Microsoft and Intel compilers, I use a pointer to a virtual inheritance type of a non-standard 12 byte, which triggered a compile-time error because a specific running environment (Workaround). If you use virtual inheritance in MSVC, use the fastdelegateDeclare macro before the declaration class. This class must use the unknown_inheritance pointer (this equivalent to a hypothetical __unknown_inheritance keyword). E.g:

FastDelegateDeclare (CDeriveDClass)

Class CDerivedClass: Virtual Public CBaseclass1, Virtual Public CBaseclass2 {

//: (ETC)

}

This macro and some constants are implemented in a hidden namespace, so it is also safe when used in other compilers. Another way to use the MSVC (7.0 or update version) uses / vmg compiler options in the project. The Inter-Interface does not work on the / vmg compiler option, so you must use macros in the virtual inheritance class. My code is because the compiler's bug can run correctly, you can view the code to learn more. There is no need to pay more in compliance with the standard compiler, and it does not hinder the use of the FastDelegateDeclare macro under any circumstances. Once you convert the class's object pointer and member function pointer to standard form, the Single-Target Delegate is easier (although it is perceived when it is tested. You only need to make a corresponding template class for each function with different parameters. The code to achieve other types of entrusted is mostly similar to this, but it is only a slight modification of the parameters.

This delegate that uses non-standard way transitions is also a benefit that can be compared between the entrusted objects. Most of the major entrustments that are currently unable to do, which makes these delegates unable to take some specific tasks, such as achieving multicast delegates [Sutter3].

Static function as a delegate target (DELEGATE TARGET)

In theory, a simple non-member function (Non-Member function), or a static member function can be used as a delegate target. This can be implemented by converting a static function into a member function. I have two ways to achieve this, both of which are implemented by making the delegate pointing to the "invoker" member function of the "Invoker" in this static function.

The first method uses an evil method (Evil Method). You can store function pointers instead of the THIS pointer, which transforms the THIS pointer to a static function pointer when calling the "Call" function, and calls this static function. The problem is that this is just a trick, it needs to be converted between the code pointer and the data pointer. In a system, the size of the code pointer is larger than the data pointer (such as when the compiler under DOS uses Medium memory mode), this method will not take place. It is a manner on all 32-bit and 64-bit processors currently I know. But because this method is still not very good, it still needs to be improved.

Another is a more secure method (SAFE METHOD), which is an additional member that will function pointer as a delegate. The delegate point to its own member function. When the delegate is replicated, these self-reference must be converted, and the operation of "=" and "==" operators is complicated. This increases the size of the commission to 4 bytes and increases the complexity of the code, but this does not affect the call speed of the delegate.

I have implemented the two methods, both have their own advantages: security methods ensure the reliability of operation, and evil methods may also produce the same assembly code as the compiler that supports the commission. In addition, security methods avoid problems that have been used in MSVC before I have previously discussed. I give the code in the code "safe method" code, but "Evil Method" in the code I will take effect by the following code:

#define (fastdelegate_usestaticfunctionhack)

Multiple-Target Delegate and its expansion

Users who use entrusted may want to make a commissioned multiple target functions, which is multiple-target delegate, also known as multicast delegate. Realizing this delegate does not reduce the call efficiency of single-target delegate, which is feasible in reality. You only need to assign space on the pile to a delegated second goal and later more targets, which means that you need to add a data pointer to the delegate class to point to the target function of the commission. The head node of a single-link table. If there is only one target function, this goal is saved in the delegate like the method described earlier. If a delegate has multiple target functions, these goals are saved in the spatial dynamically assigned linked list, if the function is to be called, delegate using a pointer to the target (member function pointer) in a linked list. In this case, if there is only one goal in the delegation, the number of function calls the storage unit is 1; if there is n (n> 0) target, the number of functions calling the memory cell is N 1 (because the function pointer is saved. In the linked list, you will have a linked watch, so you have to add one-translator's note), I think this is the most reasonable. Some problems have been made from multicast commission. How to deal with return values? (Is it bundled all the return value types or ignored?) What happens if the same goal adds the same goal? (Is it twice that call the same goal, or call it once, or is it handled as an error?) If you want to delete a goal in the delegate, what should I do? (Whether it doesn't matter, or throw an exception?)

The most important question is that there will be an unlimited cycle when using the delegate, for example, the A commissioned call, and call B delegate in this code, and the A. A commission will be called in a code called in B. Many events (event) and signal tracking systems have certain scenarios to handle this problem.

In order to end my article, my multicast entrusted implementation needs everyone to wait. This can learn from other implementations - allow non-empty return types, allowing implicit conversions of types, and uses a more simple syntax structure. If I have enough interest, I will write the code. If you can combine my entrustments and the current epidemic event handle, it will be the best thing (with voluntary?).

Use of this document

The original code includes FastDelegate.h and a file of DEMO .CPP to show the syntax using fastdelegate. For readers using MSVC, you can create an empty console application process, then add these two files into it, for GNU users, enter "GCC Demo.cpp "It's okay.

FastDelegate can run in any parameter combination, I suggest you try under the compiler as possible, you must specify the number of parameters when you declare the commission. You can use up to 8 parameters in this program, and it is easy to expand. The code uses the fastdelegate namespace, which has a Detail's internal namespace named Detail in the fastdelegate namespace.

FastDelegate uses constructor or bind () to bind a member function or a static (global) function, by default, the bound value is 0 (empty function). You can use the "!" Operator to determine it is an null value. Unlike the delegate implemented in other ways, this entrustment supports the equation operator (==,! =).

Below is an excerpt from FastDelegateDemo.cpp, which shows most permitted operations. CBaseClass is the virtual base class of CDERIVEDCLASS. You can write more exciting code based on this code, the following code is just a syntax using fastdelegate:

Using namespace fastdelegate;

Int main (void)

{

Printf ("- fastdelegate demo - / na no-parameter

Delegate is declared using fastdeLegate0 / n / n ");

FastDelegate0 NoparameterDelegate (& SimplevoidFunction);

Noparameterdelegate ();

// Call the delegate, this sentence calls simplevoidfunction ()

Printf ("/ n-- examples useing two-parameter delegates (int, char *) - / n / N");

Typedef fastdelegate2

Mydelegate;

MyDelegate funclist [12]; // Entrusted initialization, its goal is empty

CBaseClass A ("Base A");

CBASECLASS B ("Base B");

CDerivedClass D;

CDERIVEDCLASS C;

// Bind a member function

Funclist [0] .bind (& A, & CBaseClass :: SimpleMemberfunction);

/ / You can also bind a static (global) function

FunClist [1] .bind (& SimpleStaticFunction);

// Bind static member functions

Funclist [2] .bind (& CBaseClass :: staticmemberfunction);

/ / Bind the Const type member function

Funclist [3] .bind (& A, & CBaseClass :: constmemberfunction);

// Bind the virtual member function

Funclist [4] .bind (& B, & CBaseclass :: SimpleVirtualFunction);

/ / You can use "=" to assign

Funclist [5] = MyDelegate (& CBaseClass :: staticmemberfunction);

FunClist [6] .bind (& D, & CBaseclass :: SimpleVirtualFunction);

// The most troublesome situation is bound to an abstract virtual function (Abstract Virtual Function)

Funclist [7] .bind (& C, & CderiveDclass :: SimpleDeriveDfunction);

Funclist [8] .bind (& C, & Cotherclass :: trickyvirtualfunction);

Funclist [9] = MakeDelegate (& C, & CderiveDclass :: SimpleDeriveDFunction);

/ / You can also use constructor to bind

MyDelegate DG (& B, & CBaseClass :: SimpleVirtualFunction); char * msg = "looking for equal delegate";

For (int i = 0; i <12; i ) {

Printf ("% D:", i);

// You can use "=="

IF (Funclist [i] == DG) {msg = "Found Equal Delegate";

// You can use "!" To judge an air commission

IF (! Funclist [i]) {

Printf ("delegate is Empty / N");

} else {

// Call the generated optimized assembly code

FunClist [i] (i, msg);

}

}

}

Because my code takes advantage of the behavior that is not defined in the C standard, I have been tested in many compilers very carefully. Ironically, it is more portable than many so-called standards, because almost all compilers are not fully compliant. At present, the core code has successfully passed the following compiler test:

Microsoft Visual C 6.0, 7.0 (.NET) (.NET 2003) (Including / CLR 'Managed C ), GNU G 3.2 (Mingw Binaries), Borland C Builder 5.5.1, Digital Mars C 8.38 (x86, Both 32-Bit and 16-Bit), Intel C for Windows 8.0, Metroworks CodeWarrior for Windows 9.1 (in Both C And EC MODES)

For Comeau C 4.3 (X86, SPARC, Alpha, Macintosh), it can be successfully compiled, but can not be linked and run. For Intel C 8.0 for Itanium, you can successfully pass compile and link, but you cannot run.

In addition, I have tested the code on the code on the MSVC 1.5 and 4.0, Open Watcom WCL 1.2, because these compilers do not support member function templates, so they cannot compile them. For the embedded system does not support the formation limit, you need to have a wide range of modifications to the code. (This section is added in the original version of the original ")

The ultimate Fastdelegate did not conduct a comprehensive test. One reason is that I have some compiler's evaluation version of the evaluation version, and the other reason is - my daughter is born! If there is enough interest, I will let the code take test in more compilers. (This paragraph was deleted in the original version of the updated, because the author currently completed all the tests. - Translator's Note)

to sum up

In order to explain a small piece of code, I have to write such a long guide for the controversial part of this language. For two lines of assembly code, you have to do so troublesome work.唉 ~!

I hope that I have clarified the misunderstanding of members' functions and entrusted. We can see that in order to implement a member function pointer, various compilers have a thousand ways. We can also see that it is different from the popular point of view, it is not complex, not a high-level structure, in fact it is simple. I hope it can be part of this language (standard C ), and we have reason to believe that it is currently supported by some compiler, and will join the new version of standard C in the near future (go to the standard committee! " ). As far as I know, the entrustment for previous implementation did not have such a high performance as I am here. I hope that my code can help you. If I have enough interest, I will extend the code to support multi-cast delegate and more types of delegates. I have learned a lot in CodeProject, and this is my first contribution to it.

references

[GOF] "Design Patterns: Elements of Reusable Object-Oriented Software", E. Gamma, R. Helm, R. Johnson, and J. Vlissides.

I'VE LOOKED AT Dozens of WebSites While Research This Article. Here Are A Few of The Most Interesting Ones:

I got a lot of sites when I wrote this article. Here is some of the most interesting sites:

[Boost] Delegates can be implemented with a combination of boost :: function and boost :: bind. Boost :: signals is one of the most sophisticated event / messaging system available. Most of the boost libraries require a highly standards-conforming compiler. (Http://www.boost.org/)

[Loki] Loki provides 'functors' which are delegates with bindable parameters. They are very similar to boost :: function. It's likely that Loki will eventually merge with boost. (Http://sourceforge.net/projects/loki-lib)

[Qt] The Qt library includes a Signal / Slot mechanism (ie, delegates). For this to work, you have to run a special preprocessor on your code before compiling. Performance is very poor, but it works on compilers with very poor template Support. (http://doc.trolltech.com/3.0/signalsandslots.html)

. [Libsigc ] An event system based on Qt's It avoids the Qt's special preprocessor, but requires that every target be derived from a base object class (using virtual inheritance -! Yuck). (Http://libsigc.sourceforge.net/) [Hickey]. An old (1994) delegate implementation that avoids memory allocations. Assumes that all pointer-to-member functions are the same size, so it does not work on MSVC. There's a helpful discussion of the code here. (http : //www.tutok.sk/fastgl/callback.html)

[Handal]. A Website Dedicated to function Pointers ?! NOT MUCH DETAIL ABOUT MEMBER FUNCTION POINTERS THOUGH. (Http://www.function-pointer.org/)

[SUTTER1] Generalized Function Pointers: a Discussion of How Boost :: Function Has Been Accept INTO The New C Standard. (Http://www.cuj.com/documents/s=8464/cujcexp0308ster/)

[Sutter2] generalizing the observer pattern (essentially, multicast delegates) Using std :: tr1 :: function. Discusses the limitations of the failure of boost :: function to provide Operator ==.

(Http://www.cuj.com/documents/s=8840/cujexp0309sutter)

[SUTTER3] HERB SUTTER'S GURU of The Week Article On Generic Callbacks. (Http://www.gotw.ca/gotw/083.htm)

About the author Don Clugston

I work in High-Tech Startup in Australia, is a physicist and software engineer. Currently engaged in the work of the silica crystal glass (CSG) film of the sun navigation space. I am engaged in Solar research, usually like to do some software (used as mathematical models, equipment control, discrete event triggers and image processing, etc.), I have recently like to use STL and WTL write code. I miss the glory of the past :) And the most important, I have a very cute son (born in May 2002) and a very young lady (born in May 2004).

"Darkness will not overcome the sun, the sun will be dark." Translator's Note Since this article, the author may update the article or code at any time. To browse the author's latest content, please visit: http : //www.codeproject.com/cpp/fastdelegate.asp

Click on the link below to download the source code of FastDelegate:

Http://www.codeproject.com/cpp/fastdelegate/fastdelegate_src.zip

转载请注明原文地址:https://www.9cbs.com/read-15333.html

New Post(0)