## önişlemci operatörünün uygulamaları ve dikkate alınması gereken şeyler nelerdir?


88

Önceki sorularımın çoğunda belirtildiği gibi, K&R ile çalışıyorum ve şu anda ön işlemcideyim. Daha ilginç şeylerden biri - önceki C öğrenme girişimlerimden daha önce hiç bilmediğim bir şey - ##önişlemci operatörüdür. K & R'ye göre:

Önişlemci operatörü ## , makro genişletme sırasında gerçek bağımsız değişkenleri birleştirmek için bir yol sağlar. Değiştirme metnindeki bir parametre a'ya bitişikse ##, parametre gerçek bağımsız değişkenle değiştirilir, ##ve çevresindeki beyaz boşluk kaldırılır ve sonuç yeniden taranır. Örneğin, makro paste iki bağımsız değişkenini birleştirir:

#define paste(front, back) front ## back

böylece paste(name, 1)jetonu oluşturur name1.

Birisi bunu gerçek dünyada nasıl ve neden kullanır? Kullanımının pratik örnekleri nelerdir ve dikkate alınması gereken şeyler var mı?

Yanıtlar:


47

CrashRpt: Makro çok baytlı dizeleri Unicode'a dönüştürmek için ## kullanma

CrashRpt'deki (kilitlenme raporlama kitaplığı) ilginç bir kullanım şudur:

Burada karakter başına bir baytlık bir dizge yerine iki baytlık bir dizge kullanmak istiyorlar. Bu muhtemelen gerçekten anlamsız gibi görünüyor, ancak bunu iyi bir sebeple yapıyorlar.

Tarih ve saatle birlikte bir dize döndüren başka bir makro ile kullanırlar.

A'nın Lyanına koymak __ DATE __size bir derleme hatası verir.


Windows: Genel Unicode veya çok baytlı dizeler için ## kullanma

Windows, aşağıdaki gibi bir şey kullanır:

Ve _Tkodun her yerinde kullanılır


Temiz erişimci ve değiştirici adları için kullanılan çeşitli kitaplıklar:

Ayrıca erişimcileri ve değiştiricileri tanımlamak için kodda kullanıldığını gördüm:

Aynı şekilde, diğer akıllı ad oluşturma türleri için de aynı yöntemi kullanabilirsiniz.


Aynı anda birkaç değişken bildirimi yapmak için kullanan çeşitli kitaplıklar:


3
Dize değişmez değerlerini derleme zamanında birleştirebildiğiniz için, BuildDate ifadesini azaltabilir std::wstring BuildDate = WIDEN(__DATE__) L" " WIDEN(__TIME__); ve tüm dizeyi bir kerede örtük olarak oluşturabilirsiniz.
user666412

49

Belirteç yapıştırma (' ##') veya #ön işleme (' ') ön işleme operatörlerini kullanırken dikkat etmeniz gereken bir şey, bunların her durumda düzgün çalışması için fazladan bir indirme seviyesi kullanmanız gerektiğidir.

Bunu yapmazsanız ve belirteç yapıştırma operatörüne iletilen öğelerin kendileri makrolarsa, muhtemelen istediğiniz gibi olmayan sonuçlar alırsınız:

Çıktı:


1
Bu önişlemci davranışının bir açıklaması için bkz. Stackoverflow.com/questions/8231966/…
Adam Davis

@MichaelBurr cevabınızı okuyordum ve bir şüphem var. Bu LINE nasıl oluyor da satır numarasını yazdırıyor?
PLZ

3
@AbhimanyuAryan: Sorduğunuz şeyin bu olup olmadığından emin değilim, ancak __LINE__önişlemci tarafından kaynak dosyanın geçerli satır numarasıyla değiştirilen özel bir makro adı.
Michael Burr

Dil özellikleri / atıf bağlantılı olabilir eğer olduğu gibi, serin olurdu burada
Antonio

14

İşte bir derleyicinin yeni bir sürümüne yükseltirken karşılaştığım bir sorun:

Belirteç yapıştırma operatörünün ( ##) gereksiz kullanımı taşınabilir değildir ve istenmeyen boşluklar, uyarılar veya hatalar oluşturabilir.

Belirteç yapıştırma işlecinin sonucu geçerli bir ön işlemci belirteci olmadığında belirteç yapıştırma işleci gereksizdir ve muhtemelen zararlıdır.

Örneğin, belirteç yapıştırma operatörünü kullanarak derleme zamanında dize değişmezleri oluşturmaya çalışılabilir:

Bazı derleyicilerde bu, beklenen sonucu verir:

Diğer derleyicilerde bu, istenmeyen boşlukları içerecektir:

GCC'nin oldukça modern sürümleri (> = 3.3 ya da öylesine) bu kodu derleyemeyecektir:

Çözüm, önişlemci belirteçlerini C / C ++ operatörleriyle birleştirirken belirteç yapıştırma işlecini çıkarmaktır:

Birleştirme hakkındaki GCC CPP belgeleri bölümü , belirteç yapıştırma operatörü hakkında daha yararlı bilgiler içerir.


Teşekkürler - bunun farkında değildim (ama o zaman bu ön işleme operatörlerini çok fazla kullanmıyorum ...).
Michael Burr

3
Buna bir nedenle "belirteç yapıştırma" operatörü denir - amaç, işiniz bittiğinde tek bir jeton elde etmektir. Güzel yazı.
Mark Ransom

Belirteç yapıştırma işlecinin sonucu geçerli bir ön işlemci belirteci olmadığında, davranış tanımsızdır.
alecov

Onaltılık yüzer sayılar veya (C ++ 'da) basamak ayırıcılar ve kullanıcı tanımlı değişmez değerler gibi dil değişiklikleri, "geçerli bir ön işleme belirteci" neyi oluşturan şeyleri sürekli olarak değiştirir, bu nedenle lütfen asla bu şekilde kötüye kullanmayın! Belirteçleri ayırmanız gerekiyorsa (dile uygun), lütfen bunları iki ayrı simge olarak heceleyin ve önişlemci dilbilgisi ile uygun dil arasındaki yanlışlıkla etkileşimlere güvenmeyin.
Kerrek SB

6

Bu, kendinizi gereksiz yere tekrar etmemek için her türlü durumda yararlıdır. Aşağıda Emacs kaynak kodundan bir örnek verilmiştir. Bir kitaplıktan birkaç işlev yüklemek istiyoruz. "Foo" işlevi atanmalıdır fn_foo, vb. Aşağıdaki makroyu tanımlıyoruz:

Daha sonra onu kullanabiliriz:

Bunun yararı, her ikisini de yazmak zorunda kalmamak fn_XpmFreeAttributesve "XpmFreeAttributes"(ve bunlardan birini yanlış yazma riskini almak).


4

Stack Overflow ile ilgili önceki bir soru, çok fazla hataya açık yeniden yazma olmadan numaralandırma sabitleri için dize temsilleri oluşturmanın sorunsuz bir yöntemini istedi.

Bağlantı

Bu soruya cevabım, küçük önişlemci sihrinin uygulanmasının numaralandırmanızı nasıl bu şekilde tanımlamanıza izin verdiğini gösterdi (örneğin) ...;

... Makro genişletmenin yalnızca numaralandırmayı (bir .h dosyasında) değil, aynı zamanda eşleşen bir dizge dizisini de (bir .c dosyasında) tanımlaması avantajı ile;

Dize tablosunun adı, makro parametresinin (yani Renk) ## operatörü kullanılarak StringTable'a yapıştırılmasından gelir. Bunun gibi uygulamalar (püf noktaları?) # Ve ## operatörlerinin paha biçilmez olduğu yerlerdir.


3

Makro parametrelerini başka bir şeyle birleştirmeniz gerektiğinde belirteç yapıştırmayı kullanabilirsiniz.

Şablonlar için kullanılabilir:

Bu durumda LINKED_LIST (int) size

Benzer şekilde, liste geçişi için bir işlev şablonu yazabilirsiniz.


2

Bir tür arama kuralına uyması gereken bir dizi yöntem için prototipleri doğru şekilde uygulamaya yardımcı olmak için C programlarında kullanıyorum. Bir bakıma, bu, düz C'deki kötü adamın nesne yönelimi için kullanılabilir:

bunun gibi bir şeye genişler:

Bu, şunları yaptığınızda tüm "türetilmiş" nesneler için doğru parametrelendirmeyi zorlar:

üstbilgi dosyalarınızda, vb. yukarıdakiler, tanımları değiştirmek ve / veya "nesnelere" yöntemler eklemek istemeniz durumunda bile bakım için yararlıdır.


2

SGlib , C'deki şablonları temelde dolaştırmak için ## kullanır. İşlev aşırı yüklemesi olmadığından, ## tür adını oluşturulan işlevlerin adlarına yapıştırmak için kullanılır. List_t adında bir liste türüne sahip olsaydım, sglib_list_t_concat gibi adlandırılmış işlevler alırdım ve benzeri.


2

Gömülü için standart olmayan bir C derleyicisinde ev haddelenmiş bir iddia için kullanıyorum:


3
'Standart dışı' derken derleyicinin dizgi yapıştırma yapmadığını, ancak belirteç yapıştırdığını - yoksa olmasa bile işe yarayacak mıydı ##?
PJTraill

1

Makrolar tarafından tanımlanan değişkenlere özel önekler eklemek için kullanıyorum. Yani şöyle bir şey:

şuna genişler:


1

Ana kullanım, bir adlandırma kuralınız olduğunda ve makronuzun bu adlandırma kuralından yararlanmasını istediğiniz zamandır. Belki birkaç yöntem ailesiniz vardır: image_create (), image_activate () ve image_release () ayrıca file_create (), file_activate (), file_release () ve mobile_create (), mobile_activate () ve mobile_release ().

Nesne yaşam döngüsünü işlemek için bir makro yazabilirsiniz:

Tabii ki, bir tür "nesnelerin minimal versiyonu", bunun geçerli olduğu tek adlandırma kuralı değildir - adlandırma kurallarının neredeyse büyük çoğunluğu, adları oluşturmak için ortak bir alt dizeden yararlanır. İsimleri (yukarıdaki gibi) veya alan isimlerini, değişken isimlerini veya başka herhangi bir şeyi işlevlendirebilirim.


1

WinCE'de önemli bir kullanım:

Register bit tanımlamasını tanımlarken şunları yapıyoruz:

Ve BITFMASK'ı kullanırken, sadece şunu kullanın:


0

Günlük kaydı için çok kullanışlıdır. Yapabilirsin:

Veya derleyiciniz function ve func'u desteklemiyorsa :

Yukarıdaki "işlevler" mesajı günlüğe kaydeder ve tam olarak hangi işlevin bir ileti kaydettiğini gösterir.

C ++ sözdizimim tam olarak doğru olmayabilir.


1
Bununla ne yapmaya çalışıyordun? "##" olmadan da aynı şekilde çalışır, çünkü belirteç yapıştırmaya "," mesaja "gerek yoktur. Msg'yi dizginlemeye mi çalışıyordunuz? Ayrıca, FILE ve LINE büyük harf olmalı, küçük harf olmamalıdır.
bk1e

Gerçekten haklısın. ## öğesinin nasıl kullanıldığını görmek için orijinal komut dosyasını bulmam gerekiyor. Yazık bana, bugün kurabiye yok!
ya23
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.