#ifdefを使用して、開発中に異なるタイプの動作を切り替える


28

開発中に#ifdefを使用して、さまざまな種類の動作を切り替えることをお勧めしますか?たとえば、既存のコードの動作を変更したい場合、動作を変更する方法がいくつかあります。異なるアプローチをテストおよび比較するには、異なる実装を切り替える必要があります。通常、コードの変更は複雑で、異なるファイルの異なるメソッドに影響を及ぼします。

私は通常いくつかの識別子を導入し、そのようなことをします

void foo()
{
    doSomething1();
#ifdef APPROACH1
    foo_approach1();
#endif
    doSomething2();
#ifdef APPROACH2
    foo_approach2();
#endif
}

void bar()
{
    doSomething3();
#ifndef APPROACH3
    doSomething4();
#endif
    doSomething5();
#ifdef APPROACH2
    bar_approach2();
#endif
}

int main()
{
    foo();
    bar();
    return 0;
}

これにより、さまざまなアプローチをすばやく切り替えて、ソースコードの1つのコピーだけですべてを実行できます。開発のための良いアプローチですか、それともより良いプラクティスがありますか?



2
開発について話しているので、異なる実装を切り替えて実験するために、簡単にできることは何でもする必要があると思います。これは、特定の問題を解決するためのベストプラクティスではなく、開発中の個人的な好みに似ています。
エマーソンカルドーソ

1
切り替え可能な動作のために単一のプラグインポイントを維持するのに役立つため、戦略パターンまたは優れたオールポリモーフィズムを使用することをお勧めします。
pmf

4
一部のIDEは#ifdef、ブロックがオフになっている場合、ブロック内で何も評価しないことに注意してください。すべてのパスを定期的に構築しないと、コードが簡単に古くなってコンパイルできなくなる場合がありました。
ベリンロリチュ

見ていこの答えに、私は別の質問に与えたが。#ifdefs面倒を少なくするいくつかの方法を示します。
user1118321

回答:


9

このユースケースにはバージョン管理ブランチを使用することをお勧めします。これにより、実装間で差分を取り、それぞれの履歴を個別に保持できます。また、決定を下してバージョンの1つを削除する必要がある場合は、エラーが発生しやすい編集を行う代わりに、そのブランチを破棄します。


gitこの種のことは特に得意です。たぶんsvnhgまたはその他のケースではそうではないかもしれませんが、それはまだ可能です。
twalberg

それも私の最初の考えでした。「何か違うものをいじりたいですか?」git branch
ウェストールマン

42

ハンマーを持っていると、すべてが釘のように見えます。#ifdefプログラムでカスタム動作を取得するための一種の手段として使用する方法がわかれば、魅力的です。同じ間違いを犯したからです。

#ifdefプラットフォーム固有の値を定義するために既に使用されているMFC C ++で記述されたレガシープログラムを継承しました。つまり、特定のマクロ値を定義する(または場合によっては定義しない)だけで、32ビットプラットフォームまたは64ビットプラットフォームで使用するプログラムをコンパイルできます。

その後、クライアントのカスタム動作を記述する必要があるという問題が発生しました。ブランチを作成し、クライアント用に別のコードベースを作成することもできましたが、それは保守の地獄になります。起動時にプログラムによって読み取られる構成値を定義し、これらの値を使用して動作を決定することもできますが、カスタムセットアップを作成して、各クライアントの構成ファイルに適切な構成値を追加する必要があります。

私は誘惑され、屈服しました。#ifdefさまざまな動作を区別するために、コードにセクションを書きました。間違えないでください、最初はそれ以上のものではありませんでした。プログラムのバージョンをクライアントに再配布できるようにする非常に小さな動作の変更が行われ、複数のバージョンのコードベースを用意する必要はありません。

いずれにせよ、プログラムは全体的に一貫して動作しなくなっため、時間がたつにつれて、これはメンテナンス地獄になりました。プログラムのバージョンをテストする場合、クライアントが誰であるかを必ず知る必要がありました。コードはヘッダーファイルを1つまたは2つに削減しようとしましたが、非常に乱雑であり、#ifdef提供されたクイックフィックスアプローチは、そのようなソリューションが悪性ガンのようにプログラム全体に広がることを意味しました。

私はそれ以来私のレッスンを学びました、そしてあなたもそうすべきです。絶対に必要な場合に使用し、プラットフォームの変更に厳密に使用してください。プログラム(およびクライアント)の動作の違いにアプローチする最善の方法は、起動時に読み込まれる構成のみを変更することです。プログラムの一貫性は維持され、読み取りとデバッグの両方が容易になります。


「デバッグする場合、変数xを定義する」などのデバッグバージョンについては、これはロギングなどに役立つ可能性がありますが、デバッグが有効な場合と有効でない場合のプログラムの動作を完全に変更することもできます。
whn

8
@snb私はそれについて考えました。構成ファイルを変更し、より詳細にログを作成できるようにすることを今でも好みます。そうしないと、実稼働環境のプログラムで何かがおかしくなり、実行可能ファイルを完全に置き換えない限りデバッグできなくなります。理想的な状況であっても、これは望ましくありません。;)
ニール

ああ、デバッグするために再コンパイルする必要がない方が理想的です。私はそれについて考えませんでした!
whn

9
あなたが説明している極端な例については、この記事の「保守性の問題」小見出しの下の2番目の段落をご覧ください。 。 blogs.msdn.microsoft.com/vcblog/2014/06/10/...
ダン・ニーリー

2
@snbほとんどのロギングライブラリは、ロギングレベルのメカニズムを想定しています。デバッグ中に特定の情報をログに記録する場合は、低いログレベルでログに記録します(通常は「デバッグ」または「詳細」)。次に、アプリケーションには、ログに記録するレベルを指示する構成パラメーターがあります。したがって、答えはまだこの問題の構成です。これには、クライアント環境でこの低いログレベルを有効にできるという大きな利点もあります
jpmc26

21

一時的にあなたがやっていることには何も問題はありません(チェックイン前など):さまざまなテクニックの組み合わせをテストしたり、コードのセクションを無視したりするのに最適な方法です(ただし、それ自体が問題を物語っています)。

しかし、警告の言葉:#ifdefの枝を保管しないで同じことを読んで私の時間を無駄にするよりも少しイライラがあるだけで把握する、4つの異なる方法を実装し、私が読むべき1

#ifdefの読み取りは、実際にスキップすることを忘れないでください。絶対に必要以上に難しくしないでください。

#ifdefsはできる限り控えめに使用してください。一般に、デバッグ/リリースビルドなどの永続的な違いや異なるアーキテクチャ向けに、開発環境内でこれを実行する方法があります。

#ifdef分割を必要とする、含まれているライブラリバージョンに依存するライブラリ機能を記述しました。そのため、それが唯一の方法または最も簡単な方法である場合もありますが、それでも、それらを維持することに動揺する必要があります。


1

そのような#ifdefsを使用すると、コードが非常に読みにくくなります。

そのため、そのような#ifdefを使用しないでください。

ifdefを使用しない理由はたくさんありますが、私にとってはこれで十分です。

void foo()
{
    doSomething1();
#ifdef APPROACH1
    foo_approach1();
#endif
    doSomething2();
#ifdef APPROACH2
    foo_approach2();
#endif
}

できることがたくさんあります:

void foo()
{
    doSomething1();
    doSomething2();
}

void foo()
{
    doSomething1();
    foo_approach1();
    doSomething2();
}

void foo()
{
    doSomething1();
    doSomething2();
    foo_approach2();
}

void foo()
{
    doSomething1();
    foo_approach1();
    doSomething2();
    foo_approach2();
}

すべては、どのアプローチが定義されているかによって異なります。それが何をするかは、最初の一見では絶対に明確ではありません。

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