Wide
Before entering today's theme, there are some news you may be interested in news. Not long ago, Jonathan, H, Lundquist and Mat Marcus were compatible with Visual C 6 each other. Their implementation is an experiment on a certain idea and has not yet achieved a perfect point. In general, due to various compilers, in the present, Loki's role is the source of people's inspiration, rather than as a "packaged, retractable" product can be directly placed in your program. . Typical Loki users are a brave developer. He will not hide the table under the wrong message - even when the error message is enough to allow the network server "buffer overflow". However, this situation is changing when you read this article. When Rani Sharoni wrote to me a complete Loki for Visual C 7.0, I was not surprised. In addition to several places, the syntax used by the library remains unchanged, which is obvious because Visual C 7.0 (Visual C . Net) does not support partial template. If you use a compiler, maybe you also want to put the Loki to the platform you work [1]. (Translation: This article was published in August 2002. The current Loki is already compatible with VC6, VC7 and BCBs) Other related news - now on adding Loki to Boost, this move can be done Work is concentrated. Loki :: smartptr Submit work is already in the hands; just with a lot of discussion with a smart pointer, it also brings an unprecedented work efficiency. at the same time. Joe Swatosh has already submitted a very popular ScopeGuard [2], which is actually done by Petru Marinean and everyone will be included in Boost. In short, it is now the era of C rapid development. In the end, this means you can spend less strength and put more energy into a high-level design.
Variant: The work to be completed is in the first two parts of "return programming", we define the core part of the Variant class that can be identified and defined by the Variant class. This core class allows users to store an object in Variant by providing the main basic capabilities, and obtains that object in the object type in Variant, and the type of security. In addition, Variant is universal and very efficient. But these features are not enough to be excited. Of course, compatibility is good (but if you use old, incompatible compilers, what is used in light Variant's common use?) The same, it is definitely no harm. But light is not enough - when you write code for it, Variant's versatility and high efficiency cannot provide more features. The focus is, more features will be very helpful. Today we focus on adding some powerful features for Variant - some have never appeared in a similar implementation. But before this, let's talk about a few questions.
In the end, this still happened - and it will still happen again in life. No matter how you work, you can't please everyone. This is why, since "Modern C Design" has a bookstope of the bookstore. I have been waiting for comments similar to "your book", there is no doubt, or I can wait early or later. The sword of Dhakeley is a year in my head, and it is ultimately a book review on Amazon. falling down. In the comment, Modern C Design is "typical unrealistic, fake inner line, too theoretical things, is the people who do not have practical work experience and must be written for C Report every month watch". This sentence contains me - as author, and yourself, because you are looking at. Maybe we should stop right away. But maybe there is no need to stop. I have wanted to see the "Your Book" comment on the actual content; this comment is too easy to refute. The first thing, this book is based entirely on the author's C actual work every day - and puts many of the concepts in the actual work in the book. The second thing - this is why I want to organize all things - I really didn't feel any pressure after I wrote a "generic
// Database field, can include a string, a whole, or a double-precision floating point type typef variant
Assert (fld2.typeid () == TypeId (int));
You can access the actual type of type in Variant:
String * p fld1.getptr
Variant has a traditional constructor and performs cleanup when calling a destructive function.
// Default constructor, initialize the default value - // to construct the object DatabaseField Fld5; assert (fld5.gettype () == typeid (String)); assrt (Fld5.getptr); String> () -> EMPTY ()); DatabaseField FLD4 = FLD1; // Copy Construction Assert (FLD4.GETTYPE () == TypeId (String)); Assert (* fld4.getptr "> () ==" Hello , World! ");
Ok, now we will implement some important functional functions. First, we solve the value of the type of VARIANT. You also see it, getPtr is good, but it is not enough to provide sufficient features based on its method. check it out:
Void Processfield (const datafield & fld) {if (const string * ps = fld.getptr
Visitation How do we implement these functions without using Variants to perform disgusting types? A solution is to add more functions in a Variant's analog VTABLE. , Just like TypeID, Clone, etc. This can be achieved, but it will destroy the characteristics of Variant's and the application. What we want is universal, scalable variant, not something to modify each time. You will not want to inherit the variant; it is a value of a value, not a reference type (Reference Type). Access is a more suitable technology here. Visitor [4] is a design pattern that allows you to add a new action in a class level without modifying this class level. How does access work? Complete explanation exceeds the scope of this article, but if you are not familiar, it is highly recommended that you look at visitors in [4]. Visitor model is applicable in many interesting places, and at least let you see that some types of levels do not have access to accessibility is a serious design error [5]. In a Variant case, the class level is a collection of stored types. They did not form a class level, but our existing simulated vTable allows us to define polymorphisms based on types, and these types themselves. (They can even be a basic type, as shown in the code instance above.) By the way, if you are interested in this topic, but don't understand "simulation vtable", don't forget this article based on the first two [ 6,7]. In order to let Variant can be accessed, we need to perform the following steps: * Define a Basevisitor class for the Variant class. In the case of DatabaseField, the basevisitor class is like this: struct basevisitor {Virtual void visit (string &) = 0; Virtual Void ViIT (INT &) = 0; Virtual Void Visit (Double &) = 0;}; * In analog virtual function table Define the Accept function in the implementation, just in the previous article [7] ... in Variant ... Template
... in Variant ... Template
This brings us two benefits. First, you can now spread the member function of ProcessDatabaseField into each compilation unit (may specifically process string operations or digital operations separately). Second, if you add a new type to the definition of DatabaseField, for example: Typedef Variant
Template
Access adjustments are now undergoing basic concepts, we can do some improvements. For example, if the access and constant correctness (const-correctness) is mixed. But Basevisitor :: Visit function is a very square parameter. This means that the following code cannot work:
Struct Street :: strictvisitor {virtual void visit (string & s) {cout << s;} Virtual Void Visit (int & i) {cout << i;} Virtual Void Visit (Double & D) {cout << D; }}; const DatabaseField FLD (4.5); // As a constant streamer str; // error! There is no overloading streamer :: Visit version // accepts const double & fld.accept (str); from the question return, we find the root of trouble. ... in Variant ... Template
The above Accept function accepts a very quite Variant object as the first parameter. Bright words (I believe it is an ancient Greeks): "When a vTableImpl
template
The key to MakeConst is that the HEAD type is converted to Const Head when returning the Result type. With Makeconst, we define a new visitorship class, just like this: Template
So until now, how can the STRICT prefix in front of all the visitor names we define? It corresponds to strict access, there is any "non-strict" access. Consider the following requirements: if a DatabaseField is a string, it needs to be placed in square brackets [like this]. Not a string, no processing. The implementation is:
Struct Transformer :: strictvisitor {virtual void visit (string & s) {s = '[' s ']';} Virtual Void Visit (INT & I)} Virtual Void Viisit (Double & D) {// does not work}}};
In this case, although Transformer does not need to access IN and Double, it still needs to define empty Visit (INT &) and Visit (Double &) overload functions. The visitor implementation is strict: all functions must be implemented, the compiler ensures this. For the situation like Transformer, when we only need the subset of type collections, non-strict access is a method to achieve the purpose. In this way, Variant adds two types definitions: a non-harder access to the Variant object of very quantity and constant.
Template
Further improvement in the access mechanism for Variant may include a custom return type. (Now all access functions returns VOID)
The loose end is here to summarize our Variant implementation. Once you have access, you can add any features you want to increase in Variant, call the cost as the price (instead). This is to make Variant's consideration. Fortunately, this solution can expand vertically. (The overhead does not increase as the type of string of Variant is increased). The important feature of the available Variant implementation is to convert its value to another, or change the storage type in place. For example, consider the following code: DatabaseField FLD (45); assert (fld typeid () == typeid (int)); double d = fld.convertto
If it is now stored in the type of Variant object (which is int) can be converted to the required type (in this example is a double), the conversion is performed. Otherwise, ConvertTo throws an accident. One of the above changes is the type of FLD CHANGETYPE