1 2 // (C) Copyright Tobias Schwinger 3 // 4 // Use modification and distribution are subject to the boost Software License, 5 // Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt). 6 7 //------------------------------------------------------------------------------ 8 // 9 // This example implements a simple batch-style interpreter that is capable of 10 // calling functions previously registered with it. The parameter types of the 11 // functions are used to control the parsing of the input. 12 // 13 // Implementation description 14 // ========================== 15 // 16 // When a function is registered, an 'invoker' template is instantiated with 17 // the function's type. The 'invoker' fetches a value from the 'token_parser' 18 // for each parameter of the function into a tuple and finally invokes the the 19 // function with these values as arguments. The invoker's entrypoint, which 20 // is a function of the callable builtin that describes the function to call and 21 // a reference to the 'token_parser', is partially bound to the registered 22 // function and put into a map so it can be found by name during parsing. 23 24 #include <map> 25 #include <string> 26 #include <stdexcept> 27 28 #include <boost/token_iterator.hpp> 29 #include <boost/token_functions.hpp> 30 31 #include <boost/lexical_cast.hpp> 32 33 #include <boost/bind.hpp> 34 #include <boost/function.hpp> 35 36 #include <boost/type_traits/remove_cv.hpp> 37 #include <boost/type_traits/remove_reference.hpp> 38 39 #include <boost/fusion/include/push_back.hpp> 40 #include <boost/fusion/include/cons.hpp> 41 #include <boost/fusion/include/invoke.hpp> 42 43 #include <boost/mpl/begin.hpp> 44 #include <boost/mpl/end.hpp> 45 #include <boost/mpl/next.hpp> 46 #include <boost/mpl/deref.hpp> 47 48 #include <boost/utility/enable_if.hpp> 49 50 #include <boost/function_types/is_nonmember_callable_builtin.hpp> 51 #include <boost/function_types/parameter_types.hpp> 52 53 namespace example 54 { 55 namespace fusion = boost::fusion; 56 namespace ft = boost::function_types; 57 namespace mpl = boost::mpl; 58 59 class interpreter 60 { 61 class token_parser; 62 typedef boost::function<void(token_parser &)> invoker_function; 63 typedef std::map<std::string, invoker_function> dictionary; 64 65 dictionary map_invokers; 66 public: 67 // Registers a function with the interpreter. 68 template<typename Function> 69 typename boost::enable_if< ft::is_nonmember_callable_builtin<Function> 70 >::type register_function(std::string const & name, Function f); 71 72 // Parse input for functions to call. 73 void parse_input(std::string const & text) const; 74 75 private: 76 template< typename Function 77 , class From = typename mpl::begin< ft::parameter_types<Function> >::type 78 , class To = typename mpl::end< ft::parameter_types<Function> >::type 79 > 80 struct invoker; 81 }; 82 83 class interpreter::token_parser 84 { 85 typedef boost::token_iterator_generator< 86 boost::char_separator<char> >::type token_iterator; 87 88 token_iterator itr_at, itr_to; 89 public: 90 token_parser(token_iterator from,token_iterator to)91 token_parser(token_iterator from, token_iterator to) 92 : itr_at(from), itr_to(to) 93 { } 94 95 private: 96 template<typename T> 97 struct remove_cv_ref 98 : boost::remove_cv< typename boost::remove_reference<T>::type > 99 { }; 100 public: 101 // Returns a token of given type. 102 // We just apply boost::lexical_cast to whitespace separated string tokens 103 // for simplicity. 104 template<typename RequestedType> get()105 typename remove_cv_ref<RequestedType>::type get() 106 { 107 if (! this->has_more_tokens()) 108 throw std::runtime_error("unexpected end of input"); 109 110 try 111 { 112 typedef typename remove_cv_ref<RequestedType>::type result_type; 113 result_type result = boost::lexical_cast 114 <typename remove_cv_ref<result_type>::type>(*this->itr_at); 115 ++this->itr_at; 116 return result; 117 } 118 119 catch (boost::bad_lexical_cast &) 120 { throw std::runtime_error("invalid argument: " + *this->itr_at); } 121 } 122 123 // Any more tokens? has_more_tokens() const124 bool has_more_tokens() const { return this->itr_at != this->itr_to; } 125 }; 126 127 template<typename Function, class From, class To> 128 struct interpreter::invoker 129 { 130 // add an argument to a Fusion cons-list for each parameter type 131 template<typename Args> 132 static inline applyexample::interpreter::invoker133 void apply(Function func, token_parser & parser, Args const & args) 134 { 135 typedef typename mpl::deref<From>::type arg_type; 136 typedef typename mpl::next<From>::type next_iter_type; 137 138 interpreter::invoker<Function, next_iter_type, To>::apply 139 ( func, parser, fusion::push_back(args, parser.get<arg_type>()) ); 140 } 141 }; 142 143 template<typename Function, class To> 144 struct interpreter::invoker<Function,To,To> 145 { 146 // the argument list is complete, now call the function 147 template<typename Args> 148 static inline applyexample::interpreter::invoker149 void apply(Function func, token_parser &, Args const & args) 150 { 151 fusion::invoke(func,args); 152 } 153 }; 154 155 template<typename Function> 156 typename boost::enable_if< ft::is_nonmember_callable_builtin<Function> >::type register_function(std::string const & name,Function f)157 interpreter::register_function(std::string const & name, Function f) 158 { 159 // instantiate and store the invoker by name 160 this->map_invokers[name] = boost::bind( 161 & invoker<Function>::template apply<fusion::nil>, f,_1,fusion::nil() ); 162 } 163 164 parse_input(std::string const & text) const165 void interpreter::parse_input(std::string const & text) const 166 { 167 boost::char_separator<char> s(" \t\n\r"); 168 169 token_parser parser 170 ( boost::make_token_iterator<std::string>(text.begin(), text.end(), s) 171 , boost::make_token_iterator<std::string>(text.end() , text.end(), s) ); 172 173 while (parser.has_more_tokens()) 174 { 175 // read function name 176 std::string func_name = parser.get<std::string>(); 177 178 // look up function 179 dictionary::const_iterator entry = map_invokers.find( func_name ); 180 if (entry == map_invokers.end()) 181 throw std::runtime_error("unknown function: " + func_name); 182 183 // call the invoker which controls argument parsing 184 entry->second(parser); 185 } 186 } 187 188 } 189 190