0

I'm invoking some user-defined callables with arguments determined at runtime. I have the following working:

using argument_t = std::variant< int, bool, double, std::string >;
using argument_list = std::vector< argument_t >;

template < typename Callable, std::size_t... S >
auto invoke_with_args_impl( Callable&& method, const argument_list& args, std::index_sequence< S... > )
{
    return std::invoke( method, args[S]... );
}

template < std::size_t size, typename Callable >
auto invoke_with_args_impl( Callable&& method, const argument_list& args )
{
    if ( args.size() != size )
    {
        throw std::runtime_error( "argument count mismatch" );
    }
    return invoke_with_args_impl( std::forward< Callable >( method ), args, std::make_index_sequence< size >() );
}

template < typename Callable >
auto invoke_with_args( Callable&& method, const argument_list& args )
{
    switch ( args.size() )
    {
        case 0:
            return method();
        case 1:
            return invoke_with_args_impl< 1 >( std::forward< Callable >( method ), args );
        //.... ad nauseam

This all works fine, however, argument_t must be the type of all the arguments in the user-defined function for it to work successfully.

I am trying to figure out how to pass the current alternative type of the variant instead.

Something like

return std::invoke( method, std::get< args[S].index() >( args[S] )... );

but obviously that doesn't work...unfortunately I'm at the end of my skills on this one.

How can this be done?

yano
  • 4,095
  • 3
  • 35
  • 68
  • You need to dispatch somewhere, you cannot avoid it. Perhaps you are looking for [visit](https://en.cppreference.com/w/cpp/utility/variant/visit)? – j6t Aug 23 '19 at 07:09
  • 1
    this is actually double dispatch. You will need to constrain: a) the number of arguments b) the permutations of argument types – Richard Hodges Aug 23 '19 at 10:04
  • How is this not `std::visit`? – Davis Herring Aug 23 '19 at 14:29
  • Can you tell me why you not using `std::tuple` to store arguments? – 康桓瑋 Aug 24 '19 at 14:34
  • @康桓瑋 the arguments are generated at runtime with unknown data types until we parse in different orders. Otherwise `std::apply` + `std::tuple` would work well. Also due to some other circumstances, I am type-erasing the Callables when the user sets them, so I don't know the arg types off-hand – yano Aug 26 '19 at 16:46

1 Answers1

1

You might use something like:

template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

template < typename Callable, std::size_t... Is >
auto invoke_with_args_impl(Callable&& method,
                           const argument_list& args,
                           std::index_sequence<Is...>)
{
    return std::visit(overloaded{
        [&](auto&&... ts)
        -> decltype(std::invoke(method, static_cast<decltype(ts)>(ts)...))
        { return std::invoke(method, static_cast<decltype(ts)>(ts)...); },
        [](auto&&... ts)
        -> std::enable_if_t<!std::is_invocable<Callable, decltype(ts)...>::value>
        { throw std::runtime_error("Invalid arguments"); } // fallback
      },
      args[Is]...);
}

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302