有没有办法部分匹配可变参数模板参数包?

时间:2017-04-30 22:09:39

标签: c++ c++11 c++14 variadic-templates c++17

我目前有一个系统将@$sayfa=$_GET['s']; $kacar=12; $toplansayfa=ceil(mysql_num_rows($cat)/$kacar); $baslangic=($sayfa * $kacar) - $kacar; $bul_cat=mysql_query(" Select m.Image_link AS link , m.Name , m.ID , c.Name AS catName from movie m join has_category Mc on m.ID = mc.movie_id join category c on c.ID = mc.category_id where c.Name = '$category' ORDER BY ID desc limit $baslangic, $kacar "); echo mysql_error();-> “连接”到函数中。这个signal是一个可变参数模板,它具有作为模板参数的signal函数的参数。

在当前的实现中,我显然无法连接到参数不完全相同的函数(或那些可以转换为的函数)作为connect的参数。现在,当我试图模仿Qt的signal / signal / slot时,我还希望将connectsignal参数连接到N slotM<N个参数,它们是完全明确定义的(即忽略信号的>M参数并将第一个M传递给连接函数)。有关我最简单形式的代码示例,请参阅Coliru

所以问题是双重的:

  1. 如何使connect调用适用于函数void g(int);
  2. 如何使emit调用适用于函数void g(int);
  3. 我猜我必须为slot及其调用函数制作一些“魔法”参数包减速器,但是我看不出它们应该如何组合在一起因此很难实际上开始尝试编写解决方案。如果至少Clang / GCC和Visual Studio 2015可以编译它,我可以使用仅限C ++ 17的解决方案。

    上面链接的代码是为了完整性:

    #include <memory>
    #include <vector>
    
    template<typename... ArgTypes>
    struct slot
    {
        virtual ~slot() = default;
    
        virtual void call(ArgTypes...) const = 0;
    };
    
    template<typename Callable, typename... ArgTypes>
    struct callable_slot : slot<ArgTypes...>
    {
        callable_slot(Callable callable) : callable(callable) {}
    
        void call(ArgTypes... args) const override { callable(args...); }
    
        Callable callable;
    };
    
    template<typename... ArgTypes>
    struct signal
    {
        template<typename Callable>
        void connect(Callable callable)
        {
            slots.emplace_back(std::make_unique<callable_slot<Callable, ArgTypes...>>(callable));
        }
    
        void emit(ArgTypes... args)
        {
            for(const auto& slot : slots)
            {
                slot->call(args...);
            }
        }
    
        std::vector<std::unique_ptr<slot<ArgTypes...>>> slots;
    };
    
    void f(int, char) {}
    
    int main()
    {
        signal<int, char> s;
        s.connect(&f);
    
        s.emit(42, 'c');
    }
    

2 个答案:

答案 0 :(得分:3)

template<class...> struct voider { using type = void; };
template<class... Ts> using voidify = typename voider<Ts...>::type;

template<class C, class...Args>
using const_lvalue_call_t = decltype(std::declval<const C&>()(std::declval<Args>()...));

template<class T, std::size_t...Is>
auto pick_from_tuple_impl(T &&, std::index_sequence<Is...>) 
    -> std::tuple<std::tuple_element_t<Is, T>...>;

template<class Tuple, class = std::enable_if_t<(std::tuple_size<Tuple>::value > 0)>>
using drop_last = decltype(pick_from_tuple_impl(std::declval<Tuple>(), 
                        std::make_index_sequence<std::tuple_size<Tuple>::value - 1>()));

template<class C, class ArgsTuple, class = void>
struct try_call
    : try_call<C, drop_last<ArgsTuple>> {};

template<class C, class...Args>
struct try_call<C, std::tuple<Args...>, voidify<const_lvalue_call_t<C, Args...>>> {
    template<class... Ts>
    static void call(const C& c, Args&&... args, Ts&&... /* ignored */) {
        c(std::forward<Args>(args)...); 
    }
};

然后在callable_slot

void call(ArgTypes... args) const override {
     using caller = try_call<Callable, std::tuple<ArgTypes...>>;
     caller::call(callable, std::forward<ArgTypes>(args)...); 
}

对于成员指针支持(这需要SFINAE友好std::result_of),请将const_lvalue_call_t更改为

template<class C, class...Args>
using const_lvalue_call_t = std::result_of_t<const C&(Args&&...)>;

然后将try_call::call中的实际通话更改为

std::ref(c)(std::forward<Args>(args)...); 

对于左值犊牛来说,这是穷人的std::invoke。如果您有C ++ 17,请直接使用std::invoke(并使用std::void_t代替voidify,但我喜欢后者的声音。

答案 1 :(得分:1)

不确定理解你到底想要什么......但是...... std::tuplestd::make_index_sequence ......

首先,您需要一个类型特征,它可以为您提供函数的参数数量(或std::function

template <typename>
struct numArgs;

template <typename R, typename ... Args>
struct numArgs<R(*)(Args...)> 
      : std::integral_constant<std::size_t, sizeof...(Args)>
 { };

template <typename R, typename ... Args>
struct numArgs<std::function<R(Args...)>>
      : std::integral_constant<std::size_t, sizeof...(Args)>
 { };

接下来,您必须在constexpr中添加callable_slot值,以记住Callable函数中的参数数量

static constexpr std::size_t numA { numArgs<Callable>::value };

然后你必须修改call()方法以在std::tuple<ArgTypes...>中打包参数并调用另一个传递元组的方法和从0到numA的索引序列

void call(ArgTypes... args) const override
 { callI(std::make_tuple(args...), std::make_index_sequence<numA>{}); }

最后,您必须在CallI()中调用仅包含参数元组的第一个callable()元素的numA函数

template <std::size_t ... Is>
void callI (std::tuple<ArgTypes...> const & t,
            std::index_sequence<Is...> const &) const
 { callable(std::get<Is>(t)...); }

以下是一个完整的工作示例

#include <memory>
#include <vector>
#include <iostream>
#include <functional>

template <typename>
struct numArgs;

template <typename R, typename ... Args>
struct numArgs<R(*)(Args...)> 
   : std::integral_constant<std::size_t, sizeof...(Args)>
 { };

template <typename R, typename ... Args>
struct numArgs<std::function<R(Args...)>>
   : std::integral_constant<std::size_t, sizeof...(Args)>
 { };

template <typename ... ArgTypes>
struct slot
 {
   virtual ~slot() = default;

   virtual void call(ArgTypes...) const = 0;
 };

template <typename Callable, typename ... ArgTypes>
struct callable_slot : slot<ArgTypes...>
 {
   static constexpr std::size_t numA { numArgs<Callable>::value };

   callable_slot(Callable callable) : callable(callable)
    { }

   template <std::size_t ... Is>
   void callI (std::tuple<ArgTypes...> const & t,
               std::index_sequence<Is...> const &) const
    { callable(std::get<Is>(t)...); }

   void call(ArgTypes... args) const override
    { callI(std::make_tuple(args...), std::make_index_sequence<numA>{}); }

   Callable callable;
 };

template <typename ... ArgTypes>
struct signal
 {
   template <typename Callable>
   void connect(Callable callable)
    {
      slots.emplace_back(
         std::make_unique<callable_slot<Callable, ArgTypes...>>(callable));
    }

   void emit(ArgTypes... args)
    { for(const auto& slot : slots) slot->call(args...); }

   std::vector<std::unique_ptr<slot<ArgTypes...>>> slots;
 };

void f (int i, char c)
 { std::cout << "--- f(" << i << ", " << c << ")" << std::endl; }

void g (int i)
 { std::cout << "--- g(" << i << ")" << std::endl; }

struct foo
 {
   static void j (int i, char c)
    { std::cout << "--- j(" << i << ", " << c << ")" << std::endl; }

   void k (int i)
    { std::cout << "--- k(" << i << ")" << std::endl; }
 };

int main ()
 {
   std::function<void(int, char)> h { [](int i, char c)
       { std::cout << "--- h(" << i << ", " << c << ")" << std::endl; }
    };

   std::function<void(int)> i { [](int i)
       { std::cout << "--- i(" << i << ")" << std::endl; }
    };

   using std::placeholders::_1;

   foo foo_obj{};

   std::function<void(int)> k { std::bind(&foo::k, foo_obj, _1) };

   signal<int, char> s;

   s.connect(f);
   s.connect(g);
   s.connect(h);
   s.connect(i);
   s.connect(foo::j);
   s.connect(k);

   s.emit(42, 'c');
 }

此示例需要C ++ 14,因为使用std::make_index_sequencestd::index_sequence

替换它们并准备符合C ++ 11的解决方案并不是非常困难。