2010-02-03 10 views
7

Tôi đang làm việc trên một dự án mà tôi có nhiều chuỗi liên tục được tạo thành bằng cách nối (số, v.v.).Làm thế nào để chuyển đổi chuỗi nối với rộng-char với tiền xử lý C?

Ví dụ, tôi có một vĩ mô LOCATION rằng định dạng __FILE____LINE__ thành một chuỗi mà tôi có thể sử dụng để biết tôi đang ở đâu trong các mã, khi in các thông điệp hoặc lỗi:

#define _STR(x) # x 
#define STR(x)  _STR(x) 
#define LOCATION __FILE__ "(" STR(__LINE__) ")" 

Vì vậy, đây sẽ định dạng một vị trí như "file.cpp (42)". Vấn đề là khi tôi cố gắng để chuyển đổi kết quả vào một rộng chuỗi:

#define _WIDEN(x) L ## x 
#define WIDEN(x) _WIDEN(x) 
#define WLOCATION WIDEN(LOCATION) 

này chỉ hoạt động tốt với GCC, và kết quả là L "file.cpp (42)" được chèn vào trong mã của tôi. Tuy nhiên, khi cố gắng này với MSVC++ (sử dụng Visual C++ 2008 Express), tôi nhận được một lỗi:

error: Concatenating wide "file.cpp" with narrow "(" 

Tôi hiểu rằng tiền tố L được thêm vào chỉ đối với nhiệm kỳ đầu tiên trong biểu hiện của tôi. Tôi cũng đã thử điều này:

#define _WIDEN(x) L ## #x 

Những "tác phẩm", nhưng cung cấp cho chuỗi L"\"file.cpp\" \"(\" \"42\" \")\"" đó rõ ràng không phải là rất thuận tiện (và không phải những gì tôi đang tìm kiếm), đặc biệt là xem xét rằng macro này là đơn giản so với khác macro.

Vì vậy, câu hỏi của tôi là: làm thế nào tôi có thể làm cho nó áp dụng cho toàn bộ biểu thức trong MSVC++, vì vậy tôi có thể nhận được cùng một kết quả tôi nhận được với GCC? Tôi không muốn tạo một chuỗi thứ hai với các mã thông báo toàn diện, bởi vì sau đó tôi sẽ phải duy trì hai macro cho mỗi thẻ, điều này không thuận tiện và có thể dẫn đến lỗi. Thêm vào đó, tôi cũng cần phiên bản thu hẹp của mỗi chuỗi, vì vậy, việc sử dụng các chuỗi toàn bộ không phải là một tùy chọn.

Trả lời

11

Theo tiêu chuẩn C (aka "ISO-9899: 1999" aka "C99"), Visual C sai và gcc là chính xác. Trạng thái chuẩn đó, phần 6.4.5/4:

Trong giai đoạn dịch 6, chuỗi ký tự nhiều byte được chỉ định bởi bất kỳ chuỗi ký tự liền kề nào và mã chuỗi ký tự rộng được nối vào một chuỗi ký tự nhiều byte. Nếu bất kỳ thẻ nào có mã thông báo dạng chuỗi rộng, chuỗi ký tự nhiều byte kết quả được coi là một chuỗi ký tự lớn; nếu không, nó được coi là một chuỗi ký tự chữ.

Vì vậy, bạn có thể gửi khiếu nại. Có thể cho rằng, phiên bản trước của tiêu chuẩn C (hay còn gọi là "C89" hay còn gọi là "C90" hay còn gọi là "ANSI C") không ủy nhiệm hợp nhất các chuỗi rộng với các chuỗi không phải là rộng. Mặc dù C99 bây giờ đã hơn mười tuổi, có vẻ như Microsoft không quan tâm đến việc làm cho trình biên dịch C của nó phù hợp. Một số người dùng đã báo cáo rằng có thể truy cập một số tính năng "C99" bằng cách biên dịch mã C như thể nó là mã C++, vì C++ bao gồm các tính năng này - và cho C++, Microsoft đã thực hiện một nỗ lực. Nhưng điều này dường như không mở rộng đến bộ tiền xử lý.

Trong phương ngữ C89, tôi nghĩ rằng những gì bạn đang tìm kiếm là không thể (thực ra tôi khá chắc chắn về nó, và vì tôi đã viết tiền xử lý riêng của mình, tôi nghĩ mình biết mình đang nói về cái gì). Nhưng bạn có thể thêm thông số bổ sung và tuyên truyền nó:

#define W(x)   W_(x) 
#define W_(x)   L ## x 
#define N(x)   x 
#define STR(x, t)  STR_(x, t) 
#define STR_(x, t) t(#x) 

#define LOCATION_(t) t(__FILE__) t("(") STR(__LINE__, t) t(")") 
#define LOCATION  LOCATION_(N) 
#define WLOCATION  LOCATION_(W) 

nên hoạt động trên cả gcc và Visual C (ít nhất, nó hoạt động cho tôi, sử dụng Visual C 2005).

Lưu ý phụ: bạn không nên xác định các macro có tên bắt đầu bằng dấu gạch dưới. Những tên này được dành riêng, do đó, bằng cách sử dụng chúng, bạn có thể đụng độ với một số tên được sử dụng trong tiêu đề hệ thống hoặc trong các phiên bản tương lai của trình biên dịch. Thay vì _WIDEN, hãy sử dụng WIDEN_.

+0

Đó là công việc, mặc dù nó là một loại cú pháp khó xử và có rất nhiều chi phí ... nhưng nó hoạt động và tôi không phải duy trì nhiều macro. Hy vọng rằng, Microsoft sẽ cập nhật tiền xử lý của nó để hỗ trợ nhiều tính năng C99 hơn, mọi thứ sẽ đơn giản hơn nhiều theo cách này. Cảm ơn! –

1

Để nối hai chuỗi chữ rộng bạn có thể sử dụng

L"wide_a" L"wide_b" 

Vì vậy, bạn có thể định nghĩa

#define WLOCATION WIDEN(__FILE__) L"(" WIDEN(STR(__LINE__)) L")" 

(Lưu ý: không thử nghiệm trên MSVC++)

+1

Điều đó sẽ làm việc, nhưng macro mà chỉ là một trong nhiều người, và tôi thà xác định họ chỉ một lần và không phải duy trì hai bản sao (rộng và hẹp) của mỗi macro, đặc biệt nếu tôi chỉnh sửa chúng một ngày. Nó có thể dễ dàng trở thành một nỗi đau để duy trì, mặc dù tôi phải thừa nhận, điều đó sẽ hoạt động! –

0

Chỉ cần sử dụng một chuỗi rộng trống đen nên làm việc:

#define WLOCATION L"" __FILE__ "(" STR(__LINE__) ")"