Tạo macro C với ## và __LINE__ (nối mã thông báo với macro định vị)


107

Tôi muốn tạo macro C tạo một hàm có tên dựa trên số dòng. Tôi nghĩ rằng tôi có thể làm điều gì đó như (hàm thực sẽ có các câu lệnh trong dấu ngoặc nhọn):

#define UNIQUE static void Unique_##__LINE__(void) {}

Mà tôi hy vọng sẽ mở rộng thành một cái gì đó như:

static void Unique_23(void) {}

Điều đó không hiệu quả. Với nối mã thông báo, các macro định vị được xử lý theo nghĩa đen, kết thúc mở rộng thành:

static void Unique___LINE__(void) {}

Đây có phải là có thể làm gì?

(Vâng, có một lý do thực sự mà tôi muốn làm điều này cho dù điều này có vẻ vô ích như thế nào).


Tôi nghĩ rằng bạn có thể làm cho điều này hoạt động với việc mở rộng macro gián tiếp .
Ben Stiglitz

4
có thể trùng lặp của Làm thế nào để ghép hai lần với bộ tiền xử lý C và mở rộng một macro như trong "arg ## _ ## MACRO"? Điều tương tự cũng xảy ra với bất kỳ macro nào bên cạnh đó __LINE__(mặc dù đó là trường hợp sử dụng phổ biến.
Ciro Santilli 郝海东 冠状 病 六四 六四 法轮功

Câu trả lời:


176

Vấn đề là khi bạn thay thế macro, bộ tiền xử lý sẽ chỉ mở rộng các macro một cách đệ quy nếu cả toán tử xâu chuỗi và toán tử #dán mã thông báo ##đều không được áp dụng cho nó. Vì vậy, bạn phải sử dụng thêm một số lớp hướng dẫn, bạn có thể sử dụng toán tử dán mã thông báo với đối số được mở rộng đệ quy:

#define TOKENPASTE(x, y) x ## y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#define UNIQUE static void TOKENPASTE2(Unique_, __LINE__)(void) {}

Sau đó, __LINE__được mở rộng thành số dòng trong quá trình mở rộng UNIQUE(vì nó không liên quan đến #hoặc ##), và sau đó dán mã thông báo xảy ra trong quá trình mở rộng TOKENPASTE.

Cũng cần lưu ý rằng cũng có __COUNTER__macro, mở rộng thành một số nguyên mới mỗi khi nó được đánh giá, trong trường hợp bạn cần có nhiều bản khởi tạo UNIQUEmacro trên cùng một dòng. Lưu ý: __COUNTER__được hỗ trợ bởi MS Visual Studio, GCC (kể từ V4.3) và Clang, nhưng không phải là tiêu chuẩn C.


3
Tôi e rằng điều đó không hoạt động với GNU cpp. TOKENPASTE sử dụng LINE như một nghĩa đen. TOKENPASTE (Unique_, LINE ) mở rộng thành Unique___LINE__
DD.

3
@DD: D'oh, đã sửa xong. Nó cần 2 lớp về mình, chứ không phải 1.
Adam Rosenfield

Các __COUNTER__vĩ mô đã không làm việc cho tôi trong gcc; mặc dù __LINE__một trong những đã hoạt động như quảng cáo.
Tyler

2
Một chút thông tin bổ sung cho bất kỳ ai đang thử COUNTER , theo msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx, đó là một macro dành riêng cho Microsoft.
Elva

3
Bất kỳ lời giải thích nào về lý do tại sao bạn cần 2 cấp độ chuyển hướng? Tôi đã thử nó chỉ với một, không có # và ##, và điều đó không mở rộng nó trên VS2017. Rõ ràng điều này cũng đúng với GCC. Nhưng nếu bạn thêm cấp độ thứ 2 của hướng dẫn, thì nó sẽ mở rộng. Ma thuật?
Gabe Halsmer

-2

GCC không yêu cầu "gói" (hoặc nhận ra) trừ khi kết quả cần được "chuỗi". Gcc có các tính năng nhưng TẤT CẢ đều có thể được thực hiện với phiên bản C đơn giản 1 (và một số người cho rằng Berkeley 4.3 C nhanh hơn rất nhiều nên đáng để học cách sử dụng).

** Clang (llvm) KHÔNG THỰC HIỆN ĐÚNG KHÔNG GIAN TRẮNG để mở rộng macro - nó thêm khoảng trắng (chắc chắn phá hủy kết quả là một Mã định danh C để xử lý trước) **, clang chỉ đơn giản là không mở rộng macro # hoặc * như một Bộ tiền xử lý C được mong đợi trong nhiều thập kỷ. Ví dụ chính đang biên dịch X11, macro "Concat3" bị hỏng, kết quả bây giờ là Mã định danh MISNAMED C, tất nhiên không tạo được. và tôi bắt đầu cho rằng việc xây dựng thất bại là nghề của họ.

Tôi nghĩ câu trả lời ở đây là "C mới phá vỡ tiêu chuẩn là C xấu", những hack này luôn chọn (không gian tên clobber) họ thay đổi mặc định mà không có lý do gì nhưng không thực sự "cải thiện C" (trừ khi họ nói như vậy: mà tôi nói là contraption được thực hiện để giải thích tại sao họ thoát khỏi tất cả các sự cố mà chưa ai bắt họ phải chịu trách nhiệm).


Không có vấn đề gì khi các bộ xử lý tiền C trước đó không hỗ trợ UNIq_ () __ bởi vì chúng hỗ trợ #pragma cho phép " hack thương hiệu trình biên dịch trong mã được gắn cờ là hackery" và cũng hoạt động tốt mà KHÔNG ảnh hưởng đến tiêu chuẩn: chỉ như thay đổi mặc định là sự cố vỡ hoành thánh vô ích, và cũng giống như việc thay đổi chức năng hoạt động trong khi sử dụng cùng một tên (đánh dấu không gian tên) là ... phần mềm độc hại theo ý kiến ​​của tôi

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.