BOOST source code analysis: generic pointer ANY 海 百 川 (REV # 2)
Liu Weipeng
C Louvre (http://blog.9cbs.net/pongba)
motivation
C is a strong type language. All strong types of language are harsh, and the type is not complaining that you can't convert a type of type to a certain type. Of course, if you provide a conversion operation between the type The symbol or a certain degree of implicit conversion allowed by the standard (if the implicit conversion of a temporary variable through the non-Explicit constructor or the basic type between INT, long) is another matter. In general, in order to maintain type safety, C has severe requirements. However, sometimes the programmer may have this:
INT I;
Iong J;
X x; // hypothesize X for the user-defined class
Any anyval = i;
... // Use Anyval As a Int Value
Anyval = j;
... // Use anyval as a long value
Anyval = x;
... // Use anyval as a long value
Consider such a "generic pointer" how to design is a very interesting thing.
1. It itself can't be a template class, because if it is a template, you must provide template parameters for it. And in fact you don't want to do this. You want the same object to accept any type of data. This object is AnyVal in the code above. However, if you have to provide template parameters for it, then the above code looks like this:
Any
Any
...
This obviously has lost an advantage of AnyVal - accepts all types of data in a single object. It's better to write directly to it:
INT AnyintVal = i;
INT Anylongval = J;
Therefore, ANY cannot be a template class.
2. It must provide some information about the type of object it saved.
3. It must provide some way to "take it out".
In fact, the Boost library has provided such a class boost :: Any, I will tell you the principles and constructs for you.
Boost :: Any principle and structure
First, you must provide a template constructor and template Operator = operator. Because you must allow users to write:
Any any_value (val); // VAL type is any
Any_Value = VAL1; // VAL1 type is also arbitrary
Such a code.
Second, the data storage is a problem. Data should be dynamically stored, that is, the container of the data is dynamically allocated to store the data, and the pointer to this container is stored in the ANY class, which is clearly indicated by the pointer to the base class of this container, because the container must be template And the pointer members in the ANY class must not be generic (because all the ANY can't be generic, all data members in anyne cannot be generic), so the conclusion is: preparing a non-flooding for the container Base class, let the pointer points to the base class.
Let's take a look at how the Boost library is specific to these two points.
Excerpted from "boost / ann.hpp"
Class Any
{
PUBLIC:
Class PlaceHolder // Pan-type data container Holder non-float base class
{
PUBLIC:
// The false prevention function, to ensure that the derived class object can use the base class
Virtual ~ placeholder () {}
PUBLIC:
/ / Provide information about type
Virtual const st :: type_info & type () const = 0; Virtual PlaceHolder * Clone () const = 0; // Copy
}; // PlaceHolder
Template
Class Holder: Public PlaceHolder
{
PUBLIC:
Holder (Const ValueType & Value)
: HELD (Value)
{}
PUBLIC:
Virtual const st :: type_info & type () const
{
// TypeId Returns the Std :: TypeInfo object reference, the latter contains the type information of any object, such as Name, and an Operator == operator You can use TypeId (OneObj) == TypeID (Anotherobj) to more than two objects Whether the type is consistent.
Return TypeId (ValyEType);
}
Virtual PlaceHolder * Clone () Const
{
Return New Holder (HELD); // Remove the virtual function, return your own replica
}
PUBLIC:
ValueType HELD; / / Data Saved Place
}; // Holder
// Pointer to the base class of the generic data container HOLDER
Placeholder * Content;
// Template constructor, dynamically allocate data container and call its constructor
Template
ANY (Const ValueType & Value)
: Content (New Holder
{}
...
/ / Is the same as the template constructor, but use SWAP idiom
Template
Any & Operator = (Const ValueType & RHS)
{
// Create a temporary object ANY (RHS), then call the following SWAP function for underlying data exchange, pay attention to the temporary object with * this exchange data, so the underlying data of RHS is not changed, just after the SWAP ends. The object has the underlying data of * this, while * this also has a copy of the data of the RHS owned when the temporary object constructed. The temporary object is then automatically destructed due to the end of the lifetime, * The original underlying data with the smoking.
ANY (RHS) .swap (* this);
RETURN * THIS;
}
Any & Swap (any & rhs) // swap function, exchange underlayer data
{
Std :: swap (content, rhs.content); // only simply divides the value of the two pointers
RETURN * THIS;
}
~ any () // destructor
{
// Release the container, use the base class pointer, which is the reason for PlaceHold needs a false prevention function.
DELETE Content;
}
...
}
This is not all source code, but all important ideas have been revealed. The remaining part is just some simple details, please refer to the original file of the Boost library.
"But wait!", You are eager to say: "You lost the type of information." Oh ... Indeed, when you assign a value, you will lose information about the type. Consider the code you may want to write below:
INT i = 10;
Boost :: Anyval = i;
INT j = anyval;
// error, in fact, you want to assign anyval to another INT type variable, which should be allowed in some way, but it is never provide the conversion operator in the Any class, because you don't know if you have anyval in advance. Bearing what type of variable, the conversion operator is not given. When the idea of the conversion operator completely fails, we can only use certain "foreign" explicit conversion operations. Like Static_cast <>. Boost provides any_cast <>, so you can write this:
INT j = any_cast
In fact, the code of any_cast is like this:
Template
ValueType Any_cast (Const Any & Operand)
{
// Call any_cast to the version of the pointer.
Const valuePe * result = any_cast
// If the CAST fails, that is, the actual saved is not valueetype data, an exception is thrown.
IF (! result)
Throw Bad_Any_cast (); // Derived from std :: bad_cast
Return * Result;
}
And any_cast is the version of the pointer:
Template
ValueType * Any_cast (any * operand)
{
// This type of check is very important, followed by more detailed explanation
Return
Operand &&
(Operand-> Type () == TypeId (ValyEType))? // # 1
& stat_cast
: 0; // There is a down type conversion
}
These two Any_cast versions should be well understood. In addition, the type inspection of # 1 in the latter version is also necessary. If this check is not checked, consider the following code:
INT i = 10;
Boost :: Anyval = i;
// If there is no type check, this will be compiled and the running period will not be wrong, but the assignment to D will be very strange.
Double D = any_cast
This will be compiled, and the running period is usually not wrong. Here I explain why this is.
Boost :: Anyval = i; actually pointing the AnyVal.Content pointer to a Holder
// #2
Static_cast
But before, OPERAND-> Content actually points to any :: Holder
Double * Pd = (Double *) & i;
// Behavior is undefined, but usually will not be wrong, but hidden errors are more terrible, the value of Di's D is almost certainly not what you want.
Double D = * Pd;
Using TypeInfo allows us to discover this type when running does not match and promptly throw an exception. However, there is a violation of the intuitive thing to be compiled by the wrong code, and you can't stop it from compile, because Holder
Use Boost :: Any to implement Virtual Template member functions
If you know, there is no Virtual Template function in C . However, sometimes you have this need, Any can satisfy this need to some extent, for example,
Class Base
{
PUBLIC:
Virtual Void Accept (Boost :: Any Andata)
{
...
}
}
Class Derived: Public Base
{
PUBLIC:
Virtual Void Accept (Boost :: Any Andata)
{
...
}
}
Such an Accept function can accept any type of data and is a Virtual function.
Directory (expand "Boost Source Analysis" series of articles)