2009-10-08 12 views
6

tôi là tạo ra một logger với các phần sau:stringstream tạm ostream trở lại vấn đề

// #define LOG(x) // for release mode 
#define LOG(x) log(x) 

log(const string& str); 
log(const ostream& str); 

Với ý tưởng làm:

LOG("Test"); 
LOG(string("Testing") + " 123"); 
stringstream s; 
LOG(s << "Testing" << 1 << "two" << 3); 

này tất cả các công trình như dự định, nhưng khi tôi làm:

LOG(stringstream() << "Testing" << 1 << "two" << 3); 

Nó không làm việc:

void log(const ostream& os) 
{ 
    std::streambuf* buf = os.rdbuf(); 
    if(buf && typeid(*buf) == typeid(std::stringbuf)) 
    { 
    const std::string& format = dynamic_cast<std::stringbuf&>(*buf).str(); 
    cout << format << endl; 
    } 
} 

kết quả trong 'định dạng' chứa dữ liệu rác thay vì chuỗi chính xác thông thường.

Tôi nghĩ rằng điều này là do luồng thời gian tạm thời được trả về bởi nhà cung cấp < < làm nổi bật chuỗi xuất phát.

Hoặc tôi có sai không?

(Tại sao string() làm việc theo cách này? Có phải vì nó sẽ trả về một tham chiếu đến chính nó? Tôi giả định là có.)

Tôi thực sự muốn làm điều đó theo cách này như tôi sẽ bị loại bỏ phân bổ bổ sung khi đăng nhập ở chế độ phát hành.

Bất kỳ con trỏ hoặc thủ thuật nào để thực hiện theo cách này sẽ được hoan nghênh. Trong giải pháp thực tế của tôi, tôi có nhiều chức năng đăng nhập khác nhau và tất cả chúng phức tạp hơn thế này. Vì vậy, tôi muốn thực hiện điều này bằng cách nào đó trong mã gọi. (Và không bằng cách sửa đổi #define tôi nếu có thể)

Chỉ cần đưa ra một ý tưởng, một ví dụ về một trong những # định nghĩa thực tế của tôi:

#define LOG_DEBUG_MSG(format, ...) \ 
    LogMessage(DEBUG_TYPE, const char* filepos, sizeof(__QUOTE__(@__VA_ARGS__)), \ 
    format, __VA_ARGS__) 

mà phù hợp với varargs chức năng đăng nhập printf giống như dùng char *, string() và ostream() cũng như các hàm không vararg lấy string(), exception() và HRESULT.

+0

Bạn có ý nghĩa gì bởi "nó không hoạt động"? –

+0

Bạn nói đúng, bạn nên lấy một bản sao của chuỗi, do đó, 'định dạng' phải thuộc loại' std :: string' không thuộc loại 'const std :: string &'. Tuy nhiên, bạn có thể đặt 'dynamic_cast' vào biểu thức' cout' và mất hoàn toàn biến. – KayEss

+0

Không, tôi không cần phải sao chép. Nội dung của chuỗi được trả về bởi str() được đảm bảo duy trì không đổi (giống như cách string :: c_str() hiện) giữa các lần gọi tiếp theo với các phương thức này. Đối với lý do tại sao tôi làm như vậy, tôi cần chuỗi định dạng vì tôi thực sự muốn truyền nó cho một hàm tham số duy nhất lấy một chuỗi hoặc phương thức VARARGS lấy một char * tùy thuộc vào các tham số khác được nhận. (Nhưng tất cả điều này nằm ngoài phạm vi câu hỏi của tôi - được trả lời thỏa đáng) – Marius

Trả lời

7

I nghĩ Tôi thấy điều gì đang xảy ra.Điều này tạo ra sản lượng dự kiến:

log(std::stringstream() << 1 << "hello"); 

trong khi điều này không:

log(std::stringstream() << "hello" << 1); 

(nó viết một số hex, tiếp theo là "1" chữ số)

Một vài yếu tố để giải thích :

  • Giá trị không thể ràng buộc với tham chiếu không const
  • It is OK để gọi hàm thành viên trên tạm thời
  • std :: ostream có một nhà điều hành viên < < (void *)
  • std :: ostream có một nhà điều hành viên < < (int)
  • Đối với char * các nhà điều hành không phải là một thành viên, đó là điều hành < < (std :: ostream &, const char *)

Trong đoạn mã trên, std :: stringstream() tạo ra một tạm thời (một rvalue). Tuổi thọ của nó không phải là vấn đề, vì nó phải tồn tại cho toàn bộ biểu thức đầy đủ mà nó được khai báo (tức là, cho đến khi hàm call to log() trả về).

Trong ví dụ đầu tiên, mọi thứ hoạt động ok vì các nhà điều hành viên < < (int) là lần đầu tiên được gọi, và sau đó tham chiếu trở lại có thể được thông qua để điều hành < < (ostream &, const char *)

Trong ví dụ thứ hai, toán tử < < (không thể được gọi với "std :: stringstream()" làm đối số thứ nhất, vì điều này sẽ yêu cầu nó được ràng buộc với tham chiếu không phải const. Tuy nhiên, toán tử thành viên < < (void *) là ok, vì nó là một thành viên.

Nhân tiện: Tại sao không xác định hàm log() là:

void log(const std::ostream& os) 
{ 
    std::cout << os.rdbuf() << std::endl; 
} 
+1

Cảm ơn thông tin! Điều này dẫn tôi đến giải pháp ưa thích của tôi ... Tôi biết tôi đã đi đúng hướng. Cách tốt nhất để tôi làm cho nó hoạt động liên tục là như sau: 'log (stringstream(). Flush() <<" hello "<< 1);' – Marius

+0

PS: Ý nghĩa của BYT là gì? Và làm thế nào bạn xác định hàm 'log()' khác với tôi? – Marius

+0

Oups! Đó là một lỗi đánh máy. Tôi muốn viết "By The Way". Đã sửa lỗi –

6

Thay đổi LOG() macro của bạn như thế này:

#define LOG(x) do { std::stringstream s; s << x; log(s.str()); } while(0) 

Điều đó sẽ cho phép bạn sử dụng cú pháp sau đây trong các bản ghi gỡ lỗi của bạn, vì vậy bạn không cần phải tự xây dựng các dòng chuỗi.

LOG("Testing" << 1 << "two" << 3); 

Sau đó xác định nó thành không có gì để phát hành và bạn sẽ không có phân bổ thêm.

+0

Bạn không cần phải làm/trong khi xung quanh khối. Bạn có thể có niềng răng đứng miễn phí chỉ để kiểm soát phạm vi của các biểu thức. – KayEss

+0

Mục tiêu chính của tôi là tối ưu hóa. nhiều như tôi muốn giữ một LOG() vĩ mô duy nhất mà làm việc cho nhiều loại khác nhau thông qua tôi sẽ cần phải có thể tạo ra một ngoại lệ cho điều này để phân biệt nó. vấn đề là sẽ vẫn còn một số bản ghi trong chế độ phát hành (tùy thuộc vào mức độ nghiêm trọng của ghi nhật ký) và đối với những điều này tôi không muốn chi phí trên được mô tả ở trên, vì vậy tôi có thể cần phải tạo ngoại lệ #define cho mỗi mức độ nghiêm trọng. Tôi đã cố gắng giữ nguyên định dạng cú pháp ghi nhật ký càng nhiều càng tốt trong dự án kế thừa này. – Marius

+0

Tôi đánh dấu nó là hữu ích, nhưng sẽ không đánh dấu nó là giải pháp. – Marius