composite.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /* Boost.Flyweight example of a composite design.
  2. *
  3. * Copyright 2006-2014 Joaquin M Lopez Munoz.
  4. * Distributed under the Boost Software License, Version 1.0.
  5. * (See accompanying file LICENSE_1_0.txt or copy at
  6. * http://www.boost.org/LICENSE_1_0.txt)
  7. *
  8. * See http://www.boost.org/libs/flyweight for library home page.
  9. */
  10. #include <boost/flyweight.hpp>
  11. #include <boost/functional/hash.hpp>
  12. #include <boost/tokenizer.hpp>
  13. #include <boost/variant.hpp>
  14. #include <boost/variant/apply_visitor.hpp>
  15. #include <boost/variant/recursive_wrapper.hpp>
  16. #include <iostream>
  17. #include <stdexcept>
  18. #include <string>
  19. #include <vector>
  20. using namespace boost::flyweights;
  21. /* A node of a lisp-like list can be modeled as a boost::variant of
  22. * 1. A string (primitive node)
  23. * 2. A vector of nodes (embedded list)
  24. * To save space, 2 is stored as a vector of flyweights.
  25. * As is usual with recursive data structures, a node can be thought
  26. * of also as a list. To close the flyweight circle, the final
  27. * type list is a flyweight wrapper, so that the final structure can
  28. * be described as follows in BNF-like style:
  29. *
  30. * list ::= flyweight<list_impl>
  31. * list_impl ::= std::string | std::vector<list>
  32. */
  33. struct list_elems;
  34. typedef boost::variant<
  35. std::string,
  36. boost::recursive_wrapper<list_elems>
  37. > list_impl;
  38. struct list_elems:std::vector<flyweight<list_impl> >{};
  39. typedef flyweight<list_impl> list;
  40. /* list_impl must be hashable to be used by flyweight: If a
  41. * node is a std::string, its hash resolves to that of the string;
  42. * if it is a vector of flyweight nodes, we combine their corresponding
  43. * hash values. As hashing a flyweight does not involve actually hashing
  44. * its pointed-to value, the resulting computation does not recursively
  45. * descend down the entire data structure.
  46. */
  47. struct list_hasher:boost::static_visitor<std::size_t>
  48. {
  49. std::size_t operator()(const std::string& str)const
  50. {
  51. boost::hash<std::string> h;
  52. return h(str);
  53. }
  54. std::size_t operator()(const list_elems& elms)const
  55. {
  56. std::size_t res=0;
  57. for(list_elems::const_iterator it=elms.begin(),it_end=elms.end();
  58. it!=it_end;++it){
  59. boost::hash_combine(res,*it);
  60. }
  61. return res;
  62. }
  63. };
  64. #if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)
  65. namespace boost{
  66. #endif
  67. std::size_t hash_value(const list_impl& limpl)
  68. {
  69. return boost::apply_visitor(list_hasher(),limpl);
  70. }
  71. #if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)
  72. } /* namespace boost */
  73. #endif
  74. /* basic pretty printer with indentation according to the nesting level */
  75. struct list_pretty_printer:boost::static_visitor<>
  76. {
  77. list_pretty_printer():nest(0){}
  78. void operator()(const std::string& str)
  79. {
  80. indent();
  81. std::cout<<str<<"\n";
  82. }
  83. void operator()(const boost::recursive_wrapper<list_elems>& elmsw)
  84. {
  85. indent();
  86. std::cout<<"(\n";
  87. ++nest;
  88. const list_elems& elms=elmsw.get();
  89. for(list_elems::const_iterator it=elms.begin(),it_end=elms.end();
  90. it!=it_end;++it){
  91. boost::apply_visitor(*this,it->get());
  92. }
  93. --nest;
  94. indent();
  95. std::cout<<")\n";
  96. }
  97. private:
  98. void indent()const
  99. {
  100. for(int i=nest;i--;)std::cout<<" ";
  101. }
  102. int nest;
  103. };
  104. void pretty_print(const list& l)
  105. {
  106. list_pretty_printer pp;
  107. boost::apply_visitor(pp,l.get());
  108. }
  109. /* list parser */
  110. template<typename InputIterator>
  111. list parse_list(InputIterator& first,InputIterator last,int nest)
  112. {
  113. list_elems elms;
  114. while(first!=last){
  115. std::string str=*first++;
  116. if(str=="("){
  117. elms.push_back(parse_list(first,last,nest+1));
  118. }
  119. else if(str==")"){
  120. if(nest==0)throw std::runtime_error("unmatched )");
  121. return list(elms);
  122. }
  123. else{
  124. elms.push_back(list(str));
  125. }
  126. }
  127. if(nest!=0)throw std::runtime_error("unmatched (");
  128. return list(elms);
  129. }
  130. list parse_list(const std::string str)
  131. {
  132. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  133. tokenizer tok(str,boost::char_separator<char>(" ","()"));
  134. tokenizer::iterator begin=tok.begin();
  135. return parse_list(begin,tok.end(),0);
  136. }
  137. int main()
  138. {
  139. std::cout<<"enter list: ";
  140. std::string str;
  141. std::getline(std::cin,str);
  142. try{
  143. pretty_print(parse_list(str));
  144. }
  145. catch(const std::exception& e){
  146. std::cout<<"error: "<<e.what()<<"\n";
  147. }
  148. return 0;
  149. }