Dựa trên câu trả lời của @ IllidanS4, tôi đã tạo một lớp mẫu cho phép hầu như bất kỳ hàm thành viên nào với các đối số được xác định trước và cá thể lớp được chuyển qua tham chiếu để gọi sau.
template<class RET, class... RArgs> class Callback_t {
public:
virtual RET call(RArgs&&... rargs) = 0;
};
template<class T, class RET, class... RArgs> class CallbackCalltimeArgs : public Callback_t<RET, RArgs...> {
public:
T * owner;
RET(T::*x)(RArgs...);
RET call(RArgs&&... rargs) {
return (*owner.*(x))(std::forward<RArgs>(rargs)...);
};
CallbackCalltimeArgs(T* t, RET(T::*x)(RArgs...)) : owner(t), x(x) {}
};
template<class T, class RET, class... Args> class CallbackCreattimeArgs : public Callback_t<RET> {
public:
T* owner;
RET(T::*x)(Args...);
RET call() {
return (*owner.*(x))(std::get<Args&&>(args)...);
};
std::tuple<Args&&...> args;
CallbackCreattimeArgs(T* t, RET(T::*x)(Args...), Args&&... args) : owner(t), x(x),
args(std::tuple<Args&&...>(std::forward<Args>(args)...)) {}
};
Kiểm tra / ví dụ:
class container {
public:
static void printFrom(container* c) { c->print(); };
container(int data) : data(data) {};
~container() {};
void print() { printf("%d\n", data); };
void printTo(FILE* f) { fprintf(f, "%d\n", data); };
void printWith(int arg) { printf("%d:%d\n", data, arg); };
private:
int data;
};
int main() {
container c1(1), c2(20);
CallbackCreattimeArgs<container, void> f1(&c1, &container::print);
Callback_t<void>* fp1 = &f1;
fp1->call();
CallbackCreattimeArgs<container, void, FILE*> f2(&c2, &container::printTo, stdout);
Callback_t<void>* fp2 = &f2;
fp2->call();
CallbackCalltimeArgs<container, void, int> f3(&c2, &container::printWith);
Callback_t<void, int>* fp3 = &f3;
fp3->call(15);
}
Rõ ràng, điều này sẽ chỉ hoạt động nếu các đối số đã cho và lớp chủ sở hữu vẫn hợp lệ. Theo như khả năng đọc được ... xin hãy thứ lỗi cho tôi.
Chỉnh sửa: loại bỏ malloc không cần thiết bằng cách làm cho bộ lưu trữ bình thường. Đã thêm loại kế thừa cho tham chiếu. Đã thêm tùy chọn để cung cấp tất cả các đối số vào thời gian gọi thay thế. Bây giờ đang làm việc để có cả hai ....
Chỉnh sửa 2: Như đã hứa, cả hai. Chỉ có một hạn chế (mà tôi thấy) là các đối số được xác định trước phải đến trước các đối số được cung cấp thời gian chạy trong hàm gọi lại. Cảm ơn @Chipster về một số trợ giúp trong việc tuân thủ gcc. Điều này hoạt động trên gcc trên ubuntu và studio trực quan trên windows.
#ifdef _WIN32
#define wintypename typename
#else
#define wintypename
#endif
template<class RET, class... RArgs> class Callback_t {
public:
virtual RET call(RArgs... rargs) = 0;
virtual ~Callback_t() = default;
};
template<class RET, class... RArgs> class CallbackFactory {
private:
template<class T, class... CArgs> class Callback : public Callback_t<RET, RArgs...> {
private:
T * owner;
RET(T::*x)(CArgs..., RArgs...);
std::tuple<CArgs...> cargs;
RET call(RArgs... rargs) {
return (*owner.*(x))(std::get<CArgs>(cargs)..., rargs...);
};
public:
Callback(T* t, RET(T::*x)(CArgs..., RArgs...), CArgs... pda);
~Callback() {};
};
public:
template<class U, class... CArgs> static Callback_t<RET, RArgs...>* make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...));
};
template<class RET2, class... RArgs2> template<class T2, class... CArgs2> CallbackFactory<RET2, RArgs2...>::Callback<T2, CArgs2...>::Callback(T2* t, RET2(T2::*x)(CArgs2..., RArgs2...), CArgs2... pda) : x(x), owner(t), cargs(std::forward<CArgs2>(pda)...) {}
template<class RET, class... RArgs> template<class U, class... CArgs> Callback_t<RET, RArgs...>* CallbackFactory<RET, RArgs...>::make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...)) {
return new wintypename CallbackFactory<RET, RArgs...>::Callback<U, CArgs...>(owner, func, std::forward<CArgs>(cargs)...);
}