Generic Callbacks

xiaoxiao2021-03-06  37

Generic Callbacks

Open chapter translated from << Exceptional C Style >>

JG problem

1. What kind of quality is expected when designing, writing generic facilities? please explain.

Guru problem

2. The following code represents an interesting and useful customary method to pack callback functions. Please refer to the original article to get a more detailed explanation. [Kalev01]

Indicate these codes and marks:

a) can improve the choice of style to make the design to meet more C usual methods.

b) limits the availability of the facility.

Template

Class Callback

{

PUBLIC:

Callback (T & T): Object (t) {} // Assign Actual Object To T

Void Execute () {(Object. * f) ();} // launch callback function

Private:

T & Object;

}

answer

Generic quality

1. What kind of quality is expected when designing, writing generic facilities? please explain.

The generic code should be available first. That doesn't mean it must contain all the operations. It means that generic code should be reformally changed, balanced efforts to avoid at least three things:

1. Avoid inappropriate type limits. For example, if you want to write a generic container. A very reasonable requirement is that the element type in the container has a copy constructor, one does not throw an abnormal destruction function. But what about the default constructor? What is assigning an operator? Many users who want to put into the container do not include the default constructor, and if your container uses it, then this type will not use the container you have written. So your container is not generic. (Please refer to Exceptional C [SUTTER 00] to get a complete example.)

2. Avoid uncomfortable functional restrictions. If you write a facility to perform x and y, but how do users want to execute Z, how would it? The gap between Z and Y is not large. Sometimes you want your generic facilities to be more flexible, let it support Z. Sometimes you don't want to do it. A good generic design is to provide a way of choice, meaning through it, your generic facilities can be customized or expanded. This is very important in generic design, and you should not be surprised, because this principle is also the same in the design of object-oriented classes.

Policy-based design is one of many important technologies, which allows "can be insertable" in generic code. Policy-based design, please refer to the section of [AlexandRescu01]. StartPtr and Singleton are very good starts. This leads to a related problem:

3. Avoid inappropriate [Monolithic] design. This important problem does not appear in the examples you have to consider, should be considered for it, so it does not only appear in this terms, but also appear in future terms: Please check the terms 37 to 40.

In these three points, you may notice that the words repeatedly appear are "inappropriate". It means that when a balance is made between lack of sufficient generic and excessive projects, it should be judged.

Panjun callback

2. The following code represents an interesting and useful customary method to pack callback functions. Please refer to the original article to get a more detailed explanation. [Kalev01]

Look at the code:

Template

Class Callback

{

PUBLIC:

Callback (T & T): Object (t) {} // Assign Actual Object To T

Void Execute () {(Object. * f) ();} // launch callback functionprivate:

T & Object;

}

Now, in practice, there are two member functions, how many errors do each member function is a simple class? As a result, this extreme simplicity is part of the problem. This template class does not need this heavyweight, it is not required, but it can be used to represent it with a slightly lightweight.

Improving style

Indicate these codes and marks:

a) can improve the choice of style to make the design to meet more C usual methods.

How many flaws do you find? This is my summary:

4. The constructor should be Explict. Authors may not want to provide an implicit conversion from T to Callback . Good behavioral classes to avoid potential errors to their users. We are exactly what you want to provide:

Explicit Callback (T & T): Object (t) {} // Assign Actual Object To T

Although we have already analyzed this particular row, there is a style problem that is not about design, but about the description:

Principle: Try to make the constructor is explicit unless you really want to provide type conversion.

Comments are wrong. The "Assign" in the comment is incorrect, so some misunderstandings. In the constructor, it is more precisely a reference to the T object, and expand into a callback object. Similarly, I have repeatedly rereading the comment, I still don't know the meaning of "to T". So better annotations will be "binding actual objects"

Explicit Callback (T & T): Object (t) {} // Bind Actual Object

However, all the comments have been expressed, it is indeed very ridiculous, it is a good example of useless comments, so it is best to write as follows:

Explicit Callback (T & T): Object (t) {}

5. The execute function is consisting of const. After all, the Execute function does not have any changes to the status of the Callback object. This has returned to the basic question: Const correctness may be a bit old, but it is really good. At least in 1980, the value of Const correctness is already known in C, C . And its value does not disappear because of our new millennium, it will not disappear for us to write a lot of templates.

Void Execute () const {(object. * f) ();} // launch callback function

Principle: Correct use of const.

Although we have analyzed the Execute function, there is still a more serious usual method:

6. The Execute function should be written as operator (). In C , the usual method is to use a function call operator to perform a function of functionality. So the comments have become useless, which can be deleted safely because our current code has expressed its own by usual method.

Void Operator () () const {(object. * f) ();} // launch callback function

You may be surprised. "If we offer a function operator, it is not a function object?" Very good question, we continue to observe, as a function object, or the instance of the callback should be equipped.

Principle: Provide Operator () to represent the usual function object instead of providing a method called Execute.

Trap: (customary) Does this callback should be inherited from std :: unary_function? View [Meyers01] Terms 36 for more discussions about adapted, and why it is usually a good design. However, here, there are two good reasons to cause Callback to inherit from std :: unary_function: l It is not a single parameter function. It does not receive parameters, and the single parameter function receives a parameter. (Void is not counted)

l From std :: unary_function inheritance, no matter how it does not expand it. Below we will see that callbacks may be able to handle other different signs of different signatures; depending on the number of parameters, there may be no suitable base classes at all. For example, if we support the Callback of three parameters, we don't have std :: terronary_function to inherit.

Delivered from std :: unary_function or std :: binary_function, you can provide several convenience, the type definition it binds, it is dependent on the similar facilities, but these are only valid when you are ready to use these facilities that use the function object. . These facilities are unlikely to be needed because of the natural attributes of the callback and how they will be used. (For ordinary monosorption, double-ginseng, if they should be able to use, we will mention the monociety, the double-gate version should be derived from std :: unary_function and std :: binary_function.)

Correction mechanism error and restriction

b) limits the availability of the facility.

7. Consider the transfer of the callback function to receive a normal parameter instead of a template parameter. Non-type template parameters are rarely used, because in compile, it is so fixed that there is very little benefit. So we replace:

Template

Class callback {

PUBLIC:

Typedef void (T :: * func) ();

Callback (T & T, FUNC FUNC): Object (t), f (func) {} // bind actual Object

Void Operator () () const {(object. * f) ();} // launch callback function

Private:

T & Object;

Func F;

}

Now the function can be changed at runtime, it is easy to add a member function to allow the user to change the function of the saved callback object, which is impossible to implement in the previous version.

Principle: Prioritize the use of normal function parameters, then non-type parameters. Unless they are indeed a non-type template parameter.

8. Can be contained. If the program wants to save a callback object later, it may also save multiple objects. What happens if it wants to put a callback object in a container, such as what happens in Vector or List? It is impossible now, because the callback object does not support Operator =, that is, it cannot be assigned. Why not? Because it contains a reference, in the constructor, once the bind is referenced, it cannot be modified.

But the pointer does not limit this, it can be arbitrarily changed. In this case, the pointer to the object is used instead of being secure, and the copy constructor and assignment operator generated by the compiler can be used.

Template

Class callback {

PUBLIC:

Typedef void (T :: * func) ();

Callback (T & T, FUNC FUNC): Object (& T) {} // Bind Actual Object

Void Operator () () const {(object -> * f) ();} // launch callback function

Private:

T * Object;

Func F;

}

Now, there may be a type of List >.

Principle: It is preferred to be compatible with your objects and containers. In particular, in order to place objects into the container, the object must be assignable.

"But, wait", you really want to know, "If I have such a list, why can't I have any type of callback? So I can remember them and when I want to perform a callback, I can Random execution. In fact, if you add a base class, you can do it.

9. Allow polyure: providing a common base class for the callback type. If we want users to own list (or better List >), we can implement the operator in the base class by providing such a base class.

Class CallbackBase {

PUBLIC:

Virtual void Operator () () const {};

Virtual ~ CallbackBase () = 0;

}

CallbackBase :: ~ CallbackBase () {}

Template

Class Callback: Public CallbackBase {

PUBLIC:

Typedef void (T :: * func) ();

Callback (T & T, FUNC FUNC): Object (& T) {} // Bind Actual Object

Void Operator () () const {(object -> * f) ();} // launch callback function

Private:

T * Object;

Func F;

}

Now, anyone can save List and can modify the elements of the elements (). Of course, List will be better; see [SUTTER02B].

Note that adding a base class is a trade-off, but it is a small one: When the base class triggers a callback, we increase the load of two indirect calls, which means a virtual function call. However, this load is only when using the base class. The code that does not use the base class interface does not need to be paid for this.

Principle: If you work on your template class, consider allowing you to use different instances of your class to interchange. If it does work, provide a common base class for all your template classes.

10. (General Law, Weigh) should have an auxiliary function Make_Callback to deliver the type. After a period of use, the user will be tired of providing explicit template parameters for temporary objects.

List >>

L.Push_back (Callback (W, & Widget :: Somefunc);

Why write two widgets? Shouldn't the compiler do you know? However, it doesn't know, but we can help it, let it know that there is only a temporary variable. So we provide an auxiliary function that requires only one type:

List > L; L.Push_Back (Make_Callback (W, & Widget :: SomeFunc));

Make_callback and std :: make_pair work the same. The make_callback function should be a template function because this is the only template parameter type that can be derived by the compiler. The next is the auxiliary function:

Template

Callback make_callback (T & T, Void (T :: * f) ()) {

Return Callback (T, F);

}

11. (Weighing) Add other function signatures. I left this biggest job. Because the bard will say "there will be more functions sign, not just void (t :: * f) ()!"

Principle: Avoid restricting your templates; avoid hardcoding for specific types or for less common types.

If the restricted callback function is signed enough, no matter how it is over here. If we don't need it, we don't have to set the design to more complicated. If we do need it, we will have to lead the design more complicated.

I won't write all the code because it is very boring. What I did is just a summary description you have to do, and how to do it.

First of all, what is the function of the member? The easiest way to handle it is to provide a callback using the Const Signed, in which version, give a note to save a pointer or reference to the Const object.

Second, what if the function is not for VOID? The simplest way is to add another template parameter to indicate the return type.

Finally, what if the callback function needs to receive parameters? Again, add a template parameter, pay attention to the operator () to add a function parameter.

Also add new template to handle each potential number of callback parameters.

Explore all the code, you have to limit the number of function parameters that call the callback support. Perhaps in the future C 0x, we offer a template who wants Varargs to help you handle these things, but there is no.

to sum up

Put all things together, do some pure style adjustments, use TypenAme, naming rules, and space rules I prefer, this is the final result we got:

Class CallbackBase {

PUBLIC:

Virtual void Operator () () const {};

Virtual ~ CallbackBase () = 0;

}

CallbackBase :: ~ CallbackBase () {}

Template

Class Callback: Public CallbackBase {

PUBLIC:

Typedef void (T :: * f) ();

Callback (T & T, F): T _ (& T), F_ (f) {}

Void Operator () () const {(t_-> * f _) ();

Private:

T * t_;

F_

}

Template

Callback make_callback (T & T, Void (T :: * f) ()) {

Return Callback (T, F);

}

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

New Post(0)