C ++ makroları ne zaman faydalıdır? [kapalı]


177

C önişlemci haklı korkulan ve C ++ topluluğu tarafından diğerlerinden ayrı tutulur. Satır içi işlevler, sabitler ve şablonlar genellikle a'ya daha güvenli ve üstün bir alternatiftir #define.

Aşağıdaki makro:

#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)  

hiçbir şekilde kasa tipinden daha üstün değildir:

inline bool succeeded(int hr) { return hr >= 0; }

Ama siz makrolar makro bulmak kullanımları listelenmektedir lütfen onların yerine sahip olduğunuzu sen yapamazsın önişlemci olmadan .

Lütfen her kullanım senaryosunu ayrı bir cevaba koyun, böylece oy verilebilir ve eğer preprosesör olmadan cevaplardan birine nasıl ulaşılacağını biliyorsanız, bu cevabın yorumlarında nasıl olduğunu gösterin.


Bir keresinde, oluşturmak için 45 dakika süren, makroları satır içi işlevlerle değiştiren ve derlemeyi 15 dakikadan daha az bir süreye indiren makrolarla dolu bir C ++ uygulaması aldım.
endian


İş parçacığı, makroların faydalı olduğu bağlamlarla ilgilidir, yetersiz olduğu bağlamlarla değil.
underscore_d

Yanıtlar:


123

Ayıklama fonksiyonları için sargılar gibi, otomatik gibi şeyler geçmek __FILE__, __LINE__vb:

#ifdef ( DEBUG )
#define M_DebugLog( msg )  std::cout << __FILE__ << ":" << __LINE__ << ": " << msg
#else
#define M_DebugLog( msg )
#endif

14
Aslında, orijinal snippet: << FILE ":" << gayet iyi, FILE , ön işlemci tarafından tek bir dizeye ":" ile birleştirilecek bir dize sabiti oluşturur.
Frank Szczerba

12
Bunun nedeni sadece ön işlemcisi gerektirir __FILE__ve __LINE__ ayrıca ön işlemci gerektirir. Bunları kodunuzda kullanmak, önişlemci için bir enfeksiyon vektörü gibidir.
TED

93

Yöntemler her zaman eksiksiz, derlenebilir kod olmalıdır; makrolar kod parçaları olabilir. Böylece bir foreach makrosu tanımlayabilirsiniz:

#define foreach(list, index) for(index = 0; index < list.size(); index++)

Ve bu şekilde kullanın:

foreach(cookies, i)
    printf("Cookie: %s", cookies[i]);

C ++ 11'den bu yana, döngü için aralık tabanlı olur .


6
+1 Eğer gülünç derecede karmaşık yineleyici sözdizimi kullanıyorsanız, foreach tarzı bir makro yazmak kodunuzun okunmasını ve bakımını daha kolay hale getirebilir. Yaptım, işe yarıyor.
postfuturist

9
Çoğu yorum, makroların tam kod yerine kod parçaları olabileceği noktasında tamamen önemsizdir. Ama nitpick için teşekkürler.
jdmichal

12
Bu C değil C ++. C ++ yapıyorsanız yineleyiciler ve std :: for_each kullanıyor olmalısınız.
chrish

20
Kabul etmiyorum, chrish. for_eachLambda'dan önce, kötü bir şeydi, çünkü her elemanın çalıştırıldığı kod, çağrı noktasına yerel değildi. foreach, (ve BOOST_FOREACHel rulolu bir çözüm yerine tavsiye ederim ) kodu tekrarlama sitesine yakın tutup daha okunabilir hale getirelim. Bununla birlikte, lambda açıldığında, bir for_eachkez daha gidilecek yol olabilir.
GManNickG

8
Ve BOOST_FOREACH'in kendisinin bir makro (ama çok iyi düşünülmüş bir tane) olduğunu belirtmek gerekir
Tyler McHenry

59

Başlık dosyası korumaları makroları gerektirir.

Makro gerektiren başka alanlar var mı? Çok değil (varsa).

Makrolardan yararlanan başka durumlar var mı? EVET!!!

Makroları kullandığım bir yer çok tekrarlı kod. Örneğin, diğer arabirimlerle (.NET, COM, Python, vb ...) kullanılacak C ++ kodunu kaydırırken, farklı özel durum türlerini yakalamam gerekir. İşte bunu nasıl yaparım:

#define HANDLE_EXCEPTIONS \
catch (::mylib::exception& e) { \
    throw gcnew MyDotNetLib::Exception(e); \
} \
catch (::std::exception& e) { \
    throw gcnew MyDotNetLib::Exception(e, __LINE__, __FILE__); \
} \
catch (...) { \
    throw gcnew MyDotNetLib::UnknownException(__LINE__, __FILE__); \
}

Bu yakalamaları her sarmalayıcı işlevine koymam gerekiyor. Her seferinde tam catch bloklarını yazmak yerine, sadece şunu yazarım:

void Foo()
{
    try {
        ::mylib::Foo()
    }
    HANDLE_EXCEPTIONS
}

Bu aynı zamanda bakımı kolaylaştırır. Yeni bir istisna türü eklemem gerekirse, eklemem gereken tek bir yer var.

Başka yararlı örnekler de vardır: birçoğu __FILE__ve __LINE__önişlemci makrolarını içerir.

Her neyse, makrolar doğru kullanıldığında çok kullanışlıdır. Makrolar kötü değildir - kötüye kullanımları kötülüktür.


7
Çoğu derleyici #pragma oncebu gün destek , bu yüzden gardiyanlar gerçekten gerekli şüpheliyim
1800 BİLGİ

13
Onlar sadece çoğu yerine tüm derleyiciler için yazıyorsanız ;-)
Steve Jessop

30
Bu nedenle taşınabilir, standart önişlemci işlevselliği yerine, önişlemciyi kullanmaktan kaçınmak için bir önişlemci uzantısı kullanmanızı önerir misiniz? Bana biraz saçma geliyor.
Logan Capaldo

#pragma oncebirçok yaygın yapı sistemini kırıyor.
Miles Rout

4
Bu makro gerektirmediğini için bir çözüm var: void handleExceptions(){ try { throw } catch (::mylib::exception& e) {....} catch (::std::exception& e) {...} ... }. Ve fonksiyon tarafında:void Foo(){ try {::mylib::Foo() } catch (...) {handleExceptions(); } }
MikeMB

51

Çoğunlukla:

  1. Muhafızları dahil et
  2. Koşullu derleme
  3. Raporlama ( __LINE__ve benzeri önceden tanımlanmış makrolar__FILE__ )
  4. (nadiren) Yinelenen kod modellerini çoğaltma.
  5. Rakibinizin kodunda.

5 sayısını nasıl gerçekleştireceğiniz konusunda yardım mı arıyorsunuz? Bir çözüme doğru beni yönlendirebilir misiniz?
Max

50

Koşullu derleme içinde, derleyiciler arasındaki farkların üstesinden gelmek için:

#ifdef ARE_WE_ON_WIN32
#define close(parm1)            _close (parm1)
#define rmdir(parm1)            _rmdir (parm1)
#define mkdir(parm1, parm2)     _mkdir (parm1)
#define access(parm1, parm2)    _access(parm1, parm2)
#define create(parm1, parm2)    _creat (parm1, parm2)
#define unlink(parm1)           _unlink(parm1)
#endif

12
C ++ 'da, aynı satır içi işlevler kullanılarak elde edilebilir: <code> #ifdef ARE_WE_ON_WIN32 <br> satır içi int close (int i) {return _close (i); } <br> #endif </code>
paercebal

2
Bu, # tanımını kaldırır, ancak #ifdef ve #endif'i kaldırmaz. Her neyse, sana katılıyorum.
Gorpik

19
ASLA küçük harfli makrolar tanımlamayın. İşlevleri değiştirmek için makrolar benim kabusum (teşekkür ederim Microsoft). En iyi örnek ilk satırda. Birçok kütüphanenin closeişlevleri veya yöntemleri vardır. Ardından, bu kitaplığın üstbilgisini ve bu makroyu içeren üstbilgiyi büyük bir sorununuz olduğundan eklediğinizde kitaplık API'sını kullanamazsınız.
Marek R

AndrewStein, @ paercebal'ın önerisi üzerine makroların bu bağlamda kullanılmasının herhangi bir faydası var mı? Değilse, makrolar aslında minnettar görünüyor.
einpoklum

1
#ifdef WE_ARE_ON_WIN32plz :)
Orbit'te Hafiflik Yarışları

38

İfadeden bir dize yapmak istediğinizde, bunun en iyi örneği assert( #xdeğerini xbir dizeye dönüştürür).

#define ASSERT_THROW(condition) \
if (!(condition)) \
     throw std::exception(#condition " is false");

5
Sadece bir nitpick, ama ben şahsen noktalı virgül bırakacaktım.
Michael Myers

10
Aslında katılıyorum {} while (false) (başka bir highjacking önlemek için) koymak istiyorum ama basit tutmak istedim.
Motti

33

Dize değişmezleri ile a'dan daha fazlasını yapabildiğinizden, dize sabitleri bazen makro olarak daha iyi tanımlanır const char *.

Örn. String değişmezleri kolayca birleştirilebilir .

#define BASE_HKEY "Software\\Microsoft\\Internet Explorer\\"
// Now we can concat with other literals
RegOpenKey(HKEY_CURRENT_USER, BASE_HKEY "Settings", &settings);
RegOpenKey(HKEY_CURRENT_USER, BASE_HKEY "TypedURLs", &URLs);

A const char *kullanıldıysa, çalışma zamanında birleştirmeyi gerçekleştirmek için bir çeşit dize sınıfı kullanılmalıdır:

const char* BaseHkey = "Software\\Microsoft\\Internet Explorer\\";
RegOpenKey(HKEY_CURRENT_USER, (string(BaseHkey) + "Settings").c_str(), &settings);
RegOpenKey(HKEY_CURRENT_USER, (string(BaseHkey) + "TypedURLs").c_str(), &URLs);

2
C ++ 11'de, bunun en önemli kısım olduğunu düşünürüm (gardiyanlar hariç). Makrolar gerçekten derleme zamanı dize işleme için sahip olduğumuz en iyi şey. Bu, C ++ 11 ++ 'da alacağımızı umduğum bir özellik
David Stone

1
Bu bana C # makroları isteyen istedi.
Rawling

2
Keşke bunu +42 yapabilsem. Çok önemli, ancak dizgi değişmezlerinin sıkça hatırlanmayan bir yönü.
Daniel Kamil Kozar

24

Bir işlevdeki program akış ( return, breakve continue) kodunu değiştirmek istediğinizde , işlevde aslında satır içi olan koddan farklı davranır.

#define ASSERT_RETURN(condition, ret_val) \
if (!(condition)) { \
    assert(false && #condition); \
    return ret_val; }

// should really be in a do { } while(false) but that's another discussion.

İstisna atmak bana daha iyi bir alternatif gibi geliyor.
einpoklum

Python C (++) uzantıları yazarken, istisnalar bir istisna dizesi ayarlayıp ardından -1veya döndürülerek yayılır NULL. Böylece bir makro, ortak plaka kodunu büyük ölçüde azaltabilir.
black_puppydog

20

Bariz muhafızlar

#ifndef MYHEADER_H
#define MYHEADER_H

...

#endif

17

Normal bir işlev çağrısı kullanarak işlev çağrısı bağımsız değişkenlerini kısa devre gerçekleştiremezsiniz. Örneğin:

#define andm(a, b) (a) && (b)

bool andf(bool a, bool b) { return a && b; }

andm(x, y) // short circuits the operator so if x is false, y would not be evaluated
andf(x, y) // y will always be evaluated

3
Belki daha genel bir nokta: fonksiyonlar argümanlarını bir kez değerlendirir. Makrolar, argümanları daha fazla veya daha az kez değerlendirebilir.
Steve Jessop

@ [Greg Rogers] makro önişlemcinin yaptığı tek şey metindir. Bunu anladıktan sonra, bu konuda daha fazla gizem olmamalıdır.
1800 BİLGİ

İşlevi çağırmadan önce değerlendirmeyi boole yapmaya zorlamak yerine andf'yi değiştirerek eşdeğer davranışı elde edebilirsiniz. Yine de kendiniz için denemeden söylediklerinizin doğru olduğunu anlamazdım. İlginç.
Greg Rogers

Bunu bir şablonla tam olarak nasıl yapabilirsin?
1800 BİLGİ

6
Kısa devre işlemlerini bir işlev stili makrosunun arkasına saklamak, üretim kodunda gerçekten görmek istemediğim şeylerden biridir.
MikeMB

17

Diyelim ki başlık muhafızları gibi belirgin şeyleri görmezden geleceğiz.

Bazen, ön derleyici tarafından kopyalanması / yapıştırılması gereken kodlar oluşturmak istersiniz:

#define RAISE_ERROR_STL(p_strMessage)                                          \
do                                                                             \
{                                                                              \
   try                                                                         \
   {                                                                           \
      std::tstringstream strBuffer ;                                           \
      strBuffer << p_strMessage ;                                              \
      strMessage = strBuffer.str() ;                                           \
      raiseSomeAlert(__FILE__, __FUNCSIG__, __LINE__, strBuffer.str().c_str()) \
   }                                                                           \
   catch(...){}                                                                \
   {                                                                           \
   }                                                                           \
}                                                                              \
while(false)

bunu kodlamanızı sağlar:

RAISE_ERROR_STL("Hello... The following values " << i << " and " << j << " are wrong") ;

Ve aşağıdaki gibi mesajlar üretebilir:

Error Raised:
====================================
File : MyFile.cpp, line 225
Function : MyFunction(int, double)
Message : "Hello... The following values 23 and 12 are wrong"

Şablonları makrolarla karıştırmanın daha iyi sonuçlara yol açabileceğini unutmayın (örn. Değerleri değişken adlarıyla otomatik olarak yan yana oluşturma)

Diğer durumlarda, örneğin hata ayıklama bilgileri oluşturmak için bazı kodların __FILE__ ve / veya __LINE__ öğelerine ihtiyacınız vardır. Visual C ++ için bir klasik aşağıdadır:

#define WRNG_PRIVATE_STR2(z) #z
#define WRNG_PRIVATE_STR1(x) WRNG_PRIVATE_STR2(x)
#define WRNG __FILE__ "("WRNG_PRIVATE_STR1(__LINE__)") : ------------ : "

Aşağıdaki kodda olduğu gibi:

#pragma message(WRNG "Hello World")

aşağıdaki gibi mesajlar üretir:

C:\my_project\my_cpp_file.cpp (225) : ------------ Hello World

Diğer zamanlarda, bir özellik için alıcılar ve ayarlayıcılar oluşturma gibi # ve ## birleştirme işleçlerini kullanarak kod oluşturmanız gerekir (bu, oldukça sınırlı durumlar içindir).

Diğer zamanlarda, aşağıdaki gibi bir işlevle kullanılırsa derlenmeyecek bir kod üreteceksiniz:

#define MY_TRY      try{
#define MY_CATCH    } catch(...) {
#define MY_END_TRY  }

Hangi olarak kullanılabilir

MY_TRY
   doSomethingDangerous() ;
MY_CATCH
   tryToRecoverEvenWithoutMeaningfullInfo() ;
   damnThoseMacros() ;
MY_END_TRY

(yine de, bu tür kodların sadece bir kez doğru kullanıldığını gördüm )

Son, ama en az değil, ünlü boost::foreach!!!

#include <string>
#include <iostream>
#include <boost/foreach.hpp>

int main()
{
    std::string hello( "Hello, world!" );

    BOOST_FOREACH( char ch, hello )
    {
        std::cout << ch;
    }

    return 0;
}

(Not: kod kopyalama / destek ana sayfasından yapıştırıldı)

Hangisi (IMHO) daha iyi std::for_each.

Bu nedenle, makrolar her zaman yararlıdır çünkü normal derleyici kurallarının dışındadırlar. Ama çoğu zaman bir tane gördüğümde, etkili bir şekilde uygun C ++'a çevrilmemiş C kodunun kalıntıları olduğunu görüyorum.


1
CPP'yi yalnızca derleyicinin yapamayacağı şeyler için kullanın. Örneğin, RAISE_ERROR_STL, CPP'yi yalnızca dosya, satır ve işlev imzasını belirlemek için kullanmalı ve geri kalanları yapan bir işleve (muhtemelen satır içi) iletmelidir.
Rainer Blome

Lütfen cevabınızı C ++ 11'i yansıtacak ve @ RainerBlome'un yorumunu yansıtacak şekilde güncelleyin.
einpoklum

@RainerBlome: Kabul ediyoruz. RAISE_ERROR_STL makrosu C ++ 11 öncesi olduğundan, bu bağlamda tamamen haklıdır. Anlayışım (ancak bu belirli özelliklerle uğraşma fırsatım olmadı), sorunu daha zarif bir şekilde çözmek için Modern C ++ 'da varyasyon şablonları (veya makrolar?) Kullanabilmenizdir.
paercebal

@einpoklum: "Lütfen cevabınızı C ++ 11'i yansıtacak ve RainerBlome'un yorumuna cevap verecek şekilde güncelleyin" Hayır. :-). . . En iyi ihtimalle, Modern C ++ için bir bölüm ekleyeceğim, alternatif uygulamalar makro ihtiyacını azaltıyor veya ortadan kaldırıyor, ancak nokta duruyor: Makrolar çirkin ve kötüdür, ancak derleyicinin anlamadığı bir şey yapmanız gerektiğinde , makrolar aracılığıyla yaparsınız.
paercebal

C ++ 11 ile bile, bir işlevin yapılması için makronuzun yaptıklarının çoğu bırakılabilir: #include <sstream> #include <iostream> using namespace std; void trace(char const * file, int line, ostream & o) { cerr<<file<<":"<<line<<": "<< static_cast<ostringstream & >(o).str().c_str()<<endl; } struct Oss { ostringstream s; ostringstream & lval() { return s; } }; #define TRACE(ostreamstuff) trace(__FILE__, __LINE__, Oss().lval()<<ostreamstuff) int main() { TRACE("Hello " << 123); return 0; }Bu şekilde, makro çok daha kısadır.
Rainer Blome

16

UnitTest ++ gibi C ++ için birim test çerçeveleri, önişlemci makroları etrafında döner. Birkaç birim test kodu, elle yazılması hiç de eğlenceli olmayacak bir sınıflar hiyerarşisine genişler. UnitTest ++ ve önişlemci büyüsü gibi bir şey olmadan, C ++ için birim testlerini nasıl verimli bir şekilde yazacağınızı bilmiyorum.


Unittestler bir çerçeve olmadan yazmak mükemmel bir şekilde mümkündür. Sonunda, sadece gerçekten ne tür bir çıktı istediğinize bağlıdır. Umursamıyorsanız, başarı veya başarısızlığı gösteren basit bir çıkış değeri mükemmel olmalıdır.
Daha

15

C ön işlemcisinden korkmak akkor ampullerden korkmak gibidir, çünkü sadece floresan ampuller alırız. Evet, birincisi {elektrik | programcı zamanı} verimsiz. Evet, onları (tam anlamıyla) yakabilirsiniz. Ama düzgün bir şekilde idare ederseniz işi halledebilirler.

Gömülü sistemleri programladığınızda, form birleştirici arasındaki tek seçenek C'yi kullanır. C ++ ile masaüstünde programlama yaptıktan ve sonra daha küçük, gömülü hedeflere geçtikten sonra, pek çok çıplak C özelliğinin (makrolar dahil) “yetersizlikleri” hakkında endişelenmeyi bırakmayı ve bunlardan alabileceğiniz en iyi ve güvenli kullanımı anlamaya çalışmayı öğrenirsiniz. özellikleri.

Alexander Stepanov diyor :

C ++ 'da programladığımızda C mirasından utanmamalıyız, ama tam olarak kullanmalıyız. C ++ ile ilgili tek problemler ve hatta C ile ilgili tek problemler, kendileri kendi mantıklarıyla tutarlı olmadıklarında ortaya çıkar.


Bence bu yanlış bir tutum. "Düzgün bir şekilde idare etmeyi" öğrenebilmeniz, herkesin zaman ve çabaya değeceği anlamına gelmez.
Neil G

9

__FILE__Ve __LINE__makroları, QA altyapımızdaki otomatik günlük dosyası tarayıcıları ile birlikte bilgi açısından zengin istisna atma, yakalama ve günlüğe kaydetme işlemlerinde tanı amaçlı kullanırız .

Örneğin, atma makrosu OUR_OWN_THROW, metinsel bir açıklama da dahil olmak üzere istisna türü ve yapıcı parametreleriyle birlikte kullanılabilir. Bunun gibi:

OUR_OWN_THROW(InvalidOperationException, (L"Uninitialized foo!"));

Bu makro elbette InvalidOperationExceptionistisnayı yapıcı parametresi olarak açıklamayla birlikte atar , ancak aynı zamanda atışın gerçekleştiği dosya adı ve satır numarasından ve metin açıklamasından oluşan bir günlük dosyasına bir mesaj yazar. Atılan istisna, aynı zamanda günlüğe kaydedilen bir kimlik alır. Kural dışı durum kodda başka bir yere takılırsa, bu şekilde işaretlenir ve günlük dosyası bu özel kural dışı durumun ele alındığını ve bu nedenle daha sonra kaydedilebilecek herhangi bir kilitlenmenin nedeni olmadığını gösterir. İşlenmeyen istisnalar, otomatik KG altyapımız tarafından kolayca alınabilir.



9

Bazı çok gelişmiş ve kullanışlı şeyler, şablonlar da dahil olmak üzere c ++ "dil yapılarını" kullanarak asla yapamayacağınız önişlemci (makrolar) kullanılarak oluşturulabilir.

Örnekler:

Hem C tanımlayıcısı hem de dize oluşturma

C enum tipi değişkenlerini dize olarak kullanmanın kolay yolu

Önişlemci Meta Programlamasını Artırın


Üçüncü bağlantı kopmuş fyi
Robin Hartland

Daha iyi anlamak için bir göz atın stdio.hve sal.hdosya verin vc12.
Elshan

7

Bazen makroları kullanırım, böylece bilgileri tek bir yerde tanımlayabilirim, ancak kodun farklı bölümlerinde farklı şekillerde kullanabilirsiniz. Sadece biraz kötü :)

Örneğin, "field_list.h" içinde:

/*
 * List of fields, names and values.
 */
FIELD(EXAMPLE1, "first example", 10)
FIELD(EXAMPLE2, "second example", 96)
FIELD(ANOTHER, "more stuff", 32)
...
#undef FIELD

Daha sonra bir genel numaralandırma için sadece adı kullanmak için tanımlanabilir:

#define FIELD(name, desc, value) FIELD_ ## name,

typedef field_ {

#include "field_list.h"

    FIELD_MAX

} field_en;

Özel bir başlatma işlevinde, tüm alanlar bir tabloyu verilerle doldurmak için kullanılabilir:

#define FIELD(name, desc, value) \
    table[FIELD_ ## name].desc = desc; \
    table[FIELD_ ## name].value = value;

#include "field_list.h"

1
Not: Benzer bir teknik, ayrı bir dahil edilmeden bile uygulanabilir. Bakınız: stackoverflow.com/questions/147267/… stackoverflow.com/questions/126277/…
Suma

6

Yaygın bir kullanım, derleme ortamını algılamaktır, platformlar arası geliştirme için, amaçlarınız için zaten bir çapraz platform kütüphanesi mevcut değilse, linux için bir kod kümesi yazabilir ve Windows için başka bir kod yazabilirsiniz.

Yani, kaba bir örnekte, platformlar arası bir muteks

void lock()
{
    #ifdef WIN32
    EnterCriticalSection(...)
    #endif
    #ifdef POSIX
    pthread_mutex_lock(...)
    #endif
}

İşlevler için, tür güvenliğini açıkça yoksaymak istediğinizde yararlıdır. ASSERT yapmak için yukarıdaki ve aşağıdaki birçok örnek gibi. Tabii ki, birçok C / C ++ özelliği gibi kendinizi ayağınızdan çekebilirsiniz, ancak dil size araçları sağlar ve ne yapacağınıza karar vermenizi sağlar.


Soru sorandan beri: bu, platform başına farklı içerme yolları aracılığıyla farklı başlıklar ekleyerek makrolar olmadan yapılabilir. Ben makrolar genellikle daha uygun olsa da katılıyorum.
Steve Jessop

İkinci olarak. Bu amaçla makro kullanmaya başlarsanız, kod çok daha az okunabilir hale gelebilir
Nemanja Trifunovic

6

Gibi bir şey

void debugAssert(bool val, const char* file, int lineNumber);
#define assert(x) debugAssert(x,__FILE__,__LINE__);

Böylece sadece

assert(n == true);

ve n yanlışsa günlüğünüze yazdırılan sorunun kaynak dosya adını ve satır numarasını alın.

Normal bir işlev kullanıyorsanız,

void assert(bool val);

makro yerine, elde edebileceğiniz tek şey, daha az kullanışlı olacak günlüğe yazdırılan onaylama işlevinizin satır numarasıdır.


Standart Kütüphanesinin uygulamaları zaten yoluyla temin zaman neden tekerleği yeniden icat ediyorum dosya / çizgi / fonksiyon bilgi döker makro? (zaten gördüğüm tüm uygulamalarda)<cassert>assert()
underscore_d

4
#define ARRAY_SIZE(arr) (sizeof arr / sizeof arr[0])

Geçerli bir iş parçacığında tartışılan 'tercih edilen' şablon çözümünün aksine, bunu sabit bir ifade olarak kullanabilirsiniz:

char src[23];
int dest[ARRAY_SIZE(src)];

2
Bu, şablonlarla daha güvenli bir şekilde yapılabilir (bir dizi yerine bir işaretçi
geçilirse

1
Şimdi C ++ 11'de constexpr'e sahip olduğumuza göre, güvenli (makro olmayan) sürüm de sabit bir ifadede kullanılabilir. template<typename T, std::size_t size> constexpr std::size_t array_size(T const (&)[size]) { return size; }
David Stone

3

Hata ayıklama ve birim sınama senaryolarına yardımcı olması için #defines kullanabilirsiniz. Örneğin, bellek işlevlerinin özel günlük varyantlarını oluşturun ve özel bir memlog_preinclude.h oluşturun:

#define malloc memlog_malloc
#define calloc memlog calloc
#define free memlog_free

Kodunuzu kullanarak derleyin:

gcc -Imemlog_preinclude.h ...

Memlog.o dosyasındaki son resme bir bağlantı. Artık malloc, vb., Belki de günlük kaydı amacıyla veya birim testleri için ayırma hatalarını simüle etmek için kontrol ediyorsunuz.


3

Derleme zamanında Derleyici / OS / Donanım özel davranış üzerinde karar verirken.

Comppiler / OS / Donanım spesifik özelliklerine arayüz yapmanızı sağlar.

#if defined(MY_OS1) && defined(MY_HARDWARE1)
#define   MY_ACTION(a,b,c)      doSothing_OS1HW1(a,b,c);}
#elif define(MY_OS1) && defined(MY_HARDWARE2)
#define   MY_ACTION(a,b,c)      doSomthing_OS1HW2(a,b,c);}
#elif define(MY_SUPER_OS)
          /* On this hardware it is a null operation */
#define   MY_ACTION(a,b,c)
#else
#error  "PLEASE DEFINE MY_ACTION() for this Compiler/OS/HArdware configuration"
#endif

3

İstisnaları kolayca tanımlamak için makroları kullanıyorum:

DEF_EXCEPTION(RessourceNotFound, "Ressource not found")

burada DEF_EXCEPTION

#define DEF_EXCEPTION(A, B) class A : public exception\
  {\
  public:\
    virtual const char* what() const throw()\
    {\
      return B;\
    };\
  }\

2

Derleyiciler satır içi isteğinizi reddedebilir.

Makrolar her zaman yerlerini alacaktır.

Yararlı bulduğum bir şey #define hata ayıklama izleme için DEBUG - bir hata ayıklama sırasında 1 bırakabilir (hatta tüm geliştirme döngüsü boyunca açık bırakabilirsiniz) ve sonra gemi zamanı geldiğinde kapatın.


10
Derleyici satır içi isteğinizi reddederse, bunun çok iyi bir nedeni olabilir. İyi bir derleyici, doğru bir şekilde satırdan daha iyi olacaktır ve kötü bir derleyici size bundan daha fazla performans sorunu verecektir.
David Thornley

@DavidThornley Veya GCC veya CLANG / LLVM gibi mükemmel bir optimize edici derleyici olmayabilir. Bazı derleyiciler sadece saçmalık.
Miles Rout

2

Son işimde bir virüs tarayıcı üzerinde çalışıyordum. Benim hata ayıklamak için bir şey kolaylaştırmak için, her yerde sıkışmış çok sayıda günlük vardı, ama böyle bir yüksek talep app, bir fonksiyon çağrısı masrafı çok pahalı. Bu yüzden, bu küçük Makro ile geldim, bu hala bir müşteri sitesinde bir yayın sürümünde hata ayıklama günlüğünü etkinleştirmeme izin verdi, bir işlev çağrısı maliyeti olmadan hata ayıklama bayrağını kontrol edip hiçbir şey kaydetmeden geri dönecekti veya etkinleştirilmişse , günlüğe kaydetmeyi yapar ... Makro aşağıdaki gibi tanımlanmıştır:

#define dbgmsg(_FORMAT, ...)  if((debugmsg_flag  & 0x00000001) || (debugmsg_flag & 0x80000000))     { log_dbgmsg(_FORMAT, __VA_ARGS__);  }

Günlük işlevlerindeki VA_ARGS nedeniyle, bu gibi bir makro için iyi bir durumdu.

Bundan önce, yüksek güvenlikli bir uygulamada, kullanıcıya doğru erişime sahip olmadıklarını söylemek için gereken bir makro kullandım ve onlara hangi bayrağa ihtiyaç duyduklarını söyleyecekti.

Şu şekilde tanımlanan Makrolar:

#define SECURITY_CHECK(lRequiredSecRoles) if(!DoSecurityCheck(lRequiredSecRoles, #lRequiredSecRoles, true)) return
#define SECURITY_CHECK_QUIET(lRequiredSecRoles) (DoSecurityCheck(lRequiredSecRoles, #lRequiredSecRoles, false))

Ardından, kontrolleri kullanıcı arayüzünün her yerine serpiştirebiliriz ve bu rolünüz yoksa, hangi rollerin yapmaya çalıştığınız eylemi gerçekleştirmesine izin verildiğini söyleyebiliriz. İkisinin nedeni bazı yerlerde bir değer döndürmek ve diğerlerinde geçersiz bir işlevden geri dönmekti ...

SECURITY_CHECK(ROLE_BUSINESS_INFORMATION_STEWARD | ROLE_WORKER_ADMINISTRATOR);

LRESULT CAddPerson1::OnWizardNext() 
{
   if(m_Role.GetItemData(m_Role.GetCurSel()) == parent->ROLE_EMPLOYEE) {
      SECURITY_CHECK(ROLE_WORKER_ADMINISTRATOR | ROLE_BUSINESS_INFORMATION_STEWARD ) -1;
   } else if(m_Role.GetItemData(m_Role.GetCurSel()) == parent->ROLE_CONTINGENT) {
      SECURITY_CHECK(ROLE_CONTINGENT_WORKER_ADMINISTRATOR | ROLE_BUSINESS_INFORMATION_STEWARD | ROLE_WORKER_ADMINISTRATOR) -1;
   }
...

Her neyse, bu şekilde kullandım ve şablonlarla nasıl yardımcı olabileceğinden emin değilim ... Bunun dışında, GERÇEKTEN gerekli olmadıkça bunlardan kaçınmaya çalışıyorum.


2

Yine başka bir foreach makrosu. T: tip, c: konteyner, ben: yineleyici

#define foreach(T, c, i) for(T::iterator i=(c).begin(); i!=(c).end(); ++i)
#define foreach_const(T, c, i) for(T::const_iterator i=(c).begin(); i!=(c).end(); ++i)

Kullanım (konsept gösteren, gerçek değil):

void MultiplyEveryElementInList(std::list<int>& ints, int mul)
{
    foreach(std::list<int>, ints, i)
        (*i) *= mul;
}

int GetSumOfList(const std::list<int>& ints)
{
    int ret = 0;
    foreach_const(std::list<int>, ints, i)
        ret += *i;
    return ret;
}

Daha iyi uygulamalar mevcut: Google "BOOST_FOREACH"

İyi makaleler mevcut: Koşullu Sevgi: FOREACH Redux (Eric Niebler) http://www.artima.com/cppsource/foreach.html


2

Belki de makroların en büyük kullanımı platformdan bağımsız gelişmedir. Tür tutarsızlığı durumlarını düşünün - makrolarla, yalnızca farklı başlık dosyalarını kullanabilirsiniz: --WIN_TYPES.H

typedef ...some struct

--POSIX_TYPES.h

typedef ...some another struct

--program.h

#ifdef WIN32
#define TYPES_H "WINTYPES.H"
#else 
#define TYPES_H "POSIX_TYPES.H"
#endif

#include TYPES_H

Bence başka şekillerde uygulamaktan çok okunabilir.


2

Görünüşe göre VA_ARGS sadece dolaylı olarak bahsedildi:

Genel C ++ 03 kodu yazarken değişken sayıda (genel) parametreye ihtiyacınız varsa, şablon yerine makro kullanabilirsiniz.

#define CALL_RETURN_WRAPPER(FnType, FName, ...)          \
  if( FnType theFunction = get_op_from_name(FName) ) {   \
    return theFunction(__VA_ARGS__);                     \
  } else {                                               \
    throw invalid_function_name(FName);                  \
  }                                                      \
/**/

Not: Genel olarak, ad kontrolü / atımı varsayımsal get_op_from_namefonksiyona dahil edilebilir . Bu sadece bir örnek. VA_ARGS çağrısını çevreleyen başka bir genel kod olabilir.

C ++ 11 ile varyasyonlu şablonlar elde ettikten sonra, bunu "düzgün" bir şablonla çözebiliriz.


1

Bu hile önişlemci bir işlevi ile taklit edilemez akıllı bir kullanım olduğunu düşünüyorum:

#define COMMENT COMMENT_SLASH(/)
#define COMMENT_SLASH(s) /##s

#if defined _DEBUG
#define DEBUG_ONLY
#else
#define DEBUG_ONLY COMMENT
#endif

O zaman bu şekilde kullanabilirsiniz:

cout <<"Hello, World!" <<endl;
DEBUG_ONLY cout <<"This is outputed only in debug mode" <<endl;

Ayrıca bir RELEASE_ONLY makrosu tanımlayabilirsiniz.


2
Bu numara standarda göre çalışmaz. Önişlemci aracılığıyla bir açıklama işaretleyicisi oluşturmaya çalışır, ancak önişlemci çalışmadan önce yorumlar kaldırılır. Uygun bir derleyici burada bir sözdizimi hatasına neden olacaktır.
David Thornley

2
Maalesef David, ancak derleyici yorum kaldırmanın ikinci bir kopyasını içermelidir.
Joshua

hata ayıklama bayrağını global bir const bool yapmak ve şöyle bir kod kullanmak daha kolaydır: if (debug) cout << "..."; - makrolara gerek yok!
Stefan Monov

@Stefan: Gerçekten, şimdi bunu yapıyorum. Bu durumda hata ayıklama yanlışsa herhangi bir düzgün derleyici herhangi bir kod üretmez.
Mathieu Pagé

1

Veya seçeneğini #definekullanarak derleyici komut satırındaki sabitleri kullanabilirsiniz . Bu, genellikle aynı yazılımı birden çok platform için çapraz derlerken yararlıdır, çünkü dosyalarınızın her platform için hangi sabitlerin tanımlandığını kontrol etmesini sağlayabilirsiniz.-D/D

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.