I invoke a templated lambda from a templated function, the lambda parameters type are deduced. If the type of the lambda if auto, it works : https://godbolt.org/z/WYxj5G8vx
#include <iostream>
#include <cstdint>
#include <array>
#include <functional>
#include <numeric>
#include <concepts>
template <typename T>
int testf2(T, auto fun) {
std::array<std::uint8_t, sizeof(T)> ar{};
std::iota(ar.begin(), ar.end(), 0);
return fun(ar);
}
int main() {
auto f2 = []<size_t S> (std::array<uint8_t, S> arr) -> int {
return arr[S -1];
};
std::cout << "R = " << testf2(5, f2) << std::endl;
}
I wanted to use std::invocable
concept to specialize the auto fun
parameter of testf2
, to be anything but a callable that take std::array<std::uint8_t, N>
as parameter.
Using gcc11.2 or clang13, when I try
template <typename T, size_t S>
int testf2(T, std::invocable<std::array<uint8_t, S>> auto fun) {
std::array<std::uint8_t, sizeof(T)> ar{};
std::iota(ar.begin(), ar.end(), 0);
return fun(ar);
}
I get error :
candidate template ignored: couldn't infer template argument 'S' int testf2(T, std::invocable<std::array<uint8_t, S>> auto fun) {
I don't understand why the compiler can infer type when only auto is used, but not with a constraining concept.
What is the correct way to use concept in this situation ?
This is a simplified version of the code, in reality the signature of testf2
is testf2(auto fun, ARGS... args)
and the size of the array is calculated upon the parameter pack types.
============ EDIT 03/03/2022 ==================
Thanks for the correct answers, but I have oversimplified the code and the question, so I get right answer to a wrong question.
You need more context, I work with MCUs, and want to make a function that abstract some kind of spi,i2c,modbus, etc transaction where one send buffer to the slave peripheral and receive buffer in response. The function calculate write and read buffer length, serialise (doing endianness conversion if needed), call a lambda to do the actual transaction depending on the transport mechanism, deserialise and return. So the buffers lengths cannot be calculated with a (sizeof(Ts) + ...) as suggested.
I made a more realistic example :live example
// return empty array whose size is the sum of the two arrays given as parameters
template<typename T, std::size_t LL, std::size_t RL>
constexpr std::array<T, LL+RL> join(std::array<T, LL>, std::array<T, RL>)
{
return std::array<T, LL+RL>{};
}
// return an array of size sizeof(T) if T is arithmetic, otherwise an empty array
template <typename T>
constexpr auto count_ari(T) {
if constexpr (std::is_arithmetic_v<T>) {
return std::array<uint8_t, sizeof(T)>{};
} else {
return std::array<uint8_t, 0>{};
}
}
// return empty array whose size is the sum of all parameter which are arithmetic
template <typename HEAD, typename... TAIL>
constexpr auto count_ari(HEAD h, TAIL... tail) {
return join(count_ari(h), count_ari(tail...));
}
// create a iota filled array whose size is sum of all arithmetic parameters
// call a lambda given in parameter on this array
// return what has done the lambda
// it's here that I want to constrain parameter "auto fun"
template </*size_t S,*/ typename... ARGS>
int testf2(/*std::invocable<std::array<uint8_t, S>>, */ auto fun, ARGS... args) {
auto ar = count_ari(args...);
std::iota(ar.begin(), ar.end(), 1);
return fun(ar);
}
int main() {
auto f2 = []<size_t S> (std::array<uint8_t, S> arr) -> int {
return arr[S -1];
};
std::cout << "R = " << testf2(f2, 'a') << std::endl;
std::cout << "R = " << testf2(f2, 6, 7l, "foobar") << std::endl;
}
Question remains the same : is there a way to add constrain on the auto fun parameter of function testf2
Concepts (and requires clauses in general) do not participate in template argument deduction. Since your
S
in this case is justsizeof(T)
, you should use that.Then make it
sizeof(Args) + ...
.