Standart C Önişlemci
$ cat xx.c
#define VARIABLE 3
#define PASTER(x,y) x ## _ ## y
#define EVALUATOR(x,y) PASTER(x,y)
#define NAME(fun) EVALUATOR(fun, VARIABLE)
extern void NAME(mine)(char *x);
$ gcc -E xx.c
# 1 "xx.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "xx.c"
extern void mine_3(char *x);
$
İki dolaylı seviye
Başka bir cevaba yaptığı açıklamada, Cade Roux bunun neden iki dolaylı indirime ihtiyaç duyduğunu sordu . İnatçı cevap, standardın bu şekilde çalışmasını gerektirmesidir; dize oluşturma operatörü ile de eşdeğer numaraya ihtiyacınız olduğunu bulma eğilimindesiniz.
C99 standardının 6.10.3 Bölümü, 'makro değiştirme'yi ve 6.10.3.1,' bağımsız değişken değiştirme'yi kapsar.
İşlev benzeri bir makronun çağrılmasına yönelik argümanlar belirlendikten sonra, argüman ikamesi gerçekleşir. Bir #
veya ##
önişleme belirtecinden önce veya ardından bir ##
ön işlem belirteci (aşağıya bakın) gelmedikçe, değiştirme listesindeki bir parametre, içerdiği tüm makrolar genişletildikten sonra karşılık gelen argümanla değiştirilir. İkame edilmeden önce, her argümanın önişleme belirteçleri, önişleme dosyasının geri kalanını oluşturuyormuş gibi tamamen makro değiştirilir; başka ön işleme jetonu mevcut değildir.
Çağrıda NAME(mine)
argüman 'benim'; tamamen benimkine genişletildi; daha sonra yedek dizgiyle değiştirilir:
EVALUATOR(mine, VARIABLE)
Şimdi EVALUATOR makrosu keşfedildi ve argümanlar 'benim' ve 'DEĞİŞKEN' olarak izole edildi; ikincisi daha sonra tamamen '3'e genişletilir ve ikame dizesine ikame edilir:
PASTER(mine, 3)
Bunun çalışması diğer kurallar tarafından kapsanmaktadır (6.10.3.3 '## operatörü'):
İşlev benzeri bir makronun değiştirme listesinde, bir parametrenin hemen öncesinde veya ardından bir ##
önişleme belirteci varsa, parametre karşılık gelen bağımsız değişkenin önişleme belirteci dizisiyle değiştirilir; [...]
Hem nesne benzeri hem de işlev benzeri makro çağrıları için, değiştirme listesinin yerini alacak daha fazla makro adı için yeniden incelenmeden önce, değiştirme listesindeki her bir ##
ön işleme belirtecinin her örneği ( bağımsız değişkenden değil) silinir ve önceki ön işleme belirteci birleştirilir aşağıdaki önişlem belirteci ile.
Yani, yedek liste içerir x
izledi ##
ve ayrıca ##
takip y
; Böylece sahibiz:
mine ## _ ## 3
ve ##
jetonları ortadan kaldırmak ve jetonları her iki tarafta birleştirmek, 'mayın' ile '_' ve '3'ü birleştirir:
mine_3
İstenen sonuç budur.
Orijinal soruya bakarsak, kod ('some_function' yerine 'mine' kullanacak şekilde uyarlandı):
#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE
NAME(mine)
NAME'in argümanı açıkça 'benim' ve tamamen genişletildi.
6.10.3.3 kurallarına göre şunları buluyoruz:
mine ## _ ## VARIABLE
hangi zaman ##
operatörler elenir, eşleştiren:
mine_VARIABLE
tam olarak soruda bildirildiği gibi.
Geleneksel C Önişlemcisi
Robert Rüger soruyor :
Belirteç yapıştırma operatörü olmayan geleneksel C ön işlemcisi ile bunu yapmanın bir yolu var mı ##
?
Belki ve belki de değil - bu önişlemciye bağlıdır. Standart ön işlemcinin avantajlarından biri, güvenilir şekilde çalışan bu tesise sahipken standart öncesi ön işlemciler için farklı uygulamalar olmasıdır. Bir gereklilik, önişlemci bir yorumun yerini aldığında ANSI önişlemcisinin yapması gerektiği için boşluk oluşturmamasıdır. GCC (6.3.0) C Önişlemcisi bu gereksinimi karşılar; XCode 8.2.1'den Clang ön işlemcisi bunu yapmaz.
Çalıştığında, iş ( x-paste.c
):
#define VARIABLE 3
#define PASTE2(x,y) x/**/y
#define EVALUATOR(x,y) PASTE2(PASTE2(x,_),y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)
extern void NAME(mine)(char *x);
fun,
Ve VARIABLE
- arasında bir boşluk olmadığını unutmayın, çünkü bu önemliyse, çıktıya kopyalanır ve mine_ 3
tabiki sözdizimsel olarak geçerli olmayan bir adla sonuçlanırsınız. (Şimdi lütfen saçımı geri alabilir miyim?)
GCC 6.3.0 (çalışıyor cpp -traditional x-paste.c
) ile şunu elde ederim:
# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"
extern void mine_3(char *x);
XCode 8.2.1'den Clang ile şunu elde ederim:
# 1 "x-paste.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 329 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "x-paste.c" 2
extern void mine _ 3(char *x);
Bu alanlar her şeyi mahvediyor. Her iki önişlemcinin de doğru olduğunu; farklı standart öncesi ön işlemciler, her iki davranışı da sergiledi; ##
Gösterimle birlikte standart bunu kökten basitleştirir.
Bunu yapmanın başka yolları olabilir. Ancak, bu çalışmaz:
#define VARIABLE 3
#define PASTER(x,y) x/**/_/**/y
#define EVALUATOR(x,y) PASTER(x,y)
#define NAME(fun) EVALUATOR(fun,VARIABLE)
extern void NAME(mine)(char *x);
GCC şunu üretir:
# 1 "x-paste.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "x-paste.c"
extern void mine_VARIABLE(char *x);
Kapat, ama zar yok. YMMV, elbette, kullandığınız standart öncesi ön işlemciye bağlı olarak. Açıkçası, işbirliği yapmayan bir önişlemci ile sıkışırsanız, standart öncesi olanın yerine standart C önişlemcisini kullanmak daha kolay olacaktır (derleyiciyi uygun şekilde yapılandırmanın bir yolu vardır) işi yapmanın bir yolunu bulmak için çok zaman harcayın.