C ++: C ++ özellikleri yerine bir derleyici API'sı ile meta programlama


10

Bu bir SO sorusu olarak başladı, ancak oldukça alışılmadık olduğunu ve web sitelerindeki gerçek açıklamaya dayanarak, programcılara daha uygun olabileceğini fark ettim. Çünkü sorunun çok fazla kavramsal ağırlığı var.

Ben öğrenme edilmiştir clang LibTooling ve bir de, bir dost bir şekilde, kod bütün "işinin zor" açığa kapasitesine sahip çok güçlü bir araçtır anlamsal ya tahmin ederek olursa ve bunlarla. Clang kodunuzu derlemek, o zaman çınlama olduğunu belli bu kodu içine her karakterin semantik hakkında.

Şimdi bir anlığına geri çekmeme izin verin.

Biri C ++ şablon meta programlamasına dahil olduğunda (ve özellikle de şablonların ötesinde korkunç makrolar da olsa zeki bölgelere girerken) ortaya çıkan birçok pratik problem vardır. Dürüst olmak gerekirse, ben dahil birçok programcı için, şablonların sıradan kullanımlarının birçoğu da biraz korkutucu.

İyi bir örnek derleme zamanı dizeleri olurdu sanırım . Bu şu anda bir yıldan daha eski bir soru, ama şu anda C ++ 'ın sadece ölümlüler için bunu kolaylaştırdığı açık. Bu seçeneklere bakmak benim için bulantıyı tetiklemek için yeterli olmasa da, yazılımım için sahip olduğum her türlü fantezi uygulamaya uyacak şekilde büyülü, maksimum verimli makine kodu üretme konusunda emin olmamı sağlıyor.

Yani, bununla yüzleşelim, millet, teller oldukça basit ve basit. Bazılarımız sadece belirli dizeleri "pişmiş" olan makine kodunu yaymak için uygun bir yol istiyoruz. C ++ kodumuzda.

Kaynak kodun soyut sözdizimi ağacını (AST) gösteren ve basit bir özel C ++ uygulamasının, AST'deki Rewriterher şeyin zengin bir anlamsal nesne yönelimli modelinin yanı sıra ham kaynak kodunu (kullanarak ) doğru ve güvenilir bir şekilde değiştirmesine izin veren clang ve LibTooling girin . Bir çok şeyi halleder. Makro açılımlarını bilir ve bu zincirleri takip etmenizi sağlar. Evet, kaynaktan kaynağa kod dönüştürme veya çeviri hakkında konuşuyorum.

Buradaki temel tezim, clang'ın artık C ++ yazılımımız için ideal özel önişlemci aşamaları olarak işlev görebilen yürütülebilir dosyalar oluşturmamızı sağlaması ve bu metaprogramlama aşamalarını C ++ ile uygulayabilmemiz. Biz sadece bu aşamanın geçerli C ++ kodu olan girdi alması ve çıktı olarak daha geçerli C ++ kodu üretmesi gerçeği ile kısıtlanıyoruz. Ayrıca, derleme sisteminizin uyguladığı diğer kısıtlamalar ne olursa olsun.

Girdi, en azından geçerli C ++ koduna çok yakın olmalı, çünkü clang derleyici ön uçudur ve sadece API ile yaratıcılık yapıyoruz. Kullanılacak yeni sözdizimini tanımlamak için herhangi bir hüküm olup olmadığını bilmiyorum, ancak bunu yapmak için düzgün bir şekilde ayrıştırma ve clang projesine ekleme yollarını geliştirmemiz gerekiyor. Daha fazlasını beklemek clang projesinde kapsam dışı olan bir şeylere sahip olmaktır.

Problem değil. Bazı op-olmayan makro fonksiyonlarının bu görevi yerine getirebileceğini hayal ediyorum.

Ne açıklamak için bakmak başka bir yolu, kaynak kodun AST (clang ve API sayesinde) dilde kullanılabilir daha sınırlı araçları kullanarak uygulamak yerine, C ++ çalışma zamanı kullanarak metaprogramming yapıları uygulamaktır. Bu da derleme performansının net faydalarına sahiptir (şablon-ağır başlıklar derleme, bunları ne sıklıkta kullandığınıza orantılı olarak yavaşlar. Derlenmiş birçok öğe daha sonra dikkatlice eşleştirilir ve bağlayıcı tarafından atılır).

Bununla birlikte, bu, oluşturma işlemine ek bir veya iki adım ekleme ve aynı zamanda aracımızın bir parçası olarak (itirafla) biraz daha ayrıntılı bir yazılım yazma gereksinimi (ancak en azından basit çalışma zamanı C ++) gereksinimindedir. .

Resmin tamamı bu değil. Temel dil özellikleri ile son derece zor veya imkansız kod üretmekten daha büyük bir işlevsellik alanı olduğundan eminim. C ++ 'da bir şablon veya makro veya her ikisinin çılgın bir kombinasyonunu yazabilirsiniz, ancak bir clang aracında , semantik içeriğe tam erişime sahipken , çalışma zamanında C ++ ile elde edebileceğiniz HERHANGİ bir şekilde sınıfları ve işlevleri değiştirebilirsiniz , şablon ve makrolara ve diğer her şeye ek olarak.

Herkesin bunu neden yapmadığını merak ediyorum. Clang'ın bu işlevselliği çok yeni mi ve hiç kimse clang AST'sinin devasa sınıf hiyerarşisine aşina değil mi? Bu olamaz.

Belki de bunun zorluğunu biraz küçümsüyorum, ancak bir clang aracıyla "derleme zamanı dize manipülasyonu" yapmak neredeyse cezai basittir. Bu ayrıntılı, ama delicesine açık. Tek gereken, gerçek gerçek std::stringişlemlerle eşleşen bir grup işlemeyen makro işlevidir . Clang eklentisi, ilgili tüm op-op makro çağrılarını getirerek uygular ve işlemleri dizelerle gerçekleştirir. Bu araç daha sonra oluşturma işleminin bir parçası olarak eklenir. Derleme sırasında, bu işlem dışı makro işlev çağrıları otomatik olarak sonuçlarına göre değerlendirilir ve daha sonra programda eski düz derleme zamanı dizeleri olarak geri eklenir. Program daha sonra her zamanki gibi derlenebilir. Aslında sonuçta ortaya çıkan bu program, C ++ 11'i destekleyen yeni ve süslü bir derleyici gerektirmeyen sonuç olarak çok daha taşınabilir.


Bu alışılmadık derecede uzun bir soru. Belki de en alakalı noktalarınıza yoğunlaştırabilir misiniz?
amon

Çok uzun sorularım var. Ama özellikle bununla birlikte, sorunun tüm bölümlerinin önemli olduğunu düşünüyorum. Belki ilk 6 paragrafı atlayın? Haha.
Steven Lu

3
Lisp'de öncülük eden ve son zamanlarda Haxe, Nemerle, Scala ve benzer diller tarafından alınan sözdizimsel makrolar gibi görünüyor. Lisp makrolarının neden zararlı olduğu hakkında bazı okumalar var. Henüz ikna edici bir argüman duymamış olmama rağmen, insanların onları her dile eklemeye isteksiz olmasının nedenlerini bulabilirsiniz (bunun mutlaka doğru olması gerekmez).
back2dos

Evet, meta-ifying C ++. Bu da daha iyi, daha hızlı kod anlamına gelebilir. Bu dillere gelince . Peki nereden başlayayım. Bu dillerden herhangi birinde uygulanan milyonlarca dolarlık video oyunu nedir? Bu dillerden herhangi birinde uygulanan modern bir web tarayıcısı nedir? İşletim sistemi çekirdeği? Tamam, aslında Haxe'nin bir miktar çekiş gücü var gibi görünüyor, ama fikri anladınız.
Steven Lu

1
@nwp, Eh, yardım edemiyorum ama yayının tüm noktasını kaçırmış gibi görünüyorsun. Derleme zamanı dizeleri artık bizim için mevcut olan yeteneklerin en tutarlı ve minimal somut örneğidir.
Steven Lu

Yanıtlar:


7

Evet, Virginia, bir Noel Baba var.

Programları değiştirmek için program kullanma fikri uzun zamandır var. Orijinal fikir John von Neumann'dan depolanmış program bilgisayarlar şeklinde geldi . Ancak, makine kodunu keyfi bir şekilde değiştirmek makine kodu oldukça rahatsız edici.

İnsanlar genellikle kaynak kodunu değiştirmek ister . Bu çoğunlukla program dönüşüm sistemleri (PTS) şeklinde gerçekleştirilir .

PTS genellikle en az bir programlama dili için AST'lere ayrıştırma, AST'yi manipüle etme ve geçerli kaynak metni yeniden oluşturma yeteneği sunar. Aslında, çoğu ana dil için etrafta kazarsanız, birisi böyle bir araç (Clang C ++ için bir örnektir, Java derleyicisi bu yeteneği bir API olarak sunar, Microsoft Rosyln, Eclipse'nin JDT, ...) bir prosedürle sunar Aslında oldukça kullanışlı bir API. Daha geniş topluluk için, hemen hemen her dile özgü topluluk, çeşitli olgunluk düzeyleriyle (genellikle mütevazı, birçok "sadece AST üreten üreticiler)" gibi bir şeye işaret edebilir. Mutlu metaprogramlama.

[ Programlama dilinin içinden metaprogramlama yapmaya çalışan , ancak yalnızca “çalışma zamanı” davranış değişikliğine ulaşan ve yalnızca dil derleyicilerinin yansıma yoluyla bazı bilgileri sağladığı ölçüde yansıma odaklı bir topluluk vardır . LISP dışında, her zaman yansıma tarafından kullanılamayan ("Luke, kaynağa ihtiyacınız vardır") program hakkında her zaman yansıma yapabileceklerini her zaman sınırlayan detaylar vardır.]

Daha ilginç PTS bunu keyfi diller için yapar (alete, en azından BNF dahil olmak üzere yapılandırma parametresi olarak bir dil açıklaması verirsiniz). Bu tür PTS ayrıca "kaynaktan kaynağa" dönüşüm gerçekleştirmenize izin verir, örn ., Hedeflenen dilin yüzey sözdizimini kullanarak kalıpları doğrudan belirtin ; bu tür kalıpları kullanarak ilgili parçaları kodlayabilir ve / veya kod parçalarını bulabilir ve değiştirebilirsiniz. Bu, programlama API'sından çok daha uygundur, çünkü işinizin çoğunu yapmak için AST'ler hakkındaki her mikroskobik ayrıntıyı bilmek zorunda değilsiniz. Bunu meta-metaprogramlama olarak düşünün: -}

Bir dezavantajı: PTS çeşitli faydalı statik analizler (sembol tabloları, kontrol ve veri akışı analizleri) sunmadığı sürece, bu şekilde gerçekten ilginç dönüşümler yazmak zordur, çünkü çoğu pratik görev için türleri kontrol etmeniz ve bilgi akışlarını doğrulamanız gerekir. Ne yazık ki, bu özellik aslında genel PTS'de nadirdir. (Her zaman önerilen "Ayrıştırıcıya sahip olsaydım ..." ile her zaman kullanılamaz. "Ayrıştırmadan Sonra Yaşam" hakkında daha uzun bir tartışma için biyografimi görün).

Dize yeniden yazma yapabilmeniz [böylece ağaç yeniden yazma] yapabiliyorsanız, keyfi dönüşüm yapabileceğinizi söyleyen bir teorem vardır; ve böylece, sundukları ağaç yeniden yazma işlemleriyle her şeyi meta programlayabileceğinizi iddia etmek için buna dayanan bir PTS vardır. Teorem, her şeyi yapabileceğinizden emin olduğunuz anlamında tatmin edici olsa da, bir Turing Makinesinin herhangi bir şey yapabilme yeteneğinin bir Turing Makinesini programlamayı tercih edilen yöntem yapmaması gibi tatmin edici değildir. (Aynı şey, sadece prosedürel API'leri olan sistemler için de geçerlidir, eğer AST'de keyfi değişiklikler yapmanıza izin verirlerse [ve aslında bunun Clang için doğru olmadığını düşünüyorum]).

İstediğiniz şey, her iki dünyanın da en iyisi, dil parametreli PTS türünün genelliğini (birden fazla dili bile idare ediyor), ek statik analizlerle, kaynak-kaynak dönüşümlerini prosedürel ile karıştırma yeteneği sunan bir sistemdir. API'leri. Bunu yapan sadece iki tanesini biliyorum :

  • Rascal (MPL) Meta Programlama Dili
  • Yazılım Yeniden Yapılandırma Araç Setimiz

Sürece sen Yaz dilini açıklamaları ve statik analizörleri kendinizi istediğiniz olgun lisan açıklamalarıyla, istediğiniz bir PTS (C ++ bu Clang derleyici olarak ve genel usul metaprogramming vakıf olarak hem de inşa edilmiştir nedenle işin muazzam miktarda olduğu) zaten mevcut. Aksi takdirde, tüm zamanınızı PTS'yi yapılandırarak geçirirsiniz ve hiçbiri gerçekten yapmak istediğiniz işi yapmazsınız. [Rastgele, ana akım olmayan bir dil seçerseniz, bu adımdan kaçınmak çok zordur].

Rascal bunu "OPP" yi (Diğer Halkın Ayrıştırıcıları) seçerek yapmaya çalışır, ancak bu statik analiz kısmına yardımcı olmaz. Elinde Java var oldukça iyi, ama eminim onlar C veya C ++ yapmazlar. Ancak, akademik bir araştırma aracıdır; onları suçlamak zor.

Ben, vurgulamak bizim [ticari] DMS aracı C ++ tam ön uçları mevcut Java, C, var. C ++ için, GCC için C ++ 14'teki hemen hemen her şeyi ve hatta Microsoft'un varyasyonlarını (ve şimdi parlatıyoruz), makro genişletmeyi ve koşullu yönetimi ve yöntem düzeyinde kontrol ve veri akışı analizini kapsar. Ve evet, olabilir pratik bir şekilde gramer değişiklikleri belirtin; C + 'yı F90 / APL veri-paralel dizi işlemlerini kullanmak için radikal olarak genişleten bir istemci için özel bir VectorC ++ sistemi oluşturduk. DMS, büyük C ++ sistemlerinde (örn. Uygulama mimari yeniden şekillendirme) diğer devasa metaprogramlama görevlerini yerine getirmek için kullanılmıştır. (Ben DMS'nin arkasındaki mimarım).

Mutlu meta-metaprogramlama.


Harika, sanırım Clang ve DMS, bazı üst üste binme yeteneklerine sahipken, aynı kategoride olmayan yazılım parçaları. Demek istiyorum ki, biri muhtemelen gülünç derecede pahalı ve muhtemelen ona erişmek için gereken kaynakları hiçbir zaman haklı çıkaramadım, diğeri ise sınırsız serbest açık kaynak. Bu çok büyük bir fark ... Beni bu heyecan verici meta programlama yetenekleri hakkında heyecanlandıran şeylerden biri, aslında sadece özgürce kullanmakla kalmayıp aynı zamanda clang tabanlı ikili araçları serbestçe dağıtmaya izin verilmesidir.
Steven Lu

Ticari olarak satılan her şey, bedavaya kıyasla "çok pahalı". Ham maliyet sorun değil; önemli olan, bazı insanlar için, ticari ürünü elde etmenin geri ödemesinin, ücretsiz yapının geri ödemesinden daha yüksek olmasıdır, aksi takdirde ticari bir yazılım olmayacaktır. Bu açıkça sizin özel ihtiyaçlarınıza bağlıdır. Clang, alet alanında ilginç bir noktadır ve kesinlikle yararlı uygulama noktalarına sahip olacaktır. DMS'nin daha geniş yeteneklere sahip olduğunu düşünmek istiyorum (DMS mimarı olduğum için). Clang'ın örnek olarak C ++ dışındaki dilleri desteklemesi olası değildir.
Ira Baxter

Kesinlikle. DMS'nin neredeyse sihir noktasına (inanılmaz Arthur C. Clarke) inanılmaz derecede güçlü olduğu sorusu yoktur ve clang harika olsa da, gerçekten çok iyi yazılmış bir C ++ ön ucudur. İleriye doğru birçok küçük adım, yani, DMS ile karşılaştırmak hala adil olmaz. Ne yazık ki, elimizde böyle güçlü bir araç olsa bile, çalışma yazılımı kendini yazmaz. Araçları kullanarak dikkatli bir çeviri ya da (hemen hemen her zaman üstün seçenek) taze olarak yazılmalı.
Steven Lu

Clang veya DMS gibi araçlar için yeni araçlar oluşturamazsınız. Ayrıca, 5 yıldan fazla bir süredir 10 kişilik bir ekiple yazdığınız uygulamayı atmayı göze alamazsınız. Yazılım boyutları ve yaşamları büyümeye devam ettikçe, bu tür araçlara daha sık ihtiyaç duyacağız.
Ira Baxter

@StevenLu: DMS, iltifatınız için teşekkür ediyor, ama bununla ilgili sihir yok. DMS, neredeyse 2 yıllık on yıllar boyunca mühendislik ve oldukça iyi bir performans gösteren temiz bir mimari platformdan (aw, shucks, YMMV) yararlanmaktadır. Benzer şekilde, Clang'da çok iyi mühendislik var. Katılıyorum, tam olarak aynı sorunu çözmek için tasarlanmamışlardı ... DMS'nin kapsamı, sembolik program manipülasyonu konusunda açıkça daha büyük ve bir üretim derleyicisi olduğunda çok daha küçük olması amaçlanıyor.
Ira Baxter

4

Derleyicilerin API'siyle (şablonlar kullanmak yerine) C ++ 'da metaprogramlama gerçekten ilginç ve pratik olarak mümkündür. Metaprogramlama (henüz) standartlaştırılmadığından, şablonlarda geçerli olmayan belirli bir derleyiciye bağlanacaksınız.

Herkesin bunu neden yapmadığını merak ediyorum. Clang'ın bu işlevselliği çok yeni mi ve hiç kimse clang AST'sinin devasa sınıf hiyerarşisine aşina değil mi? Bu olamaz.

Birçok insan bunu yapar, ancak diğer dillerde. Benim düşüncem, çoğu C ++ (veya Java veya C) geliştiricisinin buna ihtiyaç duymadığını (belki de haklı olarak) veya metaprogramlama yaklaşımlarına alışkın olmadığını; Ayrıca, IDE'lerinin yeniden düzenleme / kod oluşturma özelliklerinden memnun olduklarını ve merak uyandıran her şeyin çok karmaşık / bakımı zor / hata ayıklaması zor olabileceğini düşünüyorum. Uygun araçlar olmadan doğru olabilir. Ayrıca eylemsizliği ve insanları işe alma ve / veya eğitme gibi teknik olmayan diğer sorunları da göz önünde bulundurmalısınız.

Bu arada, Common Lisp ve makro sisteminden bahsettiğimizden (Basile'ın cevabına bakın), dün söylemeliyim ki, Clasp serbest bırakıldı (bağlı değilim):

Clasp , LLVM IR'yi derleyen uygun bir Common Lisp uygulaması olmayı amaçlamaktadır. Dahası, Clang kütüphanelerini (AST, Matcher) geliştiriciye maruz bırakır.

  • İlk olarak, CL'de yazabileceğiniz ve artık kütüphanelerini kullanma dışında (ve makrolara ihtiyacınız varsa, CL makroları kullanın) C ++ kullanamayacağınız anlamına gelir.

  • İkinci olarak, mevcut C ++ kodunuz (analiz, yeniden düzenleme, ...) için CL'de araçlar yazabilirsiniz.


3

Bazı C ++ derleyicilerinin az çok belgelenmiş ve kararlı API'ları, özellikle çoğu özgür yazılım derleyicileri vardır.

Clang / LLVM çoğunlukla büyük bir kütüphane kümesidir ve bunları kullanabilirsiniz.

Son GCC eklentileri kabul ediyor . Özellikle, MELT (kendisi bir meta-eklenti olan ve GCC'yi genişletmek için daha üst düzey bir alana özgü dil sağlayan) kullanarak genişletebilirsiniz.

C ++ sözdiziminin GCC'de (ve muhtemelen Clang'da) kolayca genişletilemeyeceğine dikkat edin , ancak istediğiniz şeyleri yapmak için kendi pragmalarınızı, yerleşiklerinizi, niteliklerinizi ve derleyicinizi de ekleyebilirsiniz (belki de bu şeyleri çağıran bazı önişlemci makroları da sağlayabilirsiniz) kullanıcı dostu bir sözdizimi vermek için).

Sen mesela bkz çok aşamalı dilleri ve derleyicilerin ilgilenen olabilir FBDTÖ, Gensym ve Yansıma kullanarak Uygulama Çok aşamalı Diller C.Calcagno ark kağıdı. ve MetaOcaml etrafında çalışın . Kesinlikle Common Lisp'in makro tesislerine bakmalısınız . Ve tarafından ilgi olabilir JIT kütüphaneleri gibi libjit , GNU yıldırım , hatta LLVM veya sadece -en çalışma zamanı -!, Bazı C ++ kodu oluşturmak paylaşılan nesne dinamik kütüphaneye bunun bir derleme çatal, daha sonra dlopen (3) paylaşılan nesne. J.Pitrat'ın blogu da bu tür yansıtıcı yaklaşımlarla ilgilidir. Ve ayrıca RefPerSys .


İlginç. GCC'nin burada gelişmeye devam ettiğini görmek çok güzel. Bu, sorduğum her şeye hitap eden bir cevap değil, ama yine de hoşuma gidiyor.
Steven Lu

Re: yeni düzenlemeleriniz ... Bu, kod yeniden yazmanın kendisi için iyi bir nokta. Bu aslında böyle bir meta program kabiliyetini C ++ 'a da getirmeye başlıyor, eskisinden çok daha erişilebilir, bu da oldukça ilginç.
Steven Lu
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.