Ok, I have a first project. It still needs to be generalized by unpacking ARGS1 ... instead of how I did it below. But at least this first solution shows that the problem can probably be solved in its entirety.
#include <iostream> #include <string> #include <vector> #include <tuple> template <std::size_t...> struct index_sequence {}; template <std::size_t N, std::size_t... Is> struct make_index_sequence_helper : make_index_sequence_helper<N-1, N-1, Is...> {}; template <std::size_t... Is> struct make_index_sequence_helper<0, Is...> { using type = index_sequence<Is...>; }; template <std::size_t N> using make_index_sequence = typename make_index_sequence_helper<N>::type; struct Mediator { std::vector<struct Object*> objects; void registerObject (Object* o) {objects.emplace_back(o);} template <typename RET, typename... ARGS1, typename... ARGS2> RET change (Object*, RET (Object::*)(ARGS1...), ARGS2&&...); template <typename RET, typename... ARGS, std::size_t... Is> RET changeHelper (RET (Object::*)(ARGS...), const std::tuple<ARGS...>&, index_sequence<Is...>); }; struct Object { int value; double rating; char letter; long tag; Mediator& mediator; Object (int v, double r, char l, long s, Mediator& m) : value(v), rating(r), letter(l), tag(s), mediator(m) {mediator.registerObject(this);} virtual void adjust (int, double, char, long) = 0; virtual void transform (char, double, int) = 0; template <typename RET, typename... ARGS1, typename... ARGS2> RET change (RET (Object::*f)(ARGS1...), ARGS2&&... args) { return mediator.change(this, f, std::forward<ARGS2>(args)...); } }; struct A : Object { using Object::Object; virtual void adjust (int a, double b, char c, long s) override { std::cout << "Type A adjusted using values " << a << ", " << b << ", " << c << ", and " << s << "." << std::endl; } virtual void transform (char a, double b, int c) override { std::cout << "Type A transformed using values " << a << ", " << b << ", and " << c << "." << std::endl; } }; struct B : Object { using Object::Object; virtual void adjust (int a, double b, char c, long s) override { std::cout << "Type B adjusted using values " << a << ", " << b << ", " << c << ", and " << s << "." << std::endl; } virtual void transform (char a, double b, int c) override { std::cout << "Type B transformed using values " << a << ", " << b << ", and " << c << "." << std::endl; } }; struct C : Object { using Object::Object; virtual void adjust (int a, double b, char c, long s) override { std::cout << "Type C adjusted using values " << a << ", " << b << ", " << c << ", and " << s << "." << std::endl; } virtual void transform (char a, double b, int c) override { std::cout << "Type C transformed using values " << a << ", " << b << ", and " << c << "." << std::endl; } }; template <typename T, typename TUPLE> struct Concatenate; template <typename FIRST, typename ...REST> struct Concatenate<FIRST, std::tuple<REST...>> { using type = typename std::tuple<FIRST, REST...>; }; template <typename HEAD, typename TUPLE> typename Concatenate<HEAD, TUPLE>::type nextTuple (Object* o, const TUPLE& tuple) { if (std::is_same<HEAD, int>::value) return std::tuple_cat (tuple, std::tuple<int>(o->value)); else if (std::is_same<HEAD, double>::value) return std::tuple_cat (tuple, std::tuple<double>(o->rating)); else if (std::is_same<HEAD, char>::value) return std::tuple_cat (tuple, std::tuple<char>(o->letter)); else if (std::is_same<HEAD, long>::value) return std::tuple_cat (tuple, std::tuple<long>(o->tag)); } template <typename HEAD, typename TUPLE, typename FIRST, typename... REST> typename Concatenate<HEAD, TUPLE>::type nextTuple (Object* o, const TUPLE& tuple, FIRST first, REST... rest) { if (std::is_same<HEAD, FIRST>::value) return std::tuple_cat (tuple, std::tuple<FIRST>(first)); return nextTuple<HEAD, TUPLE, REST...> (o, tuple, rest...); } template <typename RET, typename... ARGS1, typename... ARGS2> std::tuple<ARGS1...> extractTuple (Object* o, RET (Object::*)(ARGS1...), ARGS2&&... args) { // Function pointer parameter needed to maintain ARGS1..., else it will become an empty pack. std::tuple<> t0; const auto t1 = nextTuple<int> (o, t0, args...); // In general, unpack ARGS1... const auto t2 = nextTuple<double> (o, t1, args...); const auto t3 = nextTuple<char> (o, t2, args...); return nextTuple<long> (o, t3, args...); } template <typename RET, typename... ARGS1, typename... ARGS2> RET Mediator::change (Object* o, RET (Object::*f)(ARGS1...), ARGS2&&... args) { const std::tuple<ARGS1...> tuple = extractTuple (o, f, args...); // The key function. changeHelper (f, tuple, make_index_sequence<sizeof...(ARGS1)>()); } template <typename RET, typename... ARGS, std::size_t... Is> RET Mediator::changeHelper (RET (Object::*f)(ARGS...), const std::tuple<ARGS...>& tuple, index_sequence<Is...>) { for (Object* x : objects) (x->*f) (std::get<Is>(tuple)...); } int main() { Mediator mediator; Object *a = new A(6, 1.2, 'a', 1111, mediator); Object *b = new B(2, 6.5, 'b', 2222, mediator); Object *c = new C(4, 0.8, 'c', 3333, mediator); c->change (&Object::adjust, 8, 'k'); // c->change (&Object::transform, 'z', 4); // This does not work though. }
Output:
Type A adjusted using values 8, 0, k, and 3333. Type B adjusted using values 8, 0, k, and 3333. Type C adjusted using values 8, 0, k, and 3333.
For some reason there were problems with std :: string, so I replaced the 4th parameter with a long one. Thus, this decision still needs to be clarified. I also introduced a new transform
member function to show that the string c->change (&Object::transform, 'z', 4);
in main () does NOT work (and does not work in the Mooing Duck solution), and therefore this solution needs more generalization, recursion with ARGS1 ... perhaps to be complete. In my solution above, only one ARGS1 ... is processed in the form (int, double, char, long).
I suspect that the Mooing Duck solution can also be generalized to handle any new member function of an object passed to Mediator :: change.
UPDATE: Ok, I replaced extractTuple
with
template <typename TUPLE, typename... ARGS2> TUPLE extractTuple (Object*, const TUPLE& tuple, ARGS2&&...) {return tuple;} template <typename TUPLE, typename FIRST, typename... REST, typename... ARGS2> auto extractTuple (Object* o, const TUPLE& current, ARGS2&&... args) -> decltype (extractTuple<typename Concatenate<FIRST, TUPLE>::type, REST...> (o, nextTuple<FIRST> (o, current, args...), args...)) { const typename Concatenate<FIRST, TUPLE>::type next = nextTuple<FIRST> (o, current, args...); return extractTuple<typename Concatenate<FIRST, TUPLE>::type, REST...> (o, next, args...); }
and I think this generalizes the above solution by unpacking ARGS1 ... Here is my new code:
#include <iostream> #include <string> #include <vector> #include <tuple> template <std::size_t...> struct index_sequence {}; template <std::size_t N, std::size_t... Is> struct make_index_sequence_helper : make_index_sequence_helper<N-1, N-1, Is...> {}; template <std::size_t... Is> struct make_index_sequence_helper<0, Is...> { using type = index_sequence<Is...>; }; template <std::size_t N> using make_index_sequence = typename make_index_sequence_helper<N>::type; struct Mediator { std::vector<struct Object*> objects; void registerObject (Object* o) {objects.emplace_back(o);} template <typename RET, typename... ARGS1, typename... ARGS2> RET change (Object*, RET (Object::*)(ARGS1...), ARGS2&&...); template <typename RET, typename... ARGS, std::size_t... Is> RET changeHelper (RET (Object::*)(ARGS...), const std::tuple<ARGS...>&, index_sequence<Is...>); }; struct Object { int value; double rating; char letter; long tag; Mediator& mediator; Object (int v, double r, char l, long s, Mediator& m) : value(v), rating(r), letter(l), tag(s), mediator(m) {mediator.registerObject(this);} virtual void adjust (int, double, char, long) = 0; virtual void transform (char, double, int) = 0; template <typename RET, typename... ARGS1, typename... ARGS2> RET change (RET (Object::*f)(ARGS1...), ARGS2&&... args) { return mediator.change(this, f, std::forward<ARGS2>(args)...); } }; struct A : Object { using Object::Object; virtual void adjust (int a, double b, char c, long s) override { std::cout << "Type A adjusted using values " << a << ", " << b << ", " << c << ", and " << s << "." << std::endl; } virtual void transform (char a, double b, int c) override { std::cout << "Type A transformed using values " << a << ", " << b << ", and " << c << "." << std::endl; } }; struct B : Object { using Object::Object; virtual void adjust (int a, double b, char c, long s) override { std::cout << "Type B adjusted using values " << a << ", " << b << ", " << c << ", and " << s << "." << std::endl; } virtual void transform (char a, double b, int c) override { std::cout << "Type B transformed using values " << a << ", " << b << ", and " << c << "." << std::endl; } }; struct C : Object { using Object::Object; virtual void adjust (int a, double b, char c, long s) override { std::cout << "Type C adjusted using values " << a << ", " << b << ", " << c << ", and " << s << "." << std::endl; } virtual void transform (char a, double b, int c) override { std::cout << "Type C transformed using values " << a << ", " << b << ", and " << c << "." << std::endl; } }; template <typename T, typename TUPLE> struct Concatenate; template <typename FIRST, typename ...REST> struct Concatenate<FIRST, std::tuple<REST...>> { using type = typename std::tuple<FIRST, REST...>; }; template <typename HEAD, typename TUPLE> typename Concatenate<HEAD, TUPLE>::type nextTuple (Object* o, const TUPLE& tuple) { if (std::is_same<HEAD, int>::value) // Using overload from some new class can probably handle all these cases better. return std::tuple_cat (tuple, std::tuple<int>(o->value)); else if (std::is_same<HEAD, double>::value) return std::tuple_cat (tuple, std::tuple<double>(o->rating)); else if (std::is_same<HEAD, char>::value) return std::tuple_cat (tuple, std::tuple<char>(o->letter)); else if (std::is_same<HEAD, long>::value) return std::tuple_cat (tuple, std::tuple<long>(o->tag)); } template <typename HEAD, typename TUPLE, typename FIRST, typename... REST> typename Concatenate<HEAD, TUPLE>::type nextTuple (Object* o, const TUPLE& tuple, FIRST first, REST... rest) { if (std::is_same<HEAD, FIRST>::value) return std::tuple_cat (tuple, std::tuple<FIRST>(first)); return nextTuple<HEAD, TUPLE, REST...> (o, tuple, rest...); } template <typename TUPLE, typename... ARGS2> TUPLE extractTuple (Object*, const TUPLE& tuple, ARGS2&&...) {return tuple;} // *** The change template <typename TUPLE, typename FIRST, typename... REST, typename... ARGS2> auto extractTuple (Object* o, const TUPLE& current, ARGS2&&... args) -> decltype (extractTuple<typename Concatenate<FIRST, TUPLE>::type, REST...> (o, nextTuple<FIRST> (o, current, args...), args...)) { const typename Concatenate<FIRST, TUPLE>::type next = nextTuple<FIRST> (o, current, args...); return extractTuple<typename Concatenate<FIRST, TUPLE>::type, REST...> (o, next, args...); } template <typename RET, typename... ARGS1, typename... ARGS2> RET Mediator::change (Object* o, RET (Object::*f)(ARGS1...), ARGS2&&... args) { const std::tuple<ARGS1...> tuple = extractTuple<std::tuple<>, ARGS1...> (o, std::tuple<>(), args...); // The key function. changeHelper (f, tuple, make_index_sequence<sizeof...(ARGS1)>()); } template <typename RET, typename... ARGS, std::size_t... Is> RET Mediator::changeHelper (RET (Object::*f)(ARGS...), const std::tuple<ARGS...>& tuple, index_sequence<Is...>) { for (Object* x : objects) (x->*f) (std::get<Is>(tuple)...); } int main() { Mediator mediator; Object *a = new A(6, 1.2, 'a', 1111, mediator); Object *b = new B(2, 6.5, 'b', 2222, mediator); Object *c = new C(4, 0.8, 'c', 3333, mediator); c->change (&Object::adjust, 8, 'k'); c->change (&Object::transform, 'z', 4); }
Note that c->change (&Object::transform, 'z', 4);
now located in main (). But my GCC 4.8.1 seems to be listening and cannot handle the ad
template <typename TUPLE, typename FIRST, typename... REST, typename... ARGS2> auto extractTuple (Object* o, const TUPLE& current, ARGS2&&... args) -> decltype (extractTuple<typename Concatenate<FIRST, TUPLE>::type, REST...> (o, nextTuple<FIRST> (o, current, args...), args...)) {
resulting in an "internal compiler error". Perhaps someone with a later compiler with C ++ 14 can check if this is a legal expression or not? I don't have C ++ 14.