我想从库中请求一系列数据,这需要我指定两种类型:
class a;
class b;
class c;
template<typename A, typename B> void get() {....}
void get_all() {
get<a,a>()
get<a,b>()
get<a,c>()
get<b,a>()
get<b,b>()
// etc...
}
我想调用get&lt; A,B&gt;函数用于一组类的每个组合(大约10种不同的变体) - 意味着大约100个不同的调用。我不想手工编写所有这些get()调用。
有没有办法自动生成这些调用?我想我可以使用预处理器宏,但我很好奇是否有办法生成这样的组合列表,给定a,b,c,d,......使用模板代码。
编辑:次要并发症:实际上,这都是成员函数。
template<typename A, typename B>
void Getter::get()
{
auto assn = fMemberObject->RetrieveAllDataOfType<association<A,B>();
.. do stuff with assn ...
}
void Getter::get_all() {
get<a,a>();
}
因此,代码需要处理传递this
和模板。
另外,如果可能的话,C ++ 11可能就我想推出语言而言......
编辑2:
结果我想避免重复的案例<a,a>
和<b,b>
......
答案 0 :(得分:4)
已更新以处理get()
未使用相同类型参数调用的新要求,并且get()
是类成员函数。我在这里打电话给班级manager
。
您可以(ab)使用C ++ 11可变参数模板来实现此目的。可能有一种更简洁的方法来做到这一点,但它有效并且(希望)可以理解:
struct manager
{
// Sample implementation of get(), showing the names of the types.
template <typename A, typename B>
void get() {
std::cout << "A=" << typeid(A).name() << " B=" << typeid(B).name() << '\n';
}
};
// Implementation, uses variadic templates with recursion.
namespace detail
{
// Helper to delimit template parameter packs.
template <typename...> struct pack;
// Terminating case, <=1 type argument(s) means we are done.
template <typename...>
struct call_get_all_beta
{
static void call(manager &) { }
};
// Invoke get<First, Second>() and recurse with <First, Tail...>.
template <typename First, typename Second, typename... Tail>
struct call_get_all_beta<First, Second, Tail...>
{
static void call(manager &m) {
m.get<First, Second>();
call_get_all_beta<First, Tail...>::call(m);
}
};
// Specialization to handle skipping over types that are the same.
template <typename First, typename... Tail>
struct call_get_all_beta<First, First, Tail...>
{
static void call(manager &m) {
call_get_all_beta<First, Tail...>::call(m);
}
};
template <typename...>
struct call_get_all_alpha;
// Terminating case, first pack is empty.
template <typename... B>
struct call_get_all_alpha<pack<>, pack<B...>>
{
static void call(manager &) { }
};
// Pass <FirstA, B...> on to call_get_all_beta, recurse with
// <pack<TailA...>, pack<B...>>.
template <typename FirstA, typename... TailA, typename... B>
struct call_get_all_alpha<pack<FirstA, TailA...>, pack<B...>>
{
static void call(manager &m) {
call_get_all_beta<FirstA, B...>::call(m);
call_get_all_alpha<pack<TailA...>, pack<B...>>::call(m);
}
};
}
// Helper to call the implementation detail.
template <typename... Types>
void get_all_permutations(manager &m)
{
detail::call_get_all_alpha<detail::pack<Types...>, detail::pack<Types...>>::call(m);
}
然后我们只做get_all_permutations<a, b, c>();
。 (Demo)
为解释这是如何工作的,detail::call_get_all_alpha
需要两个pack<...>
模板参数,两者最初都包含整个类型集。第二个保持不变,但每次递送时,每种类型都从第一个包装上剥离。第一种类型和完整的类型(通过第二包)传递给detail::call_get_all_beta
,它使用相同的&#34;剥离&#34;将<A, B, C, D>
转换为对get<A, B>()
,get<A, C>()
和get<A, D>()
的调用的递归技巧。
因此,在更高级别,detail::call_get_all_alpha
负责将第一个模板参数迭代到get()
,而detail::call_get_all_beta
负责迭代第二个模板参数。
答案 1 :(得分:3)
如果对解决模板元编程问题有疑问,可以考虑如何在正常编程中执行此操作。在这种情况下,我们需要类似的东西:
for a in type_list:
for b in type_list:
foo(a,b)
所以,让我们直接将它翻译成C ++。我们需要一种表示类型和类型列表的方法:
template <class T> struct tag { };
template <class... Ts> struct type_list { };
我们需要一种迭代类型列表并为每种类型做一些事情的方法。我们称之为函数调用。在C ++ 17中(在C ++ 14及更低版本中,您可以使用swallow / expander技巧在每个f
上调用tag<Ts>
):
template <class... Ts, class F>
void for_each(type_list<Ts...>, F f) {
(f(tag<Ts>{}), ...);
}
而那......实际上基本上就是这样。我们需要一个我们实际调用的函数,一个类型列表和代码:
template <class X, class Y>
void foo(tag<X>, tag<Y> ) {
std::cout << __PRETTY_FUNCTION__ << '\n'; // or something more meaningful
}
int main() {
type_list<a, b, c> types;
for_each(types, [=](auto x){
for_each(types, [=](auto y){
foo(x, y);
});});
}
C++11 version,有点展示了普通lambda的力量。
基于希望类型不同的更新,这也很容易更新。我们可以向tag
添加一个等于运算符:
template <class T, class U>
constexpr std::integral_constant<bool, std::is_same<T,U>::value>
operator==(tag<T>, tag<U> ) { return {}; }
template <class T, class U>
constexpr std::integral_constant<bool, !std::is_same<T,U>::value>
operator!=(tag<T>, tag<U> ) { return {}; }
请注意,我们不会返回bool
,我们会返回在类型系统中对结果进行编码的类型。然后我们可以自然地使用它:
for_each(types, [=](auto x){
for_each(types, [=](auto y){
if constexpr (x != y) {
foo(x, y);
}
});});
foo(x,y)
只有在两种标签类型不同时才会被实例化。
答案 2 :(得分:1)
您可以使用以下内容:
class a;
class b;
class c;
template<typename A, typename B> void get() { /*....*/ }
namespace detail {
template <typename> struct tag{};
template <typename Tuple, std::size_t...Is>
void get_all_pairs(tag<Tuple>, std::index_sequence<Is...>)
{
constexpr auto size = std::tuple_size<Tuple>::value;
#if 1 // Folding expression with C++17
(get<std::tuple_element_t<Is / size, Tuple>,
std::tuple_element_t<Is % size, Tuple>>(), ...);
#else
const dummy[] = {0, (get<std::tuple_element_t<Is / size, Tuple>,
std::tuple_element_t<Is % size, Tuple>>(),
void(), 0)...};
static_cast<void>(dummy); // Avoid warning for unused variable.
#endif
}
template <typename ... Ts>
void get_all_pairs() {
get_all_pairs(tag<std::tuple<Ts...>>{},
std::make_index_sequence<sizeof...(Ts) * sizeof...(Ts)>{});
}
}
void get_all() { detail::get_all_pairs<a, b, c>(); }
答案 3 :(得分:1)
如果你可以使用Boost,你可以避免很多基本的类型列表和应用程序样板,从而使代码更容易阅读。无需在这里重新发明轮子。
此示例使用Boost.MPL,它具有广泛的编译器支持,但我确信Boost.Hana中的等效解决方案更漂亮。
首先,生成对的函数(改编自this answer):
#include <utility>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/lambda.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/push_back.hpp>
// For each element in TList, append to Accumulator
// the type pair<PairFirst, element>.
template <
typename TList, typename PairFirst, typename Accumulator = boost::mpl::vector<>>
struct generate_pairs_fixed_element :
boost::mpl::fold<
TList,
Accumulator,
boost::mpl::push_back<boost::mpl::_1, std::pair<PairFirst, boost::mpl::_2>>>
{};
// For each element in TList, concatenate to Accumulator
// the result of generate_pairs_fixed_element<TList, element>.
template <typename TList, typename Accumulator = boost::mpl::vector<>>
struct generate_pairs_combinations :
boost::mpl::fold<
TList,
Accumulator,
boost::mpl::lambda<
generate_pairs_fixed_element<TList, boost::mpl::_2, boost::mpl::_1>
>>
{};
现在只需将您的类型收集到列表中并生成组合:
class a {};
class b {};
class c {};
class d {};
typedef boost::mpl::vector<a, b, c, d> all_classes_t;
typedef generate_pairs_combinations<all_classes_t>::type all_classes_pairs_t;
随心所欲地做任何事情:
#include <iostream>
#include <boost/mpl/for_each.hpp>
#include <boost/type.hpp>
template <typename A, typename B>
void get() {
std::cout << "Getting " << typeid(A).name()
<< " and " << typeid(B).name() << std::endl;
}
struct pair_visitor {
template <typename T, typename U>
void operator()(boost::type<std::pair<T, U>>) const {
get<T, U>();
}
};
int main() {
boost::mpl::for_each<all_classes_pairs_t, boost::type<boost::mpl::_>>(pair_visitor());
}