// (C) Copyright Tobias Schwinger // // Use modification and distribution are subject to the boost Software License, // Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt). //------------------------------------------------------------------------------ // // This example implements a simple batch-style interpreter that is capable of // calling functions previously registered with it. The parameter types of the // functions are used to control the parsing of the input. // // Implementation description // ========================== // // When a function is registered, an 'invoker' template is instantiated with // the function's type. The 'invoker' fetches a value from the 'token_parser' // for each parameter of the function into a tuple and finally invokes the the // function with these values as arguments. The invoker's entrypoint, which // is a function of the callable builtin that describes the function to call and // a reference to the 'token_parser', is partially bound to the registered // function and put into a map so it can be found by name during parsing. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace example { namespace fusion = boost::fusion; namespace ft = boost::function_types; namespace mpl = boost::mpl; class interpreter { class token_parser; typedef boost::function invoker_function; typedef std::map dictionary; dictionary map_invokers; public: // Registers a function with the interpreter. template typename boost::enable_if< ft::is_nonmember_callable_builtin >::type register_function(std::string const & name, Function f); // Parse input for functions to call. void parse_input(std::string const & text) const; private: template< typename Function , class From = typename mpl::begin< ft::parameter_types >::type , class To = typename mpl::end< ft::parameter_types >::type > struct invoker; }; class interpreter::token_parser { typedef boost::token_iterator_generator< boost::char_separator >::type token_iterator; token_iterator itr_at, itr_to; public: token_parser(token_iterator from, token_iterator to) : itr_at(from), itr_to(to) { } private: template struct remove_cv_ref : boost::remove_cv< typename boost::remove_reference::type > { }; public: // Returns a token of given type. // We just apply boost::lexical_cast to whitespace separated string tokens // for simplicity. template typename remove_cv_ref::type get() { if (! this->has_more_tokens()) throw std::runtime_error("unexpected end of input"); try { typedef typename remove_cv_ref::type result_type; result_type result = boost::lexical_cast ::type>(*this->itr_at); ++this->itr_at; return result; } catch (boost::bad_lexical_cast &) { throw std::runtime_error("invalid argument: " + *this->itr_at); } } // Any more tokens? bool has_more_tokens() const { return this->itr_at != this->itr_to; } }; template struct interpreter::invoker { // add an argument to a Fusion cons-list for each parameter type template static inline void apply(Function func, token_parser & parser, Args const & args) { typedef typename mpl::deref::type arg_type; typedef typename mpl::next::type next_iter_type; interpreter::invoker::apply ( func, parser, fusion::push_back(args, parser.get()) ); } }; template struct interpreter::invoker { // the argument list is complete, now call the function template static inline void apply(Function func, token_parser &, Args const & args) { fusion::invoke(func,args); } }; template typename boost::enable_if< ft::is_nonmember_callable_builtin >::type interpreter::register_function(std::string const & name, Function f) { // instantiate and store the invoker by name this->map_invokers[name] = boost::bind( & invoker::template apply, f,_1,fusion::nil() ); } void interpreter::parse_input(std::string const & text) const { boost::char_separator s(" \t\n\r"); token_parser parser ( boost::make_token_iterator(text.begin(), text.end(), s) , boost::make_token_iterator(text.end() , text.end(), s) ); while (parser.has_more_tokens()) { // read function name std::string func_name = parser.get(); // look up function dictionary::const_iterator entry = map_invokers.find( func_name ); if (entry == map_invokers.end()) throw std::runtime_error("unknown function: " + func_name); // call the invoker which controls argument parsing entry->second(parser); } } }