- ベストアンサー
ソースとヘッダについて
VC++2008を使っています。これは他でもいえるのではないかと思いますが、一応プロジェクトの種類はWindowsFormアプリケーションです。 字数削減や書きなおしを楽にするために、ある程度のマクロを織り交ぜたコーディングをしたいのですが ソースに#defineしたものの効果範囲は、#undefの場所まで あるいはそれがなければそのソースの終了地点まで でしょうか? もしそうなら、ソースに書く分なら「別のソースに同じマクロを、全く違う定義をして使っても問題ない」という事でしょうか? ヘッダに書いた場合は、なんか#undef文を下の方に書いてもまるで上に書いたかのように「駄目」という判定になってしまったのですが、かといって#undefを書かないとそのヘッダをインクルードする他のヘッダにも影響を与えてしまうようで… これをどうにかできる方法はありませんか? また、ソースでは #pragma once などをしなくてもいいのですか? (あるいは書いてもソースはオブジェクトファイルに変換され、実質的にはコンパイル後に影響が出るため、プリプロセッサはコンパイル前に完了するので、ソースにこれを書いたところで無意味ということでしょうか?)
- みんなの回答 (16)
- 専門家の回答
質問者が選んだベストアンサー
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です。
その他の回答 (15)
- D-Matsu
- ベストアンサー率45% (1080/2394)
なんか、目的が↓の入賞でも目指してるような感じに見えます。 http://www.ioccc.org/main.html #見た目で意味が取れないdefineは一般的に邪道です それはさておき。 #pragma once は、「複数回インクルード宣言があった場合でも、最初の一回しか読み込まない」ということです。 #要は多重インクルードガード なので、 > つけないヘッダなら、同じファイル内で複数回includeしてもOKになる というのは正反対の誤った理解です。 逆に何も対策していないファイルを複数回インクルードしたら多重定義エラーが多発しますね。
お礼
ありがとうございます。 >見た目で意味が については、問題ありません。 ・「き tree」は一瞬で分かります。 ・「せ き->SelectedNode」は、「せ」はSelectedNodeおよび「選択されているノード」のどちらでも「せ」なので分かります。 ・「か TN^ a=(TN^)せ」は「きゃ」(cast)でもよかったのですが、打つ時三文字なので「型」「変える」の「か」にしました。 ・「あ Nodes->Add」は、しょっちゅう書いてた「Add」の「あ」です。 ・「れ if (!a) return」は「return」の「れ」です。 なお、これらは実際よく使うので定義しました。 一度意味が分かると簡単で --------------------- か, ^b = gcnew TN; れ; a->Level ? a->Parent->あ(b) : き->あ(b); b->あ(a=a->CopyN()); せ = a; b->Expand(); --------------------- こういう風に、字数がかなり少なくて済むのと、「ひらがなとかはdefine」という自分用ルールを作っておくことで これらが「define」であることも一瞬で分かり しかも せ = a; などは 「ああ、ツリービューの選択ノードを『TN^ a』にするんだな」 と、一瞬で分かります。 >というのは正反対の誤った理解です。 >逆に何も対策していないファイルを複数回インクルードしたら多重定義エラーが多発しますね。 はい、確かに普通と逆転の発想だとは思いますが、しかし結果としてはそうですよね? 「ソースファイルではundefしなくてもほかに影響はない」 ならば define専用(および、一括undef専用)のファイルを作っておき「それをソースファイルのみに使う」とでもすれば、多重定義エラーはしないのではないでしょうか?
- zwi
- ベストアンサー率56% (730/1282)
なにか意図を聞いたほうが良いようですね。#defineマクロで同じ名前のものをソースコード毎に違う機能や値として使いたいのはどんな場合ですか? プロでも仕方なく書く場合がありますが、基本的にバグになるので避けます。どのように使うか聞かせてください。
お礼
う~ん 具体的な別利用方法の細部については決まっていないのですが 物凄く見通しが良くなった事例があったので、例えば.NET FrameworkのTreeNodeを継承したTNクラスを作り、さらにTreeView^ treeを用意したとして こういうものを使うつもりです。 #define き tree #define せ き->SelectedNode #define か TN^ a=(TN^)せ #define あ Nodes->Add #define れ if (!a) return これは、後で .NET FrameworkのTreeNodeやTreeViewクラスに相当するものを自作しようと思っていますが、その場合も、似せて作れば使いまわせるかな?とも思っています。 それとかこういうものも #define ご(x,y) if (x) goto y; こういう風に文字数が少ないと、もしかしたらいつか不足するかも(漢字を使えばいいかもしれませんが)と思いまして…
補足
あとは下記 >#pragma once について 以下の内容の真偽が分かれば最高です。 これらの解釈で問題ないでしょうか?
- zwi
- ベストアンサー率56% (730/1282)
>もしかして #define や #pragma once など、プリプロセッサについては、ソースであってもヘッダであっても、全部コンパイル前(あるいは同時…?)に処理されるのでしょうか? プリプロセス(前処理)とは、コンパイル前に行われる処理です。だから#inlucdeコードの挿入も#defineの置き換えもも全てコンパイル前に完了します。
お礼
ありがとうございます。 よかった やはりそうですよね? 最初そう思ってたんですが、質問をする直前に「あれ?」ってなって、質問後また混沌とし、やっとこれで落ち着けた形です。
- zwi
- ベストアンサー率56% (730/1282)
>ソースに#defineしたものの効果範囲は、#undefの場所まであるいはそれがなければそのソースの終了地点まででしょうか? その通りです。連続したソースの中に無いとだめです。 >もしそうなら、ソースに書く分なら「別のソースに同じマクロを、全く違う定義をして使っても問題ない」という事でしょうか? C/C++としては問題ないですが、同じ意味で違う値を持つことを意識的に行う場合を除いては危険なので避けます。同じ名前なのに値が違うことは、思い込みで錯誤が生まれやすくバグを生み出す要因だからです。 >ヘッダに書いた場合は、なんか#undef文を下の方に書いてもまるで上に書いたかのように「駄目」という判定になってしまったのですが、かといって#undefを書かないとそのヘッダをインクルードする他のヘッダにも影響を与えてしまうようで… ヘッダ中だけ有効な#defineをしたいと言う事でしょうか?可能なはずですが。 ---- xx.h ---- #define AA 1 // この間だけ有効 #undef AA >また、ソースでは#pragma onceなどをしなくてもいいのですか? #pragma onceは同じソースに#includeをプリプロセッサが一回しかしないという意味です。ソース自体はどこにも#includeしていないなら意味がありません。
お礼
ありがとうございます。 >C/C++としては問題ないです という部分が分かれば大丈夫です。ややこしすぎるのは避けますが、自分で管理するなら、場合によっては「えげつないまでの字数削減」のために、例えばひとつのものに2種類の意味を割り当てるぐらいの範囲なら大丈夫かなと思っています。 (一つのファイルに一つなら、マウスカーソルをその上に乗せれば内容が表示されますし、エラーが出ればどこがそうなったかである程度判断できると思うので、さして気にするほどの事はないかなと) >可能なはずですが。 はい、可能ですよね? おかしいなぁ…確かに今やってみると出来るのですが さっきはなぜ出来なかったのだろう…? たぶん、何か別の要因があったんですよね。 出来ると分かれば、問題はありませんし。 >#pragma once について つまり下の言葉と合成しますと 「同じソース」には「#pragma onceを書いたファイル自身」をプリプロセッサが一回しかincludeをしない でOKですか? そして、「同じヘッダにも」その効果は適用されますか? そしてもしそうならつまり、ソースあるいはヘッダが異なればそれぞれで「一回ずつは許容される」という意味になりますよね? ということは、逆に言うと#pragma once をつけないヘッダなら、同じファイル内で複数回includeしてもOKになる、ということでしょうか? む?よく考えてみると ところで…… ソースファイルは、インクルードする方法はあるのでしょうか?
- Tacosan
- ベストアンサー率23% (3656/15482)
「ソースの終了地点」というのが今一つどこをさしているのかわかりませんが, それが「マクロを定義したファイルの最後」であれば違います. #define の有効範囲は #undef があればそこまで, それがなければ「コンパイル単位の最後まで」です. これはインクルードしていようとなんだろうと同じ. ちなみに「ヘッダに書いた場合は~」のところは, 概念的な例でいいので出してもらえると解説しやすいな. まさか, 単に「インクルードしているので『ヘッダファイルの下の方』でもソースファイルとしては『上に書いたかのようにみなされる』」ってことじゃないよな? #pragma once は「1回しかインクルードしない」ということを意味していたような気がする. つまり, 「インクルードされる」可能性のあるファイルじゃないと無意味.
お礼
ありがとうございます。 「ヘッダファイルの下の方云々」については どうも奇妙なことに、私の手違いによる勘違いだった可能性が高いです。 どういう場合だとだめでどういう場合だとよかったのか、同じつもりでやったら先ほど出来てしまったので、逆にいろいろ試しすぎて何で引っ掛かったのか正確に思い出せません…(すみません) ただ、可能性の一つとしては別々のヘッダで別々の定義をした同じ名前のマクロが、それらをインクルードしたときに名前がかぶってしまうため、undefがそのいずれかに含まれる場合 位置によってインクルード順を入れ替えないと失敗する…といった類の話だった、かもしれません。 >ソースの終了地点 につきましては、私の質問の意図としてはその通りです。 その…「ソース」の実装については「コンパイルの後」という解釈だったので、「コンパイル単位の最後」というのが良く分かりません。 もしかして #define や #pragma once など、プリプロセッサについては、ソースであってもヘッダであっても、全部コンパイル前(あるいは同時…?)に処理されるのでしょうか? そして、いずれにしてもソースで#defineした場合、あるいはそのソースでしか#includeしないヘッダで#defineしたものを#includeした場合は、最後まで#undefとしなくても そのソースはそのままではほかに影響を及ぼさないため そのままで問題ないのでしょうか? あとインクルード についてなのですが こちらは#pragma onceと違って、#defineのように 行が関係ある、という事であっていますか?
補足
そして… なるほど #pragma onceは 「自分自身」に対して効果があるんですね? 今まで「それを書いたファイルに#includeするファイルすべてに適用される」と勘違いしていました。
- 1
- 2
お礼
はい、実はgotoについては( )と2つの引数(?)の実験として作ったという意味合いの方が強く、これを使おうと思う場合は結構限られてて 「沢山のif()~があって、それが同じような結果を出す場合で、try catch にしたくない場合」 「何層にもなったループ文の内側の方から、一定の状況の時に一気に抜け出したい場合」 ぐらいです。 なので、消しても問題ないぐらいだとは思っています。 他で充分同等に書けるようなところでgotoを多用すると可読性下がる可能性高し!というのは納得ですからね。 残りの事につきましても、納得しました。ありがとうございます!
補足
あ!いい事思いつきました。 万が一それでも忘れた場合の事を考えて「以下の理由づけをコメントでそれらdefineの右側にでも書いておけばいい」ですよね? そうすれば万が一忘れても、また一回見れば「ああ、そうそう」 で、それ以後またそれでやっていけますので…。 おかげさまで思いつけました。ありがとうございます。 (早速メモっておこう。)