O''''Reilly Network For Information About''''s Book part 70 potx

6 235 0
O''''Reilly Network For Information About''''s Book part 70 potx

Đang tải... (xem toàn văn)

Thông tin tài liệu

public: void operator()(int& i) const { std::cout << "It's an int: " << i << '\n'; } void operator()(std::string& s) const { std::cout << "It's a std::string: " << s << '\n'; } void operator()(double& d) const { std::cout << "It's a double: " << d << '\n'; } }; If we compile the example again, the compiler will be really upset, saying something like this: c:/boost_cvs/boost/boost/variant/variant.hpp: In member function `typename Visitor::result_type boost::detail:: variant:: invoke_visitor<Visitor>::internal_visit(T&, int) [with T = char, Visitor = print_visitor]': [Snipped lines of irrelevant information here] c:/boost_cvs/boost/boost/variant/variant.hpp:807: error: no match for call to `(print_visitor) (char&)' variant_sample1.cpp:40: error: candidates are: void print_visitor::operator()(int&) const variant_sample1.cpp:44: error: void print_visitor::operator()(std::string&) const variant_sample1.cpp:48: error: void print_visitor::operator()(double&) const This error pinpoints the problem: There is no candidate function for char arguments! That's one important reason why typesafe compile time visitation is such a powerful mechanism. It makes the visitation robust with regard to types, and avoids the tedium of type-switching. Creating visitors is just as easy as creating other function objects, so the learning curve here isn't very steep. When the set of types in the variants may change (they tend to do that!), creating visitor classes is much more robust than relying solely on get. There is a higher initial cost, but it's typically worth it for non-trivial uses. Generic Visitors By using the visitor mechanism and a parameterized function call operator, it's possible to create generic visitors that are capable of accepting values of any type (that can syntactically and semantically handle whatever the generic function call operator implementation requires). This is very useful for treating disparate types uniformly. Typical examples of "universal" features are the C++ operators, such as arithmetic and IOStreams shift operators. The following example uses operator<< to print the variant values to a stream. #include <iostream> #include <sstream> #include <string> #include <sstream> #include "boost/variant.hpp" class stream_output_visitor : public boost::static_visitor<void> { std::ostream& os_; public: stream_output_visitor(std::ostream& os) : os_(os) {} template <typename T> void operator()(T& t) const { os_ << t << '\n'; } }; int main() { boost::variant<int,std::string> var; var=100; boost::apply_visitor(stream_output_visitor(std::cout),var); var="One hundred"; boost::apply_visitor(stream_output_visitor(std::cout),var); } The idea is that the member function template for the function call operator in stream_output_visitor will be instantiated once for each type visited (int and std::string, in this case). Because std::cout << 100 and std::cout << std::string("One hundred") are both well defined, the code compiles and works flawlessly. Of course, operators are just one example of what could be used in a generic visitor; they simply happen to apply to a great many types. When calling functions on the values, or passing them as arguments to other functions, the requirements are that the member function exists for all types being passed to the operator, and that there are suitable overloads for the functions being called. Another interesting aspect of this parameterized function call operator is specializing the behavior for some types, but still allowing a generic implementation to be available for the rest of the types. In other words, you create overloaded function call operators for some types and rely on the member function template for the rest. This is, in a sense, related to template specialization, where behavior is specialized based on type information. Binary Visitors The visitors that we've seen so far have all been unarythat is, they accept one variant as their sole argument. Binary visitors accept two (possibly different) variants. This concept is, among other things, useful for implementing relations between variants. As an example, we shall create a lexicographic sort order for variant types. To do so, we'll use an enormously useful component from the Standard Library: std::ostringstream. It will take anything OutputStreamable and, on demand, produce a std::string out of it. We can thus lexically compare fundamentally different variant types, assuming that all of the bound types support streaming. Just as with regular visitors, binary visitors should derive publicly from boost::static_visitor, and the template parameter denotes the return type of the function call operator(s). Because we are creating a predicate, the return type is bool. Here, then, is the binary predicate, which we shall put to use shortly. class lexicographical_visitor : public boost::static_visitor<bool> { public: template <typename LHS,typename RHS> bool operator()(const LHS& lhs,const RHS& rhs) const { return get_string(lhs)<get_string(rhs); } private: template <typename T> static std::string get_string(const T& t) { std::ostringstream s; s << t; return s.str(); } static const std::string& get_string(const std::string& s) { return s; } }; The function call operator is parameterized on both of its arguments, which means that it accepts any combination of two types. The requirements for the set of possible types in the variants is that they be OutputStreamable. The member function template get_string uses a std::ostringstream to convert its argument to its string representationhence the OutputStreamable requirement. (To use std::ostringstream, remember to include the header <sstream>.) The member function get_string simply accounts for the fact that a value of type std::string is already of the required type and so it skips the trip through std::ostringstream and just returns its argument. After the two arguments have been converted to std::string, all that remains is to compare them, which we do using operator<. Now let's put this visitor to the test by sorting the elements of a container using its services (we'll also reuse the stream_output_visitor that we created earlier in this chapter). #include <iostream> #include <string> #include <vector> #include <algorithm> #include "boost/variant.hpp" int main() { boost::variant<int,std::string> var1="100"; boost::variant<double> var2=99.99; std::cout << "var1<var2: " << boost::apply_visitor( lexicographical_visitor(),var1,var2) << '\n'; typedef std::vector< boost::variant<int,std::string,double> > vec_type; vec_type vec; vec.push_back("Hello"); vec.push_back(12); vec.push_back(1.12); vec.push_back("0"); stream_output_visitor sv(std::cout); std::for_each(vec.begin(),vec.end(),sv); lexicographical_visitor lv; std::sort(vec.begin(),vec.end(),boost::apply_visitor(lv)); std::cout << '\n'; std::for_each(vec.begin(),vec.end(),sv); }; First of all, we apply the visitor to two variants, var1 and var2, like so: boost::apply_visitor(lexicographical_visitor(),var1,var2) As you can see, the difference from the unary visitors is that two variants are passed to the function apply_visitor. A more common example of usage is to use the predicate for sorting the elements, which we do like this: lexicographical_visitor lv; std::sort(vec.begin(),vec.end(),boost::apply_visitor(lv)); When the sort algorithm is invoked, it compares its elements using the predicate that we pass to it, which is an instance of lexicographical_visitor. Note that boost::variant already defines operator<, so it's possible to simply sort the container without our predicate. std::sort(vec.begin(),vec.end()); But the default sort order, which first checks the current value index via which, arranges the elements in the order 12, 0, Hello, 1.12, and we wanted a lexicographical order. Because both operator< and operator== is provided for the variant class, variants can be used as the element type of all Standard Library containers. When these default relations aren't enough, implement the ones you need with binary visitors. There Is More to Know We haven't covered all of the functionality of the Boost.Variant library. The remaining advanced features are not needed as often as those we have explored. However, I'll mention them briefly, so you will at least know what's available should you find that you need them. The macro, BOOST_VARIANT_ENUM_PARAMS, is useful when overloading/specializing functions and class templates for variant types. The macro helps by enumerating the set of types that the variant can contain. There is support for creating variant types using type sequencesthat is, compile-time lists that denote the set of types for the variant, through make_variant_over. Recursive variant types, which are useful for creating expressions that are themselves variant types, are available using recursive_wrapper, make_recursive_variant, and make_recursive_variant_over. If you need these additional features, the online documentation does an excellent job . print_visitor]': [Snipped lines of irrelevant information here] c:/boost_cvs/boost/boost/variant/variant.hpp:807: error: no match for call to `(print_visitor) (char&)' variant_sample1.cpp:40:. the behavior for some types, but still allowing a generic implementation to be available for the rest of the types. In other words, you create overloaded function call operators for some types. rely on the member function template for the rest. This is, in a sense, related to template specialization, where behavior is specialized based on type information. Binary Visitors The visitors

Ngày đăng: 07/07/2014, 08:20

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan