Makrolar neden çoğu modern programlama dilinde bulunmuyor?


31

C / C ++ 'da son derece güvenli bir şekilde uygulandığını biliyorum. Daha güvenli bir şekilde uygulanamazlar mı? Makroların dezavantajları sağladıkları muazzam güçten daha ağır basmak için yeterince kötü mü?


4
Makrolar tam olarak hangi güçle oldukça kolay bir şekilde elde edilemeyeceklerini sağlıyor?
Chinmay Kanchi

2
C # 'da basit bir özellik yaratma ve destek alanını tek bir bildiriye sarmak için ana dil uzantısı kullanıldı. Bu, sözcüksel makrolardan daha fazlasını gerektirir: Visual Basic'in WithEventsdeğiştiricisine karşılık gelen bir özelliği nasıl oluşturabilirsiniz ? Anlamsal makrolar gibi bir şeye ihtiyacınız var.
Jeffrey Hantin

3
Makrolardaki sorun, tüm güçlü mekanizmalar gibi, bir programcının başkalarının yaptığı veya yapacağı varsayımları kırmasına izin vermesidir. Varsayımlar mantığın anahtarıdır ve mantık hakkında mantıklı bir şekilde mantık yürütme kabiliyeti olmadan ilerleme yapmak yasaklayıcıdır.
dan_waterworth

2
@Chinmay: Makrolar kod üretir. Java dilinde bu güçle bir özellik yoktur.
kevin cline

1
@Chinmay Kanchi: Makrolar, derleme zamanında çalışma zamanı yerine kod çalıştırılmasına izin verir.
Giorgio

Yanıtlar:


57

Bence asıl sebep makroların sözlü olmasıdır . Bunun birkaç sonucu var:

  • Derleyici, bir makronun semantik olarak kapalı olduğunu, yani bir fonksiyonun yaptığı gibi “anlam birimini” temsil ettiğini kontrol etmenin bir yolunu yoktur. (Bir düşünün #define TWO 1+1- ne TWO*TWOeşittir? 3.)

  • Makrolar fonksiyonlar gibi yazılmaz. Derleyici, parametrelerin ve dönüş tipinin anlamlı olup olmadığını kontrol edemez. Yalnızca makroyu kullanan genişletilmiş ifadeyi kontrol edebilir.

  • Kod derlenmezse, derleyicide hatanın makroda mı, yoksa makronun kullanıldığı yerde olduğunu bilemez. Derleyici, zamanın yanlış yerini ya rapor edecek ya da muhtemelen bir tanesinin iyi olmasına rağmen her ikisini de rapor etmek zorunda kalacak. (Düşünün #define min(x,y) (((x)<(y))?(x):(y)): tipleri ise derleyici yapması gerekenler xve yeşleşmiyor veya uygulamak gerekmez operator<?)

  • Otomatik araçlar, bunlarla anlamsal olarak yararlı şekillerde çalışamaz. Özellikle, işlevler gibi çalışan ancak bir ifadeye genişleyen makrolar için IntelliSense gibi öğelere sahip olamazsınız. (Yine, minörnek.)

  • Bir makronun yan etkileri, programlayıcı için potansiyel karışıklığa neden olan fonksiyonlarda olduğu kadar açık değildir. ( minÖrnek olarak tekrar düşünün : bir işlev çağrısında, ifadesinin xyalnızca bir kez değerlendirildiğini bilirsiniz, ancak burada makroya bakmadan bunu bilemezsiniz.)

Dediğim gibi, bunların hepsi makroların sözcüksel olmasının bir sonucudur. Onları daha uygun bir şeye dönüştürmeye çalıştığında, işlev ve sabitlerle sonuçlanırsın.


32
Tüm makro dilleri sözcüksel değildir. Örneğin, Şema makroları sözdizimseldir ve C ++ şablonları anlamsaldır. Sözlüksel makro sistemler, sözdiziminden haberdar olmadan herhangi bir dilde takip edilebileceklerine sahiptir.
Jeffrey Hantin

8
@Jeffrey: “Makrolarım sözcüksel”, sanırım “insanlar bir programlama dilinde makrolara başvurduğunda genellikle sözcüksel makrolar hakkında düşünüyor”. Scheme'nin bu yüklü terimi temelde farklı olan bir şey için kullanması talihsiz bir durumdur. Bununla birlikte, C ++ şablonları, büyük olasılıkla tam anlamıyla sözcük olmadıkları için büyük olasılıkla tam olarak makrolar olarak adlandırılmamaktadır.
Timwi,

25
Scheme'nin makro terimini kullanmasının Lisp'e dayandığını düşünüyorum, bu muhtemelen diğer pek çok kullanımdan önce geldiği anlamına gelir. IIRC C / C ++ sistemi başlangıçta önişlemci olarak adlandırıldı .
Bevan

16
@Bevan haklı. Makroların sözcüksel olduğunu söylemek, kuşların uçamayacağını söylemek gibidir, çünkü en aşina olduğunuz kuş bir penguendir. Bununla birlikte, dile getirdiğiniz noktaların çoğu (tümü değil), belki de daha düşük bir dereceye kadar sözdizimsel makrolar için de geçerlidir.
Laurence Gonsalves

13

Ama evet, makro olabilir tasarlanmış ve C / C ++ daha iyi uygulanacaktır.

Makrolardaki sorun , kodunuzu başka bir şeye yeniden yazan bir dil sözdizimi uzantısı mekanizmasıdır.

  • C / C ++ durumunda, temel akıl sağlığı kontrolü yoktur. Dikkatliysen, her şey yolunda. Bir hata yaparsanız veya aşırı makro kullanırsanız büyük sorunlara neden olabilirsiniz.

    Buna ek olarak, (C / C ++ tarzı) makrolarla yapabileceğiniz birçok basit işlemin diğer dillerde başka şekillerde de yapılabileceğini ekleyin.

  • Çeşitli Lisp lehçeleri gibi diğer dillerde, makrolar çekirdek dil sözdizimi ile daha iyi bütünleşir, ancak yine de bir "sızıntı" makrosundaki bildirimlerle ilgili sorun yaşayabilirsiniz. Bu hijyenik makrolar tarafından ele alınmaktadır .


Kısa Tarihsel Bağlam

Makrolar (makro komutları için kısa) ilk önce assembly dili bağlamında göründü. Wikipedia'ya göre , makrolar 1950'lerde bazı IBM montajcılarında mevcuttu.

Orijinal LISP'nin makroları yoktu, ancak ilk kez 1960'ların ortalarında MacLisp'e tanıtıldı: https://stackoverflow.com/questions/3065606/when-did-the-idea-of-macros-user-defined-code -transformasyon-görünür . http://www.csee.umbc.edu/courses/331/resources/papers/Evolution-of-Lisp.pdf . Bundan önce, "fexprs" makro benzeri işlevler sağlamıştır.

C'nin en eski sürümlerinde makro yoktu ( http://cm.bell-labs.com/cm/cs/who/dmr/chist.html ). Bunlar 1972-73 dolaylarında bir önişlemci ile eklenmiştir. Ondan önce, C sadece destekledi #includeve #define.

M4 makroişlemcisi 1977 yılında ortaya çıkmıştır.

Daha yeni diller görünüşte, operasyon modelinin metin yerine sözdizimsel olduğu makroları uygular.

Bu yüzden, birisi "makro" teriminin belirli bir tanımının önceliğinden bahsettiğinde, anlamın zaman içinde geliştiğini not etmek önemlidir.


9
C ++, TWO makro sistemlerinde kutsanmış (?): Önişlemci ve şablon genişletme.
Jeffrey Hantin

Şablon genişlemesinin bir makro olduğunu iddia etmenin makro tanımını genişlettiğini düşünüyorum. Tanımınızı kullanmak, asıl soruyu anlamsız ve sadece yanlış yapar, çünkü çoğu modern dilde (tanımınıza göre) makrolar vardır. Ancak, geliştiricilerin ezici çoğunluğu olarak makroyu kullanmak, bunu iyi bir soru yapar.
Dunk

@Dunk, Lisp'te olduğu gibi bir makronun tanımı, acıklı C önişlemcisinde olduğu gibi "modern" aptallık anlayışından önce gelir.
SK-mantık

@SK: "Teknik olarak" doğru olmak ve konuşmayı şaşırtmak daha mı iyi, yoksa anlaşılması daha mı iyi?
Dunk

1
@ Dunk, terminoloji cahil orduların ait değil. Onların cehaletleri asla dikkate alınmamalı, aksi takdirde bilgisayar biliminin tüm alanını azaltmaya yol açacaktır. Birisi "makro" yu yalnızca bir C önişlemcisine referans olarak anlarsa, bu cehaletten başka bir şey değildir. Lisp ve hatta pardonnez mon français, Office'teki VBA "makrolar" ı bile duymamış insanların önemli bir kısmı olduğundan şüpheliyim.
SK-mantık

12

Makrolar, Scott'un belirttiği gibi , mantığı gizlemenize izin verebilir. Tabii ki, fonksiyonlar, sınıflar, kütüphaneler ve diğer birçok ortak cihaz da öyle.

Ancak, normalde dilde bulunmayan sözdizimi ve yapıları tasarlamanızı ve kullanmanızı sağlayan güçlü bir makro sistem daha ileriye gidebilir. Bu gerçekten harika bir araç olabilir: etki alanına özgü diller, kod üreteçleri ve daha fazlası, hepsi tek bir dil ortamının rahatlığında ...

Ancak, kötüye kullanılabilir. Kodun okunmasını, anlaşılmasını ve hata ayıklamasını zorlaştırabilir, yeni programcıların kod tabanına aşina olmaları için gereken süreyi arttırabilir ve maliyetli hatalara ve gecikmelere neden olabilir.

Dolayısıyla, programlamayı basitleştirmeyi amaçlayan diller (Java veya Python gibi) için böyle bir sistem bir anatemadır.


3
Ve sonra Java, foreach, ek açıklamalar, iddialar, jeneriklere göre silme ekleyerek raylardan çıktı ...
Jeffrey Hantin

2
@Jeffrey: ve unutmayalım, pek çok 3. parti kod üreteci var. İkinci düşüncede, bunları unutalım.
Shog9

4
Java'nın basit yerine basit bir örneği.
Jeffrey Hantin

3
@JeffreyHantin: Foreach'ın nesi var?
Casebash

1
@Dunk Bu benzetme bir gergin olabilir ... fakat bence dil genişletilebilirliği bir tür plütonyum gibidir. Uygulaması pahalı, istenilen şekillerde işlenmesi çok zor ve kötüye kullanıldığında çok tehlikeli, ancak iyi uygulandığında inanılmaz derecede güçlü.
Jeffrey Hantin

6

Makrolar bazı durumlarda çok güvenli bir şekilde uygulanabilir - örneğin Lisp'te, makrolar yalnızca dönüştürülmüş kodu veri yapısı (s-ifadesi) olarak döndüren işlevlerdir. Tabii ki, Lisp homoikonik olmasından ve "kodun veri olduğunu" gerçeğinden önemli ölçüde yararlanır .

Makroların ne kadar kolay olabileceğinin bir örneği, bir istisna durumunda kullanılacak varsayılan bir değeri belirten bu Clojure örneğidir:

(defmacro on-error [default-value code]
  `(try ~code (catch Exception ~'e ~default-value)))

(on-error 0 (+ nil nil))               ;; would normally throw NullPointerException
=> 0                                   ;l; but we get the default value

Yine de Lisps'ta bile, genel tavsiye "mecbur kalmadıkça makro kullanma" şeklindedir.

Homoikonik bir dil kullanmıyorsanız, makrolar çok daha zorlaşır ve diğer çeşitli seçeneklerin hepsinde bazı tehlikeler vardır:

  • Metin tabanlı makrolar - örneğin C önişlemcisi - uygulaması basit ancak doğru sözdizimi de dahil olmak üzere metin biçiminde doğru kaynak sözdizimini oluşturmanız gerektiğinden doğru şekilde kullanmanız çok zordur.
  • Makro tabanlı DSLS - örneğin, C ++ şablon sistemi. Karmaşık, bazı zor sözdizimine neden olabilir, derleyici ve araç yazarlarının doğru şekilde ele almaları için oldukça karmaşık olabilir, çünkü dil sözdizimi ve anlambiliminde önemli yeni karmaşıklıklar ortaya çıkarmaktadır.
  • AST / bytecode manipülasyon API'leri - örneğin Java yansıması / bytecode oluşturma - teorik olarak çok esnek ancak çok ayrıntılı olabilir: oldukça basit şeyler yapmak için çok fazla kod gerektirebilir. Üç satırlık bir işlevin eşdeğerini oluşturmak on satırlık bir kod alırsa, meta-programlama çabalarınızla pek bir şey elde edemezsiniz ...

Ayrıca, bir makronun yapabileceği her şey sonuçta bir turing tam dilinde başka bir yolla elde edilebilir (bu, çok fazla kazan yazısı yazmak olsa bile). Bütün bu hileliğin bir sonucu olarak, birçok dilin makroların gerçekten uygulamak için tüm çabaya değmeyeceğine karar vermesi şaşırtıcı değildir.


Ugh. Daha fazla değil "kod veri iyidir" çöp. Kod kod, veri veri ve ikisini doğru şekilde ayırmamak bir güvenlik açığı. SQL Injection'ın varolan en büyük güvenlik açığı sınıflarından biri olarak ortaya çıkmasının, kod ve verilerin bir kez ve herkes için kolayca değiştirilebilir olmasını sağlama fikrini geçersiz kılacağını düşünürdünüz.
Mason Wheeler

10
@Mason - Veri kodları kavramını anladığınızı sanmıyorum. Tüm C program kaynak kodu da veridir - sadece metin biçiminde ifade edilir. Lisps, kodu derlemeden önce makrolar tarafından manipüle etmesini ve dönüştürmesini sağlayan pratik ara veri yapılarında (s ifadeleri) ifade etmeleri dışında aynıdır. Her iki durumda da derleyiciye güvenilmeyen girdi göndermek bir güvenlik açığı olabilir - ancak yanlışlıkla yapmak zordur ve derleyicinin değil aptalca bir şey yapmak sizin suçunuz olur.
mikera

Pas bir başka ilginç güvenli makro sistemidir. C / C ++ sözcüksel makrolarıyla ilişkili sorun türlerinden kaçınmak için katı kurallara / anlamsallığa sahiptir ve giriş ifadesinin parçalarını daha sonra eklenecek makro değişkenlerine yakalamak için bir DSL kullanır. Lisps'in girişte herhangi bir işlevi çağırabildiği kadar güçlü değildir, ancak diğer makrolardan çağrılabilecek çok sayıda kullanışlı manipülasyon makroları sağlar.
zstewart

Ugh. Daha fazla güvenlik çöpü değil . Aksi taktirde, her sistemi von Neumann mimarisi yerleşik olarak yapın, örneğin kendi kendini değiştirebilen kod yeteneğine sahip her IA-32 işlemci. Deliği fiziksel olarak doldurabildiğinizden şüpheliyim ... Her neyse, genel olarak bununla yüzleşmelisiniz: kod ve veri arasındaki izomorfizm , çeşitli şekillerde dünyanın doğasıdır . Ve siz (muhtemelen) programcı, herhangi bir yerde erken ayrıştırmayı yapay olarak uygulamakla aynı olmayan güvenlik gereksinimlerinin değişmezliğini sürdürme görevini üstlendiniz.
FrankHB

4

Sorularınıza cevap vermek için, ağırlıklı olarak hangi makroların kullanıldığını düşünün (Uyarı: beyin-derlenmiş kod).

  • Sembolik sabitleri tanımlamak için kullanılan makrolar #define X 100

Bu kolayca değiştirilebilir: const int X = 100;

  • Satır içi tip-agnostik fonksiyonları tanımlamak için kullanılan makrolar #define max(X,Y) (X>Y?X:Y)

Fonksiyon aşırı yüklenmesini destekleyen herhangi bir dilde, bu doğru tipte aşırı yüklenmiş fonksiyonlara sahip olmak veya jenerikleri jenerik bir fonksiyonla destekleyen bir dilde çok daha güvenli bir şekilde taklit edilebilir. Makro mutlu karşılaştırmak dener şey derlemek olabilir işaretçiler veya dizeleri dahil, fakat ne istediğini hemen hemen kesinlikle değil. Öte yandan, makroları güvenli hale getirdiyseniz, aşırı yüklenmiş işlevler üzerinde hiçbir fayda veya kolaylık sağlamazlar.

  • Makrolar, sık kullanılan öğelere kısayolları belirtmek için kullanılır. #define p printf

Bu kolayca p()aynı şeyi yapan bir işlev ile değiştirilir . Bu, C ( va_arg()işlev ailesini kullanmanızı gerektiren) ile oldukça ilgilidir, ancak değişken sayıdaki işlev argümanlarını destekleyen birçok dilde, çok daha basittir.

Bu özellikleri Destekleme içinde oldukça özel bir makro dilinde daha dile basit, daha az hata eğilimli ve çok daha az kod okuma başkalarına kafa karıştırıcı olduğunu. Aslında, başka bir şekilde kolayca çoğaltılamayan makrolar için tek bir kullanım durumu düşünemiyorum . Sadece onlar gibi koşullu derleme yapılara bağlı olduğunda makro gerçekten yararlıdır yerdir #if(vs.).

Bu noktada, sizinle tartışmayacağım, çünkü popüler dillerde koşullu derlemeye önişlemci olmayan çözümlerin son derece hantal olduğunu düşünüyorum (Java'da bytecode enjeksiyonu gibi). Ancak, D gibi diller, bir önişlemci gerektirmeyen ve ön-işlemsel koşullama kullanmaktan ziyade hantal olmayan, çok daha az hataya eğilimli olan çözümler üretti.


1
#define max komutunu kullanmak zorundaysanız, lütfen parametrelerin etrafına parantez koyun, bu nedenle operatör önceliğinden beklenmeyen bir etki olmaz ... gibi #define max (X, Y) ((X)> (Y)? (X) :( Y))
foo

Bunun sadece bir örnek olduğunun farkındasın ... Niyeti göstermekti.
Chinmay Kanchi,

Konuyla ilgili bu sistematik işlem için +1. Koşullu derlemenin kolayca bir kabus haline gelebileceğini eklemeyi severim - Unifdef (?) Adında bir program olduğunu ve amacının post-işlemden sonra geriye kalanları görünür kılmak olduğunu hatırlıyorum.
Ingo,

7
In fact, I can't think of a single use-case for macros that can't easily be duplicated in another way: en azından C de, makro kullanmadan belirteç birleştirme kullanarak tanımlayıcıları (değişken isimleri) oluşturamazsınız.
Charles Salvia,

Makrolar, belirli kod ve veri yapılarının "paralel" kalmasını sağlamayı mümkün kılar. Örneğin, ilişkili iletilerle ilgili az sayıda koşul varsa ve bunlardan birinin kısa bir biçimde kalması enumgerekir, koşulları tanımlamak için bir kullanmaya çalışılması ve mesajları tanımlamak için sabit bir dize dizisi kullanılması, sorunların ortaya çıkmasına neden olabilir. enum ve dizi senkronizasyondan çıkar. Tüm (enum, string) çiftlerini tanımlamak için bir makro kullanmak ve ardından her seferinde o makroyu her seferinde kapsamda diğer uygun tanımlarla dahil etmek, her enum değerini dizesinin yanına koymasına izin verir.
supercat

2

Makrolarla karşılaştığım en büyük sorun, yoğun bir şekilde kullanıldığında kodun okunmasını ve sürdürülmesini zorlaştırabilmesidir. ).


Makro belgelendirilmemeli mi?
Casebash

@Casebash: Tabii ki, makro, diğer tüm kaynak kodları gibi belgelenmelidir ... ama pratikte, nadiren yapıldığını gördüm.
Scott Dorman

3
Hiç belgelerde hata ayıklama yaptınız mı? Kötü yazılmış kodlarla uğraşırken bu yeterli değildir.
JeffO

OOP da bu sorunların bazılarına sahip ...
aoeu256

2

C / C ++ 'daki MACRO'ların çok sınırlı, hataya açık ve gerçekten bu kadar kullanışlı olmadığını belirterek başlayabilirsiniz.

LISP ya da z / OS assembler dili olarak uygulanan makrolar güvenilir ve inanılmaz derecede faydalı olabilir.

Ancak C'deki sınırlı işlevselliğin kötüye kullanılması nedeniyle, kötü bir üne kavuştular. Böylece kimse artık makro uygulamıyor, bunun yerine eskiden kullanılan basit makroların bazılarını yapan Şablonlar ve Java'nın yaptığı daha karmaşık bazı şeyleri yapan Java ek açıklamaları gibi şeyler alıyorsunuz.

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.