it-swarm-tr.com

C++ makroları ne zaman faydalıdır?

Cönişlemcisi haklı olarak korkulur ve C++ topluluğu tarafından uzaklaştırılır. İç kısımdaki fonksiyonlar, kurallar ve şablonlar genellikle #define 'a göre daha güvenli ve üstün bir alternatiftir.

Aşağıdaki makro: 

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

güvenli olandan daha üstün değildir:

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

Ancak makroların yeri vardır, lütfen yapamadığınız işlemciler olmadan yapamayacağınız makrolar için kullandıklarınızı listeleyin. 

Lütfen her kullanım durumlarını ayrı bir cevap olarak veriniz, oylanabilsin ve cevap verenlerin cevaplarından birini nasıl elde edebileceğinizi biliyorsanız, o cevapların nasıl olduğunu belirtin.

157
Motti

Hata ayıklama işlevlerinin sarıcıları olarak, __FILE__, __LINE__, etc gibi şeyleri otomatik olarak iletmek için:

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

Yöntemler her zaman eksiksiz, derlenebilir bir 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 beri, bu döngü için aralık tabanlı ile değiştirilir.

88
jdmichal

Üstbilgi dosya korumaları makroları zorunlu kılar.

Zorunlu makro olan başka alanlar var mı? Çok fazla değil (eğer varsa).

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

Makro kullandığım bir yer çok tekrarlayan kodlar. Örneğin, C++ kodunu diğer arabirimlerle (.NET, COM, Python, vb ...) kullanacak şekilde kaydırırken, farklı istisna türleri yakalamam gerekiyor. İşte böyle yapıyorum:

#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 avları her sarmalayıcı fonksiyonuna koymak zorundayım. Her seferinde tam catch blokları yazmak yerine, sadece şunu yazarım:

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

Bu da bakımı kolaylaştırır. Yeni bir istisna türü eklemek zorunda kalırsam, eklemem gereken tek bir yer var.

Başka faydalı örnekler de var: bunların çoğu __FILE__ ve __LINE__ önişlemci makrolarını içerir.

Neyse, makrolar doğru kullanıldığında çok faydalıdır. Makrolar kötü değildir - onların kötüye kullanımı kötüdür.

55
Kevin

Çoğunlukla:

  1. Muhafızları dahil et
  2. Koşullu derleme
  3. Raporlama (__LINE__ ve __FILE__ gibi önceden tanımlanmış makrolar)
  4. (nadiren) Tekrarlayan kod kalıplarını çoğaltma.
  5. Rakibinizin kodunda.
51
David Thornley

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
50
Andrew Stein

Bir ifadeden dize yapmak istediğinizde, bunun için en iyi örnek assert'dır (#x, x'ın değerini bir dizeye çevirir).

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

String sabitleri bazen makro olarak tanımlanır, çünkü string değişmezlerini bir const char * ile yapmaktan daha fazlasını yapabilirsiniz.

Örneğin. Dize 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);

Eğer bir const char * kullanılırsa, birleştirme sırasında bir tür string sınıfının kullanılması gerekirdi.

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);
28
Motti

Bir fonksiyondaki program akışını (return, break ve continue) kodunu değiştirmek istediğinizde, fonksiyonda gerçekten belirtilen 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.
24
Motti

Bariz korumaları dahil

#ifndef MYHEADER_H
#define MYHEADER_H

...

#endif
20
Kena

Normal bir işlev çağrısı kullanarak, işlev çağrısı değişkenlerinin kısa devrelerini 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
17
1800 INFORMATION

C++ benzeri UnitTest ++ için birim test çerçeveleri önişlemci makroları etrafında dönmektedir. Birkaç birim test kodu satırı, elle yazmak hiç de eğlenceli olmayan bir sınıf hiyerarşisine yayılır. UnitTest ++ gibi bir şey olmadan ve önişlemci sihri olmadan, C++ için birim testleri nasıl verimli bir şekilde yazacağınızı bilmiyorum.

16
Joe

Diyelim ki başlık korumaları gibi bariz şeyleri görmezden geleceğiz.

Bazen, ön derleyici tarafından kopyalanması/yapıştırılması gereken kodu üretmek 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)

bu kodunuzu sağlar:

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

Ve şöyle mesajlar üretebilir:

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

Şablonların makrolarla karıştırılmasının daha iyi sonuçlara yol açabileceğini unutmayın (diğer bir deyişle, değerleri değişken adlarıyla yan yana otomatik olarak oluşturur)

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

#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")

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 ## bitiştirme işleçlerini kullanarak kod oluşturmanız gerekir (bu, oldukça sınırlı durumlar için geçerlidir).

Diğer zamanlarda, bir işlev aracılığıyla kullanılırsa derlenmeyecek kod üreteceksiniz, örneğin:

#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, sadece bu tür bir kodu doğru olarak kullandım once gördüm

Son fakat 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 yükseltme sayfasından kopyalanan/yapıştırılan kod)

Hangisi (IMHO) std::for_each 'dan çok daha iyi.

Dolayısıyla, makrolar her zaman faydalıdır çünkü normal derleyici kurallarının dışındadırlar. Ama bir tane gördüğümde, etkili bir şekilde C kodunun kalıntıları olduğunu asla doğru C++ 'a tercüme etmedim.

16
paercebal

C önişlemcisinden korkmak, akkor ampullerden korkmak gibidir, çünkü floresan ampuller alırız. Evet, eski olabilir {elektrik | programlayıcı zamanı} verimsiz. Evet, (kelimenin tam anlamıyla) onlar tarafından yakılabilir. Ancak, doğru şekilde hallederseniz işi halledebilirler. 

Gömülü sistemleri programlarken, C, form assembler dışında tek seçenek olarak kullanılır. Masaüstünde C++ ile programlama yaptıktan ve daha küçük, gömülü hedeflere geçtikten sonra, pek çok çıplak C özelliğinin (makrolar dahil) “uygunsuzlukları” hakkında endişelenmeyi bırakmayı ve bunlardan alabileceğiniz en iyi ve güvenli kullanımı bulmaya çalışmayı öğrenirsiniz. Özellikler.

Alexander Stepanov diyor :

C++ 'da programladığımız zaman C mirasından utanmamalıyız, ama yapalım. bunun tam kullanımı. C++ ile ilgili tek sorun ve hatta C ile ilgili tek sorun ortaya çıkıyor. kendileri kendi mantıklarıyla tutarlı olmadıklarında. 

14
VictorH

QA altyapımızdaki otomatik günlük dosya tarayıcıları ile birlikte bilgi bakımından zengin istisna atma, yakalama ve günlüğe kaydetme işlemlerinde tanı amaçlı amaçlar için __FILE__ ve __LINE__ makrolarını kullanırız.

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

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

Elbette bu makro, yapıcı parametresi açıklamasında InvalidOperationException istisnasını atar; ancak dosya adından ve atmanın gerçekleştiği satır numarasından ve metin açıklamasından oluşan bir günlük dosyasına da bir mesaj yazar. Atılan istisna, ayrıca günlüğe kaydedilen bir kimliği alır. İstisna kodda başka bir yerde yakalanırsa, bu şekilde işaretlenir ve günlük dosyası belirli bir istisnanın işlendiğini ve bu nedenle daha sonra oturum açılabilecek herhangi bir çökmenin nedeni olmadığını gösterir. İşlenmemiş istisnalar, otomatik KG altyapımızla kolayca toplanabilir.

9
Johann Gerell

Kod tekrarı.

Önişlemci kütüphanesini artırmak için bir göz atın , bir tür meta-meta programlama. Konu-> motivasyon konusunda iyi bir örnek bulabilirsiniz.

9
Ruggero Turra

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

Örnekler:

Hem bir C tanımlayıcısı hem de bir dize yapmak

Enum türündeki değişkenleri C dizgisi olarak kullanmanın kolay yolu

Boost Ön İşlemci Metaprogramlaması

8
Suma

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

Ö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

O zaman genel bir numaralandırma için sadece ismi kullanmak tanımlanabilir:

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

typedef field_ {

#include "field_list.h"

    FIELD_MAX

} field_en;

Özel bir init 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"
7
Andrew Johnson

Gibi bir şey

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

Böylece sadece sahip olabilirsiniz

assert(n == true);

ve n yanlışsa, kaynak dosya adını ve problemin satır numarasını günlüğünüze yazdırın.

Normal bir işlev kullanıyorsanız, örneğin

void assert(bool val);

makro yerine elde edebileceğiniz tek şey, assert işlevinizin günlüğe yazdırılan satır numarasıdır;.

6
Keshi

Yaygın kullanımlardan biri, derleme ortamını algılamaktır, platformlar arası geliştirme için, Linux için diyelim, diğeri için de bir başka kod dizisi yazabilirsiniz.

Yani, kaba bir örnekte bir çapraz platform muteksinin sahip olabileceği

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

İşlevler için, tür güvenliğini açıkça göz ardı etmek istediğinizde yararlıdırlar. ASSERT yapmak için yukarıda ve aşağıda pek çok örnek olarak. Tabii ki, C/C++ özelliklerinin birçoğu gibi ayağınıza da vurabilirsiniz, ancak dil size araçlar sunar ve ne yapacağınıza karar vermenize izin verir.

6
Doug T.
#define ARRAY_SIZE(arr) (sizeof arr / sizeof arr[0])

Geçerli bir başlıkta tartışılan 'tercih edilen' şablon çözümünden farklı olarak, onu sabit bir ifade olarak kullanabilirsiniz:

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

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

DEF_EXCEPTION(RessourceNotFound, "Ressource not found")

dEF_EXCEPTION nerede

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

Hata ayıklama ve birim test senaryolarına yardımcı olmak için #defines komutunu kullanabilirsiniz. Örneğin, bellek işlevlerinin özel günlüğe kaydetme değişkenlerini 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'nuzdaki son resme bağlantı. Artık malloc'u, örneğin günlük kaydı amacıyla veya ünite testleri için tahsisat hatalarını simüle etmek için kontrol edersiniz.

3
Andrew Johnson

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

Makrolar her zaman kendi yerlerine sahip olacaklar.

Faydalı bulduğum bir şey #define DEBUG'ı hata ayıklama izlemesi için tanımlayabiliyor - bir hatayı ayıklarken (veya tüm geliştirme döngüsü boyunca açık bırakarak) 1'i bırakıp sonra gemi zamanı geldiğinde kapatabilirsiniz.

2
unwieldy

Compiler/OS/Hardware'e özgü davranış konusunda derleme zamanında karar verirken.

Arayüzünüzü Comppiler/OS/Hardware'e özgü özelliklere getirmenizi 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
2
Martin York

Görünüşe göre VA_ARGS yalnızca şu ana kadar dolaylı olarak bahsedilmiştir:

Genel C++ 03 kodu yazarken ve değişken bir (genel) parametre sayısına ihtiyacınız varsa, şablon yerine bir 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, check/throw ismi varsayımsal get_op_from_name işlevine de dahil edilebilir. Bu sadece bir örnek. VA_ARGS çağrısını çevreleyen başka bir genel kod olabilir.

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

2
Martin Ba

Belki de makroların kullanımının büyüklüğü platformdan bağımsız bir gelişmedir .. .. Tipsiz tutarsızlık vakalarını düşünün - makrolarla, sadece farklı başlık dosyaları kullanabilirsiniz - like: -- 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

Benim düşünceme göre başka şekillerde uygulamaktan daha okunaklı.

2
rkellerm

Son işimde bir virüs tarayıcısı üzerinde çalışıyordum. Bir şeyleri hata ayıklamamı kolaylaştırmak için, her yere sıkışmış bir sürü günlük vardı, ancak bunun gibi yüksek talepli bir uygulamada, bir işlev çağrısının masrafı çok pahalı. Bu yüzden, bu küçük Makro ile geldim, bu hala bir müşterinin sitesinde bir sürüm 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 eder ve sadece herhangi bir şey kaydetmeden geri döner , günlüğü yapardı ... Makro aşağıdaki gibi tanımlandı:

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

Günlük fonksiyonlarındaki VA_ARGS nedeniyle, bu böyle bir makro için iyi bir durumdu.

Ondan önce, kullanıcıya doğru erişime sahip olmadıklarını ve ihtiyaç duydukları bayrak olduğunu söylemesi gereken yüksek güvenlikli bir uygulamada bir makro kullandım.

Olarak tanımlanan Makro (lar):

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

Sonra, tüm UI'nin her yerine çekleri dağıtabiliriz ve bu rolün sizde bulunmaması durumunda, hangi rollerin gerçekleştirmeye çalıştığı eylemi gerçekleştirmesine izin verildiğini söyleyecektir. İkisinin nedeni, bazı yerlerde bir değer döndürmek ve diğerlerinde geçersiz bir işlevden 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, onları bu şekilde kullandım ve bunun şablonlara nasıl yardımcı olabileceğinden emin değilim ... Bunun dışında, GERÇEK gerekmedikçe, onlardan kaçınmaya çalışıyorum.

2
LarryF

Yine başka bir foreach makroları. T: türü, c: konteyner, i: 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 (gerçek anlamda değil gösteren kavram):

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 Aşk: FOREACH Redux (Eric Niebler) http://www.artima.com/cppsource/foreach.html

2
Notinlist

Önişlemciyi, sabitlenmiş sayıları, derlenmiş kodda kayan nokta kullanamayan gömülü sistemlerde kullanılan kayan nokta değerlerinden hesaplamak için kullandım. Tüm Matematiğinizin Gerçek Dünya Birimleri'nde olması ve bunları sabit nokta üzerinde düşünmeniz gerekmez.

Örnek:

// TICKS_PER_UNIT is defined in floating point to allow the conversions to compute during compile-time.
#define TICKS_PER_UNIT  1024.0


// NOTE: The TICKS_PER_x_MS will produce constants in the preprocessor.  The (long) cast will
//       guarantee there are no floating point values in the embedded code and will produce a warning
//       if the constant is larger than the data type being stored to.
//       Adding 0.5 sec to the calculation forces rounding instead of truncation.
#define TICKS_PER_1_MS( ms ) (long)( ( ( ms * TICKS_PER_UNIT ) / 1000 ) + 0.5 )
1
dwj

Bir sürü şey için kullanılan alanların bir listesi varsa, ör. bir yapı tanımlamak, bu yapıyı ikilik bir formata dönüştürmek, veri tabanı ekleri yapmak, vb. sonra (tekrar tekrar!) alan listenizi tekrar etmemek için ön işlemciyi kullanabilirsiniz.

Bu kuşkusuz çirkindir. Fakat belki de uzun bir alan listesini birden fazla yerde güncellemekten daha iyidir? Bu tekniği tam olarak bir kez kullandım ve bir zamanlar çok yardımcı oldu.

Tabii ki, aynı genel fikir, doğru yansıması olan dillerde yaygın olarak kullanılmaktadır - sadece sınıfa göz atın ve her alanda sırayla çalışın. C işlemcisinde bunu yapmak kırılgan, okunaksız ve her zaman taşınabilir değildir. Bu yüzden biraz korkuyla söylüyorum. Bununla birlikte, işte burada ...

(EDIT: Şimdi bunun 9/18'de @Andrew Johnson'ın söylediğine benzer olduğunu görüyorum; ancak aynı dosyayı tekrarlı olarak tekrarlamak fikri bu fikri biraz daha ileri götürüyor.)

// file foo.h, defines class Foo and various members on it without ever repeating the
// list of fields.

#if defined( FIELD_LIST )
   // here's the actual list of fields in the class.  If FIELD_LIST is defined, we're at
   // the 3rd level of inclusion and somebody wants to actually use the field list.  In order
   // to do so, they will have defined the macros STRING and INT before including us.
   STRING( fooString )
   INT( barInt )   
#else // defined( FIELD_LIST )

#if !defined(FOO_H)
#define FOO_H

#define DEFINE_STRUCT
// recursively include this same file to define class Foo
#include "foo.h"
#undef DEFINE_STRUCT

#define DEFINE_CLEAR
// recursively include this same file to define method Foo::clear
#include "foo.h"
#undef DEFINE_CLEAR

// etc ... many more interesting examples like serialization

#else // defined(FOO_H)
// from here on, we know that FOO_H was defined, in other words we're at the second level of
// recursive inclusion, and the file is being used to make some particular
// use of the field list, for example defining the class or a single method of it

#if defined( DEFINE_STRUCT )
#define STRING(a)  std::string a;
#define INT(a)     long a;
   class Foo
   {
      public:
#define FIELD_LIST
// recursively include the same file (for the third time!) to get fields
// This is going to translate into:
//    std::string fooString;
//    int barInt;
#include "foo.h"
#endif

      void clear();
   };
#undef STRING
#undef INT
#endif // defined(DEFINE_STRUCT)


#if defined( DEFINE_ZERO )
#define STRING(a) a = "";
#define INT(a) a = 0;
#define FIELD_LIST
   void Foo::clear()
   {
// recursively include the same file (for the third time!) to get fields.
// This is going to translate into:
//    fooString="";
//    barInt=0;
#include "foo.h"
#undef STRING
#undef int
   }
#endif // defined( DEFINE_ZERO )

// etc...


#endif // end else clause for defined( FOO_H )

#endif // end else clause for defined( FIELD_LIST )
1
Eric

Derleyici komut satırında #define veya -D seçeneğini kullanarak /D sabitlerini yapabilirsiniz. Bu, aynı yazılımı birden fazla platform için çapraz derleme yaparken genellikle kullanışlıdır, çünkü makefiles'iniz her platform için hangi sabitleri tanımladığını kontrol ettirebilirsiniz.

1
bk1e

Bence bu numara, bir işlevle taklit edilemeyen önişlemcinin akıllıca kullanılması:

#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;

Bir RELEASE_ONLY makrosu da tanımlayabilirsiniz.

1
Mathieu Pagé

Bir hata ayıklama derlemesinde ek günlüğe kaydetmeyi etkinleştirebilir ve bir Boole denetiminin ek yükü olmadan bir sürüm oluşturma için devre dışı bırakabilirsiniz. Yani, yerine:

void Log::trace(const char *pszMsg) {
    if (!bDebugBuild) {
        return;
    }
    // Do the logging
}

...

log.trace("Inside MyFunction");

Alabilirsin:

#ifdef _DEBUG
#define LOG_TRACE log.trace
#else
#define LOG_TRACE void
#endif

...

LOG_TRACE("Inside MyFunction");

_DEBUG tanımlanmadığında, bu herhangi bir kod üretmez. Programınız daha hızlı çalışacak ve izleme günlüğü için yazılan metin yürütülebilir dosyalarınızda derlenmeyecektir.

0
Ates Goral

Makrolar, switch ifadelerinin sözdizimini simüle etmek için kullanışlıdır:

switch(x) {
case val1: do_stuff(); break;
case val2: do_other_stuff();
case val3: yet_more_stuff();
default:   something_else();
}

i̇ntegral olmayan değer türleri için. Bu soruda: 

switch ifadelerinde dizeleri kullanmak - C++ 17 ile nerede duruyoruz?

lambdaları içeren bazı yaklaşımları öneren cevaplar bulacaksınız, fakat ne yazık ki, bizi en çok çeken makrolar:

SWITCH(x)
CASE val1  do_stuff(); break;
CASE val2  do_other_stuff();
CASE val3  yet_more_stuff();
DEFAULT    something_else();
END
0
einpoklum
#define COLUMNS(A,B) [(B) - (A) + 1]

struct 
{
    char firstName COLUMNS(  1,  30);
    char lastName  COLUMNS( 31,  60);
    char address1  COLUMNS( 61,  90);
    char address2  COLUMNS( 91, 120);
    char city      COLUMNS(121, 150);
};
0
EvilTeach

Bunu bir satır içi işlevi olarak uygulayabilir misiniz?

#define my_free(x) do { free(x); x = NULL; } while (0)
0
mbac32768

Kaynak derleyici yalnızca onları anladığı için Visual Studio'daki kaynak tanımlayıcıları için bir makroya ihtiyacınız vardır (yani, const veya enum ile çalışmaz).

0
Harold Ekstrom