REL = "file-list" href = "entrusts .files / filelist.xml in your C program>>>
In your C
Implement in your C program
Ordinary function
Liu Qiang
Cambest@sohu.com
April 2, 2004
[Description]
This article not only introduces a very good way for C language applications (I even think of it should be attributed to a design mode), but also a good description of the underlying implementation of the C # language.
Read this article, you should know about the concept of the entrusted; when discussing the entrusted is an implementation, you should know about the LIST container in the standard template library (STL) and iterator.
In this article, the class member function is not discussed.
1. Entrusted in C #
If you know more about the C # language, you should know that there is a good feature in the C # language, that is, delegate. It greatly simplifies the processing of multiple identical form functions in some specific occasions. Especially in the Windows program, it is very convenient to use the entrusted response message. Have a common example. The Windows application framework is complicated, and an application may consist of many parts, many times they need to respond to the same message. As in one MDI, multiple subforms must respond to the WM_QUIT message of the main form. Many times, we don't need to encapsulate all the processing in the class like MFC, and we need a simple and easy way to intuitively solve this problem. This article provides you with this method.
Before you specifically introduce this article, let's first look at how to use it in C #. First, when an operation like processing a Windows message is processed, the notified object is accepted in a fixed format. In C #, the delegation of the message is defined as the following format:
Public Delegate Void WineventHandler (Object Sender, Eventargs Arg);
Assume that in one MDI, the main form is implemented by the mainframe. We define a special form to deal with WM_Quit messages:
Public Event Wineventhandler ONQUIT;
In the above statement, public indicates that the delegate can access (register / delete) external access (registration / deletion), and the Event keyword indicates that this is an event. It can only be called inside, and the externally cannot trigger it directly. In the MAINFRAME's form process function, the WM_QUIT message is distributed in this way:
Protected Virtual Void WndProc (Message MSG, Object Sender, Eventargs Arg)
{
......
Switch (msg)
{
......
Case WM_Quit:
ONQUIT (Sender, Arg);
Break;
......
}
}
So how do the MAINFRAME Subform respond to the WM_QUIT message of the main form? First, you have to implement the function of the child form Child1 to process the message, and its declaration must be the same as the WineventHandler.
// in Child1 Class
Protected void child1_on_quit (Object SD, Eventargs AGS)
{
This.saveAll (); file: // Do some afterwards, such as saving current information, etc.
......
}
The ONQUIT entrusted to the mainframe is registered to the mainframe when it is initialized. If the mainframe is a parent form of Child1, then its implementation may be like this: parent.onquit = new WineventHandler (this.child1_on_quit);
Thus, when the mainframe receives a WM_QUIT message, call the ONQUIT delegate, while Child1.child1_on_quit is also called to implement message delivery. Of course, there are also sub-forms no longer need to respond to the WM_QUIT message of the main form. We can log out from the mainframe's ONQUIT commission in the following way:
Parent.onquit - = new WineventHandler (this.child1_on_quit);
This step is also necessary. If Child1 is destroyed by the main form mainframe, while Child1_on_quit does not log out of the mainframe.onquit commission, call the ONQUIT commission when the main form is received, and it is called to Child1.child1_on_quit, and it may cause an empty reference exception. . [Detailed introduction, please see "IL code underlying operation mechanism: function related]
The entrustment can accept multiple examples, so you can register multiple methods to a commission. In fact, the delegate contains a list of methods references. When the delegate is called, it calls the method in the order in its list. I will also explain later.
2. Improvement in C
We know that the principle of commissioned in C # can be implemented in C ? The answer is yes. Different is that C # provides support for entrusting, while C does not require our delegation. In this way, each different form of delegation has different implementations, flexible. Fortunately, the author has provided a practical gadget called Delegate.exe, which can help us implement actual code from the delegate declaration. Its usage will be described in detail later.
Now, we mainly consider how to achieve our own custom commission. In front of me briefly introduced a list of factors that should be entrusted: Save Method Reference (in C is a list, but I am still here to call it as a reference) list, add / delete method reference, and the most important call routine . How many ways can implement these operations, here we use classes to implement.
The first thing to consider how to save the method reference. Because the method reference (pointer) is actually a 32-bit unsigned integer, I also use unsigned integers to store methods reference (pointers). Here, I defined this data type: typedef unsigned int nativeptr. Accept
The second is to declare this form of commission. In this example, I use a Void Handle (CHAR * STR) as an example. We define such a function pointer type TypeDef void (* handler) (CHAR * STR) as a function of the function call, as a conversion type by the unsigned shaping function pointer.
The third thing to consider how to implement multiple methods references (pointers). The easiest way is to store the List list container in STL. The List template class provides us with a set of very convenient list access operation methods, which provides usterapers that can easily use it. In the CDELEGATE class, I defined the field ftns: list
The fourth is to implement the add / delete function reference. Here two operations are implemented by addFunction / removefunction, respectively. Here, there is a problem that what kind of parameter type we accept, how to accept it. There is no doubt that it is to accept the Handler type defined earlier. However, we have used 32 to save its information for unsigned integers, so, for simplicity, the parameter of the addFunction / RemoveFunction function is a VOID * type so that it can accept many types of parameters. What type of use is used as the parameter of AddFunction / RemoveFunction, I think it may also be discussed in detail, what is the previously defined Handler type or a void * type. It should be said that both types have their own advantages, the key is to see when we are applying. Of course, using our customized delegate type (that is, the previously defined Handler type) As its parameter type, you can make the compiler to make the necessary syntax check to prevent mismatched parameters. This will look at the actual situation. Of course, overload = and - = operator must be.
Finally, it is also the most important thing to implement our Invoke method. We have a requirement for the Invoke method, which must be consistent with our customized commission. At the same time, we also need to overload () operation symbols to facilitate our call to it like a general function.
Below I give the defined section of the class:
#include
Using namespace std;
/ * Define an unsigned 32-bit integer type, which is used to store function references (pointers) * /
Typedef unsigned int nativeptr;
/ * Define function prototype, return value must be empty, parameters can change * /
Typedef void (* handler) (char *);
Class CDELEGATE
{
Private:
/ * Function list, the added function reference (pointer) is placed in this list * /
List
PUBLIC:
/ * Add function reference (pointer) * /
Void AddFunction (void *);
/ * Delete function reference (pointer) * /
Void RemoveFunction (void *);
/ * Call routines: The most important part, implement the function in the list calls one by one * /
INT invoke (char *);
/ * Operator overload AddFunction method * /
Void Operator = (void *);
/ * Operator overload RemoveFunction method * /
Void Operator - = (void *);
/ * Operator overload invoke method * /
INT Operator () (char *);
}
Let me give the code implemented by each method.
#include "delegate.h"
Void CDELEGATE :: AddFunction (void * ftn)
{
NativePtr np = (nativeptr) ftn;
FTNS.PUSH_BACK (NP);
}
The AddFunction function accepts the parameters of type VOID *, then enforces this parameter to the NativePtr (unsigned int) type, stored in the FTNS list. Note that this value is inserted from the tail of the list to implement FIFO.
Void CDELEGATE :: RemoveFunction (Void * ftn)
{
NativePtr np = (nativeptr) ftn;
FTNS.Remove (NP);
}
The RemoveFunction function accepts the parameters of type VOID *, then enforces this parameter to the NativePtr (Unsigned int) type, and then delete the same elements as its value from the FTNs. Void cdelegate :: Operator = (Void * ftn)
{
this-> addfunction (ftn);
}
= Operator overloads the AddFunction method.
Void CDELEGATE :: Operator - = (void * ftn)
{
This-> RemoveFunction (FTN);
}
- = Operator RemoveFunction method.
INT CDELEGATE :: Invoke (Char * PCH)
{
Handler handle;
List
Try
{
For (; itr! = ftns.end (); ITR )
{
Handle = (Handler) * ITR;
Handle (PCH);
}
}
Catch (char *)
{
Return 0;
}
Return 1;
}
Using the iterator provided by the List template class, traverse each element in the FTNS, sequentially convert the element into a custom function reference (pointer) type, and call the function thereof. Here, the delegate return value must be empty. If there is an abnormality, INVOKE returns 0 value.
INT CDELEGATE :: Operator () (char * pch)
{
Return INVOKE (PCH);
}
() Operators overload the INVOKE method.
It can be seen that this entrustment we achieve is actually very simple. Add the added function reference (pointer) to a list; when the delegate is called, remove the function in the list and call one by one. The same is true for the implementation of the entrustment in C #; it is delegated, and the feet of the program generator are played by the compiler. In fact, if you analyze the IL code generated by the C # compiler, each C # delegate declaration is also converted to inherit a class that supports similar functions. At the same time, it is also because it is delegated to manage the call to manage multiple methods, it cannot handle their return values, so the entrustment requires the function that the entrusted cannot have a return value.
Below is an example of running:
#include "delegate.h"
#include
#include
Void Say1 (Char * S)
{
COUT << "in function say1:";
Cout << s << endl;
}
Void Say2 (Char * S)
{
COUT << "in function say2:";
Cout << s << endl;
}
Void Stheoaie (Char * S)
{
MessageBox (Null, s, "delegate", MB_OK);
}
void main ()
{
CDELEGATE DLG;
DLG.Addfunction (SAY1);
DLG.Addfunction (SAY2);
DLG = Stheoaie;
INT RS = DLG.INVOKE ("Hello, World!");
IF (! rs) cout << "failed." << Endl;
/ *
First call results:
* /
DLG- = SAY2;
RS = DLG ("THE Second Invoking By CDelegate!"); file: // is equivalent to DLG. Invoke ("The Second Invoking By CDelegate!")
IF (! rs) cout << "failed." << Endl;
/ *
Second call:
* /
DLG- = SAY1;
DLG- = Stheoaie;
RS = DLG.INVOKE ("The Third Invoking By CDelegate!");
IF (! rs) cout << "failed." << Endl;
/ *
The third call, there is no output, because the method is deregulated:
* /
}
3. About practical gadgets delegate.exe
In order to solve the discomfort when customization, I deliberately prepare this gadget, which is convenient to convert the delegate statement into the code as described above. Here is its basic usage.
In your header file, such as Test.h, declare a commission in __dlegate keyword:
__dlegate void WinHandler (HWND HWND, UINT MESSAGE, WPARAM WPARAM, LPARAM LPARAM);
Then go to the command line mode, enter the directory of Test.h, but the key is as follows:
Delegate.exe test.h / out test.hxx
It will generate Test.hxx files. You can include this file to your source to use the delegate you are defined. For example, it can be like this: #include "test.hxx"
You can define multiple delegates in a file or define multiple delegates in multiple files. But you can only specify an output file. If you don't use the / OUT option to specify an output file, the default output is delegate.h. Such as:
Delegate.exe test.h test1.h test2.h / out test.hxx
Use the / HELP option to get help, use the / version option to get version information.
Note: The latest Visual C .NET version has supported the same name keyword __delegate, which is Microsoft to add a new keyword added to Visual C to .NET, only Visual C .NET support, other Visual C 6.0, Borland C Builder, GNU C is not supported. But there is no connection at all. Fortunately, the Delegate gadget supports the / keyword option, which can specify the keyword you define your own, such as __dlegate__.
Since it is just a test version, this gadget is not very strong. For example, you cannot have a comment in the middle of the delegate statement, but only before or after declaration or declaration. In addition, due to the time relationship, I did not do very good to deal with the delegate return value. I hope this problem will soon solve. What comments and suggestions, please feedback to my email: cambest@sohu.com.
[1] For more information on entrustment, please see the "IL code underlying operation mechanism: function related>
[2] With regard to standard template library (STL), please refer to related documents or books, such as MSDN or
[3] About Iterator,