Use crtp for better Encapsulation

Source: Internet
Author: User

Use crtp for better Encapsulation

Link: http://madmanahong.blog.163.com/blog/static/48850962008994165152/

Translation:Mad Ah Hong

Better encapsulation for the curiously recurring template Pattern
Use crtp for better Encapsulation

 

For a long time, C ++ has been prominent in excellent skills and examples. One well-known is the singular recursive template (crtp) proposed by James coplien in 1995 ). Since then, crtp has become popular and used in multiple libraries, especially boost. For example, you can see them in the boost. iterator, boost. Python, or boost. serialization library.

In this articleArticleIn, I assume that the reader is familiar with crtp. If you want to study it, I recommend that you read chapter 17th of C ++ template programming. On www.informit.com, you can find the free version of this chapter.

If you look at crtp from the perspective of OO, you will find that it shares the same characteristics with the OO framework, and it is a base class that calls virtual functions,
The real implementation is in the derived class. The following is a simple implementation of the OO framework.Code:

// Library code
Class base
{
Public:
Virtual ~ Base ();
Int Foo () {return this-> do_foo ();}

Protected:
Virtual int do_foo () = 0;
};

Here, base: Foo calls a virtual function do_foo, which declares a pure virtual function in the base class and must be implemented in the base class. That is to say, the entity of do_foo appears in the derived class.

// User code
Class derived: public Base
{
PRIVATE:
Virtual int do_foo () {return 0 ;}
};

The interesting thing here is that the do_foo function must change the access token from protection to private. This is a good access control in C ++, and you only need to enter a few simple characters to implement it. Why do we emphasize that do_foo is not commonly used here? The reason is that a user should try to hide the implementation details of the class to make the class simpler. (If you think this class has no value exposed, you should even hide the entire derived class ).

Now let's assume that there are some restrictive factors that make virtual functions not competent, and the framework author decides to use crtp.

// Library code
Template <class derivedt>
Class base
{
Public:
Derivedt & derived (){
Return static_cast <derivedt &> (* This );}
Int Foo (){
Return this-> derived (). do_foo ();}
};

// User code
Class derived: public base <derived>
{
Public:
Int do_foo () {return 0 ;}
};

Although do_foo is the same implementation, it can be accessed at will. Why not set it to private or protected? The answer is that the foo function calls derived: do_foo, or the base class directly calls a function in the derived class.

 

Now let's find the simplest way to hide the Implementation Details of derived users. It should be simple enough. Otherwise, the user will not use it. For the author of the base class, this is a little troublesome, but it should not be difficult to solve.

The most obvious method is to establish a friendship between the base class and the derived class.

// User code
Class derived: public base <derived>
{
PRIVATE:
Friend class base <derived>;
Int do_foo () {return 0 ;}
};

This solution is not perfect, just for a simple reason: each base template parameter class must define a friend declaration. If there are many template parameters, the declaration list will be very long.

To solve this problem and fix the length of the membership list, we introduce a non-template accessor for a forward call.

// Library code
Class accessor
{
PRIVATE:
Template <class> friend class base;
Template <class derivedt>
Static int Foo (derivedt & derived)
{
Return derived. do_foo ();
}
};

The base: Foo function is called accessor: Foo. It is used to forward the call to derived: do_foo.

First, this call chain will always succeed, because the base class is the accessor class's friend.

// Library code
Template <class derivedt>
Class base
{
Public:
Derivedt & derived (){
Return static_cast <derivedt &> (* This );}
Int Foo (){
Return accessor: Foo (this-> derived ());}
};

Second, it succeeds only when do_foo is public or when do_foo is a friend of the accessor class while the accessor class is derived. We are only interested in the second situation.

// User code
Class derived: public base <derived>
{
PRIVATE:
Friend class accessor;
Int do_foo () {return 0 ;}
};

This method is used by multiple boost libraries, such as boost. def_visitor_access and boost in Python. iterator_core_access of iterator should be declared as friends to access private functions defined by the user from def_visitor or iterator_facade.

Although this solution is simple. However, we still have a way to omit the list of membership declarations. In this case, do_foo cannot be private. You must change it to protection. This is actually nothing, because the difference in access control between the two is not important to crtp users. Why? Let's take a look at how users will derive from the crtp base class.

Class derived: public base <derived> {/*...*/};

Here, you will give the final class to the template parameter list. Any class that tries to derive from derived does not make much sense, because the base class <derived> only knows the derived class.

Because we don't need to consider derivation, our current goal is to implement the function derived: do_foo declared as protected in the base class.

// User code
Class derived: public base <derived>
{
Protected:
// No friend declaration here!
Int do_foo () {return 0 ;}
};

Generally, you can access a protection function in the base class in the subclass. The challenge now is how to access it in turn.

The first step is obvious. Because our only starting point is that a protection function can be accessed by future generations of derived.

Struct breakprotection: derived
{
Static int Foo (derived & derived ){
/* Call do_foo here */}
};

When we try to complete his entity:
Return derived. do_foo ();

However, breakprotection: Foo will fail because it is forbidden according to the C ++ standard.

Paragraph 11.5:

When a friend or a member function of a derived class references a protected nonstatic
Member of a base class, an access check applies in addition to those described earlier
In clause 11. Could t when forming a pointer to member (5.3.1), the Access must be through
A pointer to, reference to, or object of the derived class itself
(Or any class derived from that class) (5.2.5 ).

Therefore, this function can only be accessed by objects of the breakprotection type.

Well, if a function cannot be accessed directly, we can access it indirectly. Obtain the do_foo address in the breakprotection class.

& Breakprotection: do_foo;

There is no do_foo function in breakprotection. Therefore, the expression will be parsed to & derived: do_foo.

Since a public access pointer to a member function is allowed, let's call it quickly.

Struct breakprotection: derived
{
Static int Foo (derived & derived)
{
INT (derived: * fN) () = & breakprotection: do_foo;
Return (derived. * fN )();
}
};

For better encapsulation, breakprotection can be moved to the private section of the base template class. The final solution is:

// library code
template
class base
{< br> PRIVATE:
struct accessor: derivedt
{< br> static int Foo (derivedt & derived)
{< br> int (derivedt: * fN) () = & accessor: do_foo;
return (derived. * fN) ();
}< BR >};
Public:
derivedt & derived () {
return static_cast (* This) ;}< br> int Foo () {return accessor :: foo (
This-> derived () ;}< BR >};

// User code
Struct derived: Base <derived>
Protected:
Int do_foo () {return 1 ;}
};

 

This time, the user's code is slim and clearer than the first solution, while the library code does not change much.

Despite this, there is still a flaw. Most compilers cannot optimize indirect function pointers, even if they point to the original object.

Return (derived. * (& accessor: do_foo ))();

In any case, crtp is better than virtual function optimization. Crtp has no burden on calling virtual functions, so its efficiency is very fast.
At the same time, the code type can be exported by the compiler during compilation, so the generated code will be smaller. (Of course, the second method mentioned above still has type information. We hope that mainstream compilers in the future can optimize it .) In addition, using the member function pointer is not very convenient, especially the overload function.

References

[Coplien] James O. coplien. "curiously recurring template patterns", C ++ Report, February 1995.
[Vandevoorde-] David Vandevoorde, niclai M. josutis. "C ++ templates: the complete guide". http://www.informit.com/articles/article.asp? P = 31473
[Boost] boost libraries. http://www.boost.org.
[Standard] ISO-IEC 14882: 1998 (E), programming languages-C ++.

 

Link: http://accu.org/index.php/journals/296

 

Note: static polymorphism and crtp (singular recursive template mode) in C ++ are no longer as strange as they were five years ago. I remember when I discussed static polymorphism in domestic forums, many people responded with the same response? . I first came into contact with these files in C ++ template Chinese edition. I still remember that after reading "C ++ template Chinese version" all night long, I was excited to refactor all classes with templates. However, technologies and products are always different. technologies can be at the forefront of the industry and constantly innovated and transformed. However, products must be well-regulated and have a stable foundation, simple code that is easy to maintain will always come first. Therefore, less and less attention has been paid to C ++ 0x over the past few years. Sometimes I see code written by some colleagues. I always emphasize the meaning of the Code. In extreme programming,"Source codeIs the comment ". In fact, this sentence does not emphasize that you do not need to write a help document, or emphasize the semantics of the Code. For example, whether a class destructor is virutal does not depend on your mood. The destructor of the container class in STL do not define virtual, because without this keyword, it clearly tells you that this class does not want to be inherited. This is actually the meaning of the Code. The reason why I translated this article is not to tell you about crtp, but to translate the author's attitude. Static polymorphism is easy to implement, but too many details need to be paid attention to when implementing a good crtp. From the beginning, this article has put forward an idea, and finally wrote a method through skills, until the final perfection is almost perfect. This shows the meticulous technical spirit of foreign technical experts. Think about why we can never write a book as shocking as modern c ++ design? Maybe one day I get used to designing on demand, programming in spoken language, and pointing points under the opponent, the truth will be self-evident.

 
Keywords: crtp singular recursive template mode encapsulation static Polymorphism

Oct 09th, 2008-gnoha

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.