2011-10-07 4 views
9

Tôi đang sử dụng Visual Studio 2008 và tôi muốn triển khai chức năng định dạng chuỗi mà không có Danh sách đối số biến số.Làm cách nào để triển khai "Mẫu biến thể" với pre-C++ 0x (VS2008)?

Cách triển khai "Mẫu biến thể" với pre-C++ 0x (VS2008)?

Có thư viện nào triển khai chương trình tăng tốc này không?

Hoặc cách khác để thực hiện việc này?

Đây là mã mẫu của tôi. (tất nhiên, điều này có thể không được tuân thủ, vì tôi đang sử dụng VS2008.)

bool VarPrint(std::ostringstream& out, const std::string& s) 
{ 
    std::string::size_type offset = 0; 
    if((offset = s.find("%")) != std::string::npos) 
    { 
     if(!(offset != s.size() - 1 && s[offset + 1] == '%')) 
     { 
      ASSERT(!"Missing Arguments!"); 
      return false; 
     } 
    } 
    out << s; 
    return true; 
} 

template<typename T, typename... Args> 
bool VarPrint(std::ostringstream& out, const std::string& s, const T& value, const Args&... args) 
{ 
    std::string::size_type prev_offset = 0; 
    std::string::size_type curr_offset = 0; 
    while((curr_offset = s.find("%", prev_offset)) != std::string::npos) 
    { 
     out << s.substr(prev_offset, curr_offset); 
      if(!(curr_offset != s.size() - 1 && s[curr_offset + 1] == '%')) 
     { 
      out << value; 
      if(curr_offset + 2 < s.length()) 
       return VarPrint(out, s.substr(curr_offset + 2), args...);     return true; 
     } 

     prev_offset = curr_offset + 2; 
     if(prev_offset >= s.length) 
      break; 
    } 
    ASSERT(!"Extra Argument Provided!"); 
    return false; 
} 
+0

Không thể nếu bạn muốn bất kỳ số lượng đối số tùy ý nào. Tất cả những gì bạn có thể làm là cung cấp việc triển khai cho một số lượng đối số bị ràng buộc, như 1-10 đối số. – Dani

+0

Biên dịch ví dụ của bạn: 'prev_offset> = s.length' -> lưu ý thiếu'() 'để gọi hàm' length' :) –

+0

Cảm ơn bạn. Tôi đã bỏ lỡ '()', trong khi gõ trực tiếp. hah: D – winnerrrr

Trả lời

18

Trong C++ 03, bạn có khả năng khác nhau:

  1. tạo ra quá tải cho 0-N đối số (sử dụng Boost.Preprocessor ví dụ)
  2. sử dụng Nhược điểm-Lists (cons(1)("some string")(foo))
  3. sử dụng đối tượng và quá tải một số toán tử (ví dụ: operator() chẳng hạn hoặc operator% như Boost.Format)

Tùy chọn đầu tiên hơi phức tạp, tôi cảm thấy, bởi vì không phải mọi người đều có thể hiểu macro một cách dễ dàng, vì vậy tôi sẽ chỉ dành cho các giải pháp ngắn hạn nếu bạn dự định chuyển sang C++ 0x sớm.

Tùy chọn thứ ba có thể cung cấp một chạm tùy chỉnh đẹp (định dạng được thực hiện bằng nhiều ngôn ngữ), nhưng điều này cũng có nghĩa là bạn cần nhớ chức năng "variadic" cụ thể này hoạt động mỗi lần như thế nào.

sở thích cá nhân của tôi là cách tiếp cận cons vì nó giải quyết cả hai vấn đề:

  • định nghĩa chỉ liên quan đến các mẫu, vì vậy nó dễ đọc hơn và maintanable hơn 1.
  • bạn xác định các khuyết điểm-máy móc thiết bị cùng một lúc, và bạn có thể sau đó tái sử dụng nó cho bất kỳ chức năng "variadic" (và họ vẫn chức năng), vì vậy nó là phù hợp hơn, và giúp bạn tiết kiệm làm việc

Ví dụ, đây là cách nó có thể làm việc:

bao gồm rằng ví dụ này sẽ sử dụng:

#include <cassert> 
#include <iostream> 
#include <string> 

một helper cho các loại hình kết quả của phụ thêm một giá trị (nó có thể hiệu quả hơn với thêm vào trước, nhưng điều đó có nghĩa đi qua các đối số theo thứ tự ngược đó là phản trực giác):

template <typename T, typename Next> struct Cons; 
struct ConsEmpty; 

template <typename Cons, typename U> 
struct cons_result; 

template <typename U> 
struct cons_result<ConsEmpty, U> { 
    typedef Cons<U, ConsEmpty> type; 
}; 

template <typename T, typename U> 
struct cons_result<Cons<T, ConsEmpty>, U> { 
    typedef Cons<T, Cons<U, ConsEmpty> > type; 
}; 

template <typename T, typename Next, typename U> 
struct cons_result<Cons<T, Next>, U> { 
    typedef Cons<T, typename cons_result<Next, U>::type> type; 
}; 

Các Cons mẫu riêng của mình, với một ma thuật operator() để thêm giá trị. Lưu ý rằng nó tạo ra một mục mới với một kiểu khác nhau:

template <typename T, typename Next> 
struct Cons { 
    Cons(T t, Next n): value(t), next(n) {} 

    T value; 
    Next next; 

    template <typename U> 
    typename cons_result<Cons, U>::type operator()(U u) { 
    typedef typename cons_result<Cons, U>::type Result; 
    return Result(value, next(u)); 
    } 
}; 

struct ConsEmpty { 
    template <typename U> 
    Cons<U, ConsEmpty> operator()(U u) { 
    return Cons<U, ConsEmpty>(u, ConsEmpty()); 
    } 
}; 

template <typename T> 
Cons<T, ConsEmpty> cons(T t) { 
    return Cons<T, ConsEmpty>(t, ConsEmpty()); 
} 

Một revisited VarPrint với nó:

bool VarPrint(std::ostream& out, const std::string& s, ConsEmpty) { 
    std::string::size_type offset = 0; 
    if((offset = s.find("%")) != std::string::npos) { 
     if(offset == s.size() - 1 || s[offset + 1] != '%') { 
      assert(0 && "Missing Arguments!"); 
      return false; 
     } 
    } 
    out << s; 
    return true; 
} 

template<typename T, typename Next> 
bool VarPrint(std::ostream& out, 
       std::string const& s, 
       Cons<T, Next> const& cons) 
{ 
    std::string::size_type prev_offset = 0, curr_offset = 0; 
    while((curr_offset = s.find("%", prev_offset)) != std::string::npos) { 
     out << s.substr(prev_offset, curr_offset); 
     if(curr_offset == s.size() - 1 || s[curr_offset + 1] != '%') { 
      out << cons.value; 
      if(curr_offset + 2 < s.length()) 
       return VarPrint(out, s.substr(curr_offset + 2), cons.next); 
      return true; 
     } 
     prev_offset = curr_offset + 2; 
     if(prev_offset >= s.length()) 
      break; 
    } 
    assert(0 && "Extra Argument Provided!"); 
    return false; 
} 

Và demo

int main() { 
    VarPrint(std::cout, "integer %i\n", cons(1)); 
    VarPrint(std::cout, "mix of %i and %s\n", cons(2)("foo")); 
} 

Bạn có thể kiểm tra kết quả trên ideone:

integer 1 
mix of 2 and foo 
+0

Cool ~ Tôi đánh giá cao chi tiết của bạn! – winnerrrr

6

Không có chức năng template variadic trong C++ 03. Boost và các thư viện được thiết kế tốt khác hoạt động xung quanh điều này theo nhiều cách khác nhau. Đối với các hàm, ta có thể có một số quá tải N + 1 trong đó mỗi quá tải lấy từ 0 đến N đối số. Đối với các lớp, người ta có thể có một định nghĩa duy nhất với tối đa N đối số mặc định cho một số loại không hợp lệ. Giới hạn cao hơn này thường có thể cấu hình thông qua một số macro; bởi vì thiết lập nó ở mức cao sẽ áp đặt một chi phí trong thời gian biên dịch, và đặt nó xuống thấp sẽ khiến người dùng không thể vượt qua đủ đối số.

Đối với trường hợp cụ thể của bạn, tôi sẽ triển khai VarPrint theo cách đệ quy. Mỗi bước lên trong đệ quy sẽ xử lý một đối số duy nhất, và đưa ra một cuộc gọi đệ quy với một chuỗi định dạng đã sửa đổi và tất cả các giá trị bên trái dịch chuyển sang trái một vị trí.

+0

Cảm ơn bạn. Tôi nghĩ rằng tôi không có sự lựa chọn, nhưng để thực hiện chức năng N quá tải. :] – winnerrrr

+1

@winnerrrr: Boost.Format chỉ sử dụng toán tử quá tải và đánh giá lười biếng để làm điều đó. Bạn phải đặt '%' giữa các đối số thay vì ',' s và chúng không đi vào dấu ngoặc đơn. Nhưng nó đã có tác dụng. –