Implicit conversions with variadic templates - c++

Consider two function calls
foo({"a", 1}, {"b", "value"});
foo({"a", 1}, {"b", "value"}, {"c", 1.0});
Is there a way to write function foo for arbitrary number of argument pairs?
I was thinking something along the lines
template <typename... Args>
void foo(std::pair<const char*, Args>&&...);
which unfortunately does not work.
gcc fails with an error:
error: too many arguments to function 'void foo(std::pair<const char*, Args>&& ...) [with Args = {}]'
foo({"aa", 1});

Try to simplify a bit your example and consider this:
#include<utility>
template<typename T>
void foo(std::pair<char*, T>) {}
int main() {
foo({"a", 1});
}
It doesn't compile, as you can see.
The problem is that { "a", 1 } is not a std::pair, even if you can construct one from it as it follows:
#include<utility>
void foo(std::pair<char*, int>) {}
int main() {
foo({"a", 1});
}
The error is quite clear:
couldn't infer template argument 'T'
Why can't you?
Te compiler could construct such a pair once T is known. Anyway, T must be deduced and the compiler cannot do that because { "a", 1 } is not a pair from which it can be deduced.
Anyway, { "a", 1 } can be converted to a pair, in the specific case to a specialization of std::pair<char *, T>, but first of all T must be deduced.
Deduced from what? A pair, of course, but you don't have a pair yet.
And so on, in a loop.
Let's discuss now your attempt to do something similar that involves a variadic template: it goes without saying that, if even the simpler example shown above doesn't compile, its variadic extension (if any) would not compile as well for more or less the same reason.
Is there a way to write function foo for arbitrary number of argument pairs?
I would say no, unless you use pairs as arguments for foo.
It follows a minimal, working example:
#include<utility>
template <typename... Args>
void foo(std::pair<const char*, Args>&&...) {}
int main() {
foo(std::make_pair("a", 1), std::make_pair("b", "value"));
}
If you prefer, you can also deduce the first argument, as long as its type is fixed:
#include<utility>
template <typename T, typename... Args>
void foo(std::pair<T, Args>&&...) {}
int main() {
foo(std::make_pair("a", 1), std::make_pair("b", "value"));
}
Otherwise you can do this if it's not fixed:
#include<utility>
template <typename... First, typename... Second>
void foo(std::pair<First, Second>&&...) {}
int main() {
foo(std::make_pair("a", 1), std::make_pair(0, "value"));
}

Is there a way to write function foo for arbitrary number of argument
pairs?
There are some solutions based on variadic templates but arguments must be pairs to allow compiler to deduce types. Then something like this might work:
template<typename... Args>
void foo() {}
template<typename T, typename U, typename... Args>
void foo(const std::pair<T, U>& p, Args... args) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
foo(args...);
}
So for:
foo(std::make_pair("a", 1), std::make_pair("b", "value"), std::make_pair("c", 1.0));
The output (with clang 3.8) is:
void foo(const std::pair<T, U> &, Args...) [T = const char *, U = int, Args = <std::__1::pair<const char *, const char *>, std::__1::pair<const char *, double>>]
void foo(const std::pair<T, U> &, Args...) [T = const char *, U = const char *, Args = <std::__1::pair<const char *, double>>]
void foo(const std::pair<T, U> &, Args...) [T = const char *, U = double, Args = <>]
Here is the full working example.

To expand a bit on Edgar Rokyan's answer, you can move the pair creation into the foo function:
template<typename... Args>
void foo() {}
// Forward declaration
template<typename U, typename... Args>
void foo(const char * str, U u, Args... args);
// When given a pair
template<typename U, typename... Args>
void foo(const std::pair<const char *, U>& p, Args... args) {
std::cout << p.first << " = " << p.second << std::endl;
foo(args...);
}
// when given a C string and something else, make a pair
template<typename U, typename... Args>
void foo(const char * str, U u, Args... args) {
foo(std::make_pair(str, u), args...);
}
Then you can call it like:
foo("hi", 42,
"yo", true,
std::make_pair("Eh", 3.14),
"foo", false,
some_pair);

In c++17 you may be able to workaround the problem and cheat compiler a bit by using template deduction of constructors:
#include <iostream>
#include <utility>
template <class... Args>
struct P:std::pair<Args...> {
P(Args... args):std::pair<Args...>(args...) { }
};
template <class... Args>
void foo(std::pair<const char *, Args>&&...) {
}
int main() {
foo(P{"abc", 1}, P{"abc", "abc"}, P{"abc", 2.0});
}
[live demo]

Related

SFINAE doesn't work in recursive function

Let's create currying function.
template <typename TFunc, typename TArg>
class CurryT
{
public:
CurryT(const TFunc &func, const TArg &arg)
: func(func), arg(arg )
{}
template <typename... TArgs>
decltype(auto) operator()(TArgs ...args) const
{ return func(arg, args...); }
private:
TFunc func;
TArg arg ;
};
template <typename TFunc, typename TArg>
CurryT<decay_t<TFunc>, remove_cv_t<TArg>>
Curry(const TFunc &func, const TArg &arg)
{ return {func, arg}; }
And function that decouple function to single argument functions:
// If single argument function (F(int)).
template <typename F>
static auto Decouple(const F &f, enable_if_t<is_invocable_v<F, int>> * = nullptr)
{
return f;
}
// If multiple arguments function (F(int, int, ...)).
template <typename F>
static auto Decouple(const F &f, enable_if_t<!is_invocable_v<F, int>> * = nullptr)
{
return [f](int v) { return Decouple( Curry(f, v) ); };
}
Everything works fine if 2 arguments function is passed:
auto f1 = Decouple(
[](int a, int b)
{ std::cout << a << " " << b << std::endl; }
);
f1(3)(4); // Outputs 3 4
But if I add more arguments
auto f2 = Decouple(
[](int a, int b, int c)
{ std::cout << a << " " << b << " " << c << std::endl; }
);
f(5)(6)(7);
The compilation breaks: https://coliru.stacked-crooked.com/a/10c6dba670d17ffa
main.cpp: In instantiation of 'decltype(auto) CurryT<TFunc, TArg>::operator()(TArgs ...) const [with TArgs = {int}; TFunc = main()::<lambda(int, int, int)>; TArg = int]':
main.cpp:17:26: error: no match for call to '(const main()::<lambda(int, int, int)>) (const int&, int&)'
17 | { return func(arg, args...); }
It breaks in instantiation of std::is_invocable.
Since debugging the standard library is hard, I created simple versions of standard type traits classes:
template <typename F> true_type check(const F &, decltype( declval<F>()(1) )* );
template <typename F> false_type check(const F &, ...);
template <typename F>
struct invocable_with_int : decltype(check(declval<F>(), nullptr))
{};
template <typename F>
inline constexpr bool invocable_with_int_v = invocable_with_int<F>::value;
template<bool B>
struct my_enable_if {};
template<>
struct my_enable_if<true>
{ using type = void; };
template <bool B>
using my_enable_if_t = typename my_enable_if<B>::type;
The problem remains the same https://coliru.stacked-crooked.com/a/722a2041600799b0:
main.cpp:29:73: required by substitution of 'template<class F> std::true_type check(const F&, decltype (declval<F>()(1))*) [with F = CurryT<main()::<lambda(int, int, int)>, int>]'
It tries to resolve calling to this function:
template <typename F> true_type check(const F &, decltype( declval<F>()(1) )* );
But decltype (declval<F>()(1))*) fails. But shouldn't this function be removed from overload resolution because template substitution fails? It works when Decouple is called first time. But when it is called second time the SFINAE seems to be disabled, and the first failure of template substitution gives a compilation error. Are there some limitation on secondary SFINAE? Why calling template function recursively doesn't work?
The problem is reproduced in GCC and Clang. So it is not a compiler bug.
Your operator() overload is completely unconstrained and therefore claims to be callable with any set of arguments. Only declarations, not definitions, are inspected to determine which function to call in overload resolution. If substitution into the definition then fails, SFINAE does not apply.
So, constrain your operator() to require TFunc to be callable with TArg and TArgs... as arguments.
For example:
template <typename... TArgs>
auto operator()(TArgs ...args) const -> decltype(func(arg, args...))
For me it is strange that your CurryT::operator() accepts unknown number of arguments.
Since aim is to have a functions which accept only one argument I expected that this function will accept only one argument.
IMO depending what kind of function CurryT holds CurryT::operator() should return a different type: return type of starting function or another version of CurryT.
Here is my approach using std::bind_front from C++20:
namespace detail {
template <typename TFunc>
class CurryT
{
public:
explicit CurryT(TFunc f) : mF(std::move(f))
{}
template<typename T>
auto get(T&& x, int = 0) -> decltype(std::declval<TFunc>()(x)) {
return mF(x);
}
template<typename T>
auto get(T&& x, char) {
return CurryT<decltype(std::bind_front(mF, std::forward<T>(x)))>{
std::bind_front(mF, std::forward<T>(x))
};
}
template<typename T>
auto operator()(T&& x)
{
return this->get(std::forward<T>(x), 1);
}
private:
TFunc mF;
};
}
template<typename F>
auto Decouple(F&& f)
{
return detail::CurryT<std::decay_t<F>>{std::forward<F>(f)};
}
https://godbolt.org/z/eW9r4Y6Ea
Note with this approach integer argument is not forced like in your solution.

Variadic function that accepts arguments of same type at compile time and iterate on them

I am looking for a way to implement Variadic function that accepts arguments of same type at compile-time and should be able to iterate on them. The variadic parameters are at the end with all of them having the same type.
Something like below -
void SampleFunc(Other arguments(String may be)..., int... arg)
{
for (const auto& val : arg)
{
// Each argument available here.
}
}
then I will call this function like below -
SampleFunc("String", "{1,2,3,4})
Most important thing is that variadic parameters are hardcoded every time the function is called so I should be able to generate this Variadic argument at compile time.
Right now I am accepting function parameters as shown below -
void SampleFunc(std::string str, std::vector<int>& nums)
But this adds run time cost of constructing a vector every time function is called which I want to avoid.
UPDATE :-
I forgot to mention that this function has other parameters at the start. It is my bad, sorry about that. I have updated my example now.
If the arguments are known at compile-time, in c++17, using fold expressions you can do something like
#include <utility> // std::forward
template <typename Type>
void doSomething(Type&& arg) /* noexcept */
{
// Each argument available here.
std::cout << arg << "\n";
}
template <typename... Args>
void SampleFunc(Args&&... args) /* noexcept */
{
(doSomething(std::forward<Args>(args)), ...);
}
Now you could call the function like
SampleFunc(1, 2, 3, 4);
and using doSomething you can do something with each arguments.
(See a Demo Online)
In previous compilers, you could imitate the fold expression via expander trick, as follows
template <typename Type>
void doSomething(Type&& arg) /* noexcept */
{
// Each argument available here.
std::cout << arg << "\n";
}
template <typename... Args>
void SampleFunc(Args&&... args) /* noexcept */
{
using dummy = int[];
(void)dummy {
0, (doSomething(std::forward<Args>(args)), 0)...
};
}
(See a Demo Online)
Iterate on variadic arguments is the simplest part: you tagged C++17 so you can use template folding, as suggested by JeJo, or other ways (recursion, initialization of an unused array).
More complicated is impose that all the arguments are exactly of the same type.
Obviously you can use SFINAE to impose that the deduced type are of the same type, but if you pass arguments of the different types, by example
foo(1l, 2l, 3l, 4); // long, long, long, int
when an argument is convertible to the type of the others, the code doesn't compile.
If you accept to pass through an additional function and that your function is a method of a template struct, you can start with an using that select the type from a couple type/index
template <typename T, std::size_t>
using get_type = T;
you can write the template struct as follows
template <typename...>
struct bar;
template <typename T, std::size_t ... Is>
struct bar<T, std::index_sequence<Is...>>
{
void operator() (std::string const & str, get_type<T, Is> const & ... ts)
{ ((std::cout << ts << ' '), ..., (std::cout << '\n')); }
};
Observe that the arguments following str in the operator() are all of type T, where T is the first template argument of the struct.
The additional function is
template <typename T, typename ... Ts>
void foo (std::string const & str, Ts const & ... ts)
{ bar<T, std::index_sequence_for<Ts...>>{}(str, ts...); }
You can call foo() as follows
foo<int>("string", 1, 2, 3, 4l);
Observe that a long value (4l) is accepted because is converted to int.
You can also directly call the bar::operator(), if you prefer
bar<int, std::make_index_sequence<4u>>{}("string", 10, 20, 30, 40);
but you have to explicit the second template argument so there is some redundancies.
The following is a full compiling example
#include <string>
#include <utility>
#include <iostream>
template <typename T, std::size_t>
using get_type = T;
template <typename...>
struct bar;
template <typename T, std::size_t ... Is>
struct bar<T, std::index_sequence<Is...>>
{
void operator() (std::string const & str, get_type<T, Is> const & ... ts)
{ ((std::cout << ts << ' '), ..., (std::cout << '\n')); }
};
template <typename T, typename ... Ts>
void foo (std::string const & str, Ts const & ... ts)
{ bar<T, std::index_sequence_for<Ts...>>{}(str, ts...); }
int main ()
{
foo<int>("string", 1, 2, 3, 4l); // a long value is converted to int
bar<int, std::make_index_sequence<4u>>{}("string", 10, 20, 30, 40);
}
The variadic parameters are at the end with all of them having the same type.
Whereas std::vector might have the overhead of extra allocation, you might simply use std::initializer_list instead (of variadic).
void SampleFunc(std::string str, std::initializer_list<int>& nums)
{
for (int val : nums)
{
// Each argument available here.
}
}
With call similar to
SampleFunc("String", {1, 2, 3, 4});

Typesafe variadic function

I want to write a function that accepts a variable number of string literals. If I was writing in C, I would have to write something like:
void foo(const char *first, ...);
and then the call would look like:
foo( "hello", "world", (const char*)NULL );
It feels like it ought to be possible to do better in C++. The best I have come up with is:
template <typename... Args>
void foo(const char* first, Args... args) {
foo(first);
foo(args);
}
void foo(const char* first) { /* Do actual work */ }
Called as:
foo("hello", "world");
But I fear that the recursive nature, and the fact that we don't do any type checking until we get to a single argument, is going to make errors confusing if somebody calls foo("bad", "argument", "next", 42). What I want to write, is something like:
void foo(const char* args...) {
for (const char* arg : args) {
// Real work
}
}
Any suggestions?
Edit: There is also the option of void fn(std::initializer_list<const char *> args), but that makes the call be foo({"hello", "world"}); which I want to avoid.
I think you probably want something like this:
template<class... Args,
std::enable_if_t<(std::is_same_v<const char*, Args> && ...), int> = 0>
void foo(Args... args ){
for (const char* arg : {args...}) {
std::cout << arg << "\n";
}
}
int main() {
foo("hello", "world");
}
Note: it is not possible to match just string literals. The closest you can come is to match a const char array.
To do the type checking, use a function template which takes const char arrays.
To loop over them with range-based for, we need to convert it to an initializer_list<const char*>. We can do so directly with braces in the range-based for statement, because arrays will decay to pointers.
Here is what the function template looks like (note: this works on zero or more string literals. If you want one or more, change the function signature to take at least one parameter.):
template<size_t N>
using cstring_literal_type = const char (&)[N];
template<size_t... Ns>
void foo(cstring_literal_type<Ns>... args)
{
for (const char* arg : {args...})
{
// Real work
}
}
While all other answers solve the problem, you could also do the following:
namespace detail
{
void foo(std::initializer_list<const char*> strings);
}
template<typename... Types>
void foo(const Types... strings)
{
detail::foo({strings...});
}
This approach seems (at least to me) to be more readable than using SFINAE and works with C++11. Moreover, it allows you to move implementation of foo to a cpp file, which might be useful too.
Edit: at least with GCC 8.1, my approach seems to produce better error message when called with non const char* arguments:
foo("a", "b", 42, "c");
This implementation compiles with:
test.cpp: In instantiation of ‘void foo_1(const ArgTypes ...) [with ArgTypes = {const char*, int, const char*, const char*}]’:
test.cpp:17:29: required from here
test.cpp:12:16: error: invalid conversion from ‘int’ to ‘const char*’ [-fpermissive]
detail::foo({strings...});
~~~~~~~~~~~^~~~~~~~~~~~~~
While SFINAE-based (liliscent's implementation) produces:
test2.cpp: In function ‘int main()’:
test2.cpp:14:29: error: no matching function for call to ‘foo(const char [6], const char [6], int)’
foo("hello", "world", 42);
^
test2.cpp:7:6: note: candidate: ‘template<class ... Args, typename std::enable_if<(is_same_v<const char*, Args> && ...), int>::type <anonymous> > void foo(Args ...)’
void foo(Args... args ){
^~~
test2.cpp:7:6: note: template argument deduction/substitution failed:
test2.cpp:6:73: error: no type named ‘type’ in ‘struct std::enable_if<false, int>’
std::enable_if_t<(std::is_same_v<const char*, Args> && ...), int> = 0>
+1 for the C++17 liliscent's solution.
For a C++11 solution, a possible way is create a type traits to make an "and" of multiple values (something similar to std::conjunction that, unfortunately, is available only starting from C++17... when you can use folding and you don't need std::conjunction anymore (thanks liliscent)).
template <bool ... Bs>
struct multAnd;
template <>
struct multAnd<> : public std::true_type
{ };
template <bool ... Bs>
struct multAnd<true, Bs...> : public multAnd<Bs...>
{ };
template <bool ... Bs>
struct multAnd<false, Bs...> : public std::false_type
{ };
so foo() can be written as
template <typename ... Args>
typename std::enable_if<
multAnd<std::is_same<char const *, Args>::value ...>::value>::type
foo (Args ... args )
{
for (const char* arg : {args...}) {
std::cout << arg << "\n";
}
}
Using C++14, multAnd() can be written as a constexpr function
template <bool ... Bs>
constexpr bool multAnd ()
{
using unused = bool[];
bool ret { true };
(void)unused { true, ret &= Bs ... };
return ret;
}
so foo() become
template <typename ... Args>
std::enable_if_t<multAnd<std::is_same<char const *, Args>::value ...>()>
foo (Args ... args )
{
for (const char* arg : {args...}) {
std::cout << arg << "\n";
}
}
--- EDIT ---
Jarod42 (thanks!) suggest a far better way to develop a multAnd; something as
template <typename T, T ...>
struct int_sequence
{ };
template <bool ... Bs>
struct all_of : public std::is_same<int_sequence<bool, true, Bs...>,
int_sequence<bool, Bs..., true>>
{ };
Starting from C++14 can be used std::integer_sequence instead of it's imitation (int_sequence).
Using C++17 fold expressions on the comma operator, you can simply do the following:
#include <iostream>
#include <string>
#include <utility>
template<typename OneType>
void foo_(OneType&& one)
{
std::cout << one;
}
template<typename... ArgTypes>
void foo(ArgTypes&&... arguments)
{
(foo_(std::forward<ArgTypes>(arguments)), ...);
}
int main()
{
foo(42, 43., "Hello", std::string("Bla"));
}
Live demo here. Note I used foo_ inside the template, because I couldn't be bothered to write out 4 overloads.
If you really really really want to restrict this to string literals, change the function signature as Nevin's answer suggests:
#include <cstddef>
#include <iostream>
#include <string>
#include <utility>
template<std::size_t N>
using string_literal = const char(&)[N];
template<std::size_t N>
void foo(string_literal<N> literal)
{
std::cout << literal;
}
template<std::size_t... Ns>
void foo(string_literal<Ns>... arguments)
{
(foo(arguments), ...);
}
int main()
{
foo("Hello", "Bla", "haha");
}
Live demo here.
Note this is extremely close to the C++11 syntax to achieve the exact same thing. See e.g. this question of mine.
Well, the nearest you can get to a function accepting any arbitrary number of const char* but nothing else uses a template-function and forwarding:
void foo_impl(std::initializer_list<const char*> args)
{
...
}
template <class... ARGS>
auto foo(ARGS&&... args)
-> foo_impl({std::forward<ARGS>(args)...})
{
foo_impl({std::forward<ARGS>(args)...});
}
The subtlety is in allowing the normal implicit conversions.
#include<type_traits>
#include<iostream>
auto function = [](auto... cstrings) {
static_assert((std::is_same_v<decltype(cstrings), const char*> && ...));
for (const char* string: {cstrings...}) {
std::cout << string << std::endl;
}
};
int main(){
const char b[]= "b2";
const char* d = "d4";
function("a1", b, "c3", d);
//function(a, "b", "c",42); // ERROR
}
And now... for something completely different...
You can write a type wrapper struct as follows
template <typename, typename T>
struct wrp
{ using type = T; };
template <typename U, typename T>
using wrp_t = typename wrp<U, T>::type;
and a foo() function receiving a variadic list of char const * simply become
template <typename ... Args>
void foo (wrp_t<Args, char const *> ... args)
{
for ( char const * arg : {args...} )
std::cout << "- " << arg << std::endl;
}
The problem is that you can't call it as you want
foo("hello", "world");
because the compiler isn't able to deduce the Args... types.
Obviously you can explicit a list of dummy types
foo<void, void>("hello", "world");
but I understand that is a horrible solution.
Anyway, if you accept to pass through a trivial template function
template <typename ... Args>
void bar (Args ... args)
{ foo<Args...>(args...); }
you can call
bar("hello", "world");
The following is a full C++11 working example
#include <iostream>
template <typename, typename T>
struct wrp
{ using type = T; };
template <typename U, typename T>
using wrp_t = typename wrp<U, T>::type;
template <typename ... Args>
void foo (wrp_t<Args, char const *> ... args)
{
for ( char const * arg : {args...} )
std::cout << "- " << arg << std::endl;
}
template <typename ... Args>
void bar (Args ... args)
{ foo<Args...>(args...); }
int main ()
{
bar("hello", "world"); // compile
// bar("hello", "world", 0); // compilation error
}
Of course it is possible, this compiles and runs what you want (pay attention)
#include<iostream>
template<class... Char>
// hehe, here is the secret
auto foo(const Char*... args ) ->decltype((char const*)(*std::begin({args...})), (char const*)(*std::end({args...})), void(0))
{
for (const char* arg : {args...}) {
std::cout << arg << "\n";
}
}
int main() {
foo("no", "sense","of","humor");
}
This is #liliscent solution but with more sugar and, to please #rubenvb, without enable_if.
If you think the extra code as a comment (which is not), note that you'll see exactly the syntax you are looking for.
Note that you can only feed an homogeneous list of things that is convertible to char const*, which was one of your goals it seems.

Template functions with paired parameters

I'm trying to make a C++ function that accepts an unknown number of parameters total, but that they are always paired with specific types.
// logically, this is what the template Pair would be
// template<int, std::string> struct Pair {};
// desired:
// accept a const char * as a first parameter, and then in pairs ...
// integer, const char *
template <typename... Arguments> unsigned int onlyInPairs
(const std::string name, const Arguments& ... args) {
const unsigned numargs = sizeof...(Arguments);
// more magic would happen here with the parameters :)
return numargs;
}
int _tmain(int argc, _TCHAR* argv[])
{
// only string, [num, string] [num, string] should work
// desire that the syntax be as simple as shown, and not require
// extra classes to be created (like a Tuple) for each pair.
// this should work...
auto count = onlyInPairs("ABC", 1, "DEF", 2, "HIJ"); // works
// this should not work, as it's not number, string
count = onlyInPairs("ABC", 1, "DEF", "NOTRIGHT", 2);
return 0;
}
I've looked at parameter packs (reference), but can't seem to apply the documentation I've found to my specific problem. I'd like to try to catch the problem at compile time if the parameters are not specified correctly.
The goal was to use a syntax that was free of template noise as much as possible as the "pairs" will always be this way (and the programmer will know that). So, we wanted to just have int, string (repeat).
Ideally, the solution would work with Visual Studio 2013's C++ compiler, but I'd accept any answer that works and demonstrates the current possible shortcomings of VS C++ related to this issue.
Appendix - More details
The code being written would ultimately be often read by tech-savvy, but not formally trained C/C++ programmers (like a technical support). So, we're trying to get it to be distraction free as much as possible. There can be 2-16 pairs of values ... so keeping it distraction free and just the data is desirable.
Here's one possibility. Class template Enforce recursively inherits from itself and applies static_assert on pairs of template arguments until the specialization is picked that doesn't do anything:
#include <type_traits>
#include <string>
template<typename...Args>
struct Enforce;
template<typename T, typename T1, typename T2, typename... Args>
struct Enforce<T, T1, T2, Args...> : Enforce<T, Args...> {
static_assert( std::is_constructible<T, T2>::value, "Wrong T2!");
};
template<typename T>
struct Enforce<T> {
};
template <typename... Arguments>
void onlyInPairs (const std::string name, const Arguments& ... args)
{
Enforce<std::string, Arguments...>();
}
int main()
{
onlyInPairs("this", 1, "works", 2, "fine");
//onlyInPairs("this", 1, "doesn't", 2, 3);
}
Instead of recursive inheritance, you can use recursive typedef instead. At least in gcc, that ought to compile faster and with less noise (warning about non-virtual destructor in base class, etc.).
EDIT:
Here's another version that ANDs the checks together and saves the result:
template<typename...Args>
struct Enforce;
template<typename T, typename T1, typename T2, typename... Args>
struct Enforce<T, T1, T2, Args...> {
static const bool value =
std::is_constructible<T,T2>::value &&
Enforce<T, Args...>::value;
};
template<typename T>
struct Enforce<T> : std::true_type {
};
Now you can move the assert closer, inside onlyInPairs:
template <typename... Arguments>
void onlyInPairs (const std::string name, const Arguments& ... args)
{
static_assert( Enforce<std::string, Arguments...>::value , "Wrong second arg..." );
}
What template noise do you speak of?
void onlyInPairs(std::initializer_list<std::pair<int, std::string>>&& pairs) {}
int main() {
onlyInPairs({
{1, "abc"},
{2, "def"},
{3, "foo"},
});
}
Use compile time recursion:
void processArgPairs() {
// to stop recursion
}
template <typename Arg1, typename Arg2, typename... Arguments>
void processArgPairs(Arg1 a, Arg2 b, Arguments&& ...args){
static_assert(std::is_constructible<int, Arg1>::value, "Wrong type of first argument - int expected");
static_assert(std::is_constructible<std::string, Arg2>::value, "Wrong type of second argument - string expected
processArgPairs(std::forward<Arguments>(args)...);
}
template <typename... Arguments> unsigned int onlyInPairs
(const std::string name, Arguments&& ... args) {
const unsigned numargs = sizeof...(Arguments);
processArgPairs(std::forward<Arguments>(args)...);
return numargs;
}
Something like this?
template <typename... Arguments>
unsigned int onlyInPairs(const std::string name, const Arguments& ... args)
{
const unsigned numargs = sizeof...(Arguments);
check(args...);
return numargs;
}
template <typename... Arguments>
void check(const int i, const std::string name, const Arguments& ... args)
{
check(args...);
}
void check(const int i, const std::string name)
{
}
int main()
{
auto count = onlyInPairs("ABC", 1, "DEF", 2, "HIJ"); // works
count = onlyInPairs("ABC", 1, "DEF", "NOTRIGHT", 2); //compile error
return 0;
}
This is a fairly old-school solution: using is_convertible should be cleaner
#include <string>
template <typename... Args> struct EnforcePairsHelper;
// terminal case
template <> struct EnforcePairsHelper<> {
enum { size = 0 };
};
// multiple specializations for reliable matching:
// only the last is really required here
template <typename... ArgTail>
struct EnforcePairsHelper<int, const char *, ArgTail...> {
enum { size = 2 + EnforcePairsHelper<ArgTail...>::size };
};
template <typename... ArgTail>
struct EnforcePairsHelper<int, char *, ArgTail...> {
enum { size = 2 + EnforcePairsHelper<ArgTail...>::size };
};
template <int N, typename... ArgTail>
struct EnforcePairsHelper<int, char [N], ArgTail...> {
enum { size = 2 + EnforcePairsHelper<ArgTail...>::size };
};
template <typename... Args> unsigned onlyInPairs (const std::string name,
const Args& ... args) {
const unsigned numargs = EnforcePairsHelper<Args...>::size;
// more magic would happen here with the parameters :)
return numargs;
}
int main() {
unsigned ok = onlyInPairs("ABC", 1, "DEF", 2, "HIJ");
// unsigned no = onlyInPairs("ABC", 1, "DEF", "NOTRIGHT", 2);
}

How do I bind a ::std::vector of arguments to a functor?

I'm trying to make this program compile properly:
#include <vector>
#include <iostream>
int f(int a, int b)
{
::std::cout << "f(" << a << ", " << b << ") == " << (a + b) << '\n';
return a + b;
}
template <typename R, typename V>
R bind_vec(R (*f)(), const V &vec, int idx=0)
{
return f();
}
template <typename R, typename V, typename Arg1, typename... ArgT>
R bind_vec(R (*f)(Arg1, ArgT...), const V &vec, int idx=0)
{
const Arg1 &arg = vec[idx];
auto call = [arg, f](ArgT... args) -> R {
return (*f)(arg, args...);
};
return bind_vec(call, vec, idx+1);
}
int foo()
{
::std::vector<int> x = {1, 2};
return bind_vec(f, x);
}
Ideally I'd like bind_vec to take an arbitrary functor as an argument instead of just a function pointer. The idea is to pull the function arguments from a ::std::vector at compile time.
This isn't the final use for this, but it's a stepping stone to where I want to go. What I'm really doing is generating wrapper functions that unwrap their arguments from promises in a future/promise type system at compile time. These wrapper functions will themselves be promises.
In my ultimate use-case I can count on the functors being ::std::functions. But it would be nice to have an idea of how it should work for more general functors as well since I think this is a broadly interesting problem.
OK, first off, detecting the arity of a functor can be done, but it's a bit involved and best left to a separate question. Let's assume you will specify the arity of the functor in the call. Similarly, there are ways to obtain the return type of a callable object, but that's also beyond the scope of this question. Let's just assume the return type is void for now.
So we want to say,
call(F f, C v);
and that should say f(v[0], v[1], ..., v[n-1]), where f has arity n.
Here's an approach:
template <unsigned int N, typename Functor, typename Container>
void call(Functor const & f, Container const & c)
{
call_helper<N == 0, Functor, Container, N>::engage(f, c);
}
We need the helper:
#include <functional>
#include <cassert>
template <bool Done, typename Functor, typename Container,
unsigned int N, unsigned int ...I>
struct call_helper
{
static void engage(Functor const & f, Container const & c)
{
call_helper<sizeof...(I) + 1 == N, Functor, Container,
N, I..., sizeof...(I)>::engage(f, c);
}
};
template <typename Functor, typename Container,
unsigned int N, unsigned int ...I>
struct call_helper<true, Functor, Container, N, I...>
{
static void engage(Functor const & f, Container const & c)
{
assert(c.size() >= N);
f(c[I]...);
}
};
Example:
#include <vector>
#include <iostream>
void f(int a, int b) { std::cout << "You said: " << a << ", " << b << "\n"; }
struct Func
{
void operator()(int a, int b) const
{ std::cout << "Functor: " << a << "::" << b << "\n"; }
};
int main()
{
std::vector<int> v { 20, 30 };
call<2>(f, v);
call<2>(Func(), v);
}
Notes: In a more advanced version, I would deduce the arity of the callable object with some more template machinery, and I would also deduce the return type. For this to work, you'll need several specializations for free functions and various CV-qualified class member functions, though, and so this would be getting too large for this question.
Something like this is easily possible for (member) function pointers, but for functors with potentially overloaded operator(), this gets a dang lot harder. If we assume that you have a way to tell how many arguments a function takes (and assume that the container actually has that many elements), you can just use the indices trick to expand the vector into an argument list, for example with std::next and a begin() iterator:
#include <utility>
#include <iterator>
template<class F, class Args, unsigned... Is>
auto invoke(F&& f, Args& cont, seq<Is...>)
-> decltype(std::forward<F>(f)(*std::next(cont.begin(), Is)...))
{
return std::forward<F>(f)(*std::next(cont.begin(), Is)...);
}
template<unsigned ArgC, class F, class Args>
auto invoke(F&& f, Args& cont)
-> decltype(invoke(std::forward<F>(f), cont, gen_seq<ArgC>{}))
{
return invoke(std::forward<F>(f), cont, gen_seq<ArgC>{});
}
This implementation works really nice for random-access containers, but not so well for forward and especially input ones. To make those work in a performant fashion, you might try to go the route of incrementing the iterator with every expanded step, but you'll run into a problem: Evaluation order of arguments to a function is unspecified, so you'll very likely pass the arguments in the wrong order.
Luckily, there is a way to force evaluation left-to-right: The list-initialization syntax. Now we just need a context where that can be used to pass arguments, and a possible one would be to construct an object, pass the function and the arguments through the constructor, and call the function in there. However, you lose the ability to retrieve the returned value, since constructors can't return a value.
Something I thought of is to create an array of iterators, which point to the correct element, and expanding those again in a second step where they are dereferenced.
#include <utility>
template<class T> using Alias = T; // for temporary arrays
template<class F, class It, unsigned N, unsigned... Is>
auto invoke_2(F&& f, It (&&args)[N], seq<Is...>)
-> decltype(std::forward<F>(f)(*args[Is]...))
{
return std::forward<F>(f)(*args[Is]...);
}
template<class F, class Args, unsigned... Is>
auto invoke_1(F&& f, Args& cont, seq<Is...> s)
-> decltype(invoke_2(std::forward<F>(f), std::declval<decltype(cont.begin())[sizeof...(Is)]>(), s))
{
auto it = cont.begin();
return invoke_2(std::forward<F>(f), Alias<decltype(it)[]>{(void(Is), ++it)...}, s);
}
template<unsigned ArgC, class F, class Args>
auto invoke(F&& f, Args& cont)
-> decltype(invoke_1(std::forward<F>(f), cont, gen_seq<ArgC>{}))
{
return invoke_1(std::forward<F>(f), cont, gen_seq<ArgC>{});
}
The code was tested against GCC 4.7.2 and works as advertised.
Since you said that the functors you are getting passed are std::functions, getting the number of arguments they take is really easy:
template<class F> struct function_arity;
// if you have the 'Signature' of a 'std::function' handy
template<class R, class... Args>
struct function_arity<R(Args...)>
: std::integral_constant<std::size_t, sizeof...(Args)>{};
// if you only have the 'std::function' available
template<class R, class... Args>
struct function_arity<std::function<R(Args...)>>
: function_arity<R(Args...)>{};
Note that you don't even need function_arity to make invoke from above work for std::function:
template<class R, class... Ts, class Args>
R invoke(std::function<R(Ts...)> const& f, Args& cont){
return invoke_1(f, cont, gen_seq<sizeof...(Ts)>{})
}
I managed to do what you want. It's simplest to explain if I leave it as not deducing the correct return type at first, I'll show how to add that later on:
#include <vector>
#include <type_traits>
namespace {
int f(int a, int b) { return 0; }
}
template <typename ...Args>
constexpr unsigned nb_args(int (*)(Args...)) {
return sizeof...(Args);
}
template <typename F, typename V, typename ...Args>
auto bind_vec(F f, const V&, Args&& ...args)
-> typename std::enable_if<sizeof...(Args) == nb_args(F()),void>::type
{
f(std::forward<Args>(args)...);
}
template <typename F, typename V, typename ...Args>
auto bind_vec(F f, const V& v, Args&& ...args)
-> typename std::enable_if<sizeof...(Args) < nb_args(F()),void>::type
{
bind_vec(f, v, std::forward<Args>(args)..., v.at(sizeof...(Args)));
}
int main() {
bind_vec(&f, std::vector<int>(), 1);
return 0;
}
There are two versions of this bind_vec - one is enabled if the parameter pack is the right size for the function. The other is enabled if it is still too small. The first version simply dispatches the call using the parameter pack, whilst the second version gets the next element (as determined by the size of the parameter pack) and recurses.
There SFINAE is done on the return type of the function in order that it not interfer with the deduction of the types, but this means it needs to be done after the function since it needs to know about F. There's a helper function that finds the number of arguments needed to call a function pointer.
To deduce the return types also we can use decltype with the function pointer:
#include <vector>
#include <type_traits>
namespace {
int f(int a, int b) { return 0; }
}
template <typename ...Args>
constexpr unsigned nb_args(int (*)(Args...)) {
return sizeof...(Args);
}
template <typename F, typename V, typename ...Args>
auto bind_vec(F f, const V&, Args&& ...args)
-> typename std::enable_if<sizeof...(Args) == nb_args(F()),decltype(f(std::forward<Args>(args)...))>::type
{
return f(std::forward<Args>(args)...);
}
template <typename F, typename V, typename ...Args>
auto bind_vec(F f, const V& v, Args&& ...args)
-> typename std::enable_if<sizeof...(Args) < nb_args(F()),decltype(bind_vec(f, v, std::forward<Args>(args)..., v.at(sizeof...(Args))))>::type
{
return bind_vec(f, v, std::forward<Args>(args)..., v.at(sizeof...(Args)));
}
int main() {
bind_vec(&f, std::vector<int>(), 1);
return 0;
}