Коли корисні макроси C ++? [зачинено]


177

C препроцесор виправдано боявся й уникав спільноти C ++. Вбудовані функції, consts та шаблони, як правило, є більш безпечною та чудовою альтернативою a #define.

Наступний макрос:

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

нічим не перевершує безпечний тип:

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

Але макроси мають місце, будь ласка, перелічіть використання, яке ви знайдете для макросів, які не можна обійтися без препроцесора.

Будь ласка, покладіть на кожний випадок використання окрему відповідь, щоб за неї можна було проголосувати, і якщо ви знаєте, як досягти однієї з відповідей без попереднього довідника, вкажіть, як у коментарях відповіді.


Одного разу я взяв додаток C ++, повний макросів, на створення якого пішло 45 хвилин, замінив макроси на вбудовані функції та звів збірку до менш ніж 15 хвилин.
ендіан


Нитка стосується контекстів, у яких макроси вигідні, а не контекстів, у яких вони є неоптимальними.
підкреслюй_

Відповіді:


123

В якості обгорток для функцій налагодження, щоб автоматично передати речі , як __FILE__, __LINE__і т.д .:

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

14
Насправді оригінальний фрагмент: << ФАЙЛ “:„ << добре, FILE генерує поточну константу, яка буде об'єднана попередньою процесорою в одну рядок.
Франк Щебра

12
Це вимагає тільки препроцесора , тому що __FILE__і __LINE__ також вимагає препроцесора. Використання їх у своєму коді - це як інфекція для препроцесора.
ТЕД

93

Методи завжди повинні бути повним, складеним кодом; макроси можуть бути фрагментами коду. Таким чином, ви можете визначити макрос foreach:

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

І використовуйте його так:

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

Оскільки C ++ 11, це витісняється циклом на основі циклу .


6
+1 Якщо ви використовуєте якийсь смішно складний синтаксис ітератора, написання макросу стилю foreach може зробити ваш код набагато простішим для читання та обслуговування. Я це зробив, це працює.
постфутуріст

9
Більшість коментарів абсолютно не стосуються того, що макроси можуть бути фрагментами коду замість повного коду. Але дякую вам за приборкання.
jdmichal

12
Це C не C ++. Якщо ви робите C ++, вам слід використовувати ітератори та std :: for_each.
chrish

20
Я не згоден, хриш. Перед лямбда, це for_eachбуло неприємно, тому що код, через який проходив кожен елемент, не був локальним для точки виклику. foreach, (і я настійно рекомендую BOOST_FOREACHзамість ручного рішення) дайте вам тримати код поблизу ітераційного сайту, зробивши його більш читабельним. Це сказало, що після того, як лямбда вийде, for_eachможе знову стати дорогою.
GManNickG

8
І варто зазначити, що BOOST_FOREACH - сам макрос (але дуже продуманий)
Тайлер Макенрі

59

Захисні файли заголовка потребують макросів.

Чи є інші області, які потребують макросів? Не багато (якщо такі є).

Чи є інші ситуації, які виграють від макросів? ТАК!!!

Я використовую макроси на одному місці з дуже повторюваним кодом. Наприклад, під час упаковки коду C ++, який використовується з іншими інтерфейсами (.NET, COM, Python тощо), мені потрібно вловлювати різні винятки. Ось як я це роблю:

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

Я повинен вкласти ці уловки у кожну функцію обгортки. Замість того, щоб кожен раз вводити блоки повного улову, я просто набираю:

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

Це також полегшує обслуговування. Якщо мені колись доведеться додати новий тип винятків, мені потрібно додати лише одне місце.

Є й інші корисні приклади: багато з яких включають макроси __FILE__та __LINE__перепроцесори.

У будь-якому випадку макроси дуже корисні при правильному використанні. Макроси не є злими - їх неправильне використання - зло.


7
Більшість компіляторів підтримують #pragma onceсьогодні, тому я сумніваюся, що охоронці дійсно потрібні
1800 р. ІНФОРМАЦІЯ

13
Вони є, якщо ви пишете для всіх компіляторів, а не лише для більшості ;-)
Стів Джессоп

30
Тож замість портативного, стандартного функціоналу препроцесора ви рекомендуєте використовувати розширення препроцесора, щоб уникнути використання препроцесора? Це здається мені щось смішним.
Логан Капальдо

#pragma onceперерви на багато поширених систем побудови.
Miles Rout

4
Існує рішення для того , що не вимагає макросів: void handleExceptions(){ try { throw } catch (::mylib::exception& e) {....} catch (::std::exception& e) {...} ... }. І з боку функцій:void Foo(){ try {::mylib::Foo() } catch (...) {handleExceptions(); } }
MikeMB

51

Переважно:

  1. Включіть охоронців
  2. Умовне складання
  3. Звітування (попередньо визначені макроси, як __LINE__і __FILE__)
  4. (рідко) Дублювання повторюваних шаблонів коду.
  5. У коді вашого конкурента.

Шукаєте допомогу, як реалізувати число 5. Чи можете ви направити мене на вирішення?
Макс

50

Усередині умовної компіляції, щоб подолати розбіжності між компіляторами:

#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 ++ те саме можна отримати завдяки використанню вбудованих функцій: <code> #ifdef ARE_WE_ON_WIN32 - inline int close (int i) {return _close (i); } <br> #endif </code>
paercebal

2
Це видаляє # define, але не #ifdef та #endif. У всякому разі, я з вами згоден.
Горпик

19
НІКОЛИ не визначайте макроси нижнього регістру. Макроси для зміни функцій - це мій кошмар (спасибі Microsoft). Найкращий приклад - це перший рядок. Багато бібліотек мають closeфункції або методи. Тоді, коли ви додаєте заголовок цієї бібліотеки та заголовок із цим макросом, ніж у вас є велика проблема, ви не можете використовувати API бібліотеки.
Marek R

AndrewStein, чи бачите ви якусь користь від використання макросів у цьому контексті над пропозицією @ paercebal? Якщо ні, то, мабуть, макроси насправді безоплатно.
einpoklum

1
#ifdef WE_ARE_ON_WIN32plz :)
Гонки легкості в Орбіті

38

Коли ви хочете зробити рядок з виразу, найкращим прикладом для цього є assert( #xперетворює значення xна рядок).

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

5
Лише нитко, але я особисто залишив би крапку з комою.
Майкл Майерс

10
Я погоджуюсь, насправді я би поставив це в do {} while (false) (щоб запобігти іншим хай-джекам), але хотів би зробити це просто.
Мотті

33

Строкові константи іноді краще визначати як макроси, оскільки ви можете робити більше з рядковими літералами, ніж з a const char *.

наприклад, рядкові літерали можуть бути легко об'єднати .

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

Якщо const char *використовується, то для виконання конкатенації під час виконання потрібно було б використовувати якийсь клас рядків:

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 я вважаю це найважливішою частиною (окрім тих, що включають сторожу). Макроси справді найкраще, що ми маємо для обробки рядків під час компіляції. Це особливість, яку я сподіваюся, ми потрапимо в C ++ 11 ++
Девід Стоун

1
Це така ситуація, яка призвела до того, що я бажав макросів на C #.
Ролінг

2
Я б хотів, щоб я міг +42 цього. Дуже важливий, хоча і не часто пам’ятається аспект рядкових літералів.
Даніель Каміль Козар

24

Якщо ви хочете , щоб змінити хід виконання програми ( return, breakі continue) код в функції , поводиться інакше , ніж код , який фактично вбудовується в функції.

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

Кидати виняток мені здається кращою альтернативою.
einpoklum

Під час написання розширень python C (++) винятки поширюються шляхом встановлення рядка винятків, а потім повернення -1або NULL. Тож макрос може значно зменшити код котла.
black_puppydog

20

До очевидних належать охоронці

#ifndef MYHEADER_H
#define MYHEADER_H

...

#endif

17

Ви не можете виконувати коротке замикання аргументів виклику функції, використовуючи звичайний виклик функції. Наприклад:

#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
Можливо, більш загальний момент: функції оцінюють свої аргументи рівно один раз. Макроси можуть оцінювати аргументи більше або менше разів.
Стів Джессоп

@ [Грег Роджерс] все, що робить макропроцесор - це текст-замінник. Як тільки ви це зрозумієте, більше не повинно бути таємниці про це.
1800 р. ІНФОРМАЦІЯ

Ви можете отримати еквівалентну поведінку шляхом шаблонування andf замість того, щоб примушувати оцінку до bool перед викликом функції. Я б не зрозумів, що ти сказав, що це правда, не намагаючись це зробити на собі. Цікаво.
Грег Роджерс

Як саме ви могли це зробити з шаблоном?
1800 р. ІНФОРМАЦІЯ

6
Приховування операцій короткого замикання за макросом стилю функції - це одне з речей, які я дійсно не хочу бачити у виробничому коді.
MikeMB

17

Скажімо, ми будемо ігнорувати очевидні речі, такі як охоронці заголовків.

Іноді потрібно генерувати код, який потрібно скопіювати / вставити прекомпілятором:

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

що дозволяє кодувати це:

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

І може створювати повідомлення типу:

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

Зауважте, що змішування шаблонів з макросами може призвести до ще кращих результатів (тобто автоматично генерувати значення поряд з їх назвами змінних)

В іншому випадку вам потрібні __FILE__ та / або __LINE__ деякого коду, наприклад, для створення інформації про налагодження, наприклад. Далі є класикою для Visual C ++:

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

Як і в наступному коді:

#pragma message(WRNG "Hello World")

він генерує повідомлення типу:

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

В іншому випадку вам потрібно генерувати код за допомогою операторів конкатенації # та ##, як-от генерування getters та setters для властивості (це досить обмежений випадок).

В іншому випадку ви будете генерувати код, ніж не буде компілюватися, якщо використовується через функцію, наприклад:

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

Які можна використовувати як

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

( До сих пір я бачив тільки цей вид коду правильно використовується один раз )

І останнє, але не менш важливе, відоме 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;
}

(Примітка: копія коду / вставка з домашньої сторінки підвищення)

Який (ІМХО) спосіб краще, ніж std::for_each.

Отже, макроси завжди корисні, оскільки знаходяться за межами звичайних правил компілятора. Але я вважаю, що більшість випадків, коли я їх бачу, вони фактично залишаються кодом С, який ніколи не переводиться на належний C ++.


1
Використовуйте CPP лише для того, що компілятор не може зробити. Наприклад, RAISE_ERROR_STL повинен використовувати CPP лише для визначення підпису файлів, рядків та функцій та передавати їх функції (можливо вбудованій), яка робить решту.
Rainer Blome

Будь ласка, оновіть свою відповідь, щоб відобразити C ++ 11 та адресу @ коментар RainerBlome.
einpoklum

@RainerBlome: Ми згодні. Макрос RAISE_ERROR_STL дорівнює C ++ 11, тому в цьому контексті він повністю виправданий. Моє розуміння (але я ніколи не мав приводу розібратися з цими специфічними особливостями) полягає в тому, що ви можете використовувати різноманітні шаблони (або макроси?) В Modern C ++, щоб вирішити проблему більш елегантно.
paercebal

@einpoklum: "Будь ласка, оновіть свою відповідь, щоб відобразити C ++ 11 та адресувати коментар RainerBlome" Ні :-). . . Я вважаю, що в кращому випадку я додам розділ для Modern C ++, з альтернативними реалізаціями, зменшуючи або усуваючи потребу в макросах, але справа в тому: Макроси некрасиві і злі, але коли потрібно щось робити, компілятор не розуміє , ви робите це через макроси.
paercebal

Навіть із C ++ 11, багато чого з того, що робить ваш макрос, можна залишити для виконання функції: #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; }Таким чином, макрос значно коротший.
Rainer Blome

16

Тестові рамки модулів для C ++, як-то UnitTest ++, в основному обертаються навколо макросів препроцесора. Кілька рядків тестового коду модуля розширюються в ієрархію класів, які взагалі не було б цікаво вводити вручну. Без чогось на зразок UnitTest ++ і це магія препроцесорів, я не знаю, як би ви ефективно писали одиничні тести для C ++.


Единичні тести цілком можна записати без фреймворку. Зрештою, це дійсно залежить лише від того, який вихід ви хочете. Якщо вам все одно, просте значення виходу, що вказує на успіх чи невдачу, має бути абсолютно чудовим.
Чіткіший

15

Побоюватися препроцесора С - це як боятися лампочок розжарювання лише тому, що ми отримуємо люмінесцентні лампочки. Так, колишній може бути {електрика | час програміста} неефективний. Так, ви можете (буквально) їх спалити. Але вони можуть виконати роботу, якщо правильно впоратися з нею.

Коли ви програмуєте вбудовані системи, C використовує єдиний варіант, крім складання форми. Програмувавши на робочому столі C ++, а потім перейшовши на менші, вбудовані цілі, ви навчитесь не турбуватися про «неелементи» такої кількості голих функцій C (включені макроси) та просто намагаєтесь визначити найкраще та безпечне використання, яке ви можете отримати з цих особливості.

Олександр Степанов каже :

Коли ми програмуємо на C ++, ми не повинні соромитися своєї спадщини C, а повністю використовувати її. Єдині проблеми з C ++ і навіть єдині проблеми з C виникають, коли вони самі не відповідають власній логіці.


Я думаю, що це неправильне ставлення. Тільки тому, що ви можете навчитися "правильно справлятися з цим", не означає, що це вартий будь-якого часу і сил.
Ніл Г

9

Ми використовуємо __FILE__і __LINE__макроси для діагностичних цілей для передачі, вилучення та ведення журналів, багатих на інформацію, а також автоматизованих сканерів файлів журналів у нашій інфраструктурі QA.

Наприклад, макрос, що викидає, OUR_OWN_THROWможе використовуватися з типом виключення та параметрами конструктора для цього винятку, включаючи текстовий опис. Подобається це:

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

Цей макрос, звичайно, кине InvalidOperationExceptionвиняток із описом як конструкторського параметра, але він також напише повідомлення в файл журналу, що складається з імені файлу та номера рядка, де стався викид та його текстового опису. Викинутий виняток отримає ідентифікатор, який також отримує реєстрацію. Якщо виняток коли-небудь потрапляє десь в коді, він буде позначений як такий, і файл журналу вкаже, що цей конкретний виняток оброблений і тому, ймовірно, причиною будь-якого збою, який може бути зареєстрований пізніше. Неопрацьовані винятки можуть бути легко підібрані нашою автоматизованою інфраструктурою QA.


9

Повторення коду.

Погляньте, щоб збільшити бібліотеку препроцесорів , це свого роду мета-метапрограмування. У темі-> мотивації ви можете знайти хороший приклад.


Я майже всі, якщо не всі випадки - повторення коду можна уникнути за допомогою викликів функцій.
einpoklum

@einpoklum: Я не згоден. Перегляньте посилання
Ruggero Turra

9

Деякі дуже вдосконалені та корисні речі все ще можна створити за допомогою препроцесора (макросів), що ви ніколи не зможете зробити, використовуючи c ++ "мовні конструкції", включаючи шаблони.

Приклади:

Створення чогось і ідентифікатора C, і рядка

Простий спосіб використання змінних типів enum як рядка в C

Підвищення препроцесорного метапрограмування


Третя ланка розірвана фій
Робін Хартленд

Погляньте stdio.hі sal.hподайте файл vc12для кращого розуміння.
Ельшан

7

Я час від часу використовую макроси, щоб я міг визначити інформацію в одному місці, але по-різному використовую її в різних частинах коду. Це лише трохи зло :)

Наприклад, у "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

Тоді для громадського перерахунку можна визначити просто використання назви:

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

typedef field_ {

#include "field_list.h"

    FIELD_MAX

} field_en;

А в приватній функції init всі поля можна використовувати для заповнення таблиці з даними:

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

#include "field_list.h"

1
Примітка: подібну техніку можна реалізувати навіть без окремого включення. Дивіться: stackoverflow.com/questions/147267/… stackoverflow.com/questions/126277/…
Suma

6

Одне поширене використання - це для виявлення середовища компіляції, для розробки крос-платформ можна написати один набір коду для Linux, скажімо, і інший для Windows, коли для ваших цілей вже не існує бібліотеки крос-платформ.

Так, у приблизному прикладі може міститися міжплатформна мютекс

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

Для функцій вони корисні, коли ви хочете явно ігнорувати безпеку типу. Такі, як безліч прикладів для виконання ASSERT вище та нижче. Звичайно, як і багато функцій C / C ++, ви можете стріляти собі в ногу, але мова надає вам інструменти та дозволяє вирішити, що робити.


Оскільки запитуючий запитав: це можна зробити без макросів, включивши різні заголовки через різні включені контури на платформу. Я схильний погодитися, хоча макроси часто зручніші.
Стів Джессоп

Я другий, що. Якщо ви почнете використовувати макроси для цієї мети, код може швидко стати набагато менш читабельним
Неманья Трифунович

6

Щось на зразок

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

Так що ви можете, наприклад, мати

assert(n == true);

і отримайте назву вихідного файла та номер рядка проблеми, надруковані у вашому журналі, якщо n - помилкове.

Якщо ви використовуєте звичайний функціональний дзвінок, такий як

void assert(bool val);

замість макросу все, що ви можете отримати, - це номер рядка функції затвердження, надрукований до журналу, що було б менш корисно.


Навіщо винаходити велосипед , коли реалізація стандартної бібліотеки вже забезпечує з допомогою <cassert>в assert()макросі, який вивантажує / лінію / дані функції файлу? (у всіх реалізаціях, які я бачив, все одно)
підкреслюю

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

На відміну від «переважного» шаблонного рішення, обговорюваного в поточному потоці, ви можете використовувати його як постійний вираз:

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

2
Це можна зробити з шаблонами більш безпечним способом (який не буде компілюватися, якщо передано покажчик, а не масив) stackoverflow.com/questions/720077/calculating-size-of-an-array/…
Motti

1
Тепер, коли у нас є constexpr в C ++ 11, безпечна (не макрос) версія також може бути використана в постійному виразі. template<typename T, std::size_t size> constexpr std::size_t array_size(T const (&)[size]) { return size; }
Девід Стоун

3

Ви можете використовувати #defines, щоб допомогти з налагодженням та тестовими сценаріями. Наприклад, створіть спеціальні варіанти реєстрації функцій пам'яті та створіть спеціальний memlog_preinclude.h:

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

Складіть код за допомогою:

gcc -Imemlog_preinclude.h ...

Посилання вашого memlog.o на остаточне зображення. Тепер ви керуєте malloc тощо, можливо для цілей реєстрації або для імітації відмов розподілу для тестів одиниць.


3

Коли ви приймаєте рішення під час компіляції за специфікою поведінки компілятора / ОС / обладнання.

Це дозволяє зробити ваш інтерфейс для конкретних функцій Comppiler / OS / Hardware.

#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

Я використовую макроси, щоб легко визначити винятки:

DEF_EXCEPTION(RessourceNotFound, "Ressource not found")

де DEF_EXCEPTION

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

2

Компілятори можуть відмовити ваш запит на вказівку.

Макроси завжди знайдуть своє місце.

Щось, що я вважаю корисним, - це #define DEBUG для відстеження налагодження - ви можете залишити його 1 під час налагодження проблеми (або навіть залишити її протягом усього циклу розробки), а потім вимкнути її, коли настає час доставки.


10
Якщо компілятор відмовиться від вашого запиту на рядок, це може бути дуже вагомою причиною. Хороший компілятор буде краще вкладати правильно, ніж ти, а поганий - мати більше проблем із продуктивністю, ніж це.
Девід Торнлі

@DavidThornley Або це може бути не великий оптимізаційний компілятор, як GCC або CLANG / LLVM. Деякі компілятори просто лайно.
Майлз Рут

2

На своїй останній роботі я працював над сканером вірусів. Щоб полегшити мені налагодження, у мене було багато журналів, що застрягли в усьому місці, але в додатку з великим попитом, як це, витрати на функціональний дзвінок є занадто дорогим. Отже, я придумав цей маленький Макрос, який все ще дозволив мені ввімкнути журнал налагодження у версії випуску на сайті клієнтів, без вартості виклику функції перевіряв би прапор налагодження і просто повертався, не записуючи нічого, або за умови включення , буде робити журнал ... Макрос було визначено так:

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

Через VA_ARGS у функціях журналу, це був хороший випадок для такого макросу.

До цього я використовував макрос у додатку підвищеної безпеки, який потрібно було повідомити користувачеві, що вони не мають правильного доступу, і він би сказав їм, який прапор їм потрібен.

Макрос (и), визначені як:

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

Потім ми могли просто розсипати чеки по всьому користувальницькому інтерфейсу, і це дозволить вам сказати, які ролі дозволено виконувати дію, яку ви намагалися зробити, якщо ви ще не виконували цю роль. Причиною для двох з них було повернення значення в одних місцях, а повернення з недійсної функції в інших ...

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

У будь-якому випадку, саме так я їх використовував, і я не впевнений, як це могло б допомогти з шаблонами ... Окрім цього, я намагаюся їх уникати, якщо дійсно НЕ СПОМОГА.


2

Ще один макрос foreach. T: тип, c: контейнер, i: ітератор

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

Використання (концепція показує, не реально):

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

Доступні кращі реалізації: Google "BOOST_FOREACH"

Добрі статті: Умовна любов: FOREACH Redux (Ерік Ніблер) http://www.artima.com/cppsource/foreach.html


2

Можливо, використання макросів використовується в незалежному від платформи розвитку. Подумайте про випадки невідповідності типів - з макросами ви можете просто використовувати різні файли заголовків - наприклад: --WIN_TYPES.H

typedef ...some struct

--POSIX_TYPES.h

typedef ...some another struct

--програма.h

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

#include TYPES_H

На мою думку, це набагато читабельніше, ніж реалізовувати його іншими способами.


2

Здається, VA_ARGS досі згадувалися лише побічно:

Коли ви пишете загальний код C ++ 03, і ​​вам потрібна змінна кількість (загальних) параметрів, ви можете використовувати макрос замість шаблону.

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

Примітка. Загалом, ім'я чек / кид також може бути включено до гіпотетичної get_op_from_nameфункції. Це лише приклад. Між викликом VA_ARGS може бути інший загальний код.

Як тільки ми отримаємо різноманітні шаблони за допомогою C ++ 11, ми можемо вирішити це «належним чином» за допомогою шаблону.


1

Я думаю, що цей трюк - це розумне використання препроцесора, яке неможливо імітувати функцією:

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

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

Тоді ви можете використовувати його так:

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

Ви також можете визначити макрос RELEASE_ONLY.


2
Цей трюк не працює відповідно до стандарту. Він намагається створити маркер коментарів через препроцесор, але коментарі слід видалити до запуску препроцесора. Відповідний компілятор викличе тут синтаксичну помилку.
Девід Торнлі

2
Вибачте Девід, але компілятор повинен містити другу копію видалення коментарів.
Джошуа

набагато простіше - зробити прапор налагодження глобальним const bool і використовувати такий код: if (debug) cout << "..."; - не потрібно макросів!
Стефан Монов

@Stefan: Дійсно, це я зараз роблю. Будь-який гідний компілятор не генерує жодного коду, якщо налагодження в цьому випадку помилкове.
Патьє Матьє

1

Ви можете #defineконстанти в командному рядку компілятора за допомогою параметра -Dабо /D. Це часто корисно при перехресному компілюванні одного і того ж програмного забезпечення для декількох платформ, оскільки ви можете мати свої файли контролювати, які константи визначені для кожної платформи.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.