模板元:功能参数类型特征检测

时间:2019-05-21 14:57:19

标签: c++ template-meta-programming

因此,我尝试应用this blog post中显示的代码,尽管它似乎适用于免费功能,但是当我尝试将其与lambda函数一起使用时,它的外观却平淡无奇。

Here is a fairly minimal demo of the problem.

请注意我设置的constexpr bool“开关” using_lambda。设置为false时,程序将编译并正常运行。设置为true时,编译器(gcc / c ++ 17)死于以下诊断:

  

在prog.cc:7包含的文件中:function_traits.h:在实例化中   的“ struct function_traits>”:   prog.cc:30:37:从'void EventDispatcher :: Register(F &&)   [with F = main()::]'prog.cc:71:10:
  从这里需要function_traits.h:47:45:错误:没有命名的类型   使用'struct main()::'中的'type'   call_type = function_traits;                                                ^〜function_traits.h:47:45:错误:'结构中没有名为'type'的类型   main()::'function_traits.h:在   'struct function_traits> :: argument <0>'的实例化:prog.cc:31:66:从'void必需   EventDispatcher :: Register(F &&)[with F = main()::]'prog.cc:71:10:从此处开始   function_traits.h:47:45:错误:“结构”中没有名为“类型”的类型   main()::'

恐怕我在这里的深度不够。是否可以使它正常工作,如果可以,怎么办?还是有一种完全不同的方法可以更好地实现相同的目标?

此外,事后想想,我也无法使std::is_invocable正常工作(请参见第28行)。我怀疑只有当可调用对象对于指定的参数类型可调用 时,它才返回true。我只想检测某些论点是否可以引起人们的注意。如果有人可以阐明如何做到这一点,也将不胜感激。

main.cpp

// main.cpp
#include <iostream>
#include <unordered_map>
#include <functional>
#include <typeinfo>
#include <typeindex>
#include <type_traits>
#include "function_traits.h"

class Event
{
public:
    virtual ~Event()
    {}
protected:
    Event() = default;
};

class TestEvent : public Event
{};

class EventDispatcher
{
public:
    template<class F>
    void Register( F&& f )
    {
        // this static assert seems to always fail
        //static_assert(std::is_invocable<F>::value,"Handler must be invocable.");
        using Traits = function_traits<F>;
        static_assert(Traits::arity == 1,"Handler must have exactly 1 parameter.");
        using PCRef = typename Traits::template argument<0>::type;
        using PC = typename std::remove_reference<PCRef>::type;
        using P = typename std::remove_const<PC>::type;
        static_assert(std::is_base_of<Event,P>::value && !std::is_same<Event,P>::value,"Handler parameter must be derived from Event.");
        static_assert(std::is_reference<PCRef>::value,"Handler parameter must be passed by reference.");
        static_assert(std::is_const<PC>::value,"Handler parameter must be constant reference.");

        // wrap f in a lambda that casts to specific event handled
        handlers[typeid(P)] = [f]( const Event& e )
        {
            f( static_cast<const P&>(e) );
        };
    }
    void Dispatch( const Event& e ) const
    {
        auto i = handlers.find( typeid(e) );
        if( i != handlers.end() )
        {
            i->second( e );
        }
    }
private:
    std::unordered_map<std::type_index,std::function<void(const Event&)>> handlers;
};

void FreeFunctionHandler( [[maybe_unused]] const TestEvent& e )
{
    std::cout << "TestEvent fired; handled by free function." << std::endl;    
}

int main()
{
    constexpr bool using_lambda = true;
    EventDispatcher ed;

    if constexpr( using_lambda )
    {
        ed.Register([]( [[maybe_unused]] const TestEvent& e )
        {
            std::cout << "TestEvent fired; handled by lambda function." << std::endl;
        });
    } 
    else
    {
        ed.Register( FreeFunctionHandler );
    }

    ed.Dispatch( TestEvent{} );

    return 0;
}

function_traits.h

// function_traits.h
#pragma once
#include <tuple>

template<class F>
struct function_traits;

// function pointer
template<class R,class... Args>
struct function_traits<R( *)(Args...)> : public function_traits<R( Args... )>
{};

template<class R,class... Args>
struct function_traits<R( Args... )>
{
    using return_type = R;

    static constexpr std::size_t arity = sizeof...(Args);

    template <std::size_t N>
    struct argument
    {
        static_assert(N < arity,"error: invalid parameter index.");
        using type = typename std::tuple_element<N,std::tuple<Args...>>::type;
    };
};

// member function pointer
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...)> : public function_traits<R(C&,Args...)>
{};

// const member function pointer
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...) const> : public function_traits<R(C&,Args...)>
{};

// member object pointer
template<class C, class R>
struct function_traits<R(C::*)> : public function_traits<R(C&)>
{};

// functor
template<class F>
struct function_traits
{
private:
    using call_type = function_traits<decltype(&F::type::operator())>;
public:
    using return_type = typename call_type::return_type;

    static constexpr std::size_t arity = call_type::arity - 1;

    template <std::size_t N>
    struct argument
    {
        static_assert(N < arity,"error: invalid parameter index.");
        using type = typename call_type::template argument<N + 1>::type;
    };
};

template<class F>
struct function_traits<F&> : public function_traits<F>
{};

template<class F>
struct function_traits<F&&> : public function_traits<F>
{};

0 个答案:

没有答案