2012-06-19 38 views
6

Tôi đang sử dụng khung kiểm tra đơn vị dựa trên macro REQUIRE để thực hiện xác nhận.Mở rộng Macro tiền xử lý trước không mong muốn

giản, những tác phẩm vĩ mô như thế này:

#define REQUIRE(expr) INTERNAL_REQUIRE(expr, "REQUIRE") 

nào được định nghĩa tương tự như sau:

#define INTERNAL_REQUIRE(expr, macroName) \ 
PerformAssertion(macroName, #expr, expr); 

PerformAssertion 's hai tham số đầu tiên là của các loại: const char*. Lý do cho tham số thứ hai (#expr) là do đó biểu thức chính xác được khẳng định có thể được ghi lại. Đây là nơi vấn đề nằm. Bộ tiền xử lý mở rộng biểu thức trước khi nó được chuyển thành một const char *, do đó, nó không phải là cùng một biểu thức ban đầu được khẳng định.

Ví dụ:

REQUIRE(foo != NULL); 

sẽ dẫn đến cuộc gọi này:

PerformAssertion("REQUIRE", "foo != 0", foo != 0); 

Như bạn thấy, khái niệm được mở rộng một phần, ví dụ biểu thức foo != NULL xuất hiện trong nhật ký là foo != 0. NULL (là một macro được xác định là 0) đã được mở rộng bởi bộ tiền xử lý C trước khi xây dựng văn bản thông báo xác nhận. Có cách nào tôi có thể bỏ qua hoặc bỏ qua việc mở rộng cho văn bản tin nhắn không?

EDIT: Đây là giải pháp, cho bất cứ ai tò mò:

#define REQUIRE(expr) INTERNAL_REQUIRE(expr, #expr, "REQUIRE") 

#define INTERNAL_REQUIRE(expr, exprString, macroName) \ 
PerformAssertion(macroName, exprString, expr); 
+2

Chỉ sử dụng 'INTERNAL_REQUIRE (expr, #expr," REQUIRE ")' thay vì biểu mẫu hai đối số? – kennytm

+1

Yup, điều đó sẽ thực hiện. – Jeff

Trả lời

5

Cố gắng làm cho stringifying trước khi cuộc gọi để yêu cầu nội bộ. Vấn đề của bạn là nó được truyền cho yêu cầu nội bộ trong phần mở rộng thứ hai mở rộng NULL. Nếu bạn thực hiện việc xâu chuỗi xảy ra trước đó, ví dụ: Trong macro yêu cầu, nó sẽ không mở rộng NULL.

+0

Làm việc hoàn hảo, cảm ơn! Bạn có muốn giải thích cách nó hoạt động chi tiết hơn một chút không? – Jeff

2

Dưới đây là những gì đang xảy ra: kể từ khi bạn vĩ mô nơi "stringization" hành # được áp dụng là cấp thứ hai, chuỗi các hoạt động hoạt động như sau:

  • Preprocessor xác định các đối số của REQUIRE(NULL) và thực hiện argument substitution theo C 6.10.3.1. Tại thời điểm này, thay thế trông giống như INTERNAL_REQUIRE(0, "REQUIRE"), bởi vì NULL được mở rộng thành 0.
  • Bộ tiền xử lý tiếp tục mở rộng chuỗi macro với INTERNAL_REQUIRE; tại thời điểm này, thực tế là macro đã được gọi với NULL bị mất: theo như tiền xử lý có liên quan, biểu thức được chuyển đến INTERNAL_REQUIRE0.

Một chìa khóa để giải quyết vấn đề này là trong đoạn này từ tiêu chuẩn:

Một tham số trong danh sách thay thế, trừ khi trước bởi một # hoặC## tiền xử lý thẻ hoặc theo sau là một tiền xử lý ## token (xem bên dưới), được thay thế bằng đối số tương ứng sau khi tất cả các macro chứa trong đó đã được mở rộng.

Điều này có nghĩa là nếu bạn muốn nắm bắt biểu thức chính xác, bạn cần thực hiện nó ở cấp độ đầu tiên của việc mở rộng macro.

+0

Cảm ơn bạn, đã thỏa mãn sự tò mò của tôi và giải thích lý do giải pháp của Dani hoạt động. – Jeff

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