Style Case Studies: Construction Unions - Toward a Better Way: boost::any
(Page 8 of 9 )
4. Show a better way to achieve a generalized variant type, and comment on any tradeoffs you encounter.
The original article says:
[Y]ou might want to implement a scripting language with a single variable type that can either be an integer, a string, or a list.—[Manley02]
This is true, and there’s no disagreement so far. But the article then continues:
A union is the perfect candidate for implementing such a composite type.—[Manley02]
Rather, the article has served to show in some considerable detail just why a union is not suitable at all.
But if not a union, then what? One very good candidate for implementing such a variant type is [Boost]’s any facility, along with its many and any_cast. Interestingly, the complete implementation for the fully general any (covering any number/combination of types and even some platform-specific #ifdefs) is about the same amount of code as the sample MYUNION solution hardwired for just the special case of the three types int, list<int>, and string—and any is fully general, extensible, and type-safe to boot, and part of a healthy low-cholesterol diet.
There is still a tradeoff, however, and it is this: dynamic allocation. The boost::any facility does not attempt to achieve the potential efficiency gain of avoiding a dynamic memory allocation, which was part of the motivation in the original article. Note too that the boost::any dynamic allocation overhead is more than if the original article’s code was just modified to use (and reuse) a single dynamically allocated buffer that’s acquired once for the lifetime of MYUNION, because boost::any also performs a dynamic allocation every time the contained type is changed.
Here’s how the article’s demo harness would look if it instead used boost::any. The old code that uses the original article’s version of MYUNION is shown in comments for comparison:
any u; // instead of: MYUNION u;
Instead of a handwritten struct, which has to be written again for each use, just use any directly. Note that any is a plain class, not a template.
// access union as integer
u = 12345; // instead of: u.getint() = 12345;
The assignment shows any’s more natural syntax.
cout << “int=“ << any_cast<int>(u) << endl; // or just int(u)
// instead of: cout << “int=“ << u.getint() << endl;
I like any’s cast form better because it’s more general (including that it is a nonmember) and more natural to C++ style; you could also use the less verbose int(u) without an any_cast if you know the type already. On the other hand, MYUNION’s get[type] is more fragile, harder to write and maintain, and so forth.
// access union as std::list
u = list<int>();
list<int>& l = *any_cast<list<int> >(&u); // instead of: LIST& list = u.getlist();
l.push_back(5); // same: list.push_back(5);
l.push_back(10); // same: list.push_back(10);
l.push_back(15); // same: list.push_back(15);
I think any_cast could be improved to make it easier to get references, but this isn’t too bad. (Aside: I’d discourage using list as a variable name when it’s also the name of a template in scope; too much room for expression ambiguity.)
So far we’ve achieved some typability and readability savings. The remaining differences are more minor:
list<int>::iterator it = l.begin(); // instead of: LIST::iterator it = list.begin();
while( it != l.end() ) {
cout << “list item=“ << *(it) << endl;
it++;
} // while
Pretty much unchanged. I’ve let the original comment stand because it’s not germane to the side-by-side style comparison with any.
// access union as std::string
u = string(“Hello world!”); // instead of: STRING& str = u.getstring();
// str = “Hello world!”;
Again, about a wash; I’d say the any version is slightly simpler than the original, but only slightly.
cout << “string=‘“ << any_cast<string>(u) << “‘“ << endl; // or just “string(u)”
// instead of: cout << “string=‘“ << str.c_str() << “‘“ << endl;
As before.
This chapter is from Exceptional C++ Style, by Herb Sutter (ISBN 0201760428, copyright 2005. All rights reserved. It is reprinted with permission from Addison-Wesley Professional). Check it out at your favorite bookstore today.
Buy this book now. |
Next: Alexandrescu’s Discriminated Unions >>
More Code Examples Articles
More By Addison-Wesley/Prentice Hall PTR