2011-11-22 37 views
23

Tại: C++ FAQ - Miscellaneous technical issues - [39.6] What should be done with macros that need to paste two tokens together?Tại sao tôi cần hai lớp indirection cho macro?

Ai đó có thể giải thích cho tôi lý do tại sao? Tất cả tôi đọc là tin tưởng tôi, nhưng tôi chỉ đơn giản là không thể tin tưởng vào một cái gì đó bởi vì ai đó đã nói như vậy.

tôi đã cố gắng tiếp cận và tôi không thể tìm thấy bất kỳ lỗi xuất hiện:

#define mymacro(a) int a ## __LINE__ 
mymacro(prefix) = 5; 
mymacro(__LINE__) = 5; 
int test = prefix__LINE__*__LINE____LINE__; // fine 

Vì vậy, tại sao sao tôi cần phải làm điều đó như thế này thay vì (trích dẫn từ các trang web):

Tuy nhiên, bạn cần một lớp kép của sự hướng dẫn khi bạn sử dụng ##. Về cơ bản bạn cần phải tạo một macro đặc biệt cho "dán thẻ" như :

#define NAME2(a,b)   NAME2_HIDDEN(a,b) 
#define NAME2_HIDDEN(a,b) a ## b 

ủy thác cho tôi về vấn đề này - bạn thực sự cần phải làm này! (Và xin vui lòng không ai viết cho tôi nói đôi khi nó hoạt động mà không lớp thứ hai về mình Hãy thử concatenating một biểu tượng với __ LINE__ và xem những gì sẽ xảy ra sau đó..)

Edit: thể một người nào đó cũng giải thích tại sao ông sử dụng NAME2_HIDDEN trước khi được khai báo bên dưới? Có vẻ hợp lý hơn khi xác định macro NAME2_HIDDEN trước khi tôi sử dụng. Có một số loại lừa ở đây không?

+0

Tôi không chắc mình hiểu điều bạn đang yêu cầu ... –

+0

Tôi thấy một chút không rõ ràng, tôi sẽ chỉnh sửa. – Rookie

+0

@tenfour, xong. bạn có biết câu trả lời cho phần chỉnh sửa của tôi không? – Rookie

Trả lời

29

Phần liên quan của spec C:

6.10.3.1 thay Luận

Sau khi các đối số cho gọi các macro chức năng giống như đã được xác định, đối thay thế diễn ra. Tham số trong danh sách thay thế, trừ trước số bằng mã thông báo tiền xử lý # hoặC## hoặc theo sau là mã thông báo tiền xử lý ## (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 . Trước khi được thay thế, các mã thông báo tiền xử lý của mỗi đối số là thay thế hoàn toàn macro như thể chúng đã tạo thành phần còn lại của tệp tiền xử lý; không có sẵn mã thông báo tiền xử lý khác.

Phần quan trọng mà xác định xem bạn muốn gián tiếp đôi hay không là câu thứ hai và các ngoại lệ trong nó - nếu tham số được tham gia vào một hoạt động # hoặc ## (chẳng hạn như params trong mymacroNAME2_HIDDEN), sau đó bất kỳ macro nào khác trong đối số KHÔNG được mở rộng trước khi thực hiện # hoặc ##.Mặt khác, nếu không có # hoặc ## NGAY LẬP TỨC trong thân macro (như với NAME2), thì các macro khác trong các tham số ĐƯỢC mở rộng.

Vì vậy, nó đi xuống đến những gì bạn muốn - đôi khi bạn muốn tất cả các macro mở rộng FIRST, và sau đó làm # hoặc ## (trong trường hợp bạn muốn lớp gián tiếp đôi) và đôi khi bạn KHÔNG muốn các macro mở rộng đầu tiên (trong trường hợp đó bạn KHÔNG THỂ CÓ macro hai lớp, bạn cần phải thực hiện trực tiếp.)

+1

thực sự. tôi thấy nó rất làm phiền trang web nói với tôi LUÔN LUÔN sử dụng nó ... trong khi trong trường hợp của tôi tôi không bao giờ muốn sử dụng nó theo cách đó. – Rookie

4

__LINE__ là macro đặc biệt được yêu cầu giải quyết cho số dòng hiện tại. Khi bạn thực hiện dán mã thông báo với số __LINE__ trực tiếp, tuy nhiên, nó không có cơ hội để giải quyết, do đó bạn kết thúc bằng mã thông báo prefix__LINE__ thay vì, giả sử, prefix23, như bạn có thể mong đợi nếu bạn viết mã này hoang dã.

+0

chỉnh sửa: oh chờ đợi, bạn có nghĩa là ai đó sẽ mong đợi để cung cấp cho một số dòng vào tên biến? Ừm được. là tất cả các vấn đề nó sẽ gây ra sau đó? – Rookie

+0

bạn cũng có thể giải thích lý do tại sao anh ta đã sử dụng các macro theo thứ tự "sai"? ví dụ. anh ta nói đến macro 'NAME2_HIDDEN' trước khi được khai báo. được coi là thực hành tốt hay là một loại mẹo nào đó? tôi thử nghiệm cả hai cách và tôi dường như để có được kết quả tương tự. – Rookie

+0

Bạn có thể xác định các macro theo bất kỳ thứ tự nào, không phải là sai. –

3

Chris Dodd có một lời giải thích tuyệt vời cho phần đầu tiên của câu hỏi của bạn. Đối với phần thứ hai, về chuỗi định nghĩa, phiên bản ngắn là chỉ thị của mình không được đánh giá ở tất cả các chỉ thị #define; chúng chỉ được đánh giá và mở rộng khi biểu tượng được tìm thấy ở nơi khác trong tệp. Ví dụ:

#define A a //adds A->a to the symbol table 
#define B b //adds B->b to the symbol table 

int A; 

#undef A  //removes A->a from the symbol table 
#define A B //adds A->B to the symbol table 

int A; 

Các int A; đầu tiên trở thành int a; vì đó là cách A được xác định tại thời điểm đó trong file. Số int A; thứ hai trở thành int b; sau hai lần mở rộng. Nó được mở rộng đầu tiên thành int B;A được định nghĩa là B tại thời điểm đó trong tệp. Bộ tiền xử lý sau đó nhận ra rằng B là một macro khi nó kiểm tra bảng biểu tượng. B sau đó được mở rộng thành b.

Điều duy nhất quan trọng là định nghĩa biểu tượng tại điểm mở rộng, bất kể định nghĩa ở đâu.

+1

ồ vâng, tôi chỉ nghĩ rằng sẽ hợp lý hơn nếu đặt chúng theo thứ tự mà bạn sử dụng chúng, bằng cách nào đó có ý nghĩa hơn với tôi. cám ơn vì sự giải thích. – Rookie

1

Thứ tự các macro được khai báo không quan trọng, thứ tự mà chúng được sử dụng là. Nếu bạn thực sự sử dụng macro đó trước khi nó được khai báo - (trong mã thực tế, không phải trong macro vẫn không hoạt động cho đến khi được triệu hồi) thì bạn sẽ gặp lỗi nhưng vì hầu hết mọi người đều không đi xung quanh những thứ này, viết macro và sau đó viết một hàm sử dụng macro chưa được xác định thêm xuống, v.v. ... Có vẻ như câu hỏi của bạn không chỉ là một câu hỏi mà tôi chỉ trả lời một phần. Tôi nghĩ rằng bạn nên đã phá vỡ này xuống một chút nữa.

0

Câu trả lời hầu hết các phi kỹ thuật, mà tôi thu thập được từ tất cả các liên kết ở đây, và liên kết các liên kết;) là, một lớp gián tiếp đơn macro(x) #x stringifies tên vĩ mô đầu vào, nhưng bằng cách sử dụng lớp kép, nó sẽ stringify các đầu vào giá trị của macro.

#define valueOfPi 3 
#define macroHlp(x) #x 
#define macro(x) macroHlp(x) 
#define myVarOneLayer "Apprx. value of pi = " macroHlp(valueOfPi) 
#define myVarTwoLayers "Apprx. value of pi = " macro(valueOfPi) 

printf(myVarOneLayer); // out: Apprx. value of pi = valueOfPi 
printf(myVarOTwoLayers); // out: Apprx. value of pi = 3 

gì xảy ra tại printf(myVarOneLayer)

printf(myVarOneLayer) được mở rộng để printf("Apprx. value of pi = " macroHlp(valueOfPi))

macroHlp(valueOfPi) cố gắng để stringify đầu vào, đầu vào chính nó là không được đánh giá. Mục đích duy nhất trong cuộc sống là lấy đầu vào và xâu chuỗi. Vì vậy, nó mở rộng để "valueOfPi"

Vì vậy, những gì xảy ra tại printf(myVarTwoLayers)

printf(myVarTwoLayers) được mở rộng để printf("Apprx. value of pi = " macro(valueOfPi)

macro(valueOfPi) không có hoạt động stringification, tức là không có #x trong đó là mở rộng, nhưng có một x, vì vậy nó phải đánh giá x và nhập giá trị vào macroHlp để xâu chuỗi.Nó mở rộng thành macroHlp(3) do đó sẽ xâu chuỗi số 3, vì nó đang sử dụng #x

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