I have an example parameter pack function:
template<typename T, typename ... Args>
constexpr bool all_values_equal(T first, Args... args) {
return ((first == args) && ...);
}
static_assert(all_values_equal(1, 1, 1.0));
I often use std::tuple as I like to be able to work with templates. Extracting types, Slicing types and other arrangements.
how can I call this function with an std::tuple?
template<typename ... Args>
constexpr bool all_values_equal(std::tuple<Args...> tuple) {
return /* ??? */ ;
}
static_assert(all_values_equal(std::make_tuple(1, 1, 1.0)));
I would also like know the case for variadic template functions with no arguments:
template<typename T, typename ... Args>
constexpr bool all_types_equal() {
return (std::is_same_v<T, Args> && ...);
}
template <template<typename ... Args> class Tuple>
constexpr bool all_types_equal() {
return /* ??? */ ;
}
static_assert(all_types_equal<int, int>());
static_assert(all_types_equal<std::tuple<int, int>>());
ultimately I want to be able to call all 4 variations like this:
static_assert(all_values_equal(1, 1, 1.0));
static_assert(all_values_equal(std::make_tuple(1, 1, 1.0)));
static_assert(all_types_equal<int, int>());
static_assert(all_types_equal<std::tuple<int, int>>());
and the std::tuple functions should not re-implement the logic of the variadic template functions.
how can I achieve this in a clean and modern way?
std::apply let's you call a function with the tuple elements as parameters. You can use that with a lambda.
template<typename ... Args>
constexpr bool all_values_equal(std::tuple<Args...> tuple) {
auto cmp = [](auto&& first, auto&&... args) {
return ((first == args) && ...);
};
return std::apply(cmp, tuple);
}
To make the all_types_equal check we can use partial specialization. Something like this.
template <typename First, typename ... Rest>
constexpr bool all_types_equal_impl = (std::is_same_v<First, Rest> && ...);;
template <typename First, typename ... Rest>
constexpr bool all_types_equal_impl<std::tuple<First, Rest...>> = (std::is_same_v<First, Rest> && ...);
template <typename... Args>
constexpr bool all_types_equal() {
return all_types_equal_impl<Args...>;
}
We can refer to the template variable directly, so wrapping it in a function is not really required if we don't want to.
using an std::index_sequence allows to access each data field and each type of a std::tuple.
since C++20 lambda's can be templated, this allows an inline unpacking.
template<typename ... Args>
constexpr bool all_values_equal(std::tuple<Args...> tuple) {
constexpr auto unpack_tuple = []<typename Tuple, size_t... Ints>(Tuple tuple, std::index_sequence<Ints...>) {
return all_values_equal(std::get<Ints>(tuple)...);
};
return unpack_tuple(tuple, std::make_index_sequence<std::tuple_size_v<decltype(tuple)>>());
}
template <template<typename ... Args> class Tuple>
constexpr bool all_types_equal() {
constexpr auto unpack_tuple = []<typename Tuple, size_t... Ints>(std::index_sequence<Ints...>) {
return all_types_equal<std::tuple_element_t<Ints, Tuple>...>();
};
return unpack_tuple.template operator()<Tuple>(std::make_index_sequence<std::tuple_size_v<Tuple>>());
}
static_assert(all_values_equal(1, 1, 1.0));
static_assert(all_values_equal(std::make_tuple(1, 1, 1.0)));
static_assert(all_types_equal<int, int>());
static_assert(all_types_equal<std::tuple<int, int>>());
std::make_index_sequence and std::tuple_size_v are convenient utilities from the standard.
std::get<Int>(tuple)... unpacks the values, std::tuple_element_t<Ints, Tuple>... unpacks the types.
.template operator()<Tuple>(...) allows to specify a template of a templated lambda
Related
Is it possible to interleave template parameters inside of function call sites?
I effectively want to do implement the following, but I don't know how (psuedo code):
template <size_t... indices, typename... Ts>
void foo(const Things *things)
{
static_assert(sizeof...(indices) == sizeof...(Ts));
constexpr n = sizeof...(Ts);
bar(
indices[0], parse<Ts[0]>(things[0]),
indices[1], parse<Ts[1]>(things[1]),
...
indices[n-1], parse<Ts[n-1]>(things[n-1]));
}
Note: I know the following can be done (psuedo code):
template <size_t... indices, typename... Ts>
void foo(const Things *things)
{
static_assert(sizeof...(indices) == sizeof...(Ts));
constexpr n = sizeof...(Ts);
bar(
indices[0], indices[1], ..., indices[n-1],
parse<Ts[0]>(things[0]),
parse<Ts[1]>(things[1]),
...
parse<Ts[n-1]>(things[n-1]));
}
A partial solution I came up with is to add a swizzling component:
template <typename Func>
decltype(auto) swizzle()
{
return Func();
}
template <typename Func, typename T0>
decltype(auto) swizzle(size_t i0, T0 &&t0)
{
return Func(i0, std::forward<T0>(t0));
}
template <typename Func, typename T0, typename T1>
decltype(auto) swizzle(size_t i0, size_t i1, T0 &&t0, T1 &&t1)
{
return Func(i0, std::forward<T0>(t0), i1, std::forward<T1>(t1));
}
but I think I have to manually write each case per arity I want to consider.
Like this:
template <size_t... indices, typename... Ts>
void foo(const Things *things)
{
std::apply([](auto...args) {
bar(args...);
}, std::tuple_cat(std::make_tuple(indices, parse<Ts>(*(things++)))...));
}
If bar is a lambda instead of a function template, you can just pass bar directly as the first argument to std::apply.
If you want to avoid copying the return values of parse<Ts>(*(things++)), you can use std::forward_as_tuple instead of std::make_tuple.
One slightly more verbose alternative if the *(things++) makes you uncomfortable is to use std::index_sequence:
template <size_t... indices, typename... Ts>
void foo(const Things *things)
{
[=]<auto... Is>(std::index_sequence<Is...>) {
std::apply([](auto...args) {
bar(args...);
}, std::tuple_cat(std::make_tuple(indices, parse<Ts>(things[Is]))...));
}(std::index_sequence_for<Ts...>());
}
So I have a constexpr array, is there a way to templatize each of its elements as a template parameter, the purpose is that so I can call a function of each of them, but instead of a loop which is runtime, I'd like to call it at compile time, also be able to use static_assert on it? So far I tried the following but it didn't work, the compiler couldn't deduce the proper parameter to the func. I'm using version c++17.
template <auto param, typename Function, typename... Args>
void invoke_param(Function func, Args... args)
{
func(param, args...);
}
template <auto... params, typename Function, typename... Args>
void invoke_params(Function func, Args... args)
{
(invoke_param<params>(func, args...), ...);
}
template <auto& Array, std::size_t... Indices, typename Function, typename... Args>
auto for_each_array_impl(std::index_sequence<Indices...>, Function func, Args... args)
{
invoke_params<Array[Indices]...>(func, args...);
}
template <auto& Array, typename Function, typename... Args>
auto for_each_array(Function func, Args... args)
{
return for_each_array_impl<Array>(
std::make_index_sequence<std::size(Array)>(), func, args...);
}
static constexpr std::array<int, 5> ARR{1,2,3,4,5};
int main() {
auto fun = []<int E>() {
static_assert(E > 0);
//...;
};
for_each_array<ARR>(func);
}
You need to pass param as a template parameter if you want it to be a template parameter
template <auto param, typename Function, typename... Args>
void invoke_param(Function func, Args... args)
{
func.template operator()<param>(args...);
}
As an aside, you need C++20 for []<int E>(), before then you'd have to write it longhand.
See it on coliru
In C++20 we can condense all the boilerplate down into a single function thanks to template lambdas and expanded auto parameters:
Live Demo
template<auto& arr, auto F>
consteval auto for_each_array()
{
[]<auto... indices>(std::index_sequence<indices...>)
{
(F.template operator()<arr[indices]>(), ...);
}(std::make_index_sequence<arr.size()>{});
}
static constexpr std::array<int, 5> ARR{1,2,3,4,5};
int main() {
constexpr auto fun = []<int E>()
{
static_assert(E > 0);
//...
};
for_each_array<ARR, fun>();
}
for_each_array creates a helper lambda and immediately invokes it, making use of a fold-expression to call your function for each array value.
EDIT:
C++17 version
template<auto& arr, class F, auto... indices>
constexpr auto for_each_array_impl(F callable, std::index_sequence<indices...>)
{
(callable.template operator()<arr[indices]>(), ...);
}
template<auto& arr, class F>
constexpr auto for_each_array(F&& callable)
{
for_each_array_impl<arr>(std::forward<F>(callable),
std::make_index_sequence<arr.size()>{});
}
Called like so:
struct fun
{
template <int E>
void operator()()
{
static_assert(E > 0);
//...
}
};
static constexpr std::array<int, 5> ARR{1,2,3,4,5};
int main() {
for_each_array<ARR>(fun{});
}
This is an extension of previous question (Link)
I need to include a different number of objects depending on the provided 'define' and with different ctor parameters. First parameter is an 'index' of object from zero and to (NUMBER-1), other parameters are optional.
So far I don't have issues when only single 'index' parameter is presented but I'm still straggling to add optional parameters Args... args.
That's how I'm trying to do that. Let's say we are gonna to instantiate 2 following classes
class Output
{
public:
explicit Output(uint32_t idx) : m_idx(idx) { printf("ctor: %u\n", m_idx); };
private:
uint32_t m_idx = -1;
};
class Input
{
public:
explicit Input(uint32_t idx, std::string name) : m_idx(idx), m_name(name) { printf("ctor: %u [%s]\n", m_idx, m_name.data()); };
private:
uint32_t m_idx = -1;
std::string m_name;
};
There are 2 templates to instantiate with sequential indexing
template<typename T, typename... Args, typename TInts, TInts... I>
constexpr auto MakeArrayHelper(Args... args, std::integer_sequence<TInts, I...>)
{
return std::array<T, sizeof...(I)>{ (I)..., std::forward<Args>(args)... };
}
template <typename T, size_t Count, typename... Args, typename BaseType = uint32_t>
constexpr auto MakeArray(Args... args)
{
return MakeArrayHelper<T>((args)..., std::make_integer_sequence<BaseType, Count>());
}
And I wanted to instantiate classes like that
auto outputs = MakeArray<Output, 5>();
auto inputs = MakeArray<Input, 3>(std::string("Analog"));
expanded into:
std::array<Output, 5> = { Output{0}, Output{1}, Output{2}, Output{3}, Output{4} };
std::array<Input, 3> = { Input{0, "Analog"}, Input{1, "Analog"}, Input{2, "Analog"} };
This leaves me with a compilation error: could not deduce template argument for 'TInts'
Could you help me with understanding what I'm doing wrong.
Thanks.
It's difficult to give a full answer without a full example but... I see some problems in MakeArrayHelper()
First of all, the variadic pack of parameter has to be in last position, or the deduction fails.
So, instead of
template<typename T, typename... Args, typename TInts, TInts... I>
constexpr auto MakeArrayHelper(Args... args, std::integer_sequence<TInts, I...>)
you can try with
template<typename T, typename TInts, TInts... I, typename... Args>
constexpr auto MakeArrayHelper(std::integer_sequence<TInts, I...>, Args ... args)
Second: if you want use forwarding, the args arguments has to be forwarding references
template<typename T, typename TInts, TInts... I, typename... Args>
constexpr auto MakeArrayHelper(std::integer_sequence<TInts, I...>, Args && ... args)
// .....................................................................^^
Third: inside the function you declare a std::array<T, sizeof...(TInts)> but you initialize it with
{ (I)..., std::forward<Args>(args)... };
a sequence of sizeof...(TInts) and some args....
I don't understand what you want obtain but this e clearly wrong.
--- EDIT ---
Or maybe I understand what do you want... If I understand correctly, what you want is something as (caution: code not tested)
template <typename T, typename TInts, TInts... I, typename ... Args>
constexpr auto MakeArrayHelper (std::integer_sequence<TInts, I...>, Args const & ... args)
{
return std::array<T, sizeof...(I)>{ T{I, args...} ... };
}
template <typename T, std::size_t Count, typename BaseType = std::uint32_t, typename ... args>
constexpr auto MakeArray (Args const & ... args)
{
return MakeArrayHelper<T>(std::make_integer_sequence<BaseType, Count>(), args...);
}
Avoid forwarding args... in MakeArrayHelper() because you can't (without risks) forward the same variable more than a time.
Assume the following templates:
template<typename T, bool stack>
struct bind_argument
{
static inline T get_arg(Object& obj, u8 index)
{
return ...;
}
};
template<typename RT, typename Arg0, typename... Args>
inline RT call(Object& obj, RT(*function)(Arg0, Args...))
{
constexpr bool use_stack = ...;
return function(..., bind_argument<Args, use_stack>::get_arg(obj, 0)...);
}
For bind_argument I need to pass the index of the expansion. Another question regarding the indexed expansion showcased the use of the "indices trick" using another template, but in my case I also need to pass the expanded arguments to the call of the function in the call method. This seems to be quite a bit harder than I thought.
My original solution using the "indices trick" looked like this:
template<bool stack, typename... Args, u64... Indices>
struct bind_arguments
{
static inline Args get_args(CPU& cpu, indices<Indices...>)
{
return bind_argument<Args, stack>(cpu, Indices)...;
}
};
template<typename RT, typename Arg0, typename... Args>
inline RT call(Object& obj, RT(*function)(Arg0, Args...))
{
constexpr bool use_stack = ...;
Arg0 some_value = ...;
return function(some_value, bind_arguments<use_stack, Args...>::get_args(obj, build_indices<sizeof...(Args)>{}));
}
Unfortunately this is not going to compile. How would one perform template indexed pack expansion inside another template and after that passing the expanded values to the place intended for the expanded values? (in this case the function() call)
The intended call expansion would be as follows:
function(some_value, bind_argument<A1, use_stack>(obj, 0), bind_argument<A2, use_stack>(obj, 1), bind_argument<A3, use_stack>(obj, 2), ...)
You can do whatever you want in that other function, forwarding all necessary arguments; there's no reason to return anything but the final result:
#include <utility>
#include <cstddef>
template <typename RT, typename Arg0, typename... Args, std::size_t... Is>
inline RT call(Object& obj, RT(*function)(Arg0, Args...), std::index_sequence<Is...>)
{
return function(&obj, bind_argument<Args>::get_arg(obj, Is)...);
}
template <typename RT, typename Arg0, typename... Args>
inline RT call(Object& obj, RT(*function)(Arg0, Args...))
{
return call(obj, function, std::make_index_sequence<sizeof...(Args)>{});
}
DEMO
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do I expand a tuple into variadic template function's arguments?
“unpacking” a tuple to call a matching function pointer
In C++11 templates, is there a way to use a tuple as the individual args of a (possibly template) function?
Example:
Let's say I have this function:
void foo(int a, int b)
{
}
And I have the tuple auto bar = std::make_tuple(1, 2).
Can I use that to call foo(1, 2) in a templaty way?
I don't mean simply foo(std::get<0>(bar), std::get<1>(bar)) since I want to do this in a template that doesn't know the number of args.
More complete example:
template<typename Func, typename... Args>
void caller(Func func, Args... args)
{
auto argtuple = std::make_tuple(args...);
do_stuff_with_tuple(argtuple);
func(insert_magic_here(argtuple)); // <-- this is the hard part
}
I should note that I'd prefer to not create one template that works for one arg, another that works for two, etc…
Try something like this:
// implementation details, users never invoke these directly
namespace detail
{
template <typename F, typename Tuple, bool Done, int Total, int... N>
struct call_impl
{
static void call(F f, Tuple && t)
{
call_impl<F, Tuple, Total == 1 + sizeof...(N), Total, N..., sizeof...(N)>::call(f, std::forward<Tuple>(t));
}
};
template <typename F, typename Tuple, int Total, int... N>
struct call_impl<F, Tuple, true, Total, N...>
{
static void call(F f, Tuple && t)
{
f(std::get<N>(std::forward<Tuple>(t))...);
}
};
}
// user invokes this
template <typename F, typename Tuple>
void call(F f, Tuple && t)
{
typedef typename std::decay<Tuple>::type ttype;
detail::call_impl<F, Tuple, 0 == std::tuple_size<ttype>::value, std::tuple_size<ttype>::value>::call(f, std::forward<Tuple>(t));
}
Example:
#include <cstdio>
int main()
{
auto t = std::make_tuple("%d, %d, %d\n", 1,2,3);
call(std::printf, t);
}
With some extra magic and using std::result_of, you can probably also make the entire thing return the correct return value.
Create an "index tuple" (a tuple of compile-time integers) then forward to another function that deduces the indices as a parameter pack and uses them in a pack expansion to call std::get on the tuple:
#include <redi/index_tuple.h>
template<typename Func, typename Tuple, unsigned... I>
void caller_impl(Func func, Tuple&& t, redi::index_tuple<I...>)
{
func(std::get<I>(t)...);
}
template<typename Func, typename... Args>
void caller(Func func, Args... args)
{
auto argtuple = std::make_tuple(args...);
do_stuff_with_tuple(argtuple);
typedef redi::to_index_tuple<Args...> indices;
caller_impl(func, argtuple, indices());
}
My implementation of index_tuple is at https://gitlab.com/redistd/redistd/blob/master/include/redi/index_tuple.h
but it relies on template aliases so if your compiler doesn't support that you'd need to modify it to use C++03-style "template typedefs" and replace the last two lines of caller with
typedef typename redi::make_index_tuple<sizeof...(Args)>::type indices;
caller_impl(func, argtuple, indices());
A similar utility was standardised as std::index_sequence in C++14 (see index_seq.h for a standalone C++11 implementation).