// (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 <map> #include <string> #include <stdexcept> #include <boost/token_iterator.hpp> #include <boost/token_functions.hpp> #include <boost/lexical_cast.hpp> #include <boost/bind.hpp> #include <boost/function.hpp> #include <boost/type_traits/remove_cv.hpp> #include <boost/type_traits/remove_reference.hpp> #include <boost/fusion/include/push_back.hpp> #include <boost/fusion/include/cons.hpp> #include <boost/fusion/include/invoke.hpp> #include <boost/mpl/begin.hpp> #include <boost/mpl/end.hpp> #include <boost/mpl/next.hpp> #include <boost/mpl/deref.hpp> #include <boost/utility/enable_if.hpp> #include <boost/function_types/is_nonmember_callable_builtin.hpp> #include <boost/function_types/parameter_types.hpp> namespace example { namespace fusion = boost::fusion; namespace ft = boost::function_types; namespace mpl = boost::mpl; class interpreter { class token_parser; typedef boost::function<void(token_parser &)> invoker_function; typedef std::map<std::string, invoker_function> dictionary; dictionary map_invokers; public: // Registers a function with the interpreter. template<typename Function> typename boost::enable_if< ft::is_nonmember_callable_builtin<Function> >::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<Function> >::type , class To = typename mpl::end< ft::parameter_types<Function> >::type > struct invoker; }; class interpreter::token_parser { typedef boost::token_iterator_generator< boost::char_separator<char> >::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<typename T> struct remove_cv_ref : boost::remove_cv< typename boost::remove_reference<T>::type > { }; public: // Returns a token of given type. // We just apply boost::lexical_cast to whitespace separated string tokens // for simplicity. template<typename RequestedType> typename remove_cv_ref<RequestedType>::type get() { if (! this->has_more_tokens()) throw std::runtime_error("unexpected end of input"); try { typedef typename remove_cv_ref<RequestedType>::type result_type; result_type result = boost::lexical_cast <typename remove_cv_ref<result_type>::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<typename Function, class From, class To> struct interpreter::invoker { // add an argument to a Fusion cons-list for each parameter type template<typename Args> static inline void apply(Function func, token_parser & parser, Args const & args) { typedef typename mpl::deref<From>::type arg_type; typedef typename mpl::next<From>::type next_iter_type; interpreter::invoker<Function, next_iter_type, To>::apply ( func, parser, fusion::push_back(args, parser.get<arg_type>()) ); } }; template<typename Function, class To> struct interpreter::invoker<Function,To,To> { // the argument list is complete, now call the function template<typename Args> static inline void apply(Function func, token_parser &, Args const & args) { fusion::invoke(func,args); } }; template<typename Function> typename boost::enable_if< ft::is_nonmember_callable_builtin<Function> >::type interpreter::register_function(std::string const & name, Function f) { // instantiate and store the invoker by name this->map_invokers[name] = boost::bind( & invoker<Function>::template apply<fusion::nil>, f,_1,fusion::nil() ); } void interpreter::parse_input(std::string const & text) const { boost::char_separator<char> s(" \t\n\r"); token_parser parser ( boost::make_token_iterator<std::string>(text.begin(), text.end(), s) , boost::make_token_iterator<std::string>(text.end() , text.end(), s) ); while (parser.has_more_tokens()) { // read function name std::string func_name = parser.get<std::string>(); // 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); } } }