One solution would be to rewrite the overloads as:
template
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
#include
#include
#include
#include
#include
#include
#include
#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::type int32_;
typedef proto::terminal::type float_;
struct program_ {};
template<class E> struct program_expr;
struct call_grammar
: proto::or_<
proto::function<
proto::terminal,
proto::varargproto::_ > > >
{};
struct _var_arity
: proto::or_<
proto::whenproto::_ >, proto::make > >
{};
struct _prog_arity
: proto::or_<
proto::when<
proto::function,
proto::varargproto::_ > > >,
proto::fold >,
proto::make > > >
{};
template<typename Expr>
struct prog_arity
: boost::result_of<_prog_arity(Expr)>
{};
struct program_generator
: proto::or_<
proto::when(proto::_expr) >,
proto::otherwiseproto::_expr
{};
struct program_domain
: proto::domain
{};
struct _var_type
: proto::callable {
template<typename Sig>
struct result;
template
struct result {
typedef T type;
};
};
struct var_type
: proto::or_<
proto::whenproto::_ >,
_var_type(proto::_value)>
> {};
template<int N>
struct call_param_type
: proto::or_<
proto::when<
proto::function<
proto::terminal,
proto::varargproto::_ > > >,
var_type(proto::_child_c)>
> {};
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(const Expr &)>::type>));
std::cout << "program with one arg\n";
}
template
result_type operator()(A0 a0, A1 a1) const {
BOOST_MPL_ASSERT_RELATION(2, ==, prog_arity<Expr>::type::value);
BOOST_MPL_ASSERT((boost::is_same(const Expr &)>::type>));
BOOST_MPL_ASSERT((boost::is_same(const Expr &)>::type>));
std::cout << "program with two args\n";
}
template
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(const Expr &)>::type>));
BOOST_MPL_ASSERT((boost::is_same(const Expr &)>::type>));
BOOST_MPL_ASSERT((boost::is_same(const Expr &)>::type>));
std::cout << "program with three args\n";
}
};
template<typename A0>
typename proto::result_of::make_expr::type const
Program_(A0 const &a0)
{
return proto::make_expr(
program_(),
boost::ref(a0));
}
template
typename proto::result_of::make_expr::type const
Program_(A0 const &a0, A1 const &a1)
{
return proto::make_expr(
program_(),
boost::ref(a0),
boost::ref(a1));
}
template
typename proto::result_of::make_expr::type const
Program_(A0 const &a0, A1 const &a1, A2 const &a3)
{
return proto::make_expr(
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);
}