it-swarm-eu.dev

Kdy jsou výhodná makra C++?

Cpreprocesor je oprávněně obáván a vyhýbán komunitou C++. Vložené funkce, konstanty a šablony jsou obvykle bezpečnější a vynikající alternativou k #define.

Následující makro: 

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

není v žádném případě lepší než typ bezpečný:

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

Ale makra mají své místo, prosím, seznam použití, která najdete pro makra, která jste nemůže udělat bez preprocesoru. 

Uveďte prosím jednotlivé případy použití do samostatné odpovědi, aby bylo možné hlasovat a pokud víte, jak dosáhnout jedné z odpovědí bez předběžného testu, upozorněte na to, jak v této odpovědi komentujete.

157
Motti

Jako obálky pro funkce ladění automaticky předávat věci jako __FILE__, __LINE__ atd.:

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

Metody musí být vždy úplné, kódovatelný; makra mohou být fragmenty kódu. Tak můžete definovat makro foreach:

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

Použijte jej tak:

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

Protože C++ 11, toto je nahrazeno rozsahem pro smyčku .

88
jdmichal

Chrániče souborů záhlaví vyžadují makra.

Existují jiné oblasti, které vyžadují makra? Ne mnoho (pokud existuje).

Existují nějaké jiné situace, které využívají makra? ANO!!!

Jedno místo, kde používám makra, je s velmi opakovaným kódem. Například, když zabalím C++ kód, který má být použit s jinými rozhraními (.NET, COM, Python atd.), Musím zachytit různé typy výjimek. Zde je postup:

#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__); \
}

Musím dát tyto úlovky do každé funkce obalu. Spíše než zapisovat pokaždé plné blokování, zadávám:

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

To také usnadňuje údržbu. Pokud budu muset přidat nový typ výjimky, musím ho přidat pouze na jedno místo.

Existují i ​​další užitečné příklady: mnoho z nich zahrnuje makra __FILE__ a __LINE__ preprocesoru.

Makra jsou velmi užitečná při správném použití. Makra nejsou zlo - jejich zneužití je zlé.

55
Kevin

Většinou:

  1. Včetně strážců
  2. Podmíněná kompilace
  3. Hlášení (předdefinovaná makra jako __LINE__ a __FILE__)
  4. (zřídka) Kopírování opakujících se vzorů kódu.
  5. V kódu soutěžícího.
51
David Thornley

Uvnitř podmíněné kompilace, překonat problémy rozdílů mezi kompilátory:

#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

Pokud chcete vytvořit řetězec mimo výraz, nejlepším příkladem je assert (#x změní hodnotu x na řetězec).

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

Řetězcové konstanty jsou někdy lépe definovány jako makra, protože s řetězcovými literály můžete udělat více než s const char *.

např. Řetězové literály mohou být snadno zřetězené .

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

Pokud by bylo použito const char *, pak by bylo nutné použít nějaký řetězec třídy pro provedení zřetězení za běhu:

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

Pokud chcete změnit programový tok (return, break a continue) kód ve funkci, chová se jinak než kód, který je ve funkci ve skutečnosti zobrazen.

#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

Zřejmé jsou strážci

#ifndef MYHEADER_H
#define MYHEADER_H

...

#endif
20
Kena

Nelze provést zkratování argumentů volání funkcí pomocí volání normální funkce. Například:

#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

Jednotkové testovací rámce pro C++ jako UnitTest ++ do značné míry se točí kolem předprocesorových maker. Několik řádků testovacího kódu jednotky se rozšíří do hierarchie tříd, které by nebylo vůbec zábavné psát ručně. Bez něčeho jako UnitTest ++ a jeho preprocesorové magie nevím, jak byste efektivně psali jednotkové testy pro C++.

16
Joe

Řekněme, že budeme ignorovat jasné věci, jako jsou záhlaví hlavičky.

Někdy chcete generovat kód, který je třeba kopírovat/vložit předkompilátorem:

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

který vám umožní kódovat:

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

A mohou generovat zprávy jako:

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

Všimněte si, že míchání šablon s makry může vést k ještě lepším výsledkům (tj. Automatické generování hodnot vedle názvu proměnné)

Jindy potřebujete __FILE__ a/nebo __LINE__ nějakého kódu, například pro generování informací o ladění. Toto je klasika pro Visual C++:

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

Stejně jako u následujícího kódu:

#pragma message(WRNG "Hello World")

generuje zprávy jako:

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

Jindy je třeba generovat kód pomocí operátorů zřetězení # a ##, jako je generování getrů a setterů pro vlastnost (to je pro docela omezené případy, přes).

Jindy budete generovat kód, který nebude kompilován, pokud bude používán prostřednictvím funkce, jako například:

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

Který může být použit jako

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

(Stále jsem viděl tento druh kódu správně používán jednou)

V neposlední řadě slavný 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;
}

(Poznámka: kopírování kódu/vložení z domovské stránky podpory)

Což je (IMHO) lépe než std::for_each.

Makra jsou tedy vždy užitečná, protože jsou mimo normální pravidla kompilátoru. Ale zjistím, že většinu času, který vidím, jsou v podstatě pozůstatky C kódu, který se nikdy nepřeložil do správného C++.

16
paercebal

Obávat se, že C preprocesor je jako strach z žárovek jen proto, že dostáváme zářivky. Ano, první může být elektřina čas programátoru} neefektivní. Ano, můžete je (doslova) spálit. Ale pokud to zvládnete správně, zvládnou práci. 

Při programování vestavěných systémů, C používá jako jediná možnost kromě formuláře assembler. Po programování na desktopu s C++ a následném přepnutí na menší, vložené cíle se naučíte přestat bát o „inelegancies“ tolika holých funkcí C (včetně makra) a jen se snažíte zjistit nejlepší a nejbezpečnější použití, které můžete získat od těch funkce.

Alexander Stepanov říká :

Když programujeme v C++, neměli bychom se stydět za jeho C dědictví, ale učinit jeho plné využití. Jediné problémy s C++, a dokonce i jediné problémy s C, vznikají, když sami nejsou v souladu s vlastní logikou. 

14
VictorH

Používáme makra __FILE__ a __LINE__ pro diagnostické účely při házení, zachycení a protokolování informací, které jsou bohaté na informace, spolu s automatickými skenery souborů protokolu v naší infrastruktuře QA.

Například makro OUR_OWN_THROW pro házení může být použito s výjimkami typu a parametry konstruktoru pro tuto výjimku, včetně textového popisu. Takhle:

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

Toto makro bude samozřejmě vracet výjimku InvalidOperationException s popisem jako parametr konstruktoru, ale také zapíše zprávu do souboru protokolu, který se skládá z názvu souboru a čísla řádku, kde došlo k hodu a jeho textovému popisu. Vyhodená výjimka dostane id, který také dostane zaprotokolovaný. Pokud je výjimka někdy zachycena někde jinde v kódu, bude označena jako taková a soubor protokolu pak označí, že tato specifická výjimka byla zpracována, a že proto není pravděpodobné, že by to bylo příčinou jakéhokoli pádu, který by mohl být později zaznamenán. Neošetřené výjimky lze snadno zachytit naší automatizovanou QA infrastrukturou.

9
Johann Gerell

Opakování kódu.

Podívejme se na podporu preprocesorové knihovny , je to druh meta-meta-programování. V tématu-> motivace můžete najít dobrý příklad.

9
Ruggero Turra

Některé velmi pokročilé a užitečné věci mohou být ještě vytvořeny s použitím preprocesoru (makra), který byste nikdy nemohli dělat pomocí c ++ "jazykových konstrukcí" včetně šablon.

Příklady:

Dělat něco jako identifikátor C a řetězec

Snadný způsob, jak použít proměnné typu enum jako řetězec v C

Metaprogramování Boost Preprocessor

8
Suma

Občas používám makra, takže mohu definovat informace na jednom místě, ale použít je různými způsoby v různých částech kódu. Je to jen mírně zlé :)

Například v poli „field_list.h“:

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

Pro veřejný výčet pak můžeme definovat, že se jedná pouze o název:

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

typedef field_ {

#include "field_list.h"

    FIELD_MAX

} field_en;

A v soukromé funkci init lze všechna pole použít k vyplnění tabulky údaji:

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

#include "field_list.h"
7
Andrew Johnson

Něco jako

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

Takže můžete například mít

assert(n == true);

a získat název zdrojového souboru a číslo řádku problému vytištěny do protokolu, pokud n je false.

Pokud používáte normální funkci volání, například

void assert(bool val);

namísto makra, vše, co můžete získat, je číslo tvrzení funkce řádku vytištěné na log, což by bylo méně užitečné.

6
Keshi

Jedním z běžných způsobů je zjišťování kompilačního prostředí, pro vývoj mezi platformami můžete napsat jednu sadu kódů pro Linux, řekněme, a druhou pro okna, kdy pro vaše účely již neexistuje knihovna s více platformami.

Takže v hrubém příkladu může mít multiplatformní mutex

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

Pro funkce jsou užitečné, pokud chcete explicitně ignorovat typovou bezpečnost. Například mnoho příkladů výše a níže pro provádění ASSERT. Samozřejmě, stejně jako mnoho C/C++ funkcí, můžete střílet sami do nohy, ale jazyk vám dává nástroje a umožňuje rozhodnout, co dělat.

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

Na rozdíl od „preferovaného“ řešení šablony, které je popsáno v aktuálním vlákně, jej můžete použít jako konstantní výraz:

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

Pro snadné definování výjimek používám makra:

DEF_EXCEPTION(RessourceNotFound, "Ressource not found")

kde DEF_EXCEPTION je

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

Pomocí #defines můžete pomoci s laděním a scénáři testů jednotek. Vytvořte například speciální varianty protokolování paměťových funkcí a vytvořte speciální memlog_preinclude.h:

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

Kompilovat kód pomocí:

gcc -Imemlog_preinclude.h ...

Odkaz ve vašem memlog.o na konečný obraz. Nyní můžete ovládat malloc, atd., Možná pro účely protokolování, nebo simulovat selhání přidělení pro testy jednotek.

3
Andrew Johnson

Kompilátoři mohou odmítnout váš požadavek inline.

Makra budou mít vždy své místo.

Něco, co považuji za užitečné, je #define DEBUG pro ladění ladění - můžete ho nechat 1 při ladění problému (nebo ho nechat zapnout i během celého vývojového cyklu) a pak ho vypnout, když je čas na loď.

2
unwieldy

Když rozhodujete při kompilaci přes specifické chování Compiler/OS/Hardware.

To vám umožní, aby vaše rozhraní pro Comppiler/OS/Hardware specifické funkce.

#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

Zdá se, že VA_ARGS byly dosud zmíněny pouze nepřímo:

Při psaní generického kódu C++ 03 a potřebujete variabilní počet (obecných) parametrů, můžete použít makro místo šablony.

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

Poznámka: Obecně může být kontrola jména/hodu také začleněna do hypotetické funkce get_op_from_name. To je jen příklad. Volání VA_ARGS může obsahovat další obecný kód.

Jakmile dostaneme variadické šablony s C++ 11, můžeme to „správně“ vyřešit pomocí šablony.

2
Martin Ba

Možná, že použití velkého množství makra je ve vývoji nezávislém na platformě. Zamyslete se nad případy nekonzistence typu - s makry můžete jednoduše použít různé soubory záhlaví - jako: - 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

Podle mého názoru je mnohem srozumitelnější než jeho provádění jinými způsoby.

2
rkellerm

Ve své poslední práci jsem pracoval na antivirovém programu. Chcete-li, aby to snadnější pro mě ladit, měl jsem spoustu protokolování přilepená všude, ale v aplikaci s vysokou poptávkou takhle, náklady na volání funkce je prostě příliš drahé. Tak jsem přišel s tímto malým makrem, které mi umožnilo povolit ladění ladění na verzi pro zákazníky na stránkách zákazníků, aniž by náklady na volání funkce zkontrolovaly příznak ladění a prostě se vrátily, aniž by se přihlásily, nebo pokud jsou povoleny , udělal by to protokolování ... Makro bylo definováno následovně:

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

Kvůli VA_ARGS v logovacích funkcích to byl dobrý případ pro toto makro.

Předtím jsem použil makro v aplikaci s vysokým zabezpečením, která potřebovala říct uživateli, že nemají správný přístup, a řekne jim, co potřebují.

Makro definované jako:

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

Pak bychom mohli pospíchat šeky po celém uživatelském rozhraní a říkali byste, které role vám umožnily provést akci, kterou jste se pokusili udělat, pokud jste tuto roli ještě neměli. Důvodem dvou z nich bylo vrátit hodnotu na některých místech a vrátit se z funkce neplatné v jiných ...

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;
   }
...

Mimochodem, tak jsem je použil, a nejsem si jistý, jak by to mohlo být pomohlo se šablonami ... Jinak se jim snažím vyhnout, pokud to opravdu není nutné.

2
LarryF

Ještě další makra foreach. T: typ, c: kontejner, i: iterátor

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

Použití (koncept zobrazující, nikoli reálný):

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

K dispozici jsou lepší implementace: Google "BOOST_FOREACH"

Dostupné dobré články: Podmíněná láska: FOREACH Redux (Eric Niebler) http://www.artima.com/cppsource/foreach.html

2
Notinlist

Použil jsem předprocesor k výpočtu čísel pevných bodů z hodnot s plovoucí desetinnou čárkou používaných ve vestavěných systémech, které nemohou používat kompilovaný kód v plovoucí řádové čárce. Je užitečné mít všechny své matematiky v jednotkách reálného světa a nemusíte o nich přemýšlet v pevném bodě.

Příklad:

// 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

Pokud máte seznam polí, která se používají pro spoustu věcí, např. definovat strukturu, serializovat tuto strukturu do/z nějakého binárního formátu, dělat databázové vložky, etc., pak vy můžete (rekurzivně!) používat preprocesor vyhnout se někdy opakovat váš seznam pole.

To je nepochybně odporné. Ale možná někdy lepší než aktualizovat dlouhý seznam polí na více místech? Použil jsem tuto techniku ​​přesně jednou, a to bylo docela užitečné, že jednou.

Samozřejmě stejná obecná myšlenka je široce používána v jazycích s patřičným odrazem - stačí instrospekci třídy a operaci na každém poli. Dělat to v C preprocesoru je křehké, nečitelné a ne vždy přenosné. Tak jsem to zmínil s trochou úzkosti. Nicméně, tady je ...

(EDIT: Vidím, že je to podobné tomu, co řekl @Andrew Johnson na 9/18; myšlenka rekurzivního zahrnutí stejného souboru však tuto myšlenku trochu dále.)

// 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

Konstanty #define můžete na příkazovém řádku kompilátoru použít pomocí volby -D nebo /D. To je často užitečné při křížovém kompilaci stejného softwaru pro více platforem, protože můžete mít své makefiles řízení, jaké konstanty jsou definovány pro každou platformu.

1
bk1e

Myslím, že tento trik je chytré použití preprocesoru, který nemůže být emulován funkcí:

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

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

Pak jej můžete použít takto:

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

Můžete také definovat makro RELEASE_ONLY.

1
Mathieu Pagé

Můžete povolit další protokolování v ladění sestavení a zakázat jej pro sestavení vydání bez režijní Boolean kontroly. Takže místo:

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

...

log.trace("Inside MyFunction");

Můžeš mít:

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

...

LOG_TRACE("Inside MyFunction");

Pokud není definováno _DEBUG, nebude generovat žádný kód vůbec. Váš program bude běžet rychleji a text pro protokolování trasování nebude zkompilován do spustitelného souboru.

0
Ates Goral

Makra jsou užitečná pro simulaci syntaxe příkazů přepínače:

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

pro neintegrální typy hodnot. V této otázce: 

Použití řetězců v příkazech switch - kde stojíme s C++ 17?

najdete odpovědi, které naznačují některé přístupy zahrnující lambdas, ale bohužel je to makra, která nás nejblíže:

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

Můžete to implementovat jako inline funkci?

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

Potřebujete makra pro identifikátory prostředků v aplikaci Visual Studio, protože kompilátor prostředků jim pouze rozumí (tj. Nefunguje s const nebo enum).

0
Harold Ekstrom