Variadic concept constraints - c++

Unfortunately this code doesn't work:
#include <iostream>
#include <type_traits>
#include <utility>
#include <tuple>
template<typename FirstArg, typename ... Args>
requires (sizeof ... (Args) == 0 || (std::is_convertible_v<Args ..., FirstArg>))
constexpr void multi_max( FirstArg firstArg, Args const &... args )
{
using namespace std;
FirstArg &max = firstArg;
auto findMax = [&]<size_t ... Is>( index_sequence<Is ...> iseq, tuple<Args ...> tArgs )
{
((max = get<Is>( tArgs ) > max ? get<Is>( tArgs ) : max), ...);
};
findMax( make_index_sequence<sizeof ... (Args)>(), make_tuple( args ... ) );
}
int main()
{
multi_max( 1 );
//multi_max( 1, 2, 3 );
}
The commented section doesn't fulfill the right half of the requires-constraint. Why ? And if I remove the first constraint the compilers complains about wrong unpacking of the args into a tuple.

Looking at the relevant portion of the compiler error:
note: because substituted constraint expression is ill-formed: too many template arguments for variable template 'is_convertible_v'
You're using is_convertible_v<int, int, int>. What you want is all tests of individual arguments to be true, which you can do with a fold expression:
requires (sizeof ... (Args) == 0 || (std::is_convertible_v<Args, FirstArg> && ...))
However, for a pack of 0 elements, the fold expression will produce true for &&, so the first part is now redundant:
requires (std::is_convertible_v<Args, FirstArg> && ...)
Cleaning up to use the concept convertible_to instead of the old type trait:
requires (std::convertible_to<Args, FirstArg> && ...)
Now things are looking a little suspiciously convenient. We can actually move this to the template parameter and get rid of the requires entirely:
template<typename FirstArg, std::convertible_to<FirstArg> ... Args>
This placeholder will shove FirstArg in as the second argument and place the given type as the first, so a given T would test std::convertible_to<T, FirstArg>.

Related

static_assert each parameter's size in parameter pack

I am trying to check whether each parameter within a parameter pack can be stored within 8 bytes (sizeof <= 8)
I have the function signature:
template <typename Return, typename... Arguments>
inline auto invoke(std::uint64_t hash, Arguments... arguments) -> Return
Using fold expressions, I have tried:
static_assert((sizeof(arguments) <= 8 && ...));
Which failed to compile with unexpected token '...', expected 'expression' - I assume it's invalid or incorrect?
Using C++20 concepts and constraints, I assume something along the lines of is possible?
template <typename Return, typename... Arguments> requires (sizeof(arguments) <= 8 || ...)
inline auto invoke(std::uint64_t hash, Arguments... arguments) -> Return
I assume there is a way of using the standard library to say check that a type fits within a std::uint64_t say also?
With C++20 concepts, there are many ways how to achieve the desired behavior. For instance:
template <typename T, size_t N>
concept bool SizeLessEqual = sizeof(T) <= N;
template <SizeLessEqual<8>... Types>
void f() { }
int main() {
f<bool, char, int, double>();
f<std::string>(); // error
}
Live demo: https://wandbox.org/permlink/Q9tifNVplsx9BjGN
Another option is your solution:
template <typename... Types> requires ((sizeof(Types) <= 8) && ...)
void f() { }
Or, e.g.:
template <typename... Types> requires (std::max({ sizeof(Types)... }) <= 8)
void f() { }
Try this way:
#include <cstdint>
#include <utility>
template <typename... Arguments>
auto invoke(std::uint64_t hash, Arguments... arguments)
{
auto check = []( auto&& argument )
{
static_assert( sizeof(argument) <= 8, "size too large" );
return 0;
};
auto dummy = { 0, ( check(std::forward<Arguments>(arguments)), 0) ... };
return 0;
}
int main()
{
invoke( 0UL, '1' );
invoke( 0UL, '1', 2 );
invoke( 0UL, '1', 2, 3UL );
//invoke( 0UL, '1', 2, 3UL, static_cast<long double>(1.0) );
return 0;
}
Using the comma operator and the initializer_list to do the trick.
With C++17, we can further trim the code to:
template <typename... Arguments>
auto invoke(std::uint64_t hash, Arguments... arguments)
{
auto check = []( auto&& argument )
{
static_assert( sizeof(argument) <= 8, "size too large" );
};
(check(std::forward<Arguments>(arguments)), ...);
}
taking the advantage of fold expressions.
I do not understand the downvotes, but as this is my last post in stackoverflow, I uploaded a live example at wandbox: https://wandbox.org/permlink/NZbqpRaTs2TFOCwG

Function with a fixed amount of parameters determined by an integer

I have a class with a template that accepts an integer:
template <unsigned int N>
class Example {};
I'm looking for a way to define a (member)function that accepts some amount of Example objects as arguments. The amount is to be determined by N, so the function would be used like this:
Function(Example<2>(), Example<2>());
Function(Example<3>(), Example<3>(), Example<3>());
What I tried so far:
Using an initializer list, one is able to pass a set of objects to the function:
template <unsigned int N>
void Function(std::initializer_list<Example<N>> list);
//...
Function({Example<2>(), Example<2>()});
However, the problem besides the fact that really only one argument is passed(the list), is that with this method any number of arguments can be used:
Function({Example<2>()});
I also tried using a variadic function:
template <unsigned int N>
void Function(Example<N> e...)
{
va_list args;
va_start(args, e);
//...
}
Function(Example<2>(), Example<2>());
This makes it possible to use real parameters, but the problem of using any number of arguments remains, and it's not possible to know how many arguments were actually passed.
Assuming you want the number of arguments to be deduced from the Example<N> type, and that all Example<I> should share the same such N, a C++17 solution might be
template <unsigned int... I>
auto Function( Example<I>... ) ->
std::enable_if_t<( ( I == sizeof...(I) ) && ... )>
{
// or static_assert() if you always want an error
}
Make Function a variadic template and use std::enable_if_t to constrain your it:
Some IsExample trait can be used to make sure that all arguments are instances of Example
sizeof...(pack) can be used to get the size of the parameter pack
template <unsigned int N, typename... Ts>
auto Function(Ts... xs)
-> std::enable_if_t<(IsExample<Ts>::value && ...)
&& (sizeof...(Ts) == N)>
{
}
live example on wandbox
You should utilize variadic function template with static_assert. Unlike approaches involving enable_if this one will produce a readable error message if incorrect arguments are passed.
template<unsigned int ... I>
void Function(Example<I>... items)
{
static_assert
(
true && (... && (static_cast<unsigned int>(sizeof...(I)) == I))
, "This function accepts N arguments of type Example<N>"
);
}
Online compiler
There are many answers that cover SFINAE friendly based constraints, but I don't like placing my SFINAE in the return value:
template <unsigned int... Is,
std::enable_if_t<( ( Is == sizeof...(Is) ) && ... ), bool> = true
>
void Function( Example<Is>... examples )
{
// code
}
or
template<bool b>
using test_requirement = std::enable_if_t<b, bool>;
template <unsigned int... Is,
test_requirement<( ( Is == sizeof...(Is) ) && ... )> = true
>
void Function( Example<Is>... examples )
{
// code
}
+1 for the Massimiliano Janes's elegant solution.
Unfortunately use folding so works only for C++17.
To test, with C++11/C++14, that all I are equals to sizeof...(I) (and maybe that sizeof...(I) is equal to N, where N is the class template argument), it's enough test that a variadic type, that receive unsigned values, is the same type with a different order of values.
I mean: declaring a trivial struct as
template <std::size_t ... Is>
struct IList;
the test can be
std::is_same<IList<N, sizeof...(Is), Is...>,
IList<sizeof...(Is), Is..., N>>::value
Starting from C++14 it's possible to use std::index_sequence instead of IList.
So Example can be written as
template <unsigned int N>
struct Example
{
template <unsigned int ... Is>
auto Function (Example<Is> ...)
-> typename std::enable_if<
std::is_same<IList<N, sizeof...(Is), Is...>,
IList<sizeof...(Is), Is..., N>>::value>::type
{ /* do something */ }
};
The following is a example of use (but remember to include <type_traits>)
int main()
{
Example<1U> e1;
Example<2U> e2;
// e1.Function(); // error
e1.Function(Example<1>{}); // compile
//e1.Function(Example<1>{}, Example<1>{}); // error
// e2.Function(); // error
//e2.Function(Example<2>{}); // error
e2.Function(Example<2>{}, Example<2>{}); // compile
//e2.Function(Example<2>{}, Example<2>{}, Example<2>{}); // error
}

Resolve overload ambiguity with SFINAE

I've found similar cases, but they usually ended up doing something along the lines of what I (think) I'm doing here.
I want to be able to call a function with one or more parameters, obviously, if the function exists with overloads with multiple parameters, the correct version cannot be deduced without help.
As I am specifying the number of arguments as well, I figured this would be enough information for the compiler to deduce the correct overload.
This doesn't seem to be the case and I hope you may be able to show me why.
the code:
http://coliru.stacked-crooked.com/a/5e6fd8d5418eee3c
#include <iostream>
#include <type_traits>
#include <functional>
template < typename R, typename... A, typename... Args >
typename std::enable_if< sizeof...( A ) == sizeof...( Args ), R >::type
call_my_function( R(*func)(A...), Args ...a )
{
return func( a... );
}
int arg_count() { return 0; }
int arg_count(int) { return 1; }
int arg_count(int,int) { return 2; }
int main()
{
std::cout << call_my_function( arg_count, 0 ) << std::endl;
return 0;
}
In short, I tried to have all functions which have a different argument count than the number of arguments I supplied, fail by means of SFINAE. But it seems they are considered anyway and the ambiguity remains.
Unfortunately not; SFINAE can be used for selecting between different function template definitions, but not between function overloads passed as an argument.
This is because an overloaded function passed as an argument must be resolved to a single overload before dependent types in the template function definition are evaluated and SFINAE kicks in.
You can see this by creating n overloaded template definitions, where n - 1 is the maximum number of arguments you want to handle:
template < typename R, typename... Args >
typename std::enable_if< 0 == sizeof...( Args ), R >::type
call_my_function( R(*func)(), Args ...a )
{
return func( a... );
}
template < typename R, typename A1, typename... Args >
typename std::enable_if< 1 == sizeof...( Args ), R >::type
call_my_function( R(*func)(A1), Args ...a )
{
return func( a... );
}
template < typename R, typename A1, typename A2, typename... Args >
typename std::enable_if< 2 == sizeof...( Args ), R >::type
call_my_function( R(*func)(A1, A2), Args ...a )
{
return func( a... );
}
Here each arg_count resolves to exactly one call_my_function definition, so there is no ambiguity within a particular call_my_function definition as to which arg_count was passed.
A possible solution would be to generate these n overloads either manually or using the preprocessor (e.g. using Boost.Preprocessor).

Default initialized (with value initialization) parameter pack

Can I default initialize a parameter pack to the respective value initialization of each type ?
To elaborate a bit more, take the example of a simple function template
template<typename T>
void f(T arg = T())
{
// eg for T=int, arg is 0 (value initialization) when default initialized
}
Would it be possible to express its variadic counterpart, ie
template<typename... Args>
void F(Args... args /* how can I value initialize the parameter pack? */)
{
}
#include <iostream>
#include <utility>
#include <tuple>
#include <cstddef>
#include <type_traits>
template <typename... Args>
void F(Args... args)
{
// target function, arbitrary body
using expander = int[];
(void)expander{ 0, (void(std::cout << args << " "), 0)... };
std::cout << std::endl;
}
template <typename... Args, typename... Params, std::size_t... Is>
void F(std::index_sequence<Is...>, Params&&... params)
{
F<Args...>(std::forward<Params>(params)...
, std::decay_t<typename std::tuple_element<sizeof...(Params) + Is, std::tuple<Args...>>::type>{}...);
}
template <typename... Args, typename... Params>
auto F(Params&&... params)
-> std::enable_if_t<(sizeof...(Args) > sizeof...(Params))>
{
F<Args...>(std::make_index_sequence<sizeof...(Args) - sizeof...(Params)>{}
, std::forward<Params>(params)...);
}
Tests:
#include <string>
int main()
{
// F(int, char, float = float{}, double = double{})
F<int, char, float, double>(1, 'c');
// F(int = int{}, char = char{}, float = float{}, double = double{})
F<int, char, float, double>();
// F(const std::string&, const std::string& = std::string{})
F<const std::string&, const std::string&>("foo");
// F(int, int, int)
F(1, 2, 3);
}
Output:
1 'c' 0 0
0 '\0' 0 0
"foo" ""
1 2 3
DEMO
You can create two parameter packs, one representing the types corresponding to function parameters and one representing "defaulted parameters."
template< typename ... aux, typename ... arg >
void fn( arg ... a ) {
std::tuple< aux ... > more {}; // The tuple elements are value-initialized.
}
http://coliru.stacked-crooked.com/a/1baac4b877dce4eb
There is no way to explicitly mention the deduced template parameters for this function. Anything inside the angle braces of the call will go into aux, not arg.
Note, the initialization you get with {} is value-initialization, not default-initialization. Objects of fundamental type get zeroed, not left uninitialized.
It`s explicitly forbidden by C++ standard, you cannot do such thing.
N3376 8.3.6/3
A default argument shall be specified only in the
parameter-declaration-clause of a function declaration or in a
template-parameter (14.1); in the latter case, the initializer-clause
shall be an assignment-expression. A default argument shall not be
specified for a parameter pack.

Passing position of variadic template argument

I would like to create a function that takes a variable number of template arguments. Later with these arguments the function should pass their position like this:
template<typename R, typename Args...>
R myFunction(Data &data, void *function) {
auto f = (R (*)(Args...))function;
return f(read<Args1>(data, 1), read<Args2>(data, 2), ...);// <-- This is the problem
}
The given code is of course not compilable. Is there any way to fix it? Is there a way to do it without variadic templates without too much code duplication?
Yes, that is possible:
// we need a compile-time helper to generate indices
template< std::size_t... Ns >
struct indices
{
typedef indices< Ns..., sizeof...( Ns ) > next;
};
template< std::size_t N >
struct make_indices
{
typedef typename make_indices< N - 1 >::type::next type;
};
template<>
struct make_indices< 0 >
{
typedef indices<> type;
};
With these helpers, you need one forwarder for your function like this:
template<typename R, typename... Args, std::size_t... Ns>
R myFunctionImpl(void *Data, void *function, indices<Ns...> ) {
auto f = (R (*)(Args...))function;
return f(read<Args>(Data, Ns + 1)...);// +1 because indices is zero-based
}
template<typename R, typename... Args>
R myFunction(void *Data, void *function) {
return myFunctionImpl< R, Args... >( Data, function, typename make_indices<sizeof...(Args)>::type() );
}
EDIT: How does it work? First, we determine the size of the argument pack Args through sizeof.... make_indices<N>::type then expands into indices<0,1,2,...,N-1>. It is given as an additional parameter to the implementation function (from the forwarder who just creates a dummy instance), hence argument deduction kicks in on the implementation function's side and puts the generated indices into the argument pack Ns.
The implementation function now has two argument packs with the same size, namely Args and Ns. When expanded through the ellipsis ..., the ellipsis expands the whole expression that it's applied to and it expands all parameter packs in parallel! In the above example that expression is read<Args>(Data, Ns+1), which nicely expands into the OPs pseudo-code.