#Pragma bir kez güvenli bir güvenlik görevlisi mi?


310

Daha #pragma oncehızlı derleme ile sonuçlanabilecek bazı derleyici optimizasyonu olduğunu okudum . Bunun standart olmadığını ve bu nedenle platformlar arası bir uyumluluk sorunu oluşturabileceğini biliyorum.

Windows olmayan platformlarda (gcc) çoğu modern derleyici tarafından desteklenen bir şey mi?

Platform derleme sorunlarından kaçınmak istiyorum, ancak yedek korumaların ekstra çalışmasından da kaçınmak istiyorum:

#pragma once
#ifndef HEADER_H
#define HEADER_H

...

#endif // HEADER_H

Endişelenmeli miyim? Bunun için başka zihinsel enerji harcamalı mıyım?


3
Bir soran sonra benzer bir soru , ben öğrendim #pragma oncegörünür ben dahil korumaları kurtulmak ve birlikte hepsini yerine alma sürecinde kulüpler VS 2008 bazı sınıf görünümü sorunları önlemek için #pragma oncebu nedenle.
SmacL

Yanıtlar:


188

Kullanımı #pragma onceherhangi bir modern derleyici üzerinde çalışmalıdır, ancak standart bir #ifndefkoruma kullanmak için herhangi bir neden görmüyorum . Sadece iyi çalışıyor. Bir uyarı, GCC'nin 3.4 sürümünden#pragma once önce desteklememesidir .

Ayrıca, en azından GCC'de, standardın #ifndefkorumayı tanıdığını ve optimize ettiğini , bu yüzden daha yavaş olmaması gerektiğini buldum #pragma once.


12
Hiç daha yavaş olmamalı (yine de GCC ile).
Jason Coco

54
Bu şekilde uygulanmadı. Bunun yerine, dosya ilk kez bir #ifndef ile başlar ve bir #endif ile biterse, gcc dosyayı hatırlar ve dosyayı açmak için bile uğraşmadan gelecekte dahil olan her zaman atlar.
Jason Coco

10
#pragma oncedosya daha önceden işlenmediği için genellikle daha hızlıdır. ifndef/define/endifzaten önişleme gerektirir, çünkü bu bloktan sonra derlenebilir bir şey olabilir (teorik olarak)
Andrey

13
Muhafız makro optimizasyonu ile ilgili GCC belgeleri: gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html
Adrian

38
#ifndef FOO_BAR_HDahil etme korumalarını kullanmak için normalde "foo_bar.h" gibi bir dosya için yeni bir sembol tanımlamanız gerekir . Daha sonra bu dosyayı yeniden adlandırırsanız, içerme korumalarını bu kuralla tutarlı olacak şekilde ayarlamanız gerekir mi? Ayrıca, iki farklı foo_bar.h varsa, kod ağacınızdaki iki farklı yerde, her biri için iki farklı sembol düşünmelisiniz. Kısa cevap kullanmaktır #pragma onceve gerçekten desteklemeyen bir ortamda derlemeniz gerekiyorsa, devam edin ve o ortam için koruma ekleyin.
Brandin

329

#pragma once bir dezavantajı var (standart dışı olmak dışında) ve farklı konumlarda aynı dosyaya sahipseniz (derleme sistemimiz dosyaları kopyaladığı için buna sahibiz), derleyici bunların farklı dosyalar olduğunu düşünecektir.


36
Ama aynı zamanda HEADERFILENAME_H şeklinde iften edilir farklı #define İSİM, yaratma sıkıntısı olmayan farklı yerlerde aynı isimde iki dosya olabilir
Vargas

69
Aynı #define WHATEVER ile eğlencenin bitmesine neden olmayan iki veya daha fazla dosyaya sahip olabilirsiniz, bu yüzden pragma'yı bir kez kullanmayı tercih ederim.
Chris Huang-Leaver

107
İkna edici değil ... Derleme sistemini, dosyaları kopyalamayan ancak bunun yerine sembolik bağlantılar kullanan bir sistemle değiştirin veya aynı dosyayı her çeviri biriminde yalnızca bir konumdan ekleyin. Altyapınız yeniden yapılandırılması gereken bir karmaşa gibi görünüyor.
Yakov Galka

3
Farklı dizinlerde aynı ada sahip farklı dosyalarınız varsa, #ifdef yaklaşımı bunların aynı dosya olduğunu düşünür. Yani biri için bir dezavantaj var ve diğeri için bir dezavantaj var.
rxantos

3
@rxantos, dosyalar farklıysa #ifdefmakro değeri de farklı olabilir.
Motti

63

Keşke #pragma once(ya da onun gibi bir şey) standart olmuştu. Dahil etme muhafızları gerçek bir büyük anlaşma değil (ancak dili öğrenen insanlara açıklamak biraz zor görünüyor), ancak önlenebilecek küçük bir sıkıntı gibi görünüyor.

Aslında, zamanın% 99,98'inde, #pragma oncedavranış istenen davranış olduğundan, bir başlığın birden fazla dahil edilmesini engellemenin, derleyici tarafından #pragmaçift ​​dahil edilmesine izin veren bir veya bir şeyle otomatik olarak işlenmesi güzel olurdu .

Ama elimizde ne varsa var (sahip olmayabileceğiniz dışında #pragma once).


48
Gerçekten istediğim standart bir #importdirektif.
John

10
Standart bir ithalat direktifi geliyor: isocpp.org/blog/2012/11/… Ama henüz burada değil. Kesinlikle destekliyorum.
AHelps

6
@AHelps Vaporware. Neredeyse beş yıl oldu. Belki 2023'te bu yoruma geri dönüp "Sana söyledim" diyeceksin.
doug65536

Bu buharlı yazılım değil, sadece Teknik Şartname aşamasında. Modüller Visual Studio 2015'te ( blogs.msdn.microsoft.com/vcblog/2015/12/03/… ) ve clang'da ( clang.llvm.org/docs/Modules.html ) uygulanır. Ve ithalat, #import değil.
AH

C ++ 20'ye dönüştürmelidir.
Ionoclast Brigham

36

Hiçbir performans avantajı bilmiyorum ama kesinlikle işe yarıyor. Tüm C ++ projelerimde kullanıyorum (MS derleyicisini kullanıyorum). Kullanmaktan daha etkili olduğunu düşünüyorum

#ifndef HEADERNAME_H
#define HEADERNAME_H
...
#endif

Aynı işi yapar ve ön işlemciyi ek makrolarla doldurmaz.

GCC, 3.4 sürümünden itibaren#pragma once resmi olarak destekliyor .


25

GCC 3.4'ten #pragma onceberi destekliyor , daha fazla derleyici desteği için bkz. Http://en.wikipedia.org/wiki/Pragma_once .

#pragma onceMuhafızları dahil etmek yerine kullandığım büyük ters kopya / yapıştırma hatalarından kaçınmaktır.

Kabul edelim: çoğumuz yeni bir başlık dosyasını sıfırdan başlatabiliriz, ancak mevcut bir dosyayı kopyalayıp ihtiyaçlarımıza göre değiştiririz. #pragma onceDahil etme korumaları yerine bir çalışma şablonu oluşturmak çok daha kolaydır . Şablonu ne kadar az değiştirmem gerekirse, hatalarla karşılaşma olasılığı o kadar az olur. Farklı dosyalarda aynı korumaya sahip olmak garip derleyici hatalarına yol açar ve neyin yanlış gittiğini anlamak biraz zaman alır.

TL; DR: #pragma oncekullanımı daha kolaydır.


11

Kullanıyorum ve yeni bir başlık oluşturmak için çok daha az yazmam gerektiğinden memnunum. Benim için üç platformda iyi çalıştı: Windows, Mac ve Linux.

Performans bilgim yok, ancak #pragma ve include guard arasındaki farkın, C ++ dilbilgisini ayrıştırma yavaşlığına kıyasla hiçbir şey olmayacağına inanıyorum. Asıl sorun bu. Farkı görmek için aynı sayıda dosya ve satırı örneğin bir C # derleyicisiyle derlemeye çalışın.

Sonunda, gardiyanı veya pragmayı kullanmak hiç önemli değil.


Ben #pragma bir kez sevmiyorum, ama göreceli faydaları işaret teşekkür ederiz ... C ++ ayrıştırma "normal" bir çalışma ortamında, her şeyden çok daha pahalı. Derleme süreleri bir sorunsa, hiç kimse uzak bir dosya sisteminden derlemez.
Tom

1
Yeniden C ++ yavaşlama ve C # ayrıştırma. C # 'da, her küçük C ++ dosyası için binlerce LOC başlık dosyasını (iostream, kimse?) Ayrıştırmanız gerekmez. Ancak, bu sorunu daha küçük yapmak için önceden derlenmiş başlıkları kullanın
Eli Bendersky

11

' #pragma once' Kullanmanın herhangi bir etkisi olmayabilir (her yerde desteklenmez - giderek daha fazla destekleniyor olsa da), bu nedenle koşullu derleme kodunu yine de kullanmanız gerekir, bu durumda neden ' #pragma once' ile uğraşmalısınız ? Derleyici büyük olasılıkla zaten optimize eder. Yine de hedef platformlarınıza bağlıdır. Tüm hedefleriniz destekliyorsa, devam edin ve kullanın - ama bilinçli bir karar olmalı, çünkü sadece pragmayı kullanırsanız ve daha sonra onu desteklemeyen bir derleyiciye bağlarsanız tüm cehennem gevşeyecektir.


1
Zaten korumaları desteklemeniz gerektiğine katılmıyorum. #Pragma'yı bir kez (veya korumalar) kullanırsanız, bunun nedeni onlarsız bazı çakışmalar doğurmasıdır. Dolayısıyla, zincir aracınız tarafından desteklenmiyorsa, proje derlenmeyecektir ve eski bir K&R derleyicisinde bazı ansi C'yi derlemek istediğinizden tam olarak aynı durumdasınız demektir. Sadece daha güncel bir zincir aleti almanız veya bazı korumalar eklemek için kodu değiştirmeniz yeterlidir. Tüm cehennem kırılma, program derleniyorsa ancak çalışamazsa olurdu.
kriss

5

Performans avantajı, #pragma bir kez okunduktan sonra dosyayı yeniden açmak zorunda kalmamaktır. Muhafızlarla, derleyici dosyayı tekrar içermemesi gereken bilgileri almak için (zaman içinde maliyetli olabilir) dosyayı açmalıdır.

Bu teori sadece bazı derleyiciler, her derleme birimi için herhangi bir okuma kodu olmayan dosyaları otomatik olarak açmayacağı için.

Her neyse, tüm derleyiciler için geçerli değildir, bu yüzden ideal olarak #pragma platformlar arası kod için bir kez kaçınılmalıdır, hiç standart değildir / standart bir tanımı ve etkisi yoktur. Ancak, pratikte, gardiyanlardan gerçekten daha iyi.

Sonunda, bu durumda her derleyicinin davranışını kontrol etmek zorunda kalmadan derleyicinizden en iyi hıza sahip olduğunuzdan emin olmak için daha iyi bir öneri hem pragmayı bir kez hem de korumaları kullanmaktır.

#ifndef NR_TEST_H
#define NR_TEST_H
#pragma once

#include "Thing.h"

namespace MyApp
{
 // ...
}

#endif

Bu şekilde her ikisinden de en iyisini elde edersiniz (platformlar arası ve derleme hızına yardımcı olur).

Yazmak daha uzun olduğu için, kişisel olarak tüm bunları çok kötü bir şekilde oluşturmaya yardımcı olacak bir araç kullanıyorum (Visual Assist X).


Visual Studio #include korumalarını olduğu gibi optimize etmiyor mu? Diğer (daha iyi?) Derleyici bunu yapar ve bunun oldukça kolay olduğunu düşünüyorum.
Tom

1
Neden pragmasonra koydun ifndef? Bir fayda var mı?
user1095108

1
@ user1095108 Bazı derleyiciler, dosyanın yalnızca bir kez başlatılması gereken kod içerip içermediğini öğrenmek için üstbilgi korumalarını sınırlayıcı olarak kullanır. Bazı kodlar başlık korumalarının dışındaysa, tüm dosya birden fazla kez örneklenebilir olarak değerlendirilebilir. Aynı derleyici pragmayı bir kez desteklemiyorsa, bu talimatı görmezden gelir. Bu nedenle, pragmayı bir kez başlık korumalarının içine koymak, en azından başlık korumalarının "optimize edilebilmesini" sağlamak için en genel yoldur.
Klaim

4

Her zaman değil.

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52566 , her ikisinin de dahil edilmesi gereken, ancak aynı zaman damgaları ve içerik (aynı dosya adı değil) nedeniyle yanlışlıkla aynı olduğu düşünülen iki dosya için güzel bir örneğe sahiptir. .


10
Bu derleyicide bir hata olurdu. (almaması gereken bir kısayol almaya çalışmak).
rxantos

4
#pragma oncestandart değildir, bu yüzden bir derleyici ne yaparsa karar verir "doğru" dır. Tabii ki, o zaman "beklenen" ve "yararlı" olanlardan bahsetmeye başlayabiliriz.
user7610

2

Çok büyük ağaçlarda gcc 3.4 ve 4.1 kullanarak (bazen distcc'yi kullanarak ), #pragma'yı bir kez yerine veya standart içerme korumaları ile birlikte kullanırken herhangi bir hız görmedim.

Gerçekten gerçek bir tasarruf olmadığından, gcc'nin eski sürümlerini veya hatta diğer derleyicileri karıştırmanın ne kadar değerli olduğunu görmüyorum. Çeşitli kireç çözücüleri denemedim, ancak bunların çoğunu karıştıracağına bahse girmeye hazırım.

Ben de erken kabul edilmesini dilerdim, ama "Eğer mükemmel çalışırsa neden buna ihtiyacımız var?" C'nin birçok karanlık köşesi ve karmaşıklığı göz önüne alındığında, korumaları dahil etmek en kolay, kendini açıklayan şeylerden biridir. Önişlemcinin nasıl çalıştığına dair küçük bir bilginiz bile varsa, bunlar açıklayıcı olmalıdır.

Bununla birlikte, önemli bir hızlanma gözlemlerseniz, lütfen sorunuzu güncelleyin.


2

Bugün eski okul korumaları bir kez #pragma kadar hızlıdır. Derleyici onlara özel olarak davranmasa bile, #WATEVER ve WHATEVER tanımlı olduğunda göründüğü zaman duracaktır. Dosya açmak bugün çok ucuz. Bir gelişme olsa bile, milisaniye sırasına göre olurdu.

Hiçbir faydası olmadığı için #pragma'yı sadece bir kez kullanmıyorum. Diğer korumaları ile çakışmasını önlemek için şöyle bir şey kullanıyorum: CI_APP_MODULE_FILE_H -> CI = Şirket Baş Harfleri; APP = Uygulama adı; gerisi açıklayıcıdır.


19
Daha az yazmanın faydası değil mi?
Andrey

1
Yine de bazı milisaniyelerin yüz bin kez bazı dakikalar olduğunu unutmayın. Büyük projeler, her biri onlarca başlık içeren on binlerce dosyadan oluşur. Günümüzün çok çekirdekli işlemcileri göz önüne alındığında, giriş / çıkış, özellikle çok sayıda küçük dosya açma , en büyük darboğazlardan biridir.
Damon

1
"Bugün eski okul korumaları bir kez #pragma kadar hızlı içerir." Bugün ve ayrıca yıllar önce. GCC sitesinde en eski belgeler içindir 2,95 2001 den ve korumaları dahil optimize ardından yeni değildi. Son zamanlarda yapılan bir optimizasyon değil.
Jonathan Wakely

4
Başlıca faydası, korumaların hataya eğilimli ve wordy olmasıdır. Farklı dizinlerde aynı adlara sahip iki farklı dosyaya sahip olmak (ve içerme korumaları aynı sembol olabilir) veya kopyalama sırasında korumaları kopyala-yapıştır hataları yapmak çok kolaydır. Pragma bir zamanlar hataya daha az eğilimlidir ve tüm büyük PC platformlarında çalışır. Eğer kullanabilirsiniz, daha iyi bir stil.
AHelps

2

Temel fark, derleyicinin içerme korumasını okumak için başlık dosyasını açması gerektiğidir. Karşılaştırma olarak, pragma bir kez derleyicinin dosyayı takip etmesine ve aynı dosya için başka bir içerme ile karşılaştığında herhangi bir dosya IO yapmamasına neden olur. Bu önemsiz gibi görünse de, özellikle iyi bir başlığı olmayanlar disiplinleri içeren büyük projelerle kolayca ölçeklendirilebilir.

Bununla birlikte, bu gün derleyiciler (GCC dahil) bir kez pragma gibi korumaları içerecek kadar akıllıdır. yani dosyayı açmazlar ve dosya IO cezalarından kaçınırlar.

Pragma desteklemeyen derleyicilerde biraz hantal manuel uygulamalar gördüm.

#ifdef FOO_H
#include "foo.h"
#endif

Ben şahsen çarpışmaları ve potansiyel yazım hataları hatalarını önlemek gibi #pragma bir kez yaklaşım gibi. Ayrıca karşılaştırma ile daha zarif bir kod. Bununla birlikte, taşınabilir kod için, derleyici bundan şikayet etmedikçe her ikisine de sahip olmak acıtmamalıdır.


1
"Dedi ki, bu gün derleyiciler (GCC dahil) bir kez pragma gibi muhafızları tedavi etmek için yeterince akıllı." Bunu onlarca yıldır yapıyorlar, belki #pragma oncede varolduğundan daha uzun !
Jonathan Wakely

Beni yanlış anladığını düşün. Bir kere pragmadan önce tüm derleyiciler, önişlemci aşamasında birden çok kez dahil edilen aynı h dosyası için birden fazla IO penanterine maruz kalacaktı. Modern uygulamalar, önişlemci aşamasında dosyaların daha iyi önbelleğe alınmasını kullanır. Ne olursa olsun, pragmalar olmadan, önişlemci aşaması, içerme muhafızlarının dışındaki her şeyi dahil ederek hala sona erer. Bir kez pragma ile tüm dosya dışarıda bırakılır. Bu açıdan, pragma hala avantajlıdır.
Shammi

1
Hayır, bu yanlış, iyi derleyiciler #pragma olmadan bile tüm dosyayı dışarıda bırakır, dosyayı ikinci kez açmazlar ve ikinci kez bile bakmazlar, bkz. Gcc.gnu.org/onlinedocs/ cpp / Only-Only-Headers.html (bu önbellekleme ile ilgisi yoktur).
Jonathan Wakely

1
Bağlantınızdan, optimizasyon yalnızca cpp'de görünüyor. Ne olursa olsun, önbellekleme devreye girer. Önişlemci, korumaların dışında kod eklemeyi nasıl bilir? Örnek ... extern int foo; #ifndef INC_GUARD #define INC_GUARD sınıfı ClassInHeader {}; #endif Bu durumda önişlemcinin extern int foo içermesi gerekir; Aynı dosyayı birden çok kez eklerseniz. Günün sonunda, #pragma arasındaki farkı anladığımız ve korumaları ve çeşitli derleyicilerin her ikisiyle nasıl davrandığını sürece, bunun üzerinde tartışmak pek bir şey değil :)
Shammi

1
Açıkçası bu optimizasyonu uygulamıyor.
Jonathan Wakely

1

Eğer msvc veya Qt (Qt 4.5'e kadar) kullanırsak, GCC'den (3.4'e kadar), msvc'nin her ikisi de destekliyorsa #pragma once, kullanmamanın hiçbir nedeni göremiyorum #pragma once.

Kaynak dosya adı genellikle aynı eşit sınıf adıdır ve biliyoruz, sınıf adını yeniden adlandırmak için refactor'a ihtiyacımız var , o zaman #include XXXXda değiştirmek zorunda kaldık , bu yüzden manuel bakımın #include xxxxxakıllı bir çalışma olmadığını düşünüyorum . Visual Assist X uzantısı ile bile, "xxxx" korumak gerekli bir çalışma değildir.


1

Her zaman başlık dosyalarının otomatik olarak bir defaya mahsus olarak dahil edilmesinin arzu edildiğini düşünen kişilere ek not: On yıllardan beri başlık dosyalarının çift veya çoklu olarak dahil edilmesiyle kod üreteçleri oluşturuyorum. Özellikle protokol kitaplığı saplamalarının oluşturulması için, son derece portatif ve güçlü bir kod üretecinin ek araç ve dilleri olmadan çok rahat olduğunu düşünüyorum. Bu blogları X-Makrolar gösterdiği gibi bu düzeni kullanan tek geliştirici değilim . Bu, otomatik korumayı kaçırmadan yapmak mümkün olmazdı.


C ++ şablonları sorunu çözebilir mi? Nasıl nadiren C ++ şablonları nedeniyle makrolar için geçerli herhangi bir ihtiyaç bulmak.
Daha net

1
Uzun vadeli profesyonel deneyimimiz, olgun dil, yazılım ve araç altyapısının her zaman kullanılmasının bize hizmet sağlayıcı (Gömülü Sistemler) olarak verimlilik ve esneklikte büyük bir avantaj sağlamasıdır. C ++ tabanlı gömülü sistem yazılımı ve yığınları geliştiren rakipler, geliştiricilerinin bazılarını iş yerinde daha mutlu bulabilirler. Ancak genellikle onları piyasaya sunma süresi, işlevsellik ve esneklik açısından birden fazla kez geride bırakıyoruz. Nether, bir ve aynı aletin tekrar tekrar kullanılmasından elde edilen verimlilik kazanımlarını hafife alır. Web Geliştiricileri bunun yerine birçok Çerçeveye giden yollardan muzdariptir.
Marcel

Yine de bir not: her başlık dosyasında bir kez DRY ilkesinin kendisine karşı koruma / # pragma içermez. X-MacroÖzellikte noktanızı görebiliyorum , ancak dahil etmenin ana kullanımı değil, DRY ile yapışacak olsaydık, başlık unguard / # pragma multi gibi başka bir yol olmamalı mı?
caiohamamura

KURU "KENDİNİZİ tekrarlamayın" anlamına gelir. İnsan hakkında. Makinenin yaptığı şeyin bu paradigma ile ilgisi yoktur. C ++ şablonları çok tekrar eder, C-derleyicileri de bunu yapar (örn. Döngü açma) ve her Bilgisayar neredeyse inanılmaz olan her şeyi sık sık ve hızlı tekrarlar.
Marcel
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.