// This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009, Jonathan Turner (jturner@minnow-lang.org) // and Jason Turner (lefticus@gmail.com) // http://www.chaiscript.com #ifndef __proxy_functions_hpp__ #define __proxy_functions_hpp__ #include "boxed_value.hpp" #include "type_info.hpp" #include #include #include #include #include "proxy_functions_detail.hpp" namespace chaiscript { /** * Helper for building a list of parameters for calling a Proxy_Function * it does automatic conversion to Boxed_Value types via operator<< * * example usage: * Boxed_Value retval = dispatch(dispatchengine.get_function("+"), * chaiscript::Param_List_Builder() << 5 << 6); */ struct Param_List_Builder { Param_List_Builder &operator<<(const Boxed_Value &so) { objects.push_back(so); return *this; } template Param_List_Builder &operator<<(T t) { objects.push_back(Boxed_Value(t)); return *this; } operator const std::vector &() const { return objects; } std::vector objects; }; /** * Pure virtual base class for all Proxy_Function implementations * Proxy_Functions are a type erasure of type safe C++ * function calls. At runtime parameter types are expected to be * tested against passed in types. * Dispatch_Engine only knows how to work with Proxy_Function, no other * function classes. */ class Proxy_Function_Base { public: virtual ~Proxy_Function_Base() {} virtual Boxed_Value operator()(const std::vector ¶ms) = 0; virtual std::vector get_param_types() const = 0; virtual bool operator==(const Proxy_Function_Base &) const = 0; virtual bool call_match(const std::vector &vals) const = 0; //! Return true if the function is a possible match //! to the passed in values bool filter(const std::vector &vals) const { int arity = get_arity(); if (arity < 0) { return true; } else if (size_t(arity) == vals.size()) { if (arity == 0) { return true; } else { const std::vector &types = get_param_types(); if (types.size() < 2) { return true; } const Type_Info &ti = types[1]; if (!ti.m_bare_type_info || !(vals[0].get_type_info().m_bare_type_info) || (*ti.m_bare_type_info) == (*user_type().m_bare_type_info) || (*ti.m_bare_type_info) == (*user_type().m_bare_type_info) || (*vals[0].get_type_info().m_bare_type_info) == (*ti.m_bare_type_info)) { return true; } else { return false; } } } else { return false; } } virtual int get_arity() const = 0; virtual std::string annotation() const = 0; }; typedef boost::shared_ptr Proxy_Function; /** * Exception thrown if a function's guard fails to execute */ class guard_error : public std::runtime_error { public: guard_error() throw() : std::runtime_error("Guard evaluation failed") { } virtual ~guard_error() throw() { } }; /** * A Proxy_Function implementation that is not type safe, the called function * is expecting a vector that it works with how it chooses. */ class Dynamic_Proxy_Function : public Proxy_Function_Base { public: Dynamic_Proxy_Function( const boost::function &)> &t_f, int t_arity=-1, const std::string &t_description = "", const Proxy_Function &t_guard = Proxy_Function()) : m_f(t_f), m_arity(t_arity), m_description(t_description), m_guard(t_guard), m_types(build_param_type_list(t_arity)) { } virtual bool operator==(const Proxy_Function_Base &) const { return false; } virtual bool call_match(const std::vector &vals) const { return (m_arity < 0 || vals.size() == size_t(m_arity)) && test_guard(vals); } virtual ~Dynamic_Proxy_Function() {} virtual Boxed_Value operator()(const std::vector ¶ms) { if (m_arity < 0 || params.size() == size_t(m_arity)) { if (test_guard(params)) { return m_f(params); } else { throw guard_error(); } } else { throw arity_error(params.size(), m_arity); } } virtual int get_arity() const { return m_arity; } virtual std::vector get_param_types() const { return m_types; } virtual std::string annotation() const { return m_description; } private: bool test_guard(const std::vector ¶ms) const { if (m_guard) { try { return boxed_cast((*m_guard)(params)); } catch (const arity_error &) { return false; } catch (const bad_boxed_cast &) { return false; } } else { return true; } } static std::vector build_param_type_list(int arity) { std::vector types; types.push_back(detail::Get_Type_Info::get()); if (arity >= 0) { for (int i = 0; i < arity; ++i) { types.push_back(detail::Get_Type_Info::get()); } } else { types.push_back(detail::Get_Type_Info >::get()); } return types; } boost::function &)> m_f; int m_arity; std::string m_description; Proxy_Function m_guard; std::vector m_types; }; /** * An object used by Bound_Function to represent "_" parameters * of a binding. This allows for unbound parameters during bind. */ struct Placeholder_Object { }; /** * An implementation of Proxy_Function that takes a Proxy_Function * and substitutes bound parameters into the parameter list * at runtime, when call() is executed. * it is used for bind(function, param1, _, param2) style calls */ class Bound_Function : public Proxy_Function_Base { public: Bound_Function(const Proxy_Function &t_f, const std::vector &t_args) : m_f(t_f), m_args(t_args), m_arity(m_f->get_arity()<0?-1:(m_f->get_arity() - m_args.size())) { } virtual bool operator==(const Proxy_Function_Base &) const { return false; } virtual ~Bound_Function() {} virtual bool call_match(const std::vector &vals) const { return m_f->call_match(build_param_list(vals)); } virtual Boxed_Value operator()(const std::vector ¶ms) { return (*m_f)(build_param_list(params)); } std::vector build_param_list(const std::vector ¶ms) const { typedef std::vector::const_iterator pitr; pitr parg = params.begin(); pitr barg = m_args.begin(); std::vector args; while (true) { while (barg != m_args.end() && !(barg->get_type_info() == detail::Get_Type_Info::get())) { args.push_back(*barg); ++barg; } if (parg != params.end()) { args.push_back(*parg); ++parg; } if (barg != m_args.end() && barg->get_type_info() == detail::Get_Type_Info::get()) { ++barg; } if (parg == params.end() && barg == m_args.end()) { break; } } return args; } virtual std::vector get_param_types() const { return std::vector(); } virtual int get_arity() const { return m_arity; } virtual std::string annotation() const { return ""; } private: Proxy_Function m_f; std::vector m_args; int m_arity; }; /** * The standard typesafe function call implementation of Proxy_Function * It takes a boost::function<> object and performs runtime * type checking of Boxed_Value parameters, in a type safe manner */ template class Proxy_Function_Impl : public Proxy_Function_Base { public: Proxy_Function_Impl(const boost::function &f) : m_f(f), m_dummy_func(0), m_types(build_param_type_list(m_dummy_func)) { } virtual ~Proxy_Function_Impl() {} virtual bool operator==(const Proxy_Function_Base &t_func) const { try { dynamic_cast &>(t_func); return true; } catch (const std::bad_cast &) { return false; } } virtual Boxed_Value operator()(const std::vector ¶ms) { return call_func(m_f, params); } virtual std::vector get_param_types() const { return m_types; } virtual int get_arity() const { return m_types.size() - 1; } virtual bool call_match(const std::vector &vals) const { return compare_types(m_dummy_func, vals); } virtual std::string annotation() const { return ""; } private: boost::function m_f; Func *m_dummy_func; std::vector m_types; }; /** * Exception thrown in the case that a multi method dispatch fails * because no matching function was found * at runtime due to either an arity_error, a guard_error or a bad_boxed_cast * exception */ struct dispatch_error : std::runtime_error { dispatch_error() throw() : std::runtime_error("No matching function to dispatch to") { } virtual ~dispatch_error() throw() {} }; /** * Take a vector of functions and a vector of parameters. Attempt to execute * each function against the set of parameters, in order, until a matching * function is found or throw dispatch_error if no matching function is found */ template Boxed_Value dispatch(InItr begin, InItr end, const std::vector &plist) { while (begin != end) { try { if (begin->second->filter(plist)) { return (*begin->second)(plist); } } catch (const bad_boxed_cast &) { //parameter failed to cast, try again } catch (const arity_error &) { //invalid num params, try again } catch (const guard_error &) { //guard failed to allow the function to execute, //try again } ++begin; } throw dispatch_error(); } /** * Take a vector of functions and a vector of parameters. Attempt to execute * each function against the set of parameters, in order, until a matching * function is found or throw dispatch_error if no matching function is found */ Boxed_Value dispatch(const std::vector > &funcs, const std::vector &plist) { return dispatch(funcs.begin(), funcs.end(), plist); } } #endif