2011-11-29 22 views
5

Tôi có một số lượng lớn các lớp được sử dụng để trang trí một vài phương pháp cụ thể.Giảm boilerplate khi trang trí phương pháp

Có cách nào gọn gàng để giảm số lượng mã boilerplate (chủ yếu là tất cả các thông số của hàm tạo và các thành viên để giữ chúng) cần được thêm vào mỗi lớp này? Hoặc, thậm chí tốt hơn, có cách nào tốt hơn để làm điều này không?

Tôi không thể sử dụng các phương pháp ảo và chỉ có thể sử dụng tập con của C++ 11 tính năng được hỗ trợ bởi gcc 4.6 và vs2010.

Tôi tin rằng các nhà xây dựng kế thừa C++ 11 sẽ trợ giúp ở đây, nhưng cả trình biên dịch đều không hỗ trợ chúng và tôi không biết cách giải quyết.

Dưới đây là một ví dụ về những gì các lớp này hiện trông giống như:

template<class Underlying, class T1> 
struct A : Base<A<Underlying, T1> > 
{ 
    typedef AImpl<decltype(declval<Underlying>().foo()), T1> impl; 
    A(Underlying u, T1 t1) : 
     u_(u), 
     t1_(t1) 
    {} 

    impl foo() const { return impl(u_.foo(), t1_); } 
    impl bar() const { return impl(u_.bar(), t1_); } 

    const Underlying u_; 
    T1 t1_; 
}; 


template<class Underlying, class T1, class T2> 
struct B : Base<B<Underlying, T1, T2> > 
{ 
    typedef BImpl<decltype(declval<Underlying>().bar()), T1, T2> impl; 
    B(Underlying u, T1 t1, T2 t2) : 
     u_(u), 
     t1_(t1), 
     t2_(t2) 
    {} 

    impl foo() const { return impl(u_.bar(), 999, t2_); } 
    impl bar() const { return impl(u_.foo(), t1_, t2_); } 

    const Underlying u_; 
    T1 t1_; 
    T2 t2; 
}; 

Trả lời

1

Bạn có thể sử dụng các mẫu variadic trong GCC 4.6.

template<class Underlying, class... T> 
struct B : Base<B<Underlying, T...>> 
{ 
    typedef BImpl<decltype(declval<Underlying>().bar()), T...> impl; 

    template<typename V...> 
    B(Underlying u, V... v) : 
     u_(u), 
     t_(std::forward<V>(v)...) 
    {} 

    impl foo() const { return impl(u_.bar(), 999, std::get<1>(t_)); } // Not sure what the 999 is? 
    impl bar() const { return impl(u_.foo(), std::get<0>(t_), std::get<1>(t_)); } 

    const Underlying u_; 
    std::tuple<T...> t_; 
}; 
+0

Nhưng không phải trong VC++ 2010 ... – ildjarn

+0

Tôi nghĩ rằng việc sử dụng 'forward' có thể hơi không chính xác (ít hiệu quả nhất) tại đây. Theo mặc định, bất kỳ việc sử dụng 'forward' nào đều phải trong một cuộc gọi phương thức với các kiểu tham số được suy luận. Điều đó có nghĩa là 'mẫu B (Underlying u, V && ... t): u_ (u), t_ (std :: forward (t) ...) {}'. Ba điều: 1) Bản thân hàm phải là một mẫu. 2) Các arg nên được sử dụng như 'U && ...', và 'forward' shuld được sử dụng. Mã hiện tại sẽ * sao chép * các tham số vào 'B' và sau đó nó có thể * di chuyển * chúng vào lớp cơ sở. Lý tưởng nhất, bạn muốn không có bản sao và thay vào đó đã di chuyển tất cả các cách, khi có thể. –

+1

Hai điều, nhận được không phải là một thành viên của tuple trong việc thực hiện tiêu chuẩn (nó là trong Tăng), và việc sử dụng chuyển tiếp là không chính xác trong constructor của bạn. Nên là 'mẫu B (Theo u, V && ... v): u_ (u), t_ (std :: forward (v) ...) {}'. – DRayX

1

Nếu cần làm việc trong vs2010, thì đặt cược tốt nhất của bạn là Boost.Preprocessor. Nó không mạnh mẽ như các mẫu variadic ở chỗ nó có độ sâu đệ quy tối đa là 3 và số lần lặp tối đa là 256, nhưng nó hoạt động trong mọi trình biên dịch có hoặc không có hỗ trợ C++ 11. Nó không phải là hoàn toàn rõ ràng những gì bạn đang cố gắng để làm với những ví dụ, nhưng nếu bạn chỉ cố gắng để trang trí chức năng, tôi sử dụng sau (sử dụng C + + 11 cho chuyển tiếp hoàn hảo với Boost.Preprocessor thay vì variadic mẫu):

#define FORWARD(z, n, data) \ 
    ::std::forward<BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(2, 0, data), n)> \ 
    (BOOST_PP_CAT(BOST_PP_TUPLE_ELEM(2, 1, data), n)) \ 
    /**/ 

//------------------------------------------------------------------------ 
// template<class R, class... A> 
// ::std::function<R(A...)> decorate(::std::function<R(A...)> const& func) 
// { 
//  return [func](A&&... a) -> R 
//  { 
//   /* DECORATOR STUFF */ 
//   return func(::std::forward<A>(a)...); 
//  }; 
// } 
//------------------------------------------------------------------------ 
#define MACRO(z, n, _) \ 
    template<class R BOOST_PP_ENUM_TRAILING_PARAMS_Z(z, n, class A)> \ 
    ::std::function<R(BOOST_PP_ENUM_PARAMS_Z(z, n, a)> \ 
     decorate(::std::function<R(BOOST_PP_ENUM_PARAMS_Z(z, n, A))> const& func) \ 
    { \ 
     return [func](BOOST_PP_ENUM_BINARY_PARAMS_Z(z, n, A, &&a)) -> R \ 
     { \ 
      /* DECORATOR STUFF */ \ 
      return func(BOOST_PP_ENUM_ ## z(n, FORWARD, (A, a))); \ 
     }; \ 
    } \ 
    /**/ 

BOOST_PP_REPEAT(10, MACRO, ~) 

#undef MACRO 
#undef FORWARD 

Nếu bạn đang cố kết hợp các đối số hàm, thì std::bind là những gì bạn đang tìm kiếm. Lưu ý rằng bạn có thể thực hiện bất kỳ điều này chỉ với lambdas, nhưng các hàm trang trí có thể được sử dụng để trang trí nhiều hàm (trong đó lambdas dĩ nhiên là một lần) và ràng buộc có thể ngắn gọn hơn một chút cho các đối số liên kết (tuy nhiên, tương đương lambda đôi khi dễ đọc hơn).

Các vấn đề liên quan