2010-03-20 5 views
5

Extendscallbacks Generic

Related

Vì vậy, tôi đang cố gắng tìm hiểu mẫu Lập trình meta tốt hơn và tôi hình dung đây là một bài tập tốt cho nó.

Tôi đang cố gắng viết mã có thể gọi lại một hàm với bất kỳ số lượng đối số nào tôi muốn chuyển cho nó.

 
// First function to call 
int add(int x, int y) ; 

// Second function to call 
double square(double x) ; 

// Third func to call 
void go() ; 

Việc gọi lại mã tạo nên hình như:

 
// Write a callback object that 
// will be executed after 42ms for "add" 
Callback<int, int, int> c1 ; 
c1.func = add ; 
c1.args.push_back(2); // these are the 2 args 
c1.args.push_back(5); // to pass to the "add" function 
         // when it is called 

Callback<double, double> c2 ; 
c2.func = square ; 
c2.args.push_back(52.2) ; 

Những gì tôi đang suy nghĩ là, sử dụng mẫu Lập trình meta Tôi muốn để có thể tuyên bố callbacks như, viết một struct như thế này (hãy nhớ đây là giả RẤT)

 
<TEMPLATING ACTION <<ANY NUMBER OF TYPES GO HERE>> > 
struct Callback 
{ 
    double execTime ; // when to execute 
    TYPE1 (*func)(TYPE2 a, TYPE3 b) ; 

    void* argList ; // a stored list of arguments 
         // to plug in when it is time to call __func__ 
} ; 

vì vậy, khi gọi với

 
Callback<int, int, int> c1 ; 

Bạn sẽ tự động được xây dựng cho bạn bằng cách < HARDCORE khuôn mẫu HÀNH ĐỘNG > một struct như

 
struct Callback 
{ 
    double execTime ; // when to execute 
    int (*func)(int a, int b) ; 

    void* argList ; // this would still be void*, 
         // but I somehow need to remember 
         // the types of the args.. 
} ; 

Bất kỳ con trỏ đi đúng hướng để bắt đầu viết những dòng này?

Trả lời

1

Nhìn vào boost::bind. Tôi có ít nhiều hơn để nói ... thời gian có lẽ là tốt nhất dành poring trên nguồn của họ và cố gắng reimplement nó, nếu bạn thực sự muốn hiểu internals. Nhưng với cách họ đã đánh bóng nó, việc tái thực hiện chỉ là một sự theo đuổi học tập.

2

Bạn có thể thực hiện việc này với variadic templates, trình biên dịch của bạn có thể không hỗ trợ. Tôi đã không bao giờ sử dụng chúng bản thân mình và do đó có thể nhận được một số chi tiết sai, nhưng tôi sẽ cố gắng để mô tả chúng.

Các mẫu biến thể sử dụng toán tử "...". Trong một tuyên bố mẫu (hoặc các biểu thức kiểu khác), các dấu ba chấm biểu thị rằng tham số chính thức có thể lấy bất kỳ số lượng đối số nào.

template <typename ... Args> 
class Variadic { 
public: 
    operator()(Args&& ... args); 
}; 

Trong biểu thức gọi hàm, dấu ba chấm giải nén đối số bên trái của chúng.

Variadic<Args>::operator(Args&& ... args) { 
    func(args...); 
} 

Để chuyển tiếp, bạn có thể cần phải sử dụng std::forward; đây là một lĩnh vực mà kiến ​​thức của tôi phát triển mờ. Đặt này lại với nhau, và chúng tôi nhận được:

template <typename ReturnValue, typename ... Args> 
class Callback { 
    typedef ReturnValue (*Func)(Args ... args); 

    double execTime; 
    Func func; 
    Args... args; 

public: 
    Callback(double et, Func f) : execTime(et), func(f) {} 
    ReturnValue operator()(Args&& ... a); 
    ReturnValue operator()(); 
}; 

template <typename ReturnValue, typename ... Args> 
ReturnValue Callback<ReturnValue, Args>::operator()(Args&& ... a) { 
    return (*func)(std::forward(a)...); 
} 
template <typename ReturnValue, typename ... Args> 
ReturnValue Callback<ReturnValue, Args>::operator()() { 
    return operator(*func)(args...); 
} 
+1

Mẫu biến thể là một phần của C++ 0x. Một trình biên dịch hỗ trợ chúng (hoặc thậm chí là một trình biên dịch với hỗ trợ ít hơn C++ 0x) cũng sẽ bao gồm các đối tượng hàm C++ 0x và 'std :: bind', tương tự như' boost :: bind' và thực hiện những gì bạn đang nói về. – Potatoswatter

0

C++ 0x thêm mẫu variadic, mà trực tiếp hỗ trợ một mẫu mà phải mất một số tùy ý các thông số. Nếu không có điều đó, bạn có thể sử dụng một phần chuyên môn hóa để mô phỏng nó, mặc dù nó đòi hỏi một chuyên môn riêng biệt cho mỗi số tham số. Ví dụ, bạn có thể hỗ trợ 1-3 thông số như thế này:

class null_class {}; 

template <class func, class arg1, class arg2, class arg3> 
class callback_t { 
    func f; 
    arg1 a; 
    arg2 b; 
    arg3 c; 
public: 
    callback_t(func f, arg1 a, arg2 b, arg3 c) : f(f), a(a), b(b), c(c) {} 
    double operator()() const { return f(a, b, c); } 
}; 

template <class func, class arg1, class arg2> 
class callback_t<func, arg1, arg2, null_class> { 
    func f; 
    arg1 a; 
    arg2 b; 
public: 
    callback_t(func f, arg1 a, arg2 b) : f(f), a(a), b(b) {} 
    double operator()() const { return f(a, b); } 
}; 

template <class func, class arg1> 
class callback_t<func, arg1, null_class, null_class> { 
    func f; 
    arg1 a; 
public: 
    callback_t(func f, arg1 a) : f(f), a(a) {} 
    double operator()() const { return f(a); } 
}; 

template <class func, class arg1, class arg2, class arg3> 
callback_t<func, arg1, arg2, arg3> 
callback(func f, arg1 a, arg2 b, arg3 c) { 
    return callback_t<func, arg1, arg2, arg3>(f, a, b, c); 
} 

template <class func, class arg1, class arg2> 
callback_t<func, arg1, arg2, null_class> 
callback(func f, arg1 a, arg2 b) { 
    return callback_t<func, arg1, arg2, null_class>(f, a, b); 
} 

template <class func, class arg> 
callback_t<func, arg, null_class, null_class> 
callback(func f, arg a) { 
    return callback_t<func, arg, null_class, null_class>(f, a); 
} 

#ifdef TEST 
#include <iostream> 

double square(double d) { 
    return d * d; 
} 

double add(double a, double b) { 
    return a + b; 
} 

double sum(double a, double b, double c) { 
    return a + b + c; 
} 

int main() { 
    double a = 2.0, b = 3.0, c=4.0; 

    double d = callback(square, a)(); 
    double e = callback(add, b, c)(); 
    double f = callback(sum, a, b, c)(); 

    std::cout << "2.0 squared = " << d << "\n"; 
    std::cout << "3.0 + 4.0 = " << e << "\n"; 
    std::cout << "Sum = " << f << "\n"; 
    return 0; 
} 

#endif 

Kiểu trả về có thể được templated là tốt, nhưng tôi đã rời khỏi đó ra vì lợi ích của sự đơn giản (hoặc ít nhất là giảm độ phức tạp).

0

Để bắt đầu, bạn nên kiểm tra Boost.Function, vì nó là về chức năng gói tự động, nó sẽ cung cấp cho bạn những ý tưởng tôi nghĩ;)

Thứ hai, cú pháp của bạn là một chút vụng về. Bạn hoàn toàn có thể sử dụng chức năng chữ ký như một phần như các thông số mẫu, mà độc đáo giao dịch với các vấn đề của mẫu variadic vì nó cho phép bạn vượt qua một số tùy ý các loại;)

Callback< int(int,int) > callback; 

Sẽ chỉ ra rằng callback của bạn sẽ mất một trỏ đến một hàm có chữ ký tương tự như add: int add(int, int) của bạn. Tôi thực sự thích cú pháp này vì nó làm cho nó rõ ràng hơn những gì chúng tôi đang đi qua.

Trước khi bắt đầu, tôi có câu hỏi: bạn muốn làm gì về loại trả lại?

1. Tài liệu tham khảo

Sau đó, có những thứ như Boost.Fusion thư viện mà có thể giúp bạn rất nhiều (về cơ bản, các tuple).

Ngoài ra, hãy xem Boost.FunctionTypes cung cấp các phương tiện để phân tích chữ ký chức năng.

2. Trên đường một lần nữa

// It is nice to have a base class 
// Generally callbacks do not return anything though... 
struct CallbackBase 
{ 
    virtual ~CallbackBase(); 
    virtual void execute() const = 0; 
}; 

namespace func_ = boost::function_types; 

template < 
    class Parameters, 
    class N = typename mpl_::minus< 
    typename mpl_::size<Parameters>::type, 
    mpl_::size_t<1> 
    >::type 
> 
class Streamer 
{ 
public: 
    typedef Streamer< Parameters, typename mpl_::minus<N,1>::type > next_type; 
    typedef typename mpl_::size<Parameters>::type size_type; 
    typedef typename mpl_::minus< size_type, N >::type index_type; 
    typedef typename mpl_::at<Parameters, index_type>::type arg_type; 

    Streamer(Parameters& p): mParameters(p) {} 

    next_type operator<<(arg_type arg) 
    { 
    boost::fusion::at_c<index_type>(mParameters) = arg; 
    return next_type(mParameters); 
    } 

private: 
    Parameters& mParameters; 
}; 

template <class Parameters> 
struct Streamer<Paramaters,0> 
{ 
    Streamer(Parameters&) {} 
}; 


template <class Function> 
class Callback: public CallbackBase 
{ 
public: 
    typedef typename func_::result_type<Function>::type result_type; 
    typedef typename func_::parameters_type<Function>::type parameters_type; 
    typedef typename func_::function_pointer< 
    typename func_::components<Function>::type 
    >::type function_pointer; 

    Callback(function_pointer f): mFunction(f) {} 

    virtual void execute() const 
    { 
    mReturn = Invoke<function_pointer>::Do(f,mParameters); 
    } 

    Streamer<parameters_type> operator<<(typename mpl_::at<parameters_type, 0>::type arg) 
    { 
    boost::fusion::at_c<0>(mParameters) = arg; 
    return Streamer<parameters_type>(mParameters); 
    } 

private: 
    function_pointer f; 
    result_type mResult; 
    parameters_type mParameters; 
}; 

Vâng, đó là tôi đã đi được bao xa. Tôi đã không xử lý với lời gọi thực tế mà yêu cầu giải nén các tuple để vượt qua các đối số cho hàm.

sử dụng Cho đến nay sẽ là:

int add(int,int); 

void pass(const CallbackBase& b); 

int main(int argc, char* argv[]) 
{ 
    Callback<int(int,int)> c(&add); 
    c << 2 << 4; 
    pass(c); 
} 

tôi khuyến khích bạn đi sâu vào Boost.Fusion nếu bạn muốn theo đuổi các nghiên cứu của mình trong lĩnh vực này, như tinh khiết mẫu Lập trình meta thường là vô ích nếu bạn không thể đưa kết quả vào thế giới thời gian chạy :)