C++ 是否有方法部分匹配可变模板参数包?
我目前有一个系统可以将信号“连接”到功能。此C++ 是否有方法部分匹配可变模板参数包?,c++,c++11,c++14,variadic-templates,c++17,C++,C++11,C++14,Variadic Templates,C++17,我目前有一个系统可以将信号“连接”到功能。此信号是一个可变模板,其模板参数为可连接到的函数的参数 在当前的实现中,我显然无法连接到参数与信号的参数不完全相同(或可转换为)的函数。现在,当我试图模拟Qt的信号/插槽/连接,我还想将一个信号的N参数连接到一个插槽的MM信号参数,并将第一个m传递给连接的函数)。有关最简单的代码示例,请参阅 因此,问题有两个方面: 如何使函数的connect调用工作void g(int) 如何使emit调用对函数void g(int)起作用 我猜我必须为插槽及其调用函数
信号
是一个可变模板,其模板参数为可连接到的函数的参数
在当前的实现中,我显然无法连接到参数与信号
的参数不完全相同(或可转换为)的函数。现在,当我试图模拟Qt的信号
/插槽
/连接
,我还想将一个信号
的N
参数连接到一个插槽
的MM
信号参数,并将第一个m传递给连接的函数)。有关最简单的代码示例,请参阅
因此,问题有两个方面:
connect
调用工作void g(int)代码>
emit
调用对函数void g(int)起作用代码>
插槽及其调用函数制作一些“神奇”的参数包缩减器,但我看不出它们应该如何组合在一起,所以很难真正开始编写解决方案。如果至少Clang/GCC和VisualStudio2015可以编译,我可以使用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');
}
#包括
#包括
模板
结构槽
{
virtual~slot()=默认值;
虚拟无效调用(ArgTypes…)const=0;
};
模板
结构可调用\u插槽:插槽
{
callable_slot(callable callable):callable(callable){}
无效调用(ArgTypes…args)常量重写{callable(args…;}
可调用的可调用的;
};
模板
结构信号
{
模板
void connect(可调用可调用)
{
slots.emplace_back(std::make_unique(callable));
}
无效发射(ArgTypes…args)
{
用于(常量自动和插槽:插槽)
{
插槽->呼叫(args…);
}
}
向量时隙;
};
void f(int,char){}
int main()
{
信号s;
s、 连接(&f);
s、 发射(42,'c');
}
对于成员指针支持(这需要SFINAE友好的std::result\u of
),请将const\u lvalue\u call\u t
更改为
template<class C, class...Args>
using const_lvalue_call_t = std::result_of_t<const C&(Args&&...)>;
std::ref(c)(std::forward<Args>(args)...);
这是穷人对左值可调用项的std::invoke
。如果您有C++17,只需直接使用std::invoke
(并使用std::void\t
而不是voidify
,尽管我喜欢后者的声音)。不确定您到底想要什么,但。。。使用std::tuple
和std::make_index_sequence
首先,您需要一个类型traits,它为您提供函数的参数数量(或std::function
)
然后必须修改call()
方法,将参数打包到std::tuple
中,并调用另一个方法,将tuple和索引序列从0传递到numA
void call(ArgTypes... args) const override
{ callI(std::make_tuple(args...), std::make_index_sequence<numA>{}); }
下面是一个完整的工作示例
#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');
}
#包括
#包括
#包括
#包括
模板
结构numArgs;
模板
结构numArgs
:std::积分常数
{ };
模板
结构numArgs
:std::积分常数
{ };
模板
结构槽
{
virtual~slot()=默认值;
虚拟无效调用(ArgTypes…)const=0;
};
模板
结构可调用\u插槽:插槽
{
静态constexpr std::size\u t numA{numArgs::value};
可调用\u插槽(可调用):可调用(可调用)
{ }
模板
void callI(std::tuple const&t,
std::index_序列常数&)常数
{可调用(std::get(t)…);}
无效调用(ArgTypes…args)常量重写
{callI(std::make_tuple(args…,std::make_index_sequence{})}
可调用的可调用的;
};
模板
结构信号
{
模板
void connect(可调用可调用)
{
把你放回原处(
std::使_唯一(可调用));
}
无效发射(ArgTypes…args)
{for(const auto&slot:slots)slot->call(args…)}
向量时隙;
};
空f(整数i,字符c)
{std::cout注意,我希望在任何情况下保持对第一个M
参数的严格类型检查,这样做没有必要取消类型安全性。另一方面,如果我被迫绕过虚拟将函数调用转发到实际可调用对象并实现“不可能的快速代表"我会考虑这个问题,因为这种类型的东西使我很难理解。在我需要很多额外的代码来处理lambda和成员函数时,这似乎是有限的,或者说我错了吗?@ RuNVB -大量的额外代码?不,我不这么认为。在我看来,这就足以添加<代码> NUMAGS/<代码>专业化,用于<代码> STD::函数
。真正的问题是lambda函数没有一个真正的类型(任何lambda函数都构成一个类型);因此您需要将它们包装在std::function
s中(这对于结构/类的非静态方法也很有用,与std::bind
);对于结构/类的静态方法,代码工作没有问题。答案针对lambda和方法(带示例)进行了修改。我不想要std::function
的开销,也不想要任何类似的std::bind
。请注意我拥有的插槽(完整的东西,不是这个有限的示例)其行为相当于可重置的std::function
(可重置成员函数指针及其调用对象)@rubenvb-我明白了…嗯,如果没有std::function
,我不知道如何管理lambda;也许其他人可以解决这个问题。这看起来很有希望。我想我可以强制使用参数较少的函数构造可调用的_插槽,然后我就拥有了它。谢谢!嗯,看起来像:(.让我们看看我是否可以强迫它这么做…多谢大家,我让它工作了:。仍然比真实版本的功能要弱一点,但是
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)>
{ };
static constexpr std::size_t numA { numArgs<Callable>::value };
void call(ArgTypes... args) const override
{ callI(std::make_tuple(args...), std::make_index_sequence<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');
}