Modern C ++ design note Chapter 1 multimethods (1)

Source: Internet
Author: User

If we have already practiced the martial arts to the tenth realm, the last chapter means that we are about to open the second pulse of Ren du and soar overnight :) based on such a key time point, we are willing to carefully discuss this issue three times and make it clear. For the first time, we will talk about the beginning and the first solution of this multimethod topic.

The problem with this multimethod is that C ++ does not support multi-parameter function polymorphism (in fact, I didn't think we had so many such types of requirements, but when we seeI don't know the name.) And the requirements here are surprisingly similar to those of Alex's sample. Here we will not describe the problem too much, but go straight to the topic to find the first and most intuitive solution.

This solution is named the brute-force, which is used repeatedly by dynamic_cast to determine the specific type. After determining the type, directly call the corresponding type of sub-function, the original implementation code is as follows:

// Various intersection algorithms <br/> void dohatcharea1 (rectangle &, rectangle &); <br/> void dohatcharea2 (rectangle &, ellipse &); <br/> void dohatcharea3 (rectangle &, poly &); <br/> void dohatcharea4 (ellipse &, poly &); <br/> void dohatcharea5 (ellipse &, ellipse &); <br/> void dohatcharea6 (poly &, poly &); <br/> void doubledispatch (shape & LHS, shape & RHs) <br/> {<br/> If (rectangle * P1 = dynamic_cast <rectangle *> (& LHS )) <br/>{< br/> If (rectangle * P2 = dynamic_cast <rectangle *> (& RHs) <br/> dohatcharea1 (* P1, * P2 ); <br/> else if (ellipse P2 = dynamic_cast <ellipse *> (& RHs) <br/> 231 <br/> dohatcharea2 (* P1, * P2 ); <br/> else if (poly P2 = dynamic_cast <poly *> (& RHs) <br/> dohatcharea3 (* P1, * P2 ); <br/> else <br/> error ("undefined intersection "); <br/>}< br/> else if (ellipse * P1 = dynamic_cast <ellipse *> (& LHS )) <br/>{< br/> If (rectangle * P2 = dynamic_cast <rectangle *> (& RHs) <br/> dohatcharea2 (* P2, * P1 ); <br/> else if (ellipse * P2 = dynamic_cast <ellipse *> (& RHs) <br/> dohatcharea5 (* P1, * P2 ); <br/> else if (poly * P2 = dynamic_cast <poly *> (& RHs) <br/> dohatcharea4 (* P1, * P2 ); <br/> else <br/> error ("undefined intersection "); <br/>}< br/> else if (poly * P1 = dynamic_cast <poly *> (& LHS )) <br/>{< br/> If (rectangle * P2 = dynamic_cast <rectangle *> (& RHs) <br/> dohatcharea2 (* P2, * P1 ); <br/> else if (ellipse * P2 = dynamic_cast <ellipse *> (& RHs) <br/> dohatcharea4 (* P2, * P1 ); <br/> else if (poly * P2 = dynamic_cast <poly *> (& RHs) <br/> dohatcharea6 (* P1, * P2 ); <br/> else <br/> error ("undefined intersection "); <br/>}< br/> else <br/>{< br/> error ("undefined intersection"); <br/>}< br/>}

The first response of many people seeing such code is disgusting. Such code is a nightmare for any programmer who needs to maintain it. Of course, this era requires masters to help us mask these things and provide us with more elegant implementations. If we still remember typelist, the beauty of recursion, all of this will be suddenly enlightened. If the compiler can help us hide the IF-Else In the recursion of typelist, it may still be a good idea to some extent, so that the design starts with the template parameter, we need the following parameters:

Template <br/> class executor, <br/> class baselhs, <br/> class typeslhs, <br/> class baserhs = baselhs, <br/> class typesrhs = typeslhs, <br/> typename resulttype = void <br/> class staticdispatcher;

This includes Executor (that is, the class with a group of similar interfaces). baselhs is the base class of the first parameter, and typeslhs is the list of the first parameter, the same is followed by the list of base classes with the second parameter, and the most is the returned value of resulttype. In the same way, we still perform matching. For example, we have such a search method for the first parameter:

Template <class head, class tail> <br/> static resulttype dispatchlhs (baselhs & LHS, baserhs & RHS, <br/> executor exec, typelist <pead, tail>) <br/>{< br/> If (Head * P1 = dynamic_cast <pead *> (& LHS) <br/>{< br/> return dispatchrhs (* P1, RHS, exec, typesrhs (); <br/>}< br/> return dispatchlhs (LHS, RHS, exec, tail (); <br/>}

If it is head *, the second parameter will be processed. If not, the traversal will continue from here in LHS. We can imagine that for the second parameter RHS, we adopt the same method, but the advantage is that the Code is more elegant. For any maintenance service, you do not need to add the new classes. Next, we will post the implementation of a complete staticdispatcher:

//////////////////////////////////////// //////////////////////////////////////// <Br/> // class template invocationtraits (helper) <br/> // helps implementing optional handle ry <br/> ////////////////////////// //////////////////////////////////////// //// // <br/> namespace private <br/> {<br/> template <class somelhs, class somerhs, <br/> class executor, typename resulttype> <br/> struct invocationtraits <br/>{< br/> static resulttype <br/> dodispatch (somelhs & LHS, somerhs & RHS, <br/> executor & exec, int2type <false>) <br/>{< br/> return exec. fire (LHS, RHS); <br/>}< br/> static resulttype <br/> dodispatch (somelhs & LHS, somerhs & RHS, <br/> executor & exec, int2type <true>) <br/>{< br/> return exec. fire (RHs, LHS); <br/>}< br/> }; <br/>}< br/> /////////////////////////////// //////////////////////////////////////// /// // <br/> // class template staticdispatcher <br/> // implements an Automatic Static double dispatcher based on two typelists <br/> // //////////////////////////////////////// /// // <br /> template <br/> class executor, <br/> class baselhs, <br/> class typeslhs, <br/> bool policric = true, <br/> class baserhs = baselhs, <br/> class typesrhs = typeslhs, <br/> typename resulttype = void <br/> class staticdispatcher <br/>{< br/> template <class somelhs> <br/> static resulttype dispatchrhs (somelhs & LHS, baserhs & RHS, <br/> executor exec, nulltype) <br/> {return exec. onerror (LHS, RHS) ;}</P> <p> template <class head, class tail, class somelhs> <br/> static resulttype dispatchrhs (somelhs & LHS, baserhs & RHS, <br/> executor exec, typelist <pead, tail>) <br/>{< br/> If (Head * P2 = dynamic_cast <pead *> (& RHs )) <br/>{< br/> int2type <(symmetric & <br/> int (TL: indexof <typesrhs, head >:: value) <br/> int (TL: indexof <typeslhs, somelhs >:: value)> i2t; <br/> typedef PRIVATE: invocationtraits <br/> somelhs, head, executor, resulttype> calltraits; </P> <p> return calltraits: dodispatch (LHS, * P2, exec, i2t ); <br/>}< br/> return dispatchrhs (LHS, RHS, exec, tail ()); <br/>}</P> <p> static resulttype dispatchlhs (baselhs & LHS, baserhs & RHS, <br/> executor exec, nulltype) <br/> {return exec. onerror (LHS, RHS) ;}</P> <p> template <class head, class tail> <br/> static resulttype dispatchlhs (baselhs & LHS, baserhs & RHS, <br/> executor exec, typelist <pead, tail>) <br/> {<br/> If (Head * P1 = dynamic_cast <pead *> (& LHS )) <br/>{< br/> return dispatchrhs (* P1, RHS, exec, typesrhs (); <br/>}< br/> return dispatchlhs (LHS, RHS, exec, tail (); <br/>}< br/> Public: <br/> static resulttype go (baselhs & LHS, baserhs & RHS, <br/> executor Exec) <br/> {return dispatchlhs (LHS, RHS, exec, typeslhs () ;}< br/> };

When we see this complete implementation, we need to add a few points:

1. recursion always requires convergence, so no matter dispatchrhs or dispatchlhs, there will be a special onerror processing for nulltype. This requires that we have an onerror implementation in this executor.

2. We noticed that there is also a declaration of bool distributed Ric = true. We can know from the name that this is a symmetric switch, which is more natural. If there are two parameters rectangle & and poly &, it is best for me to call either func (rectangle &, poly &) or func (poly &, rectangle &). So the traits here helps us. Note that the indexof here depends on the order of LHS and RHS. In addition, when searching for LHS and RHS, the first or second parameters are not determined. Here, dispatchlhs or dispatchrhs are only sure that these RHS and LHS exist in baselist! Therefore, it makes sense to discuss the symmetry when the two list elements are the same.

3. Another problem is the smoothness problem in typelist. This problem is a bit difficult to catch the order of exceptions. If the base class is in the front, the derived classes will not be accessed. If typelist_4 (rectangle, ellipse, poly, roundedrectangle) is such a statement, the type of roundedrectangle & is successfully intercepted in Rectangle & dynamic_cast. Therefore, you should manually organize the inheritance relationship or use the derivetofront in Chapter 3 to let the compiler help you organize it.

In general, such an elegant implementation has not changed the idea itself, and it is still a match. Match the corresponding function based on the actual type of the variable. It should be said that it is easy to use and quick to use when the class structure is relatively simple. However, when the type is complex, it is not so practical.

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.