Referring to the code below, can someone figure out how to adapt
template <typename RET, typename... ARGS1, typename... ARGS2>
RET Mediator::change (Object* o, RET (Object::*f)(ARGS1...), ARGS2&&... args) {
const std::tuple<ARGS2...> t(args...);
for (Object* x : objects)
(x->*f)(std::get<0>(t), o->rating, std::get<1>(t), o->str);
}
so that I don't have to rewrite different versions of this every time ARGS2... is to be changed. I don't mind doing it in the case where the argument consists of only 4 parameters, but you can imagine that the generalization would be needed if it was much greater than 4. The types in ARGS1... shall consist of different types, so there should be a way get std::get<0>(t), std::get<1>(t), ... correctly placed so that there is no need to do it manually like above (even if there were duplicate types, then they can simply be placed in the first slot of the duplicate types). Here is the full code below (the context is that as each Object subscriber to a Mediator changes, the other Object subscribers to the Mediator shall change accordingly):
#include <iostream>
#include <string>
#include <vector>
#include <tuple>
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&&...);
};
struct Object {
int value;
double rating;
char letter;
std::string str;
Mediator& mediator;
Object (int v, double r, char l, const std::string& s, Mediator& m) :
value(v), rating(r), letter(l), str(s), mediator(m) {mediator.registerObject(this);}
virtual void adjust (int, double, char, const std::string&) = 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, const std::string& s) override {
std::cout << "Type A adjusted using values " << a << ", " << b << ", " << c << ", and " << s << "." << std::endl;
}
};
struct B : Object {
using Object::Object;
virtual void adjust (int a, double b, char c, const std::string& s) override {
std::cout << "Type B adjusted using values " << a << ", " << b << ", " << c << ", and " << s << "." << std::endl;
}
};
struct C : Object {
using Object::Object;
virtual void adjust (int a, double b, char c, const std::string& s) override {
std::cout << "Type C adjusted using values " << a << ", " << b << ", " << c << ", and " << s << "." << std::endl;
}
};
template <typename RET, typename... ARGS1, typename... ARGS2>
RET Mediator::change (Object* o, RET (Object::*f)(ARGS1...), ARGS2&&... args) {
const std::tuple<ARGS2...> t(args...);
for (Object* x : objects)
(x->*f)(std::get<0>(t), o->rating, std::get<1>(t), o->str);
}
int main() {
Mediator mediator;
Object *a = new A(6, 1.2, 'a', "alan", mediator);
Object *b = new B(2, 6.5, 'b', "bob", mediator);
Object *c = new C(4, 0.8, 'c', "craig", mediator);
c->change (&Object::adjust, 8, 'k');
}
Output:
Type A adjusted using values 8, 0.8, k, and craig.
Type B adjusted using values 8, 0.8, k, and craig.
Type C adjusted using values 8, 0.8, k, and craig.
This is as far as I've gotten with my solution. It gives the same output, but the line marked // Here!
is what I need generated automatically.
#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;
std::string str;
Mediator& mediator;
Object (int v, double r, char l, const std::string& s, Mediator& m) :
value(v), rating(r), letter(l), str(s), mediator(m) {mediator.registerObject(this);}
virtual void adjust (int, double, char, const std::string&) = 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, const std::string& s) override {
std::cout << "Type A adjusted using values " << a << ", " << b << ", " << c << ", and " << s << "." << std::endl;
}
};
struct B : Object {
using Object::Object;
virtual void adjust (int a, double b, char c, const std::string& s) override {
std::cout << "Type B adjusted using values " << a << ", " << b << ", " << c << ", and " << s << "." << std::endl;
}
};
struct C : Object {
using Object::Object;
virtual void adjust (int a, double b, char c, const std::string& s) override {
std::cout << "Type C adjusted using values " << a << ", " << b << ", " << c << ", and " << s << "." << std::endl;
}
};
template <typename RET, typename... ARGS1, typename... ARGS2>
RET Mediator::change (Object* o, RET (Object::*f)(ARGS1...), ARGS2&&... args) {
const std::tuple<ARGS2...> t(args...);
// Here!
const std::tuple<ARGS1...> tuple(std::get<0>(t), o->rating, std::get<1>(t), o->str);
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', "alan", mediator);
Object *b = new B(2, 6.5, 'b', "bob", mediator);
Object *c = new C(4, 0.8, 'c', "craig", mediator);
c->change (&Object::adjust, 8, 'k');
}
How to auto-generate the tuple
const std::tuple<ARGS1...> tuple(std::get<0>(t), o->rating, std::get<1>(t), o->str);
using something along the lines of
template <typename... ARGS1, typename... ARGS2>
std::tuple<ARGS1...> extractTuple (Object* o, ARGS2&&... args);
so that new versions of Mediator::change will not be needed for different (possibly many, if ARGS1... is large) choices of ARGS2... ? My current idea is to use a recursive helper method, std::is_same, std::tuple_cat, etc... but I'm having problems (I think we are unpacking ARGS2... within unpacking ARGS1... during the checking of types).