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{});
}
Related
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
template <std::size_t N, class... Args>
using type = typename std::tuple_element<N, std::tuple<Args...>>::type;
template<typename ...Args, typename Func>
typename std::result_of<Func(Args...)>::type functionOnParams(Func fp, Args&&...args)
{
using Indices = std::make_index_sequence<sizeof...(Args)>;
int ints[2] = {1,2};
return fp(std::forward<Args>( static_cast<type<index_of_args, Args...>>(ints[index_of_args++]) )...);
}
Hi,
I have spent a number of hours trying to figure to how to get the index_of_args for the above code. Any help would be appreciated. My searches keep going around in circles. index_of_args does not exist. I need to generate this index for each element of the std::forward.
Thank you
you can get make an std::index_sequence from Args and pass them to a helper function (by template arguments deduction). and then do anything you want in the helper function.
template<typename Func, typename... Args, size_t... Is, typename RealArgs>
std::invoke_result_t<Func&&, Args&&...> helper(Func&& func, std::index_sequence<Is...>* indicesTag, std::tuple<Args...>* argsTypeTag, RealArgs&& args){
return std::forward<Func>(func)(std::forward<Args>(std::forward<RealArgs>(args)[Is])...);
}
template<typename Func, typename... Args>
std::invoke_result_t<Func&&, Args&&...> functionOnParams(Func&& func, Args&&... args){
int a[] = { 0, 1 };
return helper(std::forward<Func>(func), (std::index_sequence_for<Args...>*)nullptr, (std::tuple<Args...>*)nullptr, a /* anything to replace the arguments */);
}
int main(){
auto x = functionOnParams(std::plus<>(), 0, 1.0f);
std::cout << x;
return 0;
}
in the other hand, you can also use class template specialization to pass the std::index_sequence and Args.
template<typename, typename>
struct Helper;
template<typename... Args, size_t... Is>
struct Helper<std::index_sequence<Is...>, std::tuple<Args...>>{
template<typename Func, typename RealArgs>
static std::invoke_result_t<Func&&, Args&&...> call(Func&& func, RealArgs&& args){
return std::forward<Func>(func)(std::forward<Args>(std::forward<RealArgs>(args)[Is])...);
}
};
template<typename Func, typename... Args>
std::invoke_result_t<Func&&, Args&&...> functionOnParams(Func&& func, Args&&... args){
int a[] = { 0, 1 };
return Helper<std::index_sequence_for<Args...>, std::tuple<Args...>>::call(std::forward<Func>(func), a /* anything to replace the arguments */);
}
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.
I'd like to write a template function which can invoke a function with given parameters.
For instance, I can write a simple invoke function:
template<class F, class... Args>
inline auto invoke(F &&func, Args&&... args) -> decltype(auto)
{
return std::forward<F>(func)(std::forward<Args>(args)...);
}
This invoke accepts same count of parameter which f requires. However, I want to this template function allow additional unused parameters.
That is, I want to write some code like:
auto f = [] (auto a) {...};
invoke(f, 1, 2, 3);
Here, f accepts only one parameter so, I wish invoke ignore other parameters except the first one.
This can be accomplished very easily by get arity of the lambda unless the lambda is generic.
Since f here is generic lambda, as far as I know, there's no general way to figure out arity of f without explicit instantiation of its template operator()<...>.
How can I wirte my invoke?
One possibility:
#include <utility>
#include <cstddef>
#include <tuple>
template <std::size_t... Is, typename F, typename Tuple>
auto invoke_impl(int, std::index_sequence<Is...>, F&& func, Tuple&& args)
-> decltype(std::forward<F>(func)(std::get<Is>(std::forward<Tuple>(args))...))
{
return std::forward<F>(func)(std::get<Is>(std::forward<Tuple>(args))...);
}
template <std::size_t... Is, typename F, typename Tuple>
decltype(auto) invoke_impl(char, std::index_sequence<Is...>, F&& func, Tuple&& args)
{
return invoke_impl(0
, std::index_sequence<Is..., sizeof...(Is)>{}
, std::forward<F>(func)
, std::forward<Tuple>(args));
}
template <typename F, typename... Args>
decltype(auto) invoke(F&& func, Args&&... args)
{
return invoke_impl(0
, std::index_sequence<>{}
, std::forward<F>(func)
, std::forward_as_tuple(std::forward<Args>(args)...));
}
DEMO
Here's a code I've written for my needs based on Piotr's answer:
template <std::size_t... Is, typename F, typename Tuple>
void invoke_impl(
std::index_sequence<Is...>, F&& f, Tuple&& args,
std::enable_if_t<std::is_invocable_v<F, std::tuple_element_t<Is, Tuple>...>,
void>* = nullptr)
{
std::invoke(std::forward<F>(f), std::get<Is>(args)...);
}
template <std::size_t... Is, typename F, typename Tuple>
void invoke_impl(
std::index_sequence<Is...>, F&& f, Tuple&& args,
std::enable_if_t<
!std::is_invocable_v<F, std::tuple_element_t<Is, Tuple>...>, void>* =
nullptr)
{
static_assert(sizeof...(Is) > 0, "Not invocable with arguments supplied");
if constexpr (sizeof...(Is) > 0)
{
invoke_impl(std::make_index_sequence<sizeof...(Is) - 1>(),
std::forward<F>(f), std::move(args));
}
}
// invokes function with possibly too many parameters provided
template <typename F, typename... Args>
void invoke(F&& f, Args&&... args)
{
invoke_impl(std::make_index_sequence<sizeof...(Args)>(), std::forward<F>(f),
std::forward_as_tuple(std::forward<Args>(args)...));
}
It is a little bit improved:
it leverages std::invoke so it works with more use cases, like pionter to members
it matches parameters count starting from sizeof...(Args) so chooses better matched operator() overload
Here's a demo:
struct test
{
void foo()
{
std::cout << "foo" << std::endl;
}
void operator()()
{
std::cout << "operator()()" << std::endl;
}
void operator()(float)
{
std::cout << "operator()(float)" << std::endl;
}
};
int main()
{
test obj;
invoke(&test::foo, &obj, 1, 2, 3); // foo
invoke(obj); // operator()()
invoke(obj, 1, 2, 3); // operator()(float)
// invoke(5); // assert failed
}
Here's a live demo
I am trying to write a general static_for implementation that can accept bounds, an increment function & a comparison function to run a loop through. I have been using this construct with simple loops that increment by 1. In that case it is easy to stop the loop unrolling by simply specializing on the IDX & END being equal.
However when the increment could be with an arbitrary integer, it is not guaranteed that the IDX & END will always be equal. The if conditional is only evaluated at run time. In the code snippet below I was trying to specialize on the std::false_type which stops the recursion. The integral_constant is constructed by evaluating the std::less functional (which could be substituted by the user for any other evaluation). Unfortunately this comparator functional is also evaluated only at run time and therefore the compiler fails. Could someone advise on how to get this to work?
NOTE: Using C++11.
template <int idx, int end, typename eval, int count, typename comparator>
struct static_for_loop {
template <typename Lambda, typename... Args>
void operator()(const Lambda& function, Args... args) const {
if (comparator()(idx, end)) {
std::integral_constant<int, idx> i;
function(i, args...);
constexpr bool lesser = comparator()(idx + count, end);
static_for_loop<idx + count, end, std::integral_constant<bool, lesser>, count,
comparator>()(function, args...);
}
}
};
template <int idx, int end, int count, typename comparator>
struct static_for_loop<idx, end, std::false_type, count, comparator> {
template <typename Lambda, typename... Args>
void operator()(const Lambda& function, Args... args) const {}
};
template <int idx, int end, int count = 1, typename comparator = std::less<int>>
struct static_for {
template <typename Lambda, typename... Args>
void operator()(const Lambda& function, Args... args) const {
static_for_loop<idx, end, std::true_type, count, comparator>()(function, args...);
}
};
I find it easier to just wrap everything in an object:
template <int S, int E, int step>
struct iter {
auto next() { return iter<std::min(E, S+step), E, step>{}; }
};
And then you just have an overload for the case where it's done and the case where it's not:
template <int S, int E, int step, class F, class... Args>
void for_loop(iter<S, E, step> i, F func, Args... args) {
func(S, args...);
for_loop(i.next(), func, args...);
}
template <int E, int step, class F, class... Args>
void for_loop(iter<E, E, step>, F, Args... ) {
}
For instance:
// prints 0 4 8
for_loop(iter<0, 10, 4>{}, [](int i){std::cout << i << ' ';});
Alternatively, could use enable_if to differentiate the cases to avoid the need for min:
template <int S, int E, int step, class F, class... Args>
std::enable_if_t<(S<E)> for_loop(iter<S, E, step>, F func, Args... args)
{
func(S, args...);
for_loop(iter<S+step, E, step>{}, func, args...);
}
template <int S, int E, int step, class F, class... Args>
std::enable_if_t<!(S<E)> for_loop(iter<S, E, step>, F , Args... )
{
}
YMMV on which you prefer.
You can use sfinae to overcome the problem:
template <int idx, int end, typename eval, int count, typename Comparator>
struct static_for_loop {
template <typename Lambda, typename... Args>
auto operator()(Lambda&& function, Args&&... args) const
-> std::enable_if_t<Comparator{}(idx, end)> {
std::integral_constant<int, idx> i;
std::forward<Lambda>(function)(i, std::forward<Args>(args)...);
constexpr bool lesser = comparator{}(idx + count, end);
static_for_loop<
idx + count,
END,
std::integral_constant<bool, lesser>,
count,
Comparator
>()(std::forward<Lambda>(function), std::forward<Args>(args)...);
}
// do nothing when false
template <typename Lambda, typename... Args>
auto operator()(Lambda&& function, Args&&... args) const
-> std::enable_if_t<!Comparator{}(idx, end)> {
}
};
std::enable_if will select the right function with sfinae. It will act as a compile time if.
I used perfect forwarding too, as your code didn't work in all case, like passing non copiable or a mutable lambda. Now it will.
If you do not have c++14, you can write typename std::enable_if<...>::type instead.
Try to use less all uppercase name, it hurts lisibility.
Isn't the problem that you are underspecifying comparator? Just specify your API such that comparator<IDX>::type is std::true_type if the loop should continue for IDX, and stop when it's false_type. Your simple loop case then uses template<int IDX> using Comp = std::integral_constant<bool, (IDX < 5)>`.