C ++: İkili Düzeyde Standardizasyon Eksikliği


14

ISO / ANSI neden C ++ 'ı ikili düzeyde standartlaştırmadı? C ++ ile ilgili birçok taşınabilirlik sorunu vardır, bunun nedeni sadece ikili düzeydeki standardizasyonunun olmamasıdır.

Don Box yazıyor ( Essential COM kitabından alıntı, COM As A Better C ++ bölümünden )

C ++ ve Taşınabilirlik


C ++ sınıfını DLL olarak dağıtmaya karar verildikten sonra, biri C ++ 'ın temel zayıflıklarından biri , yani ikili düzeyde standardizasyon eksikliği ile karşı karşıya kalır . ISO / ANSI C ++ Taslak Çalışma Belgesi, hangi programların derleneceğini ve çalıştırılmasının anlamsal etkilerinin ne olacağını kodlamaya çalışsa da, C ++ ikili çalışma zamanı modelini standartlaştırmaya çalışmaz. İlk kez bir istemci FastString DLL'nin ithalat kitaplığı FastString DLL oluşturmak için kullanılan dışında bir C ++ geliştirme ortamından bağlantı çalıştığında bu sorun belirgin hale gelecektir .

Bu ikili standardizasyon eksikliğinden daha fazla fayda veya kayıp var mı?


Bu daha sübjektif bir soru daha nasıl olduğunu görmek , programmers.stackexchange.com daha iyi sorulur mu?
Stephen Furlani

1
İlgili sorularım aslında: stackoverflow.com/questions/2083060/…
AraK

4
Don Box bir zealot. Onu görmezden gel.
John Dibling

8
C, ikili düzeyde ANSI / ISO tarafından standartlaştırılmamıştır; OTOH C de jure yerine de facto standart ABI'ye sahiptir . Farklı üreticilerin uygulamaları ile farklı hedefleri olduğu için C ++ 'nın böyle standart bir ABI'si yoktur. Örneğin, Windows SEH'nin üstünde VC ++ sırtlamada özel durumlar. POSIX'in SEH'si yoktur ve bu nedenle bu modeli almak mantıklı olmazdı (Yani G ++ ve MinGW bu modeli kullanmıyor).
Billy ONeal

3
Bunu bir zayıflık değil bir özellik olarak görüyorum. Bir uygulamayı belirli bir ABI'ye bağlarsanız, hiçbir zaman yeniliğe sahip olmayacağız ve yeni donanım dilin tasarımına bağlı olacaktır (ve her yeni sürüm arasında donanım endüstrisinde uzun süredir 15 yıl olduğu için) ve boğarak kod daha verimli yürütmek için yeni fikirler yenilik yapılmayacaktır. Fiyat, bir çalıştırılabilir tüm kod aynı derleyici / sürüm (bir sorun ama büyük bir değil) tarafından inşa edilmesi gerektiğidir.

Yanıtlar:


16

İkili uyumlu derlenmiş biçime sahip diller nispeten yeni bir aşamadır [*], örneğin JVM ve .NET çalışma zamanları. C ve C ++ derleyicileri genellikle yerel kod yayar.

Avantajı, bir JIT'e, bir bayt kod yorumlayıcısına, bir VM'ye veya başka herhangi bir şeye gerek olmamasıdır. Örneğin, makine başlangıçta Java bayt kodunu yürütebiliyorsa veya Java'dan ikili olmayan uyumlu bir yerel dosyaya bir tür dönüştürücünüz yoksa, makine başlangıcında çalışan önyükleme kodunu güzel, taşınabilir Java bayt kodu olarak yazamazsınız. yürütülebilir kod (teoride: bunun bootstrap kodu için pratikte önerilebileceğinden emin değilim). Sihirli donanım adresleriyle çok fazla uğraşacağından, kaynak düzeyinde bile taşınabilir C ++ olmasa da, az ya da çok C ++ ile yazabilirsiniz .

Dezavantajı elbette yerli kod sadece onun için derlendi mimarisine hiç çalışır ve yürütülebilir sadece kendi çalıştırılabilir biçimini anlayan bir yükleyici tarafından yüklenebilir ve aynı mimari için diğer yürütülebilir içine yalnızca bağlantı ve çağrı olduğunu ve ABI.

Bu kadar ilerlemiş olsanız bile, iki yürütülebilir dosyayı birbirine bağlamak, yalnızca şu kadar doğru bir şekilde çalışacaktır: (a) Farklı tanımlayıcılar / seçenekler / herhangi bir şekilde derlenmişlerse yapılması kolay olan Tek Tanımlama Kuralını ihlal etmiyorsanız, öyle ki, aynı sınıfın farklı tanımlarını kullanıyorlardı (bir başlıkta ya da her biri farklı uygulamalara statik olarak bağlı oldukları için); ve (b) yapı düzeni gibi ilgili tüm uygulama ayrıntıları, her biri derlendiğinde yürürlükteki derleyici seçeneklerine göre aynıdır.

Tüm bunları tanımlamak için C ++ standardı, uygulayıcılar için mevcut olan özgürlüklerin çoğunu ortadan kaldıracaktır. Uygulayıcılar , özellikle C ++ 'da (ve aynı soruna sahip C) çok düşük seviyeli kod yazarken bu özgürlükleri kullanıyorlar .

B + C gibi görünen bir şey yazmak istiyorsanız, ikili taşınabilir bir hedef için, .NET ve Mono'yu hedefleyen C ++ / CLI vardır, böylece .NET'i Windows'tan başka bir yerde çalıştırabilirsiniz. MS'in derleyicisini Mono üzerinde çalışacak saf CIL montajları üretmeye ikna etmenin mümkün olduğunu düşünüyorum.

Ayrıca, ikili taşınabilir C veya C ++ ortamı oluşturmak için örneğin LLVM ile yapılabilecek potansiyel şeyler de vardır. Yine de yaygın bir örnek ortaya çıktığını bilmiyorum.

Ancak bunların hepsi, C ++ 'ın uygulamaya bağlı hale getirdiği (türlerin boyutları gibi) birçok şeyi düzeltmeye dayanır. Daha sonra, taşınabilir ikili dosyaları anlayan ortam, kodun çalıştırılacağı sistemde bulunmalıdır. Taşınabilir olmayan ikili dosyalara izin vererek, C ve C ++ taşınabilir ikili dosyaların bulunamayacağı yerlere gidebilir ve bu nedenle standart ikili dosyalar hakkında hiçbir şey söylemez.

Sonra herhangi bir platformda, uygulamaları genellikle hala standart onları durdurmanın olmamasına rağmen, seçeneklerin farklı set arasında ikili uyumluluk sağlamaz. Don Box, derleyicinin seçeneklerine göre Microsoft'un derleyicilerinin aynı kaynaktan uyumsuz ikili dosyalar üretmesini sevmiyorsa, şikayet etmesi gereken derleyici ekibi. C ++ dili, bir derleyicinin veya işletim sisteminin gerekli tüm ayrıntıları sabitlemesini yasaklamaz , bu nedenle kendinizi Windows ile sınırladığınızda, C ++ ile temel bir sorun değildir. Microsoft bunu yapmamayı seçti .

Farklılıklar genellikle yanlış ve programınız çökebilir bir şey daha tezahür, ancak, örneğin, bir dll sürümüne uyumsuz hata ayıklama vs arasında uyumsuzluk önemli kazançlar olabilir.

[*] Fikrin ilk ne zaman icat edildiğinden emin değilim, muhtemelen 1642 falan, ama mevcut popülerlikleri, C ++ 'ın ikili taşınabilirliği tanımlamayı engelleyen tasarım kararlarına bağlı olduğu zamana kıyasla nispeten yeni.


@Steve But C'nin i386 ve AMD64 üzerinde iyi tanımlanmış bir ABI'si var, bu yüzden GCC sürüm X tarafından derlenen bir işleve MSVC sürüm Y tarafından derlenen bir işleve bir işaretçi iletebilirim.
user877329

7

Platformlar arası ve derleyiciler arası uyumluluk C ve C ++ 'ın arkasındaki temel hedefler değildi. Bir çağda doğdular ve platforma özgü ve derleyiciye özgü zaman ve mekan minimizasyonlarının çok önemli olduğu amaçlar için tasarlandılar.

Stroustrup'un "C ++ Tasarımı ve Evrimi" nden:

"Açık hedef, çalışma zamanı, kod kompaktlığı ve veri kompaktlığı açısından C'yi eşleştirmekti. ... İdeal - elde edilen - C sınıfı için C'nin kullanılabileceği her şey için kullanılabilmesiydi."


1
+1 - tam olarak. Hem ARM hem de Intel kutularında çalışan standart bir ABI nasıl oluşturulur? Mantıklı olmaz!
Billy ONeal

1
ne yazık ki, bu başarısız oldu. Çalışma zamanında dinamik olarak bir C ++ modülü yüklemek dışında C'nin yaptığı her şeyi yapabilirsiniz. açıktaki arayüzde C işlevlerini kullanmaya 'geri dönmeniz' gerekir.
gbjbaanb

6

Bu bir hata değil, bir özelliktir! Bu, uygulayıcılara uygulamalarını ikili düzeyde optimize etme özgürlüğü verir. Küçük endian i386 ve yavruları var olan veya var olan tek CPU'lar değildir.


6

Alıntıda açıklanan sorun, sembol adı yönetim şemalarının standardizasyonundan oldukça kasıtlı olarak kaçınılmasından kaynaklanmaktadır (bence " ikili düzeyde standardizasyon " bu konuda yanıltıcı bir ifadedir, ancak sorun bir derleyicinin Uygulama İkili Arayüzü ile ilgilidir ( ABI).

C ++, bir işlevin veya veri nesnesinin imza ve tür bilgisini kodlar ve sembol adına sınıf / ad alanı üyeliği yapar ve farklı derleyicilerin farklı şemalar kullanmasına izin verilir. Sonuç olarak, statik bir kitaplık, DLL veya nesne dosyasındaki bir sembol farklı bir derleyici (veya muhtemelen aynı derleyicinin farklı bir sürümü) kullanılarak derlenen kodla bağlantı oluşturmaz.

Sorun, farklı derleyiciler tarafından kullanılan şema örnekleri ile muhtemelen burada yapabileceğimden daha iyi açıklanmış ve açıklanmıştır .

Kasıtlı standardizasyon eksikliğinin nedenleri de burada açıklanmaktadır .


3

ISO / ANSI'nin amacı, yıllarca dil standartlarının ve derleyici desteğinin güncellenmesini gerektirecek kadar karmaşık görünen C ++ dilini standartlaştırmaktı.

İkili uyumluluk, ikili işlemcilerin farklı CPU mimarileri ve farklı işletim sistemi ortamlarında çalışması gerektiğinden, çok daha karmaşıktır.


Doğru, ancak alıntıda açıklanan sorun aslında "ikili seviye uyumluluğu" (yazarın bu terimi kullanmasına rağmen) ile ilgili hiçbir şey değildir. Aslında uyumsuz isim yönetim şemaları konusunu anlatıyor.

@Clifford: ad yönetim şeması, ikili düzey uyumluluğunun sadece bir alt kümesidir. ikincisi daha çok bir şemsiye terimi gibidir!
Nawaz

Bir windows makinesinde bir Linux ikili çalıştırmaya çalışırken bir sorun olduğundan şüpheliyim. Platform başına bir ABI varsa işler çok daha iyi olurdu, çünkü en azından bir komut dosyası dili aynı platformda bir ikili dosyayı dinamik olarak yükleyebilir ve çalıştırabilir veya uygulamalar farklı bir derleyici ile oluşturulan bileşenleri kullanabilir. Bugün linux üzerinde bir C dll kullanamazsınız ve kimse şikayet etmez, ancak C dll hala fayda tahakkuk eden bir python uygulaması tarafından yüklenebilir.
gbjbaanb

2

Andy'nin söylediği gibi, platformlar arası uyumluluk büyük bir hedef değildi, geniş platform ve donanım uygulaması ise bir hedefti, net sonuç ile çok çeşitli sistemler için uygun uygulamalar yazabilirsiniz. İkili standardizasyon bunu pratik olarak ulaşılmaz hale getirirdi.

C uyumluluğu da önemliydi ve bunu önemli ölçüde karmaşıklaştırırdı.

Daha sonra , bir dizi alt uygulama için ABI'yi standardize etmek için bazı çabalar olmuştur .


Lanet olsun, C uyumluluğunu unuttum. İyi bir nokta, +1!
Andy Thomas

1

Bence C ++ için bir standart eksikliği bugünün bağlanmamış, modüler programlama dünyasında bir sorun. Ancak, böyle bir standarttan ne istediğimizi tanımlamamız gerekiyor.

Aklı başında hiç kimse bir ikili program için uygulamayı veya platformu tanımlamak istemez. Yani bir x86 Windows dll alıp bir x86_64 Linux platformunda kullanmaya başlayamazsınız. Bu biraz fazla olurdu.

Bununla birlikte, insanların istediği şey C modülleriyle aynı şeydir - ikili düzeyde standart bir arayüz (yani bir kez derlendi). Şu anda, modüler bir uygulamada bir dll yüklemek istiyorsanız, C işlevlerini dışa aktarır ve çalışma zamanında onlara bağlarsınız. Bunu bir C ++ modülü ile yapamazsınız. Eğer bir derleyici ile yazılmış dlls farklı bir tarafından yüklenebileceği anlamına da gelebilir, harika olurdu. Elbette, hala uyumsuz bir platform için oluşturulmuş bir dll'yi yükleyemezdiniz, ancak bu düzeltilmesi gereken bir sorun değil.

Standartlar gövdesi bir modülün hangi arabirimi açığa çıkardığını tanımlasaydı, C ++ modüllerini yüklemede çok daha fazla esnekliğe sahip olsaydık, C ++ kodunu C kodu olarak göstermemize gerek kalmazdı ve muhtemelen çok daha fazla faydalanırdık komut dosyalarında C ++.

Bu soruna bir çözüm sağlama girişiminde bulunan COM gibi şeylere de maruz kalmak zorunda kalmayacağız.


1
+1. Evet katılıyorum. Buradaki diğer cevaplar temelde ikili standardizasyonun mimariye özgü optimizasyonları yasaklayacağını söyleyerek sorunu ortadan kaldırıyor. Ama mesele bu değil. Kimse bazı platformlar arası ikili yürütülebilir formatı tartışmıyor. Sorun, C ++ modüllerini dinamik olarak yüklemek için standart bir arabirim olmamasıdır.
Charles Salvia

1

C ++ ile ilgili birçok taşınabilirlik sorunu vardır, bunun nedeni sadece ikili düzeydeki standardizasyonunun olmamasıdır.

Bence bu kadar basit değil. Verilen cevaplar, standardizasyona odaklanmama konusunda mükemmel bir gerekçe oluşturuyor, ancak C ++, bir ABI standardı olarak C ile gerçekten rekabet etmek için çok uygun bir dil için çok zengin olabilir .

İşlev aşırı yüklemesi, değişken uyumsuzluklar, modül sınırlarını aşan istisnalarla uyumsuzluklardan vb. Kaynaklanan manglinge gidebiliriz. Bunların hepsi gerçek bir acıdır ve keşke en azından vtable düzenlerini standart hale getirebilseler.

Ancak bir ABI standardı sadece bir derleyicide üretilen C ++ boyacılarının farklı bir derleyici tarafından oluşturulan başka bir ikili dosya tarafından kullanılabilmesi anlamına gelmez. ABI çapraz diller kullanılır . En azından ilk kısmı kapsayabilirlerse iyi olur, ancak C ++ 'ın en yaygın uyumlu dylibs yapmak için çok önemli olan evrensel ABI seviyesinde C ile gerçekten rekabet ettiğini görmem mümkün değil.

Bu şekilde dışa aktarılan basit bir çift işlev düşünün:

void f(Foo foo);
void f(Bar bar, int val);

... ve parametreleştirilmiş kurucular, kopya kurucular, hareket kurucular ve önemsiz yıkıcılar içeren sınıflar hayal edin Foove Barbunlardı.

Ardından bir Python / Lua / C # / Java / Haskell / etc senaryosunu alın. geliştirici bu modülü içe aktarmaya ve kendi dilinde kullanmaya çalışıyor.

Öncelikle, aşırı işlev yükünü kullanarak sembollerin nasıl dışa aktarılacağına ilişkin bir ad yönetim standardına ihtiyacımız var. Bu daha kolay bir bölüm. Yine de gerçekten "mangling" adı olmamalı. Dylib kullanıcılarının sembolleri ada göre aramaları gerektiğinden, buradaki aşırı yüklemeler tam bir karmaşaya benzemeyen isimlere yol açmalıdır. Belki sembol isimleri "f_Foo" "f_Bar_int"ya da buna benzer bir şey olabilir . Aslında geliştirici tarafından tanımlanmış bir adla çatışmayacaklarından emin olmalıyız, belki de ABI kullanımı için bazı sembolleri / karakterleri / kuralları saklıyorlar.

Ama şimdi daha zorlu bir senaryo. Örneğin Python geliştiricisi, hareket kurucuları, kopya kurucuları ve yıkıcıları nasıl çağırır? Belki bunları dylib'in bir parçası olarak ihraç edebiliriz. Peki ya farklı modüllerde dışa aktarılırsa Foove Bardışa aktarılırsa? Bu dylib ile ilişkili sembolleri ve uygulamaları kopyalamalı mıyız? Yapmamızı öneririm, çünkü burada sadece bir nesne oluşturmak, buraya geçmek, oradan kopyalamak, yok etmek için birden fazla dylib arayüzüne karışmak zorunda kalmak gerçekten çok can sıkıcı olabilir. Aynı temel endişe C'de biraz daha geçerli olsa da (sadece daha manuel / açık bir şekilde), C bundan sadece insanların kendisiyle programlanma şekline bağlı olarak bundan kaçınma eğilimindedir.

Bu sadece garipliğin küçük bir örneğidir. fYukarıdaki işlevlerden biri BazExceptionJavaScript'e (ayrıca yapıcılar ve yıkıcılar ve std :: exception türetilmiş bir C ++ sınıfı) JavaScript'e attığında ne olur ?

En iyi ihtimalle sadece bir C ++ derleyicisi tarafından üretilen bir ikili dosyadan diğeri tarafından üretilen başka bir ikili dosyaya çalışan bir ABI'yi standartlaştırmayı umabileceğimizi düşünüyorum. Tabii ki bu harika olurdu, ama sadece bunu belirtmek istedim. Tipik olarak, çapraz derleyiciler çalıştıran genelleştirilmiş bir kütüphaneyi dağıtmak için bu tür endişelere eşlik etmek, onu genellikle genelleştirilmiş ve uyumlu çapraz diller haline getirme arzusudur.

Önerilen çözüm

COM tarzı arabirimlerle yıllarca API'ler / ABI'ler için C ++ arabirimlerini kullanmanın yollarını bulmaya çalıştıktan sonra önerdiğim çözüm, yalnızca bir "C / C ++" (pun) geliştiricisi olmaktır.

Uygulama için C ++ ile bu evrensel ABI'leri oluşturmak için C'yi kullanın. Yine de, yığın üzerinde bu tür nesneleri oluşturmak ve yok etmek için açık işlevlerle opak C ++ sınıflarına işaretçiler döndüren dışa aktarma işlevleri gibi şeyler yapabiliriz. Uygulama için tamamen C ++ kullanıyor olsak bile, ABI perspektifinden bu C estetiğine aşık olmaya çalışın. Soyut arayüzler fonksiyon göstergeleri tabloları kullanılarak modellenebilir. Bu şeyleri bir C API'sine sarmak sıkıcıdır, ancak bununla birlikte gelen dağıtımın faydaları ve uyumluluğu onu çok değerli hale getirecektir.

O zaman bu arayüzü bu kadar doğrudan kullanmaktan hoşlanmıyorsak (muhtemelen en azından RAII nedenleriyle olmamalıyız), SDK ile birlikte gönderdiğimiz statik olarak bağlı bir C ++ kütüphanesine istediğimiz her şeyi sarabiliriz. C ++ istemcileri bunu kullanabilir.

Python istemcileri, C veya C ++ arayüzünü doğrudan kullanmak istemeyecektir, çünkü bu pitoniği yapmanın bir yolu yoktur. Bunu kendi pitonik arayüzlerine sarmak isteyecekler, bu yüzden bunu mümkün olduğunca kolaylaştırmak için sadece minimum bir minimum C API / ABI ihraç ediyoruz.

C ++ endüstrisinin birçoğu, inatla COM tarzı arayüzler ve benzeri şeyler göndermeye çalışmak yerine bunu yapmaktan fayda sağlayacağını düşünüyorum. Bu dylibs kullanıcılarının garip ABI'lerle uğraşmak zorunda kalmaması da tüm hayatımızı kolaylaştırır. C bunu basitleştirir ve ABI perspektifinden sadeliği, her türlü FFI için doğal olarak ve minimalizmle çalışan API'ler / ABI'ler oluşturmamızı sağlar.


1
"Uygulama için C ++ ile, bu evrensel ABI oluşturmak için C kullanın." ... ben de aynısını yapıyorum, diğerleri gibi!
Nawaz

-1

Neden ikili düzeyde standartlaşmadığını bilmiyorum. Ama bu konuda ne yaptığımı biliyorum. Windows üzerinde fonksiyon extern "C" BOOL WINAPI beyan ederim. (Tabii ki BOOL işlevin türü ne olursa olsun değiştirin.) Ve bunlar temiz bir şekilde dışa aktarılır.


2
Ancak, beyan ederseniz extern "C", herhangi bir komite tarafından dayatılmasa da, ortak PC donanımında fiili bir standart olan C ABI'yi kullanacaktır .
Billy ONeal

-3

unzip foo.zip && make foo.exe && foo.exeKaynağınızın taşınabilirliğini istiyorsanız kullanın .

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.