title: Template Meta: Function Parameter Type Trait Detection
body: <p>So I tried to apply the code shown in <a href="https://functionalcpp.wordpress.com/2013/08/05/function-traits/" rel="nofollow noreferrer">this blog post</a>, and while it seems to be working for free functions, it falls flat on its face when I try to use it with a lambda function.</p>
  
  <p><a href="https://wandbox.org/permlink/IZC32rI3FxlRYT9D" rel="nofollow noreferrer">Here is a fairly minimal demo of the problem.</a></p>
  
  <p>Note the <code>constexpr bool</code> "switch" <code>using_lambda</code> I've set up. When set to <code>false</code>, the program compiles and runs fine. When set to <code>true</code>, the compiler (gcc/c++17) dies with these diagnostics:</p>
  
  <blockquote>
    <p>In file included from prog.cc:7: function_traits.h: In instantiation
    of 'struct function_traits >':
    prog.cc:30:37:   required from 'void EventDispatcher::Register(F&amp;&amp;)
    [with F = main()::]' prog.cc:71:10:<br>
    required from here function_traits.h:47:45: error: no type named
    'type' in 'struct main()::'   using
    call_type = function_traits;
                                                 ^~ function_traits.h:47:45: error: no type named 'type' in 'struct
    main()::' function_traits.h: In
    instantiation of 'struct function_traits >::argument&lt;0>': prog.cc:31:66:   required from 'void
    EventDispatcher::Register(F&amp;&amp;) [with F = main()::]' prog.cc:71:10:   required from here
    function_traits.h:47:45: error: no type named 'type' in 'struct
    main()::'</p>
  </blockquote>
  
  <p>I'm afraid I'm a little (a lot) out of my depth here. Is it possible to get this working, and if so how? Or is there a completely different approach that will serve the same goals better?</p>
  
  <p>Also, as an afterthought, I am having trouble getting <code>std::is_invocable</code> working as well (see line 28). I suspect it only returns true when the invocable object is invocable <em>for the specified argument types</em>. I only wish to detect whether something is invocable with any argument(s). If someone could shed some light on how this may be done that would also be appreciated.</p>
  
  <p><strong>main.cpp</strong>:</p>
  
  <pre><code>// main.cpp
  #include &lt;iostream&gt;
  #include &lt;unordered_map&gt;
  #include &lt;functional&gt;
  #include &lt;typeinfo&gt;
  #include &lt;typeindex&gt;
  #include &lt;type_traits&gt;
  #include "function_traits.h"
  
  class Event
  {
  public:
      virtual ~Event()
      {}
  protected:
      Event() = default;
  };
  
  class TestEvent : public Event
  {};
  
  class EventDispatcher
  {
  public:
      template&lt;class F&gt;
      void Register( F&amp;&amp; f )
      {
          // this static assert seems to always fail
          //static_assert(std::is_invocable&lt;F&gt;::value,"Handler must be invocable.");
          using Traits = function_traits&lt;F&gt;;
          static_assert(Traits::arity == 1,"Handler must have exactly 1 parameter.");
          using PCRef = typename Traits::template argument&lt;0&gt;::type;
          using PC = typename std::remove_reference&lt;PCRef&gt;::type;
          using P = typename std::remove_const&lt;PC&gt;::type;
          static_assert(std::is_base_of&lt;Event,P&gt;::value &amp;&amp; !std::is_same&lt;Event,P&gt;::value,"Handler parameter must be derived from Event.");
          static_assert(std::is_reference&lt;PCRef&gt;::value,"Handler parameter must be passed by reference.");
          static_assert(std::is_const&lt;PC&gt;::value,"Handler parameter must be constant reference.");
  
          // wrap f in a lambda that casts to specific event handled
          handlers[typeid(P)] = [f]( const Event&amp; e )
          {
              f( static_cast&lt;const P&amp;&gt;(e) );
          };
      }
      void Dispatch( const Event&amp; e ) const
      {
          auto i = handlers.find( typeid(e) );
          if( i != handlers.end() )
          {
              i-&gt;second( e );
          }
      }
  private:
      std::unordered_map&lt;std::type_index,std::function&lt;void(const Event&amp;)&gt;&gt; handlers;
  };
  
  void FreeFunctionHandler( [[maybe_unused]] const TestEvent&amp; e )
  {
      std::cout &lt;&lt; "TestEvent fired; handled by free function." &lt;&lt; std::endl;    
  }
  
  int main()
  {
      constexpr bool using_lambda = true;
      EventDispatcher ed;
  
      if constexpr( using_lambda )
      {
          ed.Register([]( [[maybe_unused]] const TestEvent&amp; e )
          {
              std::cout &lt;&lt; "TestEvent fired; handled by lambda function." &lt;&lt; std::endl;
          });
      } 
      else
      {
          ed.Register( FreeFunctionHandler );
      }
  
      ed.Dispatch( TestEvent{} );
  
      return 0;
  }
  </code></pre>
  
  <p><strong>function_traits.h</strong>:</p>
  
  <pre><code>// function_traits.h
  #pragma once
  #include &lt;tuple&gt;
  
  template&lt;class F&gt;
  struct function_traits;
  
  // function pointer
  template&lt;class R,class... Args&gt;
  struct function_traits&lt;R( *)(Args...)&gt; : public function_traits&lt;R( Args... )&gt;
  {};
  
  template&lt;class R,class... Args&gt;
  struct function_traits&lt;R( Args... )&gt;
  {
      using return_type = R;
  
      static constexpr std::size_t arity = sizeof...(Args);
  
      template &lt;std::size_t N&gt;
      struct argument
      {
          static_assert(N &lt; arity,"error: invalid parameter index.");
          using type = typename std::tuple_element&lt;N,std::tuple&lt;Args...&gt;&gt;::type;
      };
  };
  
  // member function pointer
  template&lt;class C, class R, class... Args&gt;
  struct function_traits&lt;R(C::*)(Args...)&gt; : public function_traits&lt;R(C&amp;,Args...)&gt;
  {};
  
  // const member function pointer
  template&lt;class C, class R, class... Args&gt;
  struct function_traits&lt;R(C::*)(Args...) const&gt; : public function_traits&lt;R(C&amp;,Args...)&gt;
  {};
  
  // member object pointer
  template&lt;class C, class R&gt;
  struct function_traits&lt;R(C::*)&gt; : public function_traits&lt;R(C&amp;)&gt;
  {};
  
  // functor
  template&lt;class F&gt;
  struct function_traits
  {
  private:
      using call_type = function_traits&lt;decltype(&amp;F::type::operator())&gt;;
  public:
      using return_type = typename call_type::return_type;
  
      static constexpr std::size_t arity = call_type::arity - 1;
  
      template &lt;std::size_t N&gt;
      struct argument
      {
          static_assert(N &lt; arity,"error: invalid parameter index.");
          using type = typename call_type::template argument&lt;N + 1&gt;::type;
      };
  };
  
  template&lt;class F&gt;
  struct function_traits&lt;F&amp;&gt; : public function_traits&lt;F&gt;
  {};
  
  template&lt;class F&gt;
  struct function_traits&lt;F&amp;&amp;&gt; : public function_traits&lt;F&gt;
  {};
  </code></pre>