
One solution would be to rewrite the overloads as:
template<typename A0, typename A1> result_type operator()(A0 const &a0, A1 const &a0) const
and then within the function add compile-time assertions that (a) the number of arguments is currect, and (b) that each argument type is implicitly convertible to the expected types.
Here is the code as per the above suggestion, preserved for posterity : #include <boost/proto/proto.hpp> #include <boost/proto/proto_typeof.hpp> #include <boost/mpl/bool.hpp> #include <boost/mpl/assert.hpp> #include <boost/mpl/not.hpp> #include <boost/mpl/identity.hpp> #include <boost/utility.hpp> #include <boost/type_traits/is_same.hpp> #include <iostream> #include <sstream> #include <string> namespace proto=boost::proto; namespace mpl=boost::mpl; unsigned int ids; template<typename VT> struct Var { unsigned int id; Var() { id = ++ids; } }; typedef proto::terminal<Var<int> >::type int32_; typedef proto::terminal<Var<float> >::type float_; struct program_ {}; template<class E> struct program_expr; struct call_grammar : proto::or_< proto::function< proto::terminal<program_>, proto::vararg<proto::terminal<Var<proto::_> > > >
{};
struct _var_arity : proto::or_< proto::when<proto::terminal<Var<proto::_> >, proto::make<mpl::int_<1> > >
{};
struct _prog_arity : proto::or_< proto::when< proto::function<proto::terminal<program_>, proto::vararg<proto::terminal<Var<proto::_> > > >, proto::fold<proto::functional::pop_front(proto::_expr), proto::make<mpl::int_<0> >, proto::make<mpl::plus<proto::_state, _var_arity> > > >
{};
template<typename Expr> struct prog_arity : boost::result_of<_prog_arity(Expr)> {}; struct program_generator : proto::or_< proto::when<call_grammar, proto::pod_generator<program_expr>(proto::_expr) >, proto::otherwise<proto::_expr>
{};
struct program_domain : proto::domain<program_generator> {}; struct _var_type : proto::callable { template<typename Sig> struct result; template<typename This, typename T> struct result<This(const Var<T> &)> { typedef T type; }; }; struct var_type : proto::or_< proto::when<proto::terminal<Var<proto::_> >, _var_type(proto::_value)> > {}; template<int N> struct call_param_type : proto::or_< proto::when< proto::function< proto::terminal<program_>, proto::vararg<proto::terminal<Var<proto::_> > > >, var_type(proto::_child_c<N+1>)> > {}; template <typename Expr> struct program_expr { BOOST_PROTO_BASIC_EXTENDS(Expr, program_expr<Expr>, program_domain); BOOST_PROTO_EXTENDS_SUBSCRIPT(); typedef void result_type; template<typename A0> result_type operator()(A0 a0) const { BOOST_MPL_ASSERT_RELATION(1, ==, prog_arity<Expr>::type::value); BOOST_MPL_ASSERT((boost::is_same<A0, typename boost::result_of<call_param_type<0>(const Expr &)>::type>)); std::cout << "program with one arg\n"; } template<typename A0, typename A1> result_type operator()(A0 a0, A1 a1) const { BOOST_MPL_ASSERT_RELATION(2, ==, prog_arity<Expr>::type::value); BOOST_MPL_ASSERT((boost::is_same<A0, typename boost::result_of<call_param_type<0>(const Expr &)>::type>)); BOOST_MPL_ASSERT((boost::is_same<A1, typename boost::result_of<call_param_type<1>(const Expr &)>::type>)); std::cout << "program with two args\n"; } template<typename A0, typename A1, typename A2> result_type operator()(A0 a0, A1 a1, A2 a2) const { BOOST_MPL_ASSERT_RELATION(3, ==, prog_arity<Expr>::type::value); BOOST_MPL_ASSERT((boost::is_same<A0, typename boost::result_of<call_param_type<0>(const Expr &)>::type>)); BOOST_MPL_ASSERT((boost::is_same<A1, typename boost::result_of<call_param_type<1>(const Expr &)>::type>)); BOOST_MPL_ASSERT((boost::is_same<A2, typename boost::result_of<call_param_type<2>(const Expr &)>::type>)); std::cout << "program with three args\n"; } }; template<typename A0> typename proto::result_of::make_expr<proto::tag::function , program_domain , program_ , A0 const &>::type const Program_(A0 const &a0) { return proto::make_expr<proto::tag::function, program_domain>( program_(), boost::ref(a0)); } template<typename A0, typename A1> typename proto::result_of::make_expr<proto::tag::function , program_domain , program_ , A0 const & , A1 const &>::type const Program_(A0 const &a0, A1 const &a1) { return proto::make_expr<proto::tag::function, program_domain>( program_(), boost::ref(a0), boost::ref(a1)); } template<typename A0, typename A1, typename A2> typename proto::result_of::make_expr<proto::tag::function , program_domain , program_ , A0 const & , A1 const & , A2 const &>::type const Program_(A0 const &a0, A1 const &a1, A2 const &a3) { return proto::make_expr<proto::tag::function, program_domain>( program_(), boost::ref(a0), boost::ref(a1), boost::ref(a3)); } int main() { int32_ a,b,c; int p1 = 10, p2 = 20, p3 = 30; Program_(a)(p1); Program_(a,b)(10, p2); Program_(a,b,c)(10, 20, p3); }