C ++マクロが役立つのはいつですか?[閉まっている]


177

Cのプリプロセッサは、正当に恐れやC ++コミュニティで敬遠されます。インライン関数、consts、およびテンプレートは通常、の安全で優れた代替手段#defineです。

次のマクロ:

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

タイプセーフより優れているわけではありません。

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

ただし、マクロにはその位置があります。プリプロセッサなしでは実行できないマクロの用途を挙げてください。

投票できるように、各ユースケースを別々の回答に入れてください。また、前任者なしで回答の1つを達成する方法を知っている場合は、その回答のコメントでその方法を指摘してください。


私はかつて、ビルドに45分かかったマクロでいっぱいのC ++アプリケーションを取り、マクロをインライン関数に置き換え、ビルドを15分未満にしました。
エンディアン


スレッドは、マクロが有益であるコンテキストに関するものであり、マクロが最適でないコンテキストに関するものではありません。
underscore_d

回答:


123

自動的にのようなものを渡すためのデバッグ機能のラッパーとして__FILE____LINE__など:

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

14
実際には、元のスニペット:<< ファイルは「:」<<微細で、FILEは「:」プレプロセッサによって、単一の文字列にして連結される文字列定数を生成します。
フランク・シュツェルバ2009年

12
これはのみプリプロセッサを必要と__FILE__し、__LINE__ また、プリプロセッサを必要とします。コードでそれらを使用することは、プリプロセッサの感染経路のようなものです。
TED

93

メソッドは常に完全でコンパイル可能なコードでなければなりません。マクロはコードフラグメントの場合があります。したがって、foreachマクロを定義できます。

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

次のように使用します。

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

C ++ 11以降、これは範囲ベースのforループによって置き換えられます


6
+1途方もなく複雑なイテレータ構文を使用している場合、foreachスタイルのマクロを作成すると、コードの読み取りと保守がはるかに容易になります。私はそれをやった、それはうまくいく。
未来主義者、2008年

9
ほとんどのコメントは、マクロが完全なコードではなくコードフラグメントである可能性があるという点とは完全に無関係です。しかし、つまらないことをありがとう。
jdmichal 2008年

12
これはC ++ではなくCです。C ++を実行している場合は、イテレーターとstd :: for_eachを使用する必要があります。
2009年

20
私はそうは思いません。ラムダ式の前for_eachは、各要素が実行されたコードが呼び出しポイントにローカルではなかったため、厄介なことでした。 foreach、(そしてBOOST_FOREACH手巻きのソリューションの代わりに強くお勧めします)、コードを反復サイトの近くに置いて読みやすくします。とは言っても、ラムダがリリースされると、for_each再び行く道になるかもしれません。
GManNickG 2009

8
そして、BOOST_FOREACH自体がマクロであることは注目に値します(ただし、非常によく考えられたマクロです)
タイラーマクヘンリー

59

ヘッダーファイルガードにはマクロが必要です。

マクロが必要な他の領域はありますか?多くはありません(ある場合)。

マクロの恩恵を受ける他の状況はありますか?はい!!!

私がマクロを使用する1つの場所は、非常に反復的なコードです。たとえば、他のインターフェイス(.NET、COM、Pythonなど)で使用するC ++コードをラップする場合、さまざまな種類の例外をキャッチする必要があります。これが私のやり方です:

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

これにより、メンテナンスも容易になります。新しい例外タイプを追加する必要がある場合でも、追加する必要がある場所は1つだけです。

他にも便利な例があります。その多くには、__FILE__および__LINE__プリプロセッサマクロが含まれています。

とにかく、マクロは正しく使用すると非常に便利です。マクロは悪ではありません-それらの誤用は悪です。


7
ほとんどのコンパイラは最近サポートしている#pragma onceので、ガードが本当に必要だとは思いません
1800 INFORMATION

13
ほとんどのものだけでなく、すべてのコンパイラー用に作成している場合です;-)
Steve Jessop '18

30
したがって、移植可能な標準プリプロセッサ機能の代わりに、プリプロセッサの使用を避けるためにプリプロセッサ拡張を使用することをお勧めしますか?ちょっとばかげているようです。
Logan Capaldo 2009年

#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 ++では、インライン関数を使用して同じことを得ることができます。} <br> #endif </ code>
paercebal 2008年

2
これにより、#defineは削除されますが、#ifdefと#endifは削除されません。とにかく、私はあなたに同意します。
Gorpik、

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)(他のハイジャックを防ぐため)に入れますが、単純にしたかったのです。
Motti

33

文字列リテラルは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 ++で入手できる機能であることを期待しています
David Stone

1
これは、C#でマクロを希望するようになった状況です。
Rawling、

2
これが+42できたらいいのに。非常に重要ですが、文字列リテラルの側面はよく思い出されません。
Daniel Kamil Kozar 2016年

24

関数内のプログラムフロー(returnbreakおよび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(++)拡張機能を作成する場合、例外文字列を設定してから例外が伝播され、次に-1orが返されNULLます。したがって、マクロを使用すると、定型コードを大幅に削減できます。
black_puppydog 2016年

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
多分もっと一般的なポイント:関数は引数を正確に1回評価します。マクロは引数をより多くまたはより少なく評価できます。
スティーブジェソップ

@ [Greg Rogers]マクロプリプロセッサが行うのは、置換テキストだけです。それを理解したら、それ以上の謎はないはずです。
1800情報

関数を呼び出す前に評価をブール値にする代わりに、andfをテンプレート化することで同等の動作を得ることができます。自分で試してみないと、あなたの言ったことが本当だと気づかなかったでしょう。面白い。
グレッグロジャース

テンプレートを使用してどのように正確にそれを行うことができますか?
1800情報

6
関数スタイルマクロの背後にある短絡演算を非表示にすることは、プロダクションコードで本当に見たくないものの1つです。
MikeMB 2016

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

また、プロパティのゲッターやセッターを生成するような(#と##の連結演算子を使用してコードを生成する必要がある場合もあります)

また、次のような関数を介して使用した場合にコンパイルされないコードが生成されることもあります。

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

(注:コードのコピー/ boostホームページから貼り付け)

これは(私見)より優れていstd::for_eachます。

したがって、マクロは通常のコンパイラルールの範囲外であるため、常に役立ちます。しかし、私が見たほとんどの場合、それらは事実上、適切なC ++に変換されていないCコードの残りです。


1
CPPは、コンパイラーが実行できないことに対してのみ使用してください。たとえば、RAISE_ERROR_STLはCPPを使用してファイル、行、関数のシグネチャを決定し、残りを行う関数(おそらくインライン)にそれらを渡す必要があります。
Rainer Blome、2016

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、2016

16

UnitTest ++のようなC ++のユニットテストフレームワークは、プリプロセッサマクロを中心に展開します。数行の単体テストコードは、手動で入力するのはまったく面白くないクラスの階層に展開されます。UnitTest ++のようなものがなくて、それがプリプロセッサの魔法なので、C ++の単体テストを効率的に作成する方法がわかりません。


ユニットテストはフレームワークなしで書くことが完全に可能です。結局、それは本当にあなたが望む種類の出力にのみ依存します。気にしない場合は、成功または失敗を示す単純な終了値で十分です。
18年

15

Cのプリプロセッサを恐れることは、蛍光灯を入手したからといって白熱電球を恐れるようなものです。はい、前者は{電気| プログラマ時間}非効率。はい、あなたは彼らに(文字通り)火傷する可能性があります。しかし、適切に処理すれば、彼らは仕事を成し遂げることができます。

組み込みシステムをプログラムする場合、Cは、フォームアセンブラを分離する唯一のオプションです。デスクトップでC ++を使用してプログラミングしてから、より小さな埋め込みターゲットに切り替えた後、多くのベアC機能(マクロを含む)の「不一致」について心配するのをやめ、それらから得られる最良かつ安全な使用法を理解しようとするだけです。特徴。

アレクサンダー・ステパノフは言います

C ++でプログラミングするときは、Cの遺産を恥じるべきではありませんが、それを最大限に活用します。C ++の唯一の問題、さらにはCの唯一の問題は、それら自体が独自のロジックと整合していない場合に発生します。


これは間違った態度だと思います。「適切に処理する」ことを学ぶことができるからといって、それが誰かの時間と努力の価値があることを意味するわけではありません。
Neil G

9

情報の豊富な例外のスロー、キャッチ、ロギング、およびQAインフラストラクチャの自動化されたログファイルスキャナーで、診断目的で__FILE__および__LINE__マクロを使用します。

たとえば、スローマクロOUR_OWN_THROWは、例外の種類とその例外のコンストラクターパラメーター(テキストによる説明を含む)と共に使用される場合があります。このような:

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

このマクロはもちろんInvalidOperationException、コンストラクターパラメーターとしての説明と共に例外をスローしますが、スローが発生したファイル名と行番号とそのテキストの説明で構成されるログファイルにもメッセージを書き込みます。スローされた例外はIDを取得し、これもログに記録されます。例外がコード内のどこかでキャッチされた場合は、そのようにマークされ、ログファイルにその特定の例外が処理されたことが示されるため、後でログオンする可能性のあるクラッシュの原因ではない可能性があります。未処理の例外は、自動化されたQAインフラストラクチャによって簡単にピックアップできます。


9

コードの繰り返し。

プリプロセッサライブラリ強化する方法を見てみましょう。これは一種のメタメタプログラミングです。トピック->動機であなたは良い例を見つけることができます。


私は、すべてではないにしても、ほとんどすべての場合-関数の呼び出しでコードの繰り返しを回避できます。
einpoklum

@einpoklum:同意しません。リンク
Ruggero Turra

9

テンプレートを含むc ++の「言語構造」を使用して実行することは決してできないであろう、非常に高度で有用なものは、まだプリプロセッサ(マクロ)を使用して構築できます。

例:

Cの識別子と文字列の両方を作成する

列挙型の変数をCの文字列として使用する簡単な方法

ブーストプリプロセッサメタプログラミング


3番目のリンクが壊れています
Robin Hartland

見てくださいstdio.hsal.h内のファイルvc12より良い理解のために。
Elshan 2016年

7

情報を1か所で定義できるようにときどきマクロを使用しますが、コードのさまざまな部分でさまざまな方法で使用します。それはほんの少し悪です:)

たとえば、「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;

また、プライベート初期化関数では、すべてのフィールドを使用して、テーブルにデータを入力できます。

#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/...
須磨

6

一般的な用途の1つはコンパイル環境を検出することです。クロスプラットフォーム開発では、たとえばLinux用に1セットのコードを記述し、目的に合わせてクロスプラットフォームライブラリがまだ存在しない場合はWindows用に1セットのコードを記述できます。

したがって、大まかな例では、クロスプラットフォームのミューテックスは

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

関数の場合、タイプセーフを明示的に無視する場合に役立ちます。ASSERTを実行するための上記および下記の多くの例など。もちろん、多くのC / C ++機能と同じように、自分の足で撃つことができますが、言語によってツールが提供され、何をすべきかを決定できます。


質問者が尋ねたので:これは、プラットフォームごとに異なるインクルードパスを介して異なるヘッダーを含めることにより、マクロなしで実行できます。マクロの方が便利な場合が多いと思います。
スティーブジェソップ

同感です。その目的でマクロを使い始めると、コードがすぐに読みにくくなる可能性があります
Nemanja Trifunovic

6

何かのようなもの

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

たとえば、次のようにすることができます

assert(n == true);

nがfalseの場合、問題のソースファイル名と行番号をログに出力します。

次のような通常の関数呼び出しを使用する場合

void assert(bool val);

マクロの代わりに、取得できるのは、ログに出力されるassert関数の行番号だけです。これはあまり役に立ちません。


標準ライブラリの実装がファイル/行/関数情報をダンプするマクロを介し<cassert>てすでに提供しているのに、なぜホイールを再発明するのassert()ですか?(とにかく私が見たすべての実装で)
underscore_d

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
C ++ 11にconstexprがあるので、安全な(非マクロ)バージョンも定数式で使用できます。template<typename T, std::size_t size> constexpr std::size_t array_size(T const (&)[size]) { return size; }
David Stone、

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

コンパイル時にコンパイラ/ OS /ハードウェア固有の動作を決定するとき。

コンパイラ/ OS /ハードウェア固有の機能へのインターフェースを作成できます。

#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 for debug tracingを使用すると便利です。問題のデバッグ中は1のままにして(または開発サイクル全体でオンのままにして)、出荷するときにオフにしてください。


10
コンパイラーがインラインへの要求を拒否する場合、それは非常に正当な理由がある可能性があります。良いコンパイラーはあなたよりも適切にインライン化することでより良くなり、悪いものはこれよりも多くのパフォーマンス問題を与えるでしょう。
David Thornley、

@DavidThornleyまたは、GCCやCLANG / LLVMのような最適化コンパイラではないかもしれません。一部のコンパイラはただのがらくたです。
Miles Rout

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

次に、UI全体にチェックを振りかけるだけで、実行しようとしたアクションの実行が許可されているロールがまだない場合は、そのロールが通知されます。2つの理由は、一部の場所では値を返し、他の場所ではvoid関数から戻るためです...

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"

利用可能な良い記事:Conditional Love:FOREACH Redux(Eric Niebler)http://www.artima.com/cppsource/foreach.html


2

たぶん、マクロのすばらしい使い方は、プラットフォームに依存しない開発にあるのでしょう。型の不一致のケースについて考えてください-マクロを使用すると、次のような異なるヘッダーファイルを使用できます--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

私の意見では、他の方法で実装するよりも読みやすいです。


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
このトリックは、標準に従って機能しません。プリプロセッサを介してコメントマーカーを作成しようとしますが、プリプロセッサを実行する前にコメントを削除する必要があります。適合コンパイラは、ここで構文エラーを引き起こします。
David Thornley、

2
申し訳ありませんが、コンパイラにはコメント削除の2番目のコピーが含まれている必要があります。
ジョシュア

デバッグフラグをグローバルconst boolにして、次のようなコードを使用する方がはるかに簡単です。if(debug)cout << "..."; -マクロは必要ありません!
Stefan Monov

@Stefan:確かに、それは私が今やっていることです。デバッグがfalseの場合、適切なコンパイラはコードを生成しません。
MathieuPagé10年

1

またはオプション#defineを使用して、コンパイラのコマンドラインで定数を指定できます。これは、複数のプラットフォームで同じソフトウェアをクロスコンパイルする場合に役立ちます。これは、メイクファイルで各プラットフォームに定義されている定数を制御できるためです。-D/D

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.