In C# 7 is it possible to deconstruct tuples as method arguments - tuples

For example I have
private void test(Action<ValueTuple<string, int>> fn)
{
fn(("hello", 10));
}
test(t =>
{
var (s, i) = t;
Console.WriteLine(s);
Console.WriteLine(i);
});
I would like to write something like this
private void test(Action<ValueTuple<string, int>> fn)
{
fn(("hello", 10));
}
test((s,i) =>
{
Console.WriteLine(s);
Console.WriteLine(i);
});
Is this possible with some proper notation?

You can shorten it to:
void test( Action<ValueTuple<string, int>> fn)
{
fn(("hello", 10));
}
test(((string s, int i) t) =>
{
Console.WriteLine(t.s);
Console.WriteLine(t.i);
});
Hopefully, one day we might be able to splat the parameters from a tuple to the method invocation:
void test(Action<ValueTuple<string, int>> fn)
{
fn(#("hello", 10)); // <-- made up syntax
}
test((s, i) =>
{
Console.WriteLine(s);
Console.WriteLine(i);
});
But not at the moment.

I. Examples of Action /Func delegates with distinct-args vs. single n-tuple arguments:
// 1. Action with 3 distinct 'int' parameters
Action<int, int, int> ArgsAction = (i1, i2, i3) => i1 += i2 += i3;
// 2. Func with 3 distinct 'int' parameters, returning 'long'
Func<int, int, int, long> ArgsFunc = (i1, i2, i3) => (long)i1 + i2 + i3;
// 3. Action with a single 3-tuple parameter
Action<(int, int, int)> TupleAction = args => args.Item1 += args.Item2 += args.Item3;
// 4. Action with a single 3-tuple parameter, returning 'long'
Func<(int, int, int), long> TupleFunc = args => (long)args.Item1 + args.Item2 + args.Item3;
II. Demonstrate direct usage of the above examples
long r;
// pass distinct params to multi-arg methods
ArgsAction(1, 2, 3); // 1.
r = ArgsFunc(1, 2, 3); // 2.
// pass tuple to tuple-taking methods
TupleAction((1, 2, 3)); // 3.
r = TupleFunc((1, 2, 3)); // 4.
The examples in the next two sections invoke the delegates in their respective non-native argument forms. To delay the method call or to retain an adapted delegate for caching or delayed/multiple-call secenarios, see VI. and VII.
III. disperse ("splat") a tuple into multi-arg methods.
(1, 2, 3).Scatter(ArgsAction); // 1.
r = (1, 2, 3).Scatter(ArgsFunc); // 2.
IV. pass distinct args into tuple-taking methods:
TupleAction.Gather(1, 2, 3); // 3.
r = TupleFunc.Gather(1, 2, 3); // 4.
V. Extension methods Scatter and Gather used above in (III) and (IV):
// disperse n-tuple into Action arguments
public static void Scatter<T0, T1>(in this (T0 i0, T1 i1) t, Action<T0, T1> a) => a(t.i0, t.i1);
public static void Scatter<T0, T1, T2>(in this (T0 i0, T1 i1, T2 i2) t, Action<T0, T1, T2> a) => a(t.i0, t.i1, t.i2);
public static void Scatter<T0, T1, T2, T3>(in this (T0 i0, T1 i1, T2 i2, T3 i3) t, Action<T0, T1, T2, T3> a) => a(t.i0, t.i1, t.i2, t.i3);
// disperse n-tuple into Func arguments
public static TResult Scatter<T0, T1, TResult>(in this (T0 i0, T1 i1) t, Func<T0, T1, TResult> f) => f(t.i0, t.i1);
public static TResult Scatter<T0, T1, T2, TResult>(in this (T0 i0, T1 i1, T2 i2) t, Func<T0, T1, T2, TResult> f) => f(t.i0, t.i1, t.i2);
public static TResult Scatter<T0, T1, T2, T3, TResult>(in this (T0 i0, T1 i1, T2 i2, T3 i3) t, Func<T0, T1, T2, T3, TResult> f) => f(t.i0, t.i1, t.i2, t.i3);
// accumulate 'n' distinct args and pass into Action as an n-tuple
public static void Gather<T0, T1>(this Action<(T0, T1)> a, T0 i0, T1 i1) => a((i0, i1));
public static void Gather<T0, T1, T2>(this Action<(T0, T1, T2)> a, T0 i0, T1 i1, T2 i2) => a((i0, i1, i2));
public static void Gather<T0, T1, T2, T3>(this Action<(T0, T1, T2, T3)> a, T0 i0, T1 i1, T2 i2, T3 i3) => a((i0, i1, i2, i3));
// accumulate 'n' distinct args and pass into Func as an n-tuple
public static TResult Gather<T0, T1, TResult>(this Func<(T0, T1), TResult> f, T0 i0, T1 i1) => f((i0, i1));
public static TResult Gather<T0, T1, T2, TResult>(this Func<(T0, T1, T2), TResult> f, T0 i0, T1 i1, T2 i2) => f((i0, i1, i2));
public static TResult Gather<T0, T1, T2, T3, TResult>(this Func<(T0, T1, T2, T3), TResult> f, T0 i0, T1 i1, T2 i2, T3 i3) => f((i0, i1, i2, i3));
VI. Bonus round. If you plan to call a tuple- or distinct-arg-taking delegate multiple times in its alternate form, or if you're not ready to actually invoke it yet, you may wish to explicitly pre-convert the delegate from tuple-taking form to the equivalent distinct-args delegate, or vice-versa. You can cache the converted delegate for multiple or arbitrary later re-use.
var ga = ArgsAction.ToGathered(); // 1.
// later...
ga((1, 2, 3));
// ...
ga((4, 5, 6));
var gf = ArgsFunc.ToGathered(); // 2.
// later...
r = gf((1, 2, 3));
// ...
r = gf((4, 5, 6));
var sa = TupleAction.ToScattered(); // 3.
// later...
sa(1, 2, 3);
// ...
sa(4, 5, 6);
var sf = TupleFunc.ToScattered(); // 4.
// later...
r = sf(1, 2, 3);
// ...
r = sf(4, 5, 6);
// of course these approaches also supports in-situ usage:
ArgsAction.ToGathered()((1, 2, 3)); // 1.
r = ArgsFunc.ToGathered()((1, 2, 3)); // 2.
TupleAction.ToScattered()(1, 2, 3); // 3.
r = TupleFunc.ToScattered()(1, 2, 3); // 4.
VII. Extension methods for bonus examples shown in VI.
// convert tuple-taking Action delegate to distinct-args form
public static Action<T0, T1> ToScattered<T0, T1>(this Action<(T0, T1)> a) => (i0, i1) => a((i0, i1));
public static Action<T0, T1, T2> ToScattered<T0, T1, T2>(this Action<(T0, T1, T2)> a) => (i0, i1, i2) => a((i0, i1, i2));
public static Action<T0, T1, T2, T3> ToScattered<T0, T1, T2, T3>(this Action<(T0, T1, T2, T3)> a) => (i0, i1, i2, i3) => a((i0, i1, i2, i3));
// convert tuple-taking Func delegate to its distinct-args form
public static Func<T0, T1, TResult> ToScattered<T0, T1, TResult>(this Func<(T0, T1), TResult> f) => (i0, i1) => f((i0, i1));
public static Func<T0, T1, T2, TResult> ToScattered<T0, T1, T2, TResult>(this Func<(T0, T1, T2), TResult> f) => (i0, i1, i2) => f((i0, i1, i2));
public static Func<T0, T1, T2, T3, TResult> ToScattered<T0, T1, T2, T3, TResult>(this Func<(T0, T1, T2, T3), TResult> f) => (i0, i1, i2, i3) => f((i0, i1, i2, i3));
// convert distinct-args Action delegate to tuple-taking form
public static Action<(T0, T1)> ToGathered<T0, T1>(this Action<T0, T1> a) => t => a(t.Item1, t.Item2);
public static Action<(T0, T1, T2)> ToGathered<T0, T1, T2>(this Action<T0, T1, T2> a) => t => a(t.Item1, t.Item2, t.Item3);
public static Action<(T0, T1, T2, T3)> ToGathered<T0, T1, T2, T3>(this Action<T0, T1, T2, T3> a) => t => a(t.Item1, t.Item2, t.Item3, t.Item4);
// convert distinct-args Func delegate to its tuple-taking form
public static Func<(T0, T1), TResult> ToGathered<T0, T1, TResult>(this Func<T0, T1, TResult> f) => t => f(t.Item1, t.Item2);
public static Func<(T0, T1, T2), TResult> ToGathered<T0, T1, T2, TResult>(this Func<T0, T1, T2, TResult> f) => t => f(t.Item1, t.Item2, t.Item3);
public static Func<(T0, T1, T2, T3), TResult> ToGathered<T0, T1, T2, T3, TResult>(this Func<T0, T1, T2, T3, TResult> f) => t => f(t.Item1, t.Item2, t.Item3, t.Item4);

There are two ways of looking at your request, but neither is supported in C# 7.0.
One is splatting of tuples into arguments: calling a method with a tuple, and have the elements of the tuple splat into distinct arguments of the method. You can do this today manually by invoking M(tuple.first, tuple.second).
The other is deconstruction in lambda parameters: when a lambda is invoked with a parameter, deconstruct that parameters into elements and use those elements in the lambda body. You can do this today manually by defining a lambda as x => { var (first, second) = x; Write(first); Write(second); }.
There are proposals being discussed in the csharplang design repo.
https://github.com/dotnet/csharplang/issues/424 (splatting of tuples into arguments)
https://github.com/dotnet/csharplang/issues/258 (deconstruction in lambda parameters)

One option is to use TupleSplatter (https://github.com/chartjunk/TupleSplatter):
using TupleSplatter;
void test(Action<string, int> fn)
{
fn.SplatInvoke(("hello", 10));
// or
("hello", 10).Splat(fn);
}
test((s,i) => {
Console.WriteLine(s);
Console.WriteLine(i);
});

Here are more concise syntax variations that don't require any extra imports. No, it does not resolve wishes for "splatter" syntax discussed in comments, but no other answer used the ValueTuple syntax for the initial parameter definition.
void test(Action<(string, int)> fn)
{
fn(("hello", 10));
}
// OR using optional named ValueTuple arguments
void test(Action<(string word, int num)> fn)
{
fn((word: "hello", num: 10));
}
The invocation using a lambda expression is not that verbose, and the ValueTuple components can still be retrieved using minimal syntax:
test( ((string, int) t) => {
var (s, i) = t;
Console.WriteLine(s);
Console.WriteLine(i);
});

The closest that I could get.
public static class DeconstructExtensions
{
public static Action<T1, T2> Deconstruct<T1, T2>(this Action<(T1, T2)> action) => (a, b) => action((a, b));
public static Action<(T1, T2)> Construct<T1, T2>(this Action<T1, T2> action) => a => action(a.Item1, a.Item2);
}
class Test
{
private void fn((string, int) value) { }
private void test(Action<ValueTuple<string, int>> fn)
{
fn(("hello", 10));
}
private void Main()
{
var action = new Action<string, int>((s, i) =>
{
Console.WriteLine(s);
Console.WriteLine(i);
});
test(action.Construct());
}
}

Related

A compile-time counter for distinct Type identifier

I am writing some template meta programming code. For some reasons, I want to make every object in my code has different type. The original code looks like this:
template<unsigned int index>
class Class1{
};
template<typename T1, typename T2, unsigned int index>
class Class2{
std::tuple<T1*, T2*> v;
public:
Class2(T1* t1, T2* t2): v(std::tuple<T1*, T2*>(t1, t2)) {}
};
template<unsigned int index>
auto makeClass1() {
return Class1<index>();
}
template<unsigned int index, typename T1, typename T2>
auto mul(T1& t1, T2& t2) {
return Class2<T1, T2, index>(&t1, &t2);
}
int main() {
auto t1 = makeClass1<0>(); // Type of TT1 is Class1<0>
auto t2 = makeClass1<1>(); // Type of TT2 is Class1<1>
auto m1 = mul<0>(t1, t2);
auto m2 = mul<1>(t1, t2); // Type of m2 is different from type of m1.
}
This code is work, but I wish my code is easy to use. So I want to ask is there any solution that can make the code look like this:
template<unsigned int index>
class Class1{
};
template<typename T1, typename T2, unsigned int index>
class Class2{
std::tuple<T1*, T2*> v;
public:
Class2(T1* t1, T2* t2): v(std::tuple<T1*, T2*>(t1, t2)) {}
};
template<unsigned int index = IncreaseCounter<?>::value>
auto makeClass1() {
return Class1<index>();
}
template<unsigned int index = IncreaseCounter<?>::value, typename T1, typename T2>
auto operator*(T1& t1, T2& t2) {
return Class2<T1, T2, index>(&t1, &t2);
}
int main() {
auto t1 = makeClass1(); // Type of TT1 is Class1<0>
auto t2 = makeClass1(); // Type of TT2 is Class1<1>
auto m1 = t1*t2
auto m2 = t1*t2; // Type of m2 is different from type of m1.
}
Note: I think I need a compile-time counter. But except the macro solution:__COUNTER__ and __LINE__ , I can't find any other compile-time solution. The macro solution is ineffective to my code.
Except the compile-time counter, any other solution is ok.
Thank you for reading my question. Due to my poor english expression ability,please bear with me for the wrong sentences.
In C++20, you might do:
template <typename = decltype([]{})>
class Class1{};
template<typename T1, typename T2, typename = decltype([]{})>
class Class2{
std::tuple<T1*, T2*> v;
public:
Class2(T1* t1, T2* t2): v(std::tuple<T1*, T2*>(t1, t2)) {}
};
template <typename T = decltype([]{})>
auto makeClass1() { return Class1<T>();}
template<typename T1, typename T2, typename T = decltype([]{})>
auto operator*(T1& t1, T2& t2) {
return Class2<T1, T2, T>(&t1, &t2);
}
int main() {
auto t1 = makeClass1();
auto t2 = makeClass1(); // Type of t2 is different from type of t1.
auto m1 = t1*t2;
auto m2 = t1*t2; // Type of m2 is different from type of m1.
static_assert(!std::is_same_v<decltype(t1), decltype(t2)>);
static_assert(!std::is_same_v<decltype(m1), decltype(m2)>);
}
Demo.
Let's regard the core of your question:
auto m1 = t1*t2;
auto m2 = t1*t2; // Type of m2 is different from type of m1.
You have exactly the same expression (t1*t2) but you wish this expression to produce two different types!
Overall your idea of counting objects in compile time is loose. What would you expect in this case?
for (int i = 0; i < 10; ++i) {
auto m = t1*t2;
}
Returning back to your code: you somehow need to introduce this compile-time index, like you do in mul<index>: either in mul or in the result type (explicit type instead of auto + appropriate conversion).

How to create a functor as the sum of 2 functors?

Let's say I have 2 functors add and mult. add2(x) = x+2 and mult3(x) = 3x.
I want to create a combined tensor t such that t(x) = x+2 + 3x.
I came up with the solution of creating a pair functor, but I am not quite satisfied, as this solution does not scale to a higher number of functors.
What are my options?
struct add {
private:
double c_;
public:
add(double& c): c_(c) {}
double operator()(double& x) {
return c_ + x;
}
};
struct mult {
private:
double c_;
public:
mult(double& c): c_(c) {}
double operator()(double& x) {
return c_ * x;
}
}
template<typename T1, typename T2>
struct pair {
private:
T1 t1_;
T2 t2_;
public:
pair(T1 t1, T2 t2): t1_(t1), t2_(t2) {}
double operator()(double& x) {
return t1_(x) + t2_(x);
}
}
add add2 = add(2);
mult mult3 = mult(3);
pair combined = pair(add2, mult3);
The name pair doesn't really mean much about what the functor does. I think it make sense to create something along the following lines:
template<typename T1, typename T2>
struct plus
{
plus(T1 t1, T2 t2) : t1_(t1), t2_(t2) {}
double operator()(double x)
{
return t1_(x) + t2_(x);
}
T1 t1_;
T2 t2_;
};
and provide a helper function to make an instance of such a type.
template<typename T1, typename T2>
plus<T1, T2> make_plus(T1 t1, T2 t2)
{
return plus<T1, T2>(t1, t2);
}
Then, you can use:
add add2 = add(2);
mult mult3 = mult(3);
auto comb1 = make_plus(add2, mult3);
auto comb2 = make_plus(mult(10), make_plus(add2, mult3));
Here's a program that adds a minus for good measure.
#include <iostream>
struct add
{
add(double c): c_(c) {}
double operator()(double x) {
return c_ + x;
}
double c_;
};
struct mult
{
mult(double c): c_(c) {}
double operator()(double x) {
return c_ * x;
}
double c_;
};
template<typename T1, typename T2>
struct plus
{
plus(T1 t1, T2 t2) : t1_(t1), t2_(t2) {}
double operator()(double x)
{
return t1_(x) + t2_(x);
}
T1 t1_;
T2 t2_;
};
template<typename T1, typename T2>
plus<T1, T2> make_plus(T1 t1, T2 t2)
{
return plus<T1, T2>(t1, t2);
}
template<typename T1, typename T2>
struct minus
{
minus(T1 t1, T2 t2) : t1_(t1), t2_(t2) {}
double operator()(double x)
{
return t1_(x) - t2_(x);
}
T1 t1_;
T2 t2_;
};
template<typename T1, typename T2>
minus<T1, T2> make_minus(T1 t1, T2 t2)
{
return minus<T1, T2>(t1, t2);
}
int main()
{
add add2 = add(2);
mult mult3 = mult(3);
auto comb1 = make_plus(add2, mult3);
auto comb2 = make_plus(mult(10), make_plus(add2, mult3));
auto comb3 = make_minus(mult(10), make_plus(add2, mult3));
std::cout << comb1(10) << std::endl;
std::cout << comb2(10) << std::endl;
std::cout << comb3(10) << std::endl;
}
Output:
42
142
58
Another option will be to create types add, mult, plus, and minus in an application specific namespace and then add operator+ and operator- overloads to make the code in main a bit more intuitive.
#include <iostream>
namespace myapp
{
struct identity
{
double operator()(double x) { return x;}
};
struct add
{
add(double c): c_(c) {}
double operator()(double x) {
return c_ + x;
}
double c_;
};
struct mult
{
mult(double c): c_(c) {}
double operator()(double x) {
return c_ * x;
}
double c_;
};
template<typename T1, typename T2>
struct plus
{
plus(T1 t1, T2 t2) : t1_(t1), t2_(t2) {}
double operator()(double x)
{
return t1_(x) + t2_(x);
}
T1 t1_;
T2 t2_;
};
template<typename T1, typename T2>
plus<T1, T2> make_plus(T1 t1, T2 t2)
{
return plus<T1, T2>(t1, t2);
}
template<typename T1, typename T2>
plus<T1, T2> operator+(T1 t1, T2 t2)
{
return make_plus(t1, t2);
}
template<typename T1, typename T2>
struct minus
{
minus(T1 t1, T2 t2) : t1_(t1), t2_(t2) {}
double operator()(double x)
{
return t1_(x) - t2_(x);
}
T1 t1_;
T2 t2_;
};
template<typename T1, typename T2>
minus<T1, T2> make_minus(T1 t1, T2 t2)
{
return minus<T1, T2>(t1, t2);
}
template<typename T1, typename T2>
minus<T1, T2> operator-(T1 t1, T2 t2)
{
return make_minus(t1, t2);
}
}
int main()
{
using namespace myapp;
add add2 = add(2);
mult mult3 = mult(3);
auto comb1 = add2 + mult3;
auto comb2 = mult(10) + add2 + mult3;
auto comb3 = mult(10) - (add2 + mult3);
auto comb4 = identity{} + add(25);
std::cout << comb1(10) << std::endl;
std::cout << comb2(10) << std::endl;
std::cout << comb3(10) << std::endl;
std::cout << comb4(10) << std::endl;
}
Here is a fairly concise solution using polymorphic lambdas (C++14 required). To keep it short, this example uses named functions and supports only one function argument. However, it would be fairly easy to extend this approach to support multiple arguments and a more natural calling syntax using operator overloading.
Demo on Wandbox
#include <type_traits>
struct arg_t {};
auto eval(auto const& x, arg_t) {
return x;
}
template <typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
auto eval(auto const&, T const& t) {
return t;
}
template <typename F, std::enable_if_t<!std::is_arithmetic<F>::value, int> = 0>
auto eval(auto const& x, F const& f) {
return f(x);
}
auto binary(auto const& a, auto const& b, auto const& f) {
return [a, b, f](auto const& v) { return f(eval(v, a), eval(v, b)); };
}
auto sum(auto const& a, auto const& b) {
return binary(a, b, [](auto const& a, auto const& b) { return a + b; });
}
auto product(auto const& a, auto const& b) {
return binary(a, b, [](auto const& a, auto const& b) { return a * b; });
}
Then call like so:
#include <iostream>
int main() {
arg_t x;
auto f = sum(sum(x, 2), product(3, x));
std::cout << f(4) << "\n";
}

returning three values from a function

Hi I wanted to implement tuple.
Let me know what is going wrong in this and how to implement it correctly. I want to return three values from an function where the first values will be Integers and the last value will be an array.
template <typename T1, typename T2, typename T3>
struct t_untuple
{
T1& a1;
T2& a2;
T3& a3;
explicit t_untuple(T1& a1, T2& a2, T3& a3) : a1(a1), a2(a2), a3(a3) { }
t_untuple<T1, T2, T3>& operator = (const tuple <T1, T2, T3>& p)
{
a1 = p.first;
a2 = p.second;
a3 = p.third;
return *this;
}
};
// Our functor helper (creates it)
template <typename T1, typename T2, typename T3>
t_untuple<T1, T2, T3> unpair(T1& a1, T2& a2, T3& a3)
{
return t_unpair<T1, T2, T3>(a1, a2, a3);
}
Help me out in this.
I am getting Symbol 'tuple' could not be resolved at const tuple & p due to which the p.third is also an error
If you know what types you want to return, why dont you use a simple struct:
template <typename T>
struct return_type {
int a;
int b;
std::array<T> c;
}
Assuming array type is invariant. Either use std::tuple
std::tuple<int,int,std::array<std::string>> MyFunc()
here is man page http://en.cppreference.com/w/cpp/utility/tuple
or create concrete class
struct IIA
{
int a;
int b;
std::array<std::string> arr;
}
IAA Myfunc(){}
Looks like you try to expand code from using unpair and std::pair:
t_untuple<T1, T2, T3>& operator = (const tuple <T1, T2, T3>& p)
{
a1 = p.first;
a2 = p.second;
a3 = p.third;
return *this;
}
};
but access fields of tuple are done different way:
t_untuple<T1, T2, T3>& operator = (const std::tuple <T1, T2, T3>& p)
{
a1 = std::get<0>(p);
a2 = std::get<1>(p);
a3 = std::get<2>(p);
return *this;
}
};
you also need to replace unpair to untuple in your copy-paste:
template <typename T1, typename T2, typename T3>
t_untuple<T1, T2, T3> untuple(T1& a1, T2& a2, T3& a3)
{
return t_untuple<T1, T2, T3>(a1, a2, a3);
}
do not forget to #include <tuple> and you have to make sure that you compiler is at least in c++11 mode

creating template function with different parameters

So for example I want to create a function that add two numbers and return total.
template<typename T1, typename T2>
T1 add( T1 n1, T2 n2 ){
return n1 + n2;
}
Problems is if T1 is int and T2 is float. Then function will return int. But I want it to return float. Is there a trick or a way to achieve it?
If you are using C++11
decltype is your friend
template<typename T1, typename T2>
auto add( T1 n1, T2 n2 ) -> decltype(n1 + n2) {
return n1 + n2;
}
This will use the type resulting from n1 + n2
More at http://en.wikipedia.org/wiki/C%2B%2B11#Alternative_function_syntax
Yes
template<typename RT, typename T1, typename T2>
RT add( T1 n1, T2 n2 ){
return n1 + n2;
}
Now call it like:-
add<float>(2,3.0);
If you are using C++14:
template<typename T1, typename T2>
auto add(T1 n1, T2 n2)
{
return n1 + n2;
}
This is like the C++11 version, but does not require you to write out the trailing-return-type yourself (which is nice).
Another C++11 solution would be to use std::common_type<T1, T2>::type as return type.

Adding a callback to a variadic template class - impossible?

I'm trying to make a templated class, which has an Add method, that attaches a function callback to the class, so when I can then call it from there with the specified arguments list.It compiles fine, except for the part where I invoke the callback.It just doesn't accept the arguments, I've tried everything I can think of, but it still gives me the same "cannot expand parameter pack" error.Im using Visual Studio 2012 with the Microsoft Visual C++ Compiler Nov 2012 CTP (v120_CTP_Nov2012)
Here is the example source:
template
class Variadic
{
private:
void (*)($arguments...) callbackPtr;
public:
Variadic();
~Variadic();
void Attach(void (*callback)($arguments...));
void operator()($arguments... arguments);
};
I then add a callback to it:
template<typename... $arguments>
void Variadic<$arguments...>::Attach(void (*callback)($arguments...))
{
callbackPtr = callback;
}
And with the () operator I execute it:
template<typename... $arguments>
void Variadic<$arguments...>::operator()($arguments... arguments)
{
(callbackPtr)(arguments...);
}
In main.cpp I do a small test:
void test(int testInt, float testFloat)
{
//DoNothing
}
int main()
{
Variadic<int, float> var; //create one that will have a callback that takes an int and a float argument
var.Attach(test); //attach test, which takes an int and a float as arguments
var(2, 3.0f); //try to call it
}
The problem comes when I build - it gives me 2 errors at this exact line: (callbackPtr)(arguments...);
The errors are:
error C3546: '...' : there are no parameter packs available to expand
error C2065: 'arguments' : undeclared identifier
At first I thought it was a syntax problem and I wasn't passing arguments... properly, but I tried every way possible, it still gives me the same error.I can't find much info on "parameter pack expansion" in google either.What could I be doing wrong?I'm sure I'm somehow incorrectly using the (callbackPtr)(arguments...); call, but can't figure out how.
Any help would be appreciated.
Before I get into the answer, some things you should know:
The Microsoft VC++ November 2012 CTP does not play nice with Variadics and Function Pointers / Function Signatures. In almost all cases, it is necessary to expand them manually by hand. It sucks majorly, but you'll have to live with it until all that money we throw at VS and VC++ actually bears fruit and we get a compiler with a good chunk of C++11 features that other compilers are already supporting.
Passing function pointers and having the compiler determine the right type is a little trickier than most people would guess at first blush. There's a lot of type seducing deducting and template specialization that goes into it.
With that aside, it takes a lot of template magic and a lot of interesting functionality to have callbacks based on function pointers and member functions, rather than just using std::function<>. Before I show you the solution I ended up using, I seriously encourage you to use a std::vector<std::function<[RETURNTYPE]( [PARAMS] )> > (or just std::function for a single return) to save yourself the massive headache of trying to make this all work out. In either case, see my answer underneath #Insilico's for a Callback and Event system that works fine in GCC with variadic templates.
For a version that works in VC++, as I said before, you have to hack away at the various definitions manually, which I ended up creating a Callback Class and an Event class to do. It's for multiple callbacks, but you can simplify the Event class to be a single attach/callback if you need to:
template<typename TFuncSignature>
class Callback;
/////////////////
/* 2 ARGUMENT */
/////////////////
template<typename R, typename T1, typename T2>
class Callback<R (T1, T2)> {
public:
typedef R (*TFunc)(void*, T1, T2);
const static size_t Arity = 2;
Callback() : obj(0), func(0) {}
Callback(void* o, TFunc f) : obj(o), func(f) {}
R operator()(T1 t1, T2 t2) const {
return (*func)(obj, t1, t2);
}
typedef void* Callback::*SafeBoolType;
operator SafeBoolType () const {
return func != 0? &Callback::obj : 0;
}
bool operator! () const {
return func == 0;
}
bool operator== ( const Callback<R (T1, T2)>& right ) const {
return obj == right.obj && func == right.func;
}
bool operator!= ( const Callback<R (T1, T2)>& right ) const {
return obj != right.obj || func != right.func;
}
private:
void* obj;
TFunc func;
};
namespace detail {
template<typename R, class T, typename T1, typename T2>
struct DeduceConstMemCallback2 {
template<R(T::*Func)(T1, T2) const> inline static Callback<R(T1, T2)> Bind(T* o) {
struct _ { static R wrapper(void* o, T1 t1, T2 t2) { return (static_cast<T*>(o)->*Func)(std::forward<T1>(t1, t2); } };
return Callback<R(T1, T2)>(o, (R(*)(void*, T1, T2)) _::wrapper);
}
};
template<typename R, class T, typename T1, typename T2>
struct DeduceMemCallback2 {
template<R(T::*Func)(T1, T2)> inline static Callback<R(T1, T2)> Bind(T* o) {
struct _ { static R wrapper(void* o, T1 t1, T2 t2) { return (static_cast<T*>(o)->*Func)(t1, t2)); } };
return Callback<R(T1, T2)>(o, (R(*)(void*, T1, T2)) _::wrapper);
}
};
template<typename R, typename T1, typename T2>
struct DeduceStaticCallback2 {
template<R(*Func)(T1, T2)> inline static Callback<R(T1, T2)> Bind() {
struct _ { static R wrapper(void*, T1 t1, T2 t2) { return (*Func)(t1), t2); } };
return Callback<R(T1, T2)>(0, (R(*)(void*, T1, T2)) _::wrapper);
}
};
}
template<typename R, class T, typename T1, typename T2>
detail::DeduceConstMemCallback2<R, T, T1, T2> DeduceCallback2(R(T::*)(T1, T2) const) {
return detail::DeduceConstMemCallback2<R, T, T1, T2>();
}
template<typename R, class T, typename T1, typename T2>
detail::DeduceMemCallback2<R, T, T1, T2> DeduceCallback2(R(T::*)(T1, T2)) {
return detail::DeduceMemCallback2<R, T, T1, T2>();
}
template<typename R, typename T1, typename T2>
detail::DeduceStaticCallback2<R, T1, T2> DeduceCallback2(R(*)(T1, T2)) {
return detail::DeduceStaticCallback2<R, T1, T2>();
}
template <typename T1, typename T2> class Event2 {
public:
typedef void(* TSignature)(T1, T2);
typedef Callback<void(T1, T2)> TCallback;
typedef std::vector<TCallback> InvocationTable;
protected:
InvocationTable invocations;
public:
const static int ExpectedFunctorCount = 2;
Event2 () : invocations() {
invocations.reserve( ExpectedFunctorCount );
}
Event2 ( int expectedfunctorcount ) : invocations() {
invocations.reserve( expectedfunctorcount );
}
template <void (* TFunc)(T1, T2)> void Add ( ) {
TCallback c = DeduceCallback2( TFunc ).template Bind< TFunc >( );
invocations.push_back( c );
}
template <typename T, void (T::* TFunc)(T1, T2)> void Add ( T& object ) {
Add<T, TFunc>( &object );
}
template <typename T, void (T::* TFunc)(T1, T2)> void Add ( T* object ) {
TCallback c = DeduceCallback2( TFunc ).template Bind< TFunc >( object );
invocations.push_back( c );
}
template <typename T, void (T::* TFunc)(T1, T2) const> void Add ( T& object ) {
Add<T, TFunc>( &object );
}
template <typename T, void (T::* TFunc)(T1, T2) const> void Add ( T* object ) {
TCallback c = DeduceCallback2( TFunc ).template Bind< TFunc >( object );
invocations.push_back( c );
}
void Invoke ( T1 t1, T2 t2 ) {
size_t i;
for ( i = 0; i < invocations.size(); ++i ) {
invocations[i]( t1, t2 );
}
}
void operator() ( T1 t1, T2 t2 ) {
size_t i;
for ( i = 0; i < invocations.size(); ++i ) {
invocations[i]( t1, t2 );
}
}
size_t InvocationCount ( ) {
return invocations.size( );
}
template <void (* TFunc)(T1, T2)> bool Remove ()
{ return Remove (DeduceCallback2(TFunc).template Bind<TFunc>()); }
template <typename T, void (T::* TFunc)(T1, T2)> bool Remove (T& object)
{ return Remove <T, TFunc>(&object); }
template <typename T, void (T::* TFunc)(T1, T2)> bool Remove (T* object)
{ return Remove (DeduceCallback2(TFunc).template Bind<TFunc>(object)); }
template <typename T, void (T::* TFunc)(T1, T2) const> bool Remove (T& object)
{ return Remove <T, TFunc>(&object); }
template <typename T, void (T::* TFunc)(T1, T2) const> bool Remove (T* object)
{ return Remove (DeduceCallback2(TFunc).template Bind<TFunc>(object)); }
protected:
bool Remove( TCallback const& target ) {
auto it = std::find(invocations.begin(), invocations.end(), target);
if ( it == invocations.end())
return false;
invocations.erase(it);
return true;
}
};