Analysis Boost

xiaoxiao2021-03-11  193

(1) ANY In the write program, we often need to save a "generic" object in the running period, that is, this pointer may be a pointer to any object, in which case we generally put this pointer CAST is a void * or an int, and then Cast is coming back when needed, so it is often lacking types of security, and our programmers like to use C transformation operations rather than C transformation, resulting in a T * pointer Casted into a pointer of a U * and the compiler does not do any representation, in order to ensure the type of security, this time we should check before the transformation, see if such a transformation is correct. Boost :: Any helped us complete this. His concrete implementation principle is this:

Template Class Any {...

PlaceHolder * Content; // A interface pointer, you can no matter the}; template ) {} Template Any & Operator = (const valueType & rhs) {

ANY (RHS) .swap (* this);

Return * this;}

The above principle is to deliver the object of the object through the parameters of the template, pass the "pseudo copy constructor" and "pseudo-equivalent operators" to a Nest Class, this Class is:

Template Class Holder;

Holder saves a value we passed to him and exposes an interface that get std :: type_info & clones, so that the original compiler is performed by the parameters of the object we passed through the target through the operating period. Std :: type_info is guided out. as follows:

class placeholder {public: virtual ~ placeholder () {} virtual const std :: type_info & type () const = 0; virtual placeholder * clone () const = 0;}; template class holder: public placeholder {public : Holder (Value): Held (Value) {} Virtual const st: Type_info & type () const {return};} const {returnholder * clone () const {return.com;} ValueType helpt;};

Finally, we get the type of information, but we have not used him yet, Boost is convenient for our use by providing the following help functions: see the core:

template valueType * any_cast (any * operand) {return operand && operand-> type () == typeid (valueType) & static_cast *> (operand-> content) -?> held : 0;} We have gained a convenient tool, more importantly, we have obtained a method of encapsulating an object's type, and the specific method is as follows: 1. Types and objects are not the same level, we must specify the type when we pass the object, but we can delegate this designation process to the compiler to complete (real parametric interpretation) 2. We can only use the type "Save" type, but we can use a built-in type to help us save the model, so that the external type is unique. 3. To export from multiple types, you can use interfaces to fuzzy differential differences (2) Bind We often need to delay some calls when writing programs, this is the so-called "command" mode, a typical The example is the dirty rectangular management of the drawing. We don't draw directly when calling the drawing function, but create a command, then save these Command to a queue, and then calculate the dirty rectangle when you finally need to draw. Then draw again, do this:

struct DrawCommand {Image * m_iamge; RECT m_rect; int m_flag; DrawCommand (Image * iamge, RECT & rect, int flag): m_iamge (image), m_rect (rect), m_flag (flag) {assert (image);} void Draw ( RECT & rect) {assert (m_iamge); m_iamge-> Draw_Impl (rect, m_flag);}} class DirtyRect_Drawer {private: std :: vector m_draw_queue; RECT m_dirtyrect; private: void CalcDirtyRect () {... / / use m_draw_queue to calc m_dirtyrect} static void Draw (DrawCommand * p) {assert (p); p-> Draw (m_dirtyrect); delete p;} public: void push (DrawCommand * p) {assert (p); m_draw_queue- > push_back (p);} void flushdraw () {CALCDIRTYRECT (); std :: for_each (m_draw_queue.begin (), m_draw_queue.end (), m_draw_queue.clear ();}};

The above DrawCommand should use your own memory allocation scheme, that is, to overload New, or you will be punished by efficiency, because C default new is slow to die. And dirtyRect_drawer should be a single piece. Now we will use this below:

Namespace {dirtyRect_drawer _drawer;}; extern image * image1; extern image * image2; int main () {Rect Rect1 (0, 0, 100, 100), RECT2 (50, 50, 100, 100); _DRAWER.PUSH (New Drawcommand (Image1, Rect1, Simple_Draw); _drawer.push (image1, rect2, simple_draw); _drawer.flushdraw ();} is like this, but when such Command is more and more, the code repeat Greater bigger, realization is also boring. At this time we need a solution that can be used. This is function and bind. Let's take a look at his effect and then look at his implementation principle.

typedef boost :: function DrawCommand; class DirtyRect_Drawer {private: std :: vector m_draw_queue; RECT m_dirtyrect; private: void CalcDirtyRect () {... // use m_draw_queue} static void Draw ( Drawcommand * p) {assert (p); (m_dirtyRect); delete p;} public: void push (drawcommand * p) {assert (p); m_draw_queue-> push_back (p);} void flushdraw () {CalcDirtyRect (); std :: for_each (m_draw_queue.begin (), m_draw_queue.end (), Draw); m_draw_queue.clear ();}}; namespace {DirtyRect_Drawer _Drawer;}; extern Image * image1; extern Image * image2 Int main () {Rect Rect1 (0, 0, 100, 100), RECT2 (50, 50, 100, 100); _DRAWER.PUSH (NEW DRAWCOMMAND (IMAGE:: 4DRAW); _drawer .push (new drawcommand); _DRAWER.FLUSHDRAW ();

Is it very convenient? Drawcommand does not write, he essentially: boost :: _ bi: bind_t >, we use boost :: function Come and save him, and Command is actually: Operator (Rect &). Not only that, but Bind also provides a method for specifying a part of the parameters:

Void Kao (INT I, FLOAT J) {} INT i = 10; Boost :: Bind (& Kao, _1, 10.0F) (i);

In addition, there are many powerful capabilities, you can go to Boost's DOC, now let's take a look at his implementation principle: The Command mode is the value of a part of the parameters, and execute this Command in the future. use. Let's take a look at how this is achieved, that is, how to pass on the payment, image1, rect1, Simple_Draw is saved, they are different types, I only introduce his basic principles, so The code will be deleted and more, but the design principle of the code will not change. His probably the principle is this: Let's take a look at the actual bind function, I only use the bind function demonstration of the two parameters. Template _BI :: Bind_T :: type> bind (f, A1 A1, A2 A2) {typedef Typename _bi :: list_av_2 :: type list_type; return ::: bind_t (f, list_type (a1, a2));}

1. First, we pass the value of all parameters and some placeholders to the bind function, and the BIND function saves these parameter values ​​in a model called List_av_2, calls as follows:

Boost :: Bind (& Kao, _1, 10.0F); Namespace _bi {// list_av_2 template struct list_av_2 {typef typef typeName Add_Value :: type b1; // add_value is a primitive type Retrieved class typef typef typef typename add_value :: Type B2; type;}; // add_value, implementation principle is template offset Template struct add_value {typedef value type;}; template struct add_value > {typedef value type;}; template struct add_value > {typedef reference_wrapper type;}; Template Struct Add_Value > {typef Boost :: Arg type;}; template struct add_value (*) ()> {typedef boost :: arg (* type) ();}; template struct add_value > {typedef bind_t type;}; / /}; // arg, his use is occupying, and provides information for future recognition Namespace Boost {Template class arg {};}; // Unified Name Namespace { Static Boost :: Arg <1> _1; static boost :: arg <2> _2; ...}; // 2. We found that the above list_av_2 was just a transfer role. He extracted real types and passed him into a list2 to bind_t, now passing Operator () to bind the parameters to bind_t, as follows:

INT i = 0; boost :: bind (& kao, _1, 10.0f) (i); namespace _bi {// type template class type {}; // bind_t template Class bind_t {public: bind_t (f, l const & l): f_ (f), l_ (l) {} // Transfer function and imitation function, and the list of parameters is passed to bind_t typef typefename result_traits :: type result_type; // Get optimized and correct return Valued value result_type Operator () // Exposure to the interface to us call {list0 a; return L_ (Type (), f_, a );} // We use result_type to identify the same reference but return value, but if result_type is large, or quote other resources, then pay a lot, so use type () To identify. Result_Type Operator () const // exposed to the interface {list0 a; return L_ (Type (), f_, a);} ... A1 omitted Template Result_Type Operator () (A1 & A1, A2 & A2) // Exposure to the interface to us call {List2 A (A1, A2); Return L_ (Type (), F_, A } Template Result_Type Operator () (A1 & A1, A2 & A2) Const // The interface to us call {List2 A (A1, A2); Return L_ (Type (), F_, A);} ... A1, A2, A3, etc. PRIVATE: F_; // Function and Parameter List of the Imitation Function L1_; // ListN; //};

In Operator (), a list2 is created, and the instance of this list2 is merged by the newly passed the parameter value and the parameter value of the previous list2, replacing the original list2 instance. Parameters in Operator (). This is quite convenient, which is realized by overloading Operator [], we identify the parameters of different locations by accepting different types of Operator [], that is, _1, _2, ... and other parameter types I will go. Some of this codes in this class, they are mainly some code that is not related to this topic.

Namespace _bi {// value template class value {public: value (t): t_ (t) {} T & gET () {return t_;} t_ {return () const {Return T_ : Private: t t_;}; // list2template Class List2 {public: list2 (A1 A1, A2 A2): A1_ (A1), A2_ (A2) {} // In this step The parameter value is saved to list2 a1 operator [] (boost :: arg <1>) const {return A1_;} a2 operator [] (boost :: arg <2>) const {return a2_;} template t & Operator [] (Value & V) const {return v.get ();} template t const & operator [] (value const & v) const {return v.get (); } Template t & operator [] (Reference_Wrapper const & v) const {return v.get ();} ... template r operator () (TYPE , f, a & a) const {return unwrap (f, 0) (a [A1_], a [A2_]); // Unwarp's purpose is to separate the true type from some packages Come out} ... private: a1 a1_; a2 a2 _;}; // unwrapTemplate inline F & unwrap (F & f, long) // utilized {Return F;} Template Inline F & Unwrap (Reference_Wrapper & f, int) // utilizes offset {RETURN F;} template inline F & unwrap (Reference_Wrapper const & f, int) // Utilization {RETURN F;} //}; namespace _bi {// bind_t template class bind_t {public: bind_t (f, l const & l): f_ (f) , l_ (l) {} ... template result_type operator () (A1 & A1) {List1 A (A1); RETURN L_ (Type (), F_, A); } Template

Result_Type Operator () (A1 & A1, A2 & A2) Const {List2 A (A1, A2); RETURN L_ (Type (), F_, A); // Here, We created a list2, then passed our parameters to him, // then is List2 :: Operator, then unwrap (f, 0) (a [_1], a [_2]) // then As unwrap (f, 0) (A1_, A2_)} ... private: f_; l_;}; //}; you may have discovered, the number of parameters of the BINT_T we call the number of parameters and list2 The number of parameters of Operator () is different. In fact, his process is like this: boost :: bind (& kao, _1, 10.0F); put the function and the imitation function, and the parameters pass a list2 in bind_t, this The value saved by List2 is:

A1 _---> _ 1, A2 _---> 10.0F

Then Boost :: Bind (& Kao, _1, 10.0F) (i); Parameter i passes the Operator (A1 & A) to bind_t, this time generates a list1, the value he saved is:

A1 _---> i

Then transmit this LIST1 through the operator () of List2 to the original list2, and List2 calls unwrap (f, 0) internally (A1_], A [A2_]);, this A is the LIST1 that just passed to List2, At this time, the relative parameter i will call List1 :: Operator [] (boost :: arg <1>), which is actually returned to i. The parameter A2_ stored relative to the List2 itself calls List1 & V), which actually returns the parameter A2_. Thus make the previous calls actually become:

Unwrap (f, 0) (i, 10.0f);

This will achieve the goal, that is, we can call this:

Void Kao (INT I, INT J, INT K, FLOAT P) {} INT i = 0; Boost :: Bind (& Kao, _1, _1, _1, 10.0F) (i);

The above call effect is actually:

KAO (I, I, I, 10.0F);

(3) Lambda is a wonderful library. When I am studying him, the first reflection is shocking, "What, the code can be written this!?", But after thinking, my reflection is the author's imagination Admire and perseverance, why? Please look at a simple example first, then you will understand. We often write this code when we write code:

Using namespace std; extern list m_list; template struct plus {t & i; plus (t & II): i (ii) {} void operator () (const t & j) const {cout << j; COUT << (i j);}} INT i = 0; for_each (m_list.begin (), m_list.end (), Plus (i)); we are already very satisfied, isn't it? But please take a look at the code below, he and the effect:

Using namespace std; using name; extern list m_list; int i = 0; for_each (m_list.begin (), m_list.end (), (cout << _1, cout << (_1 i ))))

Maybe you are like me, the first feeling is shocked, "What is the TMD going!?", But when you listen to me, you will discover how this is now. Because this library is very large, I only introduce him basic principle, as for the code, it is not directly discussed. First, you have to stick to a little: C is not crazy, this is completely C standard. You will say, "Standard!? The original code appears in the function parameter list, it is normal!?" We know that for_each only accepts functions or imitation functions as his third parameter, that is, (cout << _1, Cout << (_1 i)) Returns an imitation function, but how to see it is not a function, this is where I am admired by the author's imagination, don't forget the Operator of the C operator, the above code actually overloads Operator Operator <<, operator ,. They all returns a Lambda's imitation function and accept the imitation function as a parameter. "What?, Can you be overloaded !?" Yes, he can, just a few people do. Lambda's authors have overloaded all overloaded operators, but also wrote a lot of judgment functions, which is where I admire him perseverance. You have seen it again _1, his role and the role of _1 in Bind is a placeholder. You can look at the code yourself. Here, you will not be surprised to see the code below.

Sort (vend (), vp.end (), * _1> * _2); for_each (vp.begin (), vp.end (), cout << constant ('/ n') << * _1) Bool flag = true; INT i = 0; (_ 1 || _ 2) (flag, i); struct a {int D;}; a * a = new a (); (_ 1 -> * & a :: d) (a); struct b {int foo (int);}; b * b = new b (); (_ 1 -> * & b :: foo) (b) (1); int i = 0; int J VAR_TYPE :: Type Vi (VAR (i)), VJ (var (j)); for_each (a.begin (), a.end (), (vj = _1, _1 = vi, vi = vj )))); int A [5] [10]; INT I; for_each (a, a 5, for_loop (var (i) = 0, VAR (i) <10, var (i), _1 [var ( I)] = 1)); for_each (A.Begin (), a.End (), IF (_1% 2 == 0) [cout << _1]) std :: for_each (v.begin (), v.end (), (Switch_Statement (_1, case_statement <0> (std :: cout << constant ("zero")), Case_Statement <1> (std :: cout << constant ("one"), default_statement (COUT << Constant ("Other:") << _1)), cout << constant ("/ n")))))); there are many, but in addition to extremely simple cases, we recommend other cases, other situations Recommended, he will not only punish you in efficiency, but how many operators actually have multiple function calls and parameters, and they can't debug them at all, and it is also difficult to reuse, and people who don't know Lambda. It is even more understandable.

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

New Post(0)