flyweight/example/composite.cpp
2014-10-15 21:53:19 +02:00

175 lines
4.2 KiB
C++

/* Boost.Flyweight example of a composite design.
*
* Copyright 2006-2014 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*
* See http://www.boost.org/libs/flyweight for library home page.
*/
#include <boost/flyweight.hpp>
#include <boost/functional/hash.hpp>
#include <boost/tokenizer.hpp>
#include <boost/variant.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
using namespace boost::flyweights;
/* A node of a lisp-like list can be modeled as a boost::variant of
* 1. A string (primitive node)
* 2. A vector of nodes (embedded list)
* To save space, 2 is stored as a vector of flyweights.
* As is usual with recursive data structures, a node can be thought
* of also as a list. To close the flyweight circle, the final
* type list is a flyweight wrapper, so that the final structure can
* be described as follows in BNF-like style:
*
* list ::= flyweight<list_impl>
* list_impl ::= std::string | std::vector<list>
*/
struct list_elems;
typedef boost::variant<
std::string,
boost::recursive_wrapper<list_elems>
> list_impl;
struct list_elems:std::vector<flyweight<list_impl> >{};
typedef flyweight<list_impl> list;
/* list_impl must be hashable to be used by flyweight: If a
* node is a std::string, its hash resolves to that of the string;
* if it is a vector of flyweight nodes, we combine their corresponding
* hash values. As hashing a flyweight does not involve actually hashing
* its pointed-to value, the resulting computation does not recursively
* descend down the entire data structure.
*/
struct list_hasher:boost::static_visitor<std::size_t>
{
std::size_t operator()(const std::string& str)const
{
boost::hash<std::string> h;
return h(str);
}
std::size_t operator()(const list_elems& elms)const
{
std::size_t res=0;
for(list_elems::const_iterator it=elms.begin(),it_end=elms.end();
it!=it_end;++it){
boost::hash_combine(res,*it);
}
return res;
}
};
#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)
namespace boost{
#endif
std::size_t hash_value(const list_impl& limpl)
{
return boost::apply_visitor(list_hasher(),limpl);
}
#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)
} /* namespace boost */
#endif
/* basic pretty printer with indentation according to the nesting level */
struct list_pretty_printer:boost::static_visitor<>
{
list_pretty_printer():nest(0){}
void operator()(const std::string& str)
{
indent();
std::cout<<str<<"\n";
}
void operator()(const boost::recursive_wrapper<list_elems>& elmsw)
{
indent();
std::cout<<"(\n";
++nest;
const list_elems& elms=elmsw.get();
for(list_elems::const_iterator it=elms.begin(),it_end=elms.end();
it!=it_end;++it){
boost::apply_visitor(*this,it->get());
}
--nest;
indent();
std::cout<<")\n";
}
private:
void indent()const
{
for(int i=nest;i--;)std::cout<<" ";
}
int nest;
};
void pretty_print(const list& l)
{
list_pretty_printer pp;
boost::apply_visitor(pp,l.get());
}
/* list parser */
template<typename InputIterator>
list parse_list(InputIterator& first,InputIterator last,int nest)
{
list_elems elms;
while(first!=last){
std::string str=*first++;
if(str=="("){
elms.push_back(parse_list(first,last,nest+1));
}
else if(str==")"){
if(nest==0)throw std::runtime_error("unmatched )");
return list(elms);
}
else{
elms.push_back(list(str));
}
}
if(nest!=0)throw std::runtime_error("unmatched (");
return list(elms);
}
list parse_list(const std::string str)
{
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
tokenizer tok(str,boost::char_separator<char>(" ","()"));
tokenizer::iterator begin=tok.begin();
return parse_list(begin,tok.end(),0);
}
int main()
{
std::cout<<"enter list: ";
std::string str;
std::getline(std::cin,str);
try{
pretty_print(parse_list(str));
}
catch(const std::exception& e){
std::cout<<"error: "<<e.what()<<"\n";
}
return 0;
}