Nikki Chumakov February 2016

Compose callable object

I need a template function:

template <typename ...Signatures, typename ...Functions>
auto make_callable_object (Functions&& ...functions);

that will return a callable object. When that callable object is called with any of the signature from Signatures, the correspondent functions from Functions should be called.

E.g.:

auto co = create_callable_object<void (int), void (std::string), void (std::exception)> (
             [] (int i) { std::cout << "int"; },
             [] (std::string) { std::cout << "string"; }
             [] (std::exception) { std::cout << "exception"; }
          );

should return an object that is equivalent to

struct { 
  void operator() (int) const { std::cout << "int"; }
  void operator() (std::string) const { std::cout << "string"; }
  void operator() (std::exception) const { std::cout << "exception"; }
};

I created some implementation, but it does not compile with clang c++11.

I'm not sure if there is compiler bug or bug in my code. I'm looking for workarounds or may be a better solution, that would compile with both gcc and clang in c++11 mode.

Coliru link

#include <iostream>
#include <tuple>
#include <type_traits>
#include <functional>

#define noexcept
#define constexpr
#define constexpr14

struct callable_impl_base {
  // will trigger if called with bad signature
  template <typename ...Ts>
  void operator() (Ts...) const { throw std::bad_function_call {}; }
};

template <typename Func, typename Base, typename Sig>
struct callable_impl;

template <typename Func, typename Base, typename R, typename ...Args>
struct callable_impl<Func, Base, R (Args...)>: public Base
{
  template <typename FF>        

Answers


Rumburak February 2016

The following approach is shorter (and IMHO easier to grok):

You did the grunt work for detecting if a callable is a match:

struct can_call_test
{
  template<typename F, typename... A>
  static decltype(std::declval<F>()(std::declval<A>()...), std::true_type())
  f(int);

  template<typename F, typename... A>
  static std::false_type
  f(...);
};

} // namespace detail

template<typename F, typename... A>
struct is_callable : decltype(detail::can_call_test::f<F, A...>(0)) { };

template<typename F, typename... A>
struct is_callable <F(A...)> : is_callable <F, A...> { };

Lets define fallback callables so that we can treat the not found case identical to all others:

template <bool Strict>
struct Fallback
{
  template<typename... Args, typename T = int>
  void operator()(Args&&...) const
  {
    static_assert (sizeof(T) == 0,
      "Bad function call: incompatible signature, see next error message");
  }
};

template <>
struct Fallback<false>
{
  template<typename... Args>
  void operator()(Args&&...) const
  {
    throw std::bad_function_call {};
  }
};

Now, given a tuple of callables, lets figure out the index of the first matching callable:

template <size_t Idx, typename Tuple, typename... Args>
struct FirstCallable;

template <size_t Idx, typename C, typename... Rest, typename... Args>
struct FirstCallable<Idx, std::tuple<C, Rest...>, Args...>
{
  static constexpr size_t index =
      is_callable<C, Args...>::value
          ? Idx
          : FirstCallable<Idx + 1, std::tuple<Rest...>, Args...>::index;
};

template <size_t Idx, typename C, typename... Args>
struct FirstCallable<Idx, std::tuple<C>, Args...>
{
  static constexpr size_t index = Idx;
};

The callable template can

Post Status

Asked in February 2016
Viewed 1,057 times
Voted 7
Answered 1 times

Search




Leave an answer