• ベストアンサー

ソースとヘッダについて

VC++2008を使っています。これは他でもいえるのではないかと思いますが、一応プロジェクトの種類はWindowsFormアプリケーションです。 字数削減や書きなおしを楽にするために、ある程度のマクロを織り交ぜたコーディングをしたいのですが ソースに#defineしたものの効果範囲は、#undefの場所まで あるいはそれがなければそのソースの終了地点まで でしょうか? もしそうなら、ソースに書く分なら「別のソースに同じマクロを、全く違う定義をして使っても問題ない」という事でしょうか? ヘッダに書いた場合は、なんか#undef文を下の方に書いてもまるで上に書いたかのように「駄目」という判定になってしまったのですが、かといって#undefを書かないとそのヘッダをインクルードする他のヘッダにも影響を与えてしまうようで… これをどうにかできる方法はありませんか? また、ソースでは #pragma once などをしなくてもいいのですか? (あるいは書いてもソースはオブジェクトファイルに変換され、実質的にはコンパイル後に影響が出るため、プリプロセッサはコンパイル前に完了するので、ソースにこれを書いたところで無意味ということでしょうか?)

質問者が選んだベストアンサー

  • ベストアンサー
  • zwi
  • ベストアンサー率56% (730/1282)
回答No.7

C++/CLI系の文法には詳しくないので内容には触れませんが、 #define き tree #define せ き->SelectedNode #define か TN^ a=(TN^)せ #define あ Nodes->Add #define れ if (!a) return これはまたすごい・・・。半年とか経つと自分でなぜこうしたか忘れないか私は不安ですが。普通マクロ名は、機能を表す名前を使います。理由は忘れるから。 じゃあ、同じソースコード内でも前半と後半でおなじ「き」でも意味が違うってコードを考えているんですね。 その1。いちいちエディタでカーソルかざさないと機能が確認できないのは可読性が低いのが問題かと。 2.#dafineで定義されたものが増えてくると混乱するかも。 3.マクロを多用するとプログラムコードが増大する(今時のメモリなら気にしなくて良いが)のと、マクロ内でエラーが発生するとデバッガのカーソルが止まるのはマクロの名前の部分なので、どの部分か判断に困りますのでそこが気になるかな。 まぁ、他の人に見せるコードじゃなければ何をやって良いのですが、一度やってみて作れるか作れないか、可読性が良いかは自分で判断すればよいと思います。デバッグで困ったり一月後に読めなくければ大丈夫かと。 #define ご(x,y) if (x) goto y; まぁ、構文的には普通ですがgotoはあまり多用しないほうがと要らぬお節介を書いときます。 >「同じソース」には「#pragma onceを書いたファイル自身」をプリプロセッサが一回しかincludeをしないでOKですか? コンパイルの単位=ソースコード内に一回しか#includeさえません。 >そして、「同じヘッダにも」その効果は適用されますか? #includeのファイルがネストしていても同じです。 #include "a.h"と成っていてa.h内で#include "b.h"だとしたらa.hとb.hに各々「#pragma once」が書かれていれば一回しかインクルードされません。 >そしてもしそうならつまり、ソースあるいはヘッダが異なればそれぞれで「一回ずつは許容される」という意味になりますよね? いえヘッダがヘッダをインクルードしている場合は、違うと思うんですけどね。実験したことが無いので試してみてください。 >ということは、逆に言うと#pragma onceをつけないヘッダなら、同じファイル内で複数回includeしてもOKになる、ということでしょうか? 同じ定義を#undefなしに#defineするとエラーになりますけどね。 >む?よく考えてみるとところで……ソースファイルは、インクルードする方法はあるのでしょうか? #includeは実は何でもインクルードできます。 #include "aa.cpp"もOKです。

LongSecret
質問者

お礼

はい、実はgotoについては( )と2つの引数(?)の実験として作ったという意味合いの方が強く、これを使おうと思う場合は結構限られてて 「沢山のif()~があって、それが同じような結果を出す場合で、try catch にしたくない場合」 「何層にもなったループ文の内側の方から、一定の状況の時に一気に抜け出したい場合」 ぐらいです。 なので、消しても問題ないぐらいだとは思っています。 他で充分同等に書けるようなところでgotoを多用すると可読性下がる可能性高し!というのは納得ですからね。 残りの事につきましても、納得しました。ありがとうございます!

LongSecret
質問者

補足

あ!いい事思いつきました。 万が一それでも忘れた場合の事を考えて「以下の理由づけをコメントでそれらdefineの右側にでも書いておけばいい」ですよね? そうすれば万が一忘れても、また一回見れば「ああ、そうそう」 で、それ以後またそれでやっていけますので…。 おかげさまで思いつけました。ありがとうございます。 (早速メモっておこう。)

その他の回答 (15)

  • FBL
  • ベストアンサー率0% (0/3)
回答No.16

補足やお礼欄に書くのは気が引けるので、状況整理用に別IDで回答欄を使わせてください。 質問者です。 今回の質問全体を眺めてみて、既にほぼ締切という地点に至ったと考えました。 以下の点に未解決の問題は含まれますでしょうか? しばらく待ってからもし何もなければ、もう内容的に十分だと思うので、これでOKとさせていただきます。 皆さんありがとうございます。

LongSecret
質問者

補足

あ・・・一般人にしたつもりが経験者にしてしまいました。(ミスです。)

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.15

やるかどうかわかりませんが マクロで気おつける点なら 引数つきのマクロを定義する際に その引数の評価順にも配慮が必要だったかと

LongSecret
質問者

お礼

ありがとうございます。 なるほど、それは「普通に字が展開されるだけでは説明できないような手順に」変わったりする、事があるのでしょうか? それともやはり「純粋に字が展開されるだけだからこそ」順番には配慮が必要 という事でしょうか?

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.14

昔から (?) よくいわれるんだけど, 「短いプログラム」と「読みやすいプログラム」は必ずしも一致しません. 変なマクロで「短くする」ことだけを考えると, #13 でいわれるように後で泣くことになりかねません. APL とか J は「はまるととても短く書ける」んだけど, メンテナンスする気にはならんなぁ. そもそも「忘れたら定義を見直せばいい」というのが既にナンセンス. そんな風にあっちこっち動くと, 本来考えなきゃいけないロジックを見失うことになりかねません. 色々言われるけど (そして個人的には嫌いだけど) 「ハンガリー記法」も変数の型を「その場でわかるようにしよう」という思想の発現なんだよね. こういうことを考える人がいるってことは, 「忘れたら定義を見直せばいい」というのがいかに非効率的かを表しているといっていいのではないかな. あと, #13 では「マクロについて」っていってるけど, 常識的に考えてマクロに限らず「違う意味」を表すなら「違う名前」にすべきだ. そうでなければ「名前を付ける」意味がない. 「名は体を表す」っていうけどまさにその通り. そういえば #undef ってあんまり使わないなぁ....

LongSecret
質問者

お礼

何度も言いましたが「私にとっては」 「tree->SelectedNode」 より 「せ」 の方が「一瞬で分かりやすい」です。 そして自分でたっぷり時間を割いて頑張って作っている作品に対して「忘れる」ということがまず考えにくいです。 もし 「tree->SelectedState」 なんてものがあったら、これだけなら楽ですが多用されるとSelectedNodeと見分けるのが少々面倒です。アルファベッドには漢字のように一時に大した意味がないので、組み合わせで読まねばなりません。 >常識的に考えてマクロに限らず「違う意味」を表すなら「違う名前」にすべきだ というのは、疑問です。 例えば「今巣に帰った鷲」 は 「いますにかえったわし」 と聞こえますが なぜ 「います」 で途切れてない事が分かるのでしょうか? またなぜ、最後の部分が 「たわし」 なのに、ごしごしこするあれを想像しないで済むのでしょうか? それは全体で文意を捉えられているからです。 それから、「イントネーションが違う」からです。 後者については、マクロにおいても「全く違う意味ならば」そうそう混乱なんてしないですむ、という考え方も出来るでしょう。 こういう事が無意識のレベルで出来るくらいなら、問題ないと思います。なので多くても2種類くらいで充分だろうと、書きました。

LongSecret
質問者

補足

…なるほど インテリセンスってああいうやつをひとくくりにした呼び方ですか。 それは便利ですので、有効活用させていただいています。こちらのように、VC++2008のクイックヒントの仕様について、以前ちょっと試したことがありました。 http://www.geocities.jp/shank_long7_02/b.html やはり使えるものは有効に使っていきたいですよね?

  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.13

>一度意味が分かると簡単で そもそも、一度みて意味を 理解しないといけない時点でナンセンス。 #defineと#undefでとありますが、再定義される識別子は、 「同じ効果を持つマクロ」なのですか? 特に、「沢山の個所で使う」マクロについて、 違う効果をもつ同じ名前の識別子を定義するなんて、 ソースを読解する上で非常に困難となるうえ、 ifやwhile、forなどでの括弧の整合性や条件式の評価副作用などを きちんと保てるかなどの問題で非常にナンセンスです。 (どっちを使っているかは、デバッガで追わないと分からないし マクロだから下手するとブレークポイントすら張れない。) >#define AA do {a=1; b=1; } while (0) 括弧の整合性を考えるならこれでいいと思います。 #よくやるのはCOMとかで、例えば #define Release(p) do{ if(p){ p->Release(); } }while(0) #とかですね。 > 字数削減や書きなおしを楽にするために とありますが、下手をするとマクロで応用が利かなくなる部分が 出てきて最終的にプリプロセスで処理されたコードの字数が増えたり、 書き直しやメンテにかかる手間が増えるだけだです。 上記のような事を踏まえてこのようなことを なさっているのでしょうか? 質問や補足を読む限りメリットと思えるようなところが 少ないと思うのですがいかがでしょう?

LongSecret
質問者

お礼

>そもそも、一度みて意味を 人に見せる場合は、という意味です。 実際には自分でいじるんですから、見なくても把握出来ます。 「このファイルでは大体こういう事をやってる」 と分かっています 一つのファイルになるべく関係性の薄いものは使わないようにする方がいいので、頻繁にundefはしません。 もしものための予防線です。 一番の目的は「目で見たときの瞬間的な」分かりやすさ(全体像を捉えやすいか否か)なので、「include2つ」だって字数と行数を割いてますし、意図に沿うなら、なるべく控えた方がすっきりしてます。 …私は少なくとも、「知らないWindowsAPI」は調べて意味を理解しないと使えませんし、文字から大よその意味は把握できても、どうやると動くのか、引数は何か、等が分からないと使えません。 ちゃんとコーディングするならどんなコードでも、どんな識別子でも、意味と使い方を把握しないと無理です。 文字の並びを見ただけで細かい挙動まで把握する事は、どの道不可能です。 (例えばifやwhile、forなどでも、誰でも一番最初は意味が分からなかったか、「おおよそこうではないか」ぐらいには予想できても、どう使うのかは分からなかったはずです) だからこれらによって >書き直しやメンテにかかる手間が増える とは思いません。例えばもしTreeview^ treeの名前を変える場合 き treeの部分だけをいじればいいです。 Nodes->Add は、自作ノードではAddのみで出来るようにしますが それもその部分だけで済みます。 ※やってない場合は全般にわたって修正する必要があります。 >括弧の整合性や条件式の評価副作用などをきちんと保てるか これは「慣れ」の問題に過ぎないと思います。 英語が全く話せない日本人が、英語しか話さない人のところに言ったら大変だと思いますが、逆もまた大変です。 「百」という字に沢山読み方があるから日本語はだめだ、というわけでは決してないと思います。 両者は 「自分が話せる言語を使う事が楽なのですが」 ある意味ではそう感じているだけです。話せるようになれば大したことないでしょう。 マクロも、慣れればそもそも最初に書く段階であまり間違いを犯さなくなります。 邪推せず「ただ単に文字を置き換える」という感覚でやればいいので、そのままこれが展開されたらどうなるかなというのは、「目で見れば分かる」のです。

LongSecret
質問者

補足

……しかし、やはり解放処理には使えますね。 それは丁度思いついて「これはいい」と思っていました。 気が合いました♪ あとは、ここが疑問点なのですが 「デバッガで追わないと分からないような」 というのが、よく分かりません。 よほど込み入ったループ文とかならあれですが それだって余計に字数を伸ばすより、無駄は省いてコンパクトに削って行った方が目で見て制御しやすいです。 (場合によっては何もしなくてもデバッガで追うのが大変な場合はありますし) >下手をするとマクロで応用が利かなくなる部分が というのは、奇妙です。 「応用が利かない事を承知で短くするのが得策でない場合」は、「応用が利くようなマクロを採用すれば良い」からです。 大抵の場合、ちょっとの工夫・ちょっとの労力で応用が利かせられるようになります。 大事なのは「どこまで全体像が見えてるか」ということです。 あまりに全体が見えない時にマクロをやたらと作るのは得策ではないのは確かです。ただ、多用する事が分かってて早々変更しないだろうという部分に使わない事に、こだわる理由もないと思います。 結果として自分のやりたい事を十分に達成できれば問題はないわけですから。 全部自分で書くのなら、全部一つ一つ書きながらやってくので、どこがどう展開されてるかどれがどういう機能でどうつながってるか把握してますし しょっちゅう使うものを定義するのですから、意味を忘れるという事も、そもそも考えにくいです。 自分で考えたのですし、それをしょっちゅう使ってたのに、半年くらいですっかり忘れる事なんてあるでしょうか…? これらは「定義だけを見るんじゃない」事が重要です。 「文中で使われている」のです。 よって、節々からノードの操作であることは明白ですし、完全な記憶喪失でもない限り、多少忘れたとしても前後の文意を一瞬見ただけで、チェックしなくても大抵思い出せる気がします。 ここがもう一つのポイントで 字数削減の効果として「ひと眼で網膜に収められる内容量」が増えます。 一回作ってほったらかしなんて事やるんなら微妙かもしれませんが、私は新事実が判明するたびに可能ならパフォーマンス・コーディング等の改良が可能かどうかを考えますので 一瞬で全体像を把握出来れば、その方が遥かに大きなメリットです。 上記のような事を踏まえてこのようなことをしていますが、実際やってて今のところかなり楽でした。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.12

「空白の有無を含めて全く同じなら問題なし」というのは, 規格上決まっているので大丈夫です. 「空白の有無」もポイントになるので #define A a -> b #define A a -> b は OK だけど #define A a -> b #define A a->b がアウトってのもまた微妙. ちなみに #define AA {a=1; b=1;} #define BB {a=2; b=2;} int a, b, c; a=b=c=0; if (c == 1) AA; else BB; printf("a = %d\n", a); というのもやりがちですね. これを避けるなら #define AA do {a=1; b=1; } while (0) とするのがオーソドックスか?

LongSecret
質問者

お礼

だぶん題意に対する私の解釈が違ってなければ、それだけの文ならこれもありではないでしょうか? #define CC a=b=(c==1) CC ?1:2; …汎用性は微妙ですけど(笑)

  • zwi
  • ベストアンサー率56% (730/1282)
回答No.11

>「;」が一個余分なのと、cをちゃんと宣言していないのが原因だと思います。 Cを書き忘れたのは私のミスですね。失礼しました。 で、「;」が一個余分なのは空文扱いで問題ないです。 実の問題はマクロが 1.#define AA a=1;b=1; と 2.#define AA { a=1;b=1; } で実行結果が変わります。 1.だとprintfされるのは、0,1,0 2.だとprintfされるのは、0,0,0 なぜでしょうか?

LongSecret
質問者

お礼

ちょ…(微笑) お気づかい本当にありがとうございます。 あ!本当だ。 空文で警告されたことがあったような気がするのですが それってどういう場合起きるんでしょうか?それとも私の思いすごしかな…? if (条件) 文1; 文2; は条件が当てはまらないので文1が実行されず 文2は無条件に実行。 if (条件){ 文1; 文2; } は文1・文2が複合文なので、条件に背けば両方飛ばされる。 という事だと思います。 こういう感じの機敏については割と好き(えげつないどっかのテンプレート本に比べれば数段マシな気持ち)かもなので、ある程度なら大丈夫ですが、やばそうな複雑な場面だったら普通に書く事を選択するかもしれません。 というより 基本的には普通に書いておいて、それでかぶりがあって今後使えそうで、かつ「こうすれば」後でいじる時手直しが楽そうなら…って考えるのがベターでしょうね。 下記のひらがな群は、そのように(書いてみて、かぶっててよく使いそうなものを…)しましたからね。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.10

えぇと, 「(空白の有無を含めて) 全く同じ定義」であれば #undef せずに #define しても問題なし, ですよね>#7. もちろん 2個以上の空白は 1個の空白とみなす. で「入力支援機構」なんだけど.... Visual Studio なら「IntelliSense」ってやつですね. これでそれなりに簡単に入力できるようになるはずですよ. メンバーくらいなら候補に入りますし.

LongSecret
質問者

お礼

あ、やはりそれってサポートされてる動作なのでしょうか? 私もやってみてたのですが、確かに同じやつなら警告出ませんでした。 IntelliSense…ですか♪ 調べてみて、分からない部分が出るか、疑問がわいたらそれ用の質問を立てようと思います。 今回おおよそ締め切っても良い地点には来れたと思いますが、ちょっと眠くなって判断力が鈍ってると思うので、実際に締め切るのは起きてからとさせてください。 皆さんありがとうございます。

  • zwi
  • ベストアンサー率56% (730/1282)
回答No.9

そうそう、昔らからマクロを書くときに気をつけることの話で以下の様なコードを知っていますか? #define AA a=1;b=1; int a,b; a=b=c=0; if( c==1 ) AA; printf( "%d,%d,%d", a,b,c ); ってコードですが、実は間違っています。 何が間違っていて、どうすれば正しいでしょう? これを理解しているなら、マクロでそうそう間違いをしでかさないと思います。が、プロがマクロを避けている理由の一つで、プロでもこれの応用で間違うことがあります。中々ミスに気づかないのもポイントです。

LongSecret
質問者

お礼

今までの事を思い出すと… 「;」が一個余分なのと、cをちゃんと宣言していないのが原因だと思います。 #define AA a=1;b=1; int a,b,c; a=b=c=0; if( c==1 ) AA printf( "%d,%d,%d", a,b,c ); なら大丈夫…かな? 感覚的には #define AA { a=1;b=1; } とした方がよりテンションは上がりますが… いかがでしょうか? もちろん、数式とかだともっとややこしい場合もあるかもしれませんが ややこしくなる場合はむしろ「一括で!」 か、関数的なら 「define」より「inline」で、というのが思いつきます。 この考え方は正しいでしょうか?

LongSecret
質問者

補足

あ、題意が「AAの中身を、判定に応じて一括」という前提がもしある場合なら 後者じゃないと×ですね。 どっちでもビルドは出来ると思いますが。 マクロは単なる文字の置き換え機能ですよね? ある意味気が利かないともいえますが そこが面白いとも取れます。

  • redfox63
  • ベストアンサー率71% (1325/1856)
回答No.8

> あ!いい事思いつきました。 > 万が一それでも忘れた場合の事を考えて「以下の理由づけをコメント > でそれらdefineの右側にでも書いておけばいい」ですよね? > そうすれば万が一忘れても、また一回見れば「ああ、そうそう」 これでは本末転倒なような気がします もともと defineで文字数削減をしたかったのでしょうから ・・・

LongSecret
質問者

お礼

「#define文を用意するヘッダの、定義の右側に…」という意味です。 それなら一か所で済みますよね? なので沢山の個所で使うなら本末転倒って事はないと思います。 どうにも忘れたら「右クリック→宣言へ移動」あるいは「定義へ移動」で分かりますよね? あと、ここまで減らせればむしろ「逆に可読性が上がる」という考え方も出来ると思います。 それも実は狙いのひとつです。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.6

「ソースの終了地点」について, 混乱させてしまったらごめんなさい. 以下追加点についてですが: そもそもヘッダファイルというのは「意識しないうちに何回も #include される可能性がある」ものです. 従って, 多くの場合「何回 #include されてもいい」ように書く必要があります. #pragma once はそのためのものですし, この機能がない場合には #ifdef~#endif によるインクルードガードを入れるのが普通です. 言い替えれば, これらのことをしなければ #include ごとに毎回そこにインクルードすることになります. いろいろなものを定義する場合にはこれでは困るのですが, 稀に「インクルードするごとに解釈される必要がある」といきもあって (例えば assert.h), その場合には当然「1回しかインクルードされないようにする」工夫は入れません. あと, プリプロセッサは「ソースファイル」とか「ヘッダファイル」とかいうことは全く気にしないので, (存在しさえすれば) 何でも #include することができます. あと, そのマクロたちだけど.... 老婆心から言わせてもらえば, やめることを強く勧めます. それこそ統合環境なりエディタなりの入力支援機構を使っていくべきです.

LongSecret
質問者

お礼

ありがとうございます。 なるほど、汎用的一括includeで手間を省ければ確かにそれもまた便利ですね。 じっくり検討・研究して最善策を導きたいと思います。 そして、便利なものはとことん知りたいのでお尋ねしますが >入力支援機構 とはいかなるものでしょうか?

関連するQ&A

専門家に質問してみよう