Property in C ++

Source: Internet
Author: User

I read more than tive C ++ and modern c ++ design, and I think I should start to practice ...... So I thought of "attributes ". The attributes I want to complete have the following features:

1. syntactically consistent with C # And ActionScript, that is, obj. Property = propvalue and propvaluevar = obj. Property;
2. Use preset getter and setter functions based on the context (L-value and R-value;
3. Since the properties only call the getter and setter functions, the memory should not be occupied;
4. Declare attributes as simple as possible. syntax similar to this: Property int propvalue (get = & Class: getter, set = & Class: setter );
5. readonly, static, and static readonly attributes ......

 

It seems that I should use proxyclass (for the "proxy" class with the sole goal of completing a function, see article 30 of "more effective C ++ ). Here, proxyclass is used to distinguish between L-value and R-value from standard use cases.

Level 1

First implementation:

Template <class proptype>
Class Property
{
PRIVATE:
Property (){}
~ Property (){}

Public:
Operator proptype () // used as R-Value
{
// Call host class's getter Function
Return getter ();
}

Property & operator = (proptype RHs) // used as l-Value
{
// Call host class's setter function
Setter (RHs );
Return * this;
}
};
Operator proptype () can automatically convert property to proptype by implicit conversion, that is, when the value is r-value. Operator = () is called when the property is assigned a value, l-value is used. With these two operators overload, you can achieve Goal 1 and provide sufficient space for Goal 2.

Level 2

But the problem arises immediately:CodeThe bold lines in are just pseudo code. How can we actually call getter and setter?

The most obvious solution is to save the pointer of the hostclass (class holding the property declaration) object and the pointer of the getter and setter functions (member function pointer) in the property and then call it. However, even this obvious implementation has a problem: how to construct a property. Currently, property must pass these three pointers in the constructor. As a result, the constructor of the property class must add the relevant code for constructing the property. (for this problem, STD :: mem_fun_t is actually my first real version )...... Excluding this, the property class is now occupying the memory. Imagine that when a class has a large number of properties, each property stores three pointers to the hostobject ......

We just need to simply redirect the attribute to a getter function and a setter function. To fully squeeze Compiler's labor, think about what can be determined during the compilation phase in the current situation. Two member function pointers are acceptable. But we still need the hostobject pointer. Well, I admit that I didn't solve this problem myself until I read chapter 35th of imperfect C ++ (for reference in this article ). We have the this pointer of the property class and know that the property object is part of the hostobject object. The offset between their respective this pointers is the third "compile-time constant ". The offsetof macro in stddef. h helps us calculate the offset between class and member. In this way, the hostclass *, & hostclass: getter, & hostclass: setter can be derived by adding three compilation constants to the this pointer of the property object.

Level 3

The next step is to use these three constants as template constant parameters to generate our property class. A new problem occurs again. The property template parameter must be declared in the hostclass declaration. The offset cannot be calculated because the hostclass has not been declared yet ...... In any case, you will get a compilation error of hostclass undefined. Imperfect C ++ helps us again: Put the calculated offset into a static member function, and use this function as a template parameter to replace the offset itself, you only need to declare this static member function before declaring the property.

Let's see what we have done so far:

Template <class proptype,
Class prophost,
Proptype (prophost: * getterfn) () const,
Void (prophost: * setterfn) (proptype ),
INT (* propoffset) ()>
Class Property
{
PRIVATE:
Friend prophost;
Property (){};

Public:
Property & operator = (proptype RHs)
{
// offset this pointer to host class pointer
// and call member function
(prophost *) (unsigned char *) this-(* propoffset) ()
-> * setterfn) (RHs);
return * This;
}

Operator proptype () const
{
Return (prophost *) (unsigned char *) This-(* propoffset )())
-> * Getterfn )();
}
};
Pretty good, right? Can complete the function without occupying the memory. More importantly, we can expect these codes to be internally linked to use attributes, thus reducing the additional overhead to 0. Declaring hostclass as a friend can prevent the property from being constructed outside the class (if you have to construct it in a member function of the class, there is no way ......). Compared to the first implementation, I removed the private destructor Declaration on the grounds that since the property cannot be constructed, there is no need to avoid it. The only situation where property is destructed is delete & testobj. testproperty, but the same syntax can also be applied to internal build types (I .e. delete & testobj. intmember), so there is no need to take responsibility for blocking such spam code writing.

Level 4

Let's take a look at how to use this class to declare attributes:

Class testclass
{
PRIVATE:
Int getintvalue () const
{
Return m_value;
}
Void setintvalue (int v)
{
M_value = V;
}
Int m_value;

Static int offset ()
{
Return offsetof (testclass, intvalue );
}
Public:
Property <int,
Testclass,
& Testclass: getintvalue,
& Testclass: setintvalue,
& Testclass: Offset,
> Intvalue;
};

Testclass T;
T. intvalue = 5;
Cout <t. intvalue <Endl; // 5
Everything works.

Do you still remember the design goals at the beginning of this article? We have completed 1, 2, 3 (3 is not complete yet, see below), but 4: The Concise declaration syntax is obviously not implemented yet.

Macros can help us achieve this goal:

# Define declare_property (prophost, modifier, proptype, propname, Getter, setter )"
PRIVATE: static int __## propname ### _ offset (){"
Return offsetof (prophost, propname );}"
Modifier: Property <proptype ,"
Prophost ,"
& Prophost ::__ ## propname ##_ offset ,"
& Prophost: setter ,"
& Prophost: getter ,"
> Propname
The modifier parameter is an access modifier, and other meanings are clear. With this macro, it is easy to declare attributes in the class:

Declare_property (testclass, public, Int, intvalue, getintvalue, setintvalue );
As you can see, such a statement is still lengthy. We hope that you do not need to write the hostclass name countless times, or add a public variable. It is best to do this:

Public:
Declare_property (INT, intvalue, getintvalue, setintvalue );
Can this be done? Yes, but there is still a problem to be explained.

Level 5

0 struct/Union/class is not allowed in C ++. If you do not believe it, you can test the following code:

Struct empty {};
Cout <sizeof (empty) <Endl; // 1
Therefore, although our property does not contain any status, the length is never 0. If a class declares a large number of attributes, it is objective to increase the class length. We can use the union structure to minimize this impact.

I think you will soon be able to think of the anonymous union structure, which can be applied to the memory rewards brought about by the Union and can be accessed without additional members. Even better, by declaring a "private" typedef testclass this_class in this anonymous union, you can do this without re-writing the hostclass ......

Unfortunately, this structure is not feasible: The anonymous union structure does not allow protected and private members. The dream of typedef is shattered, and another dream that has been done well is also shattered: In our declare_property, there is a private static member function used to calculate the Offset Value of the attribute, unless we change it to public, otherwise, do not consider compiler. The practice in imperfect C ++ is to place the function declaration for calculating the offset in another macro, so that the user needs to use this macro by himself-feeling that the macro is getting farther away from Target 4.

Let's make a little compromise. What do you think of T. properties. intvalue compared to T. intvalue? If you still want to endure it, let's take a look.

We use named union to replace anonymous union. There are many benefits. The most important thing is that it can have private members. Therefore, we can continue our dream about typedef. In addition, attributes are organized using union, and the offsets of these attributes in the class are the same. You can write only one offset function. In addition, T. properties. the attribute access syntax such as intvalue can remind users that this is only an attribute rather than a real data member, so that they can use the attribute as the reference parameter and pointer parameter of the function.

# Define begin_properties_withname (hostclass, name )"
Union _ properties"
{"
PRIVATE :"
Typedef hostclass _ hostclass ;"
Static int _ offset (){"
Return offsetof (_ hostclass, name );}"
Public:

# Define end_properties_withname (name)} Name

# Define begin_properties (hostclass )"
Begin_properties_withname (hostclass, properties)
# Define end_properties ()"
End_properties_withname (properties)

# define property_readwrite (proptype, propname, Getter, setter) "
struct {property _ hostclass, "
&__ hostclass: getter,"
&__ hostclass: setter, "
&__ hostclass :__ properties :: __offset "
propname;}; because the hostclass holding the attribute changes from prophost to prophost ::__ properties, the membership declaration in the property also needs to be modified accordingly.

Note that I used an anonymous struct structure in property_readwrite to enclose the property. The reason is that C ++ has another limit on Union: Union members cannot have Default constructors or other uncommon constructors. The general reason is that union does not know which member to construct, but cannot ensure that the default constructor of the member is called correctly. Adding this can bypass this restriction. I think this may be a bug (in Visual Studio 2008), but fortunately our property does not need constructors.

Now, declaring attributes in a class becomes intuitive:

Class testclassa
{
Public:
Explicit testclassa (int v): m_value (v ){}

PRIVATE:
Int getintvalue () const
{
STD: cout <"testclassa: getintvalue ()" <STD: Endl;
Return m_value;
}
Void setintvalue (INT value)
{
STD: cout <"testclassa: setintvalue ()" <STD: Endl;
M_value = value;
}
Int m_value;

Public:
Begin_properties (testclassa)
Property_readwrite (INT, intvalue, getintvalue, setintvalue)
End_properties ();
};
Level 6

The first four design objectives are basically well achieved. The last one is left. I want to say: let's not discuss static for the moment.

The implementation of read-only attributes is not much different from that of completed properties. I will list the two problems I encountered during my own implementation:

1. my most primitive idea is to use the template to be specific and make typedef property <proptype, prophost, getterfn, setterfn, offsetfn> Based on property <proptype, prophost, getterfn, null, offsetfn> readonlyproperty. Unfortunately, C ++ once again blocks my attempt because the type of the special constant parameter cannot be dependent. Our setter type void (prophost: * setterfn) (proptype) exactly depends on prophost and proptype ...... Therefore, only one new readonlyproperty can be rewritten, and operator = is removed from the property to implement read-only access;

2. The default operator = mechanism of c ++ allows readonlyproperty to be used as follows:

T. properties. readonlyvalue = T. properties. readonlyvalue;
We certainly cannot allow this situation. Therefore, you can explicitly disable it in the readonlyproperty declaration:

PRIVATE:
Readonlyproperty & operator = (const readonlyproperty &);
You do not need to define it (to define it as {}): because it has never been used (it has been disabled ). The problem is to put readonlyproperty in union. The Union of C ++ not only does not allow default constructor, but also does not support operator =. However, we have bypassed this issue through anonymous struct.

Level 7

There are two more questions.

What if the attribute type is not a built-in type but a struct, pointer, or class?

The solution is not to solve the problem. There is nothing to worry about. directly pass the reference type or pointer type to the property template. The specific behavior is not property, but the getter and setter functions you use. However, it is inconvenient to use this method. Specifically, the const modifier applies to the signatures of the getter and setter functions. You can write the corresponding const version of the property class to meet your needs.

Another point. See the following code:

// Inside a class declaration...
PRIVATE:
Struct point
{
Int X;
Int y;
};

Public:
Begin_properties (line)
Property_readwrite (point &, start, getstart, setstart)
Property_readwrite (point &, end, getend, setend)
End_properties ();
// Continue class declaration...
Then, how to use point: X or point: Y becomes a problem. We can only write as follows:

Int startx = static_cast <point &> (topline. properties. Start). X;
In any case, this is annoying.

You can add operator * to the property so that it returns the True Type of this property: after all, operator * means the value.

Int startx = (* topline. properties. Start). X;
In addition, operator * makes sense for built-in types. See the following code:

Float getavg (float X1, float x2)
{
Return (X1 + x2) * 0.5f;
}
Float AVG = getavg (* T. properties. intvalue, * U. properties. intvalue );

Without operator *, such implicit conversion (INT-> float) is impossible.

 

original address: http://xiaolingyao.spaces.live.com/blog/cns! 6a8f02d95d2dde46! 201. Entry

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.