C++

マクロの可変個実引数の型を強制する

今回は単なる思いつきを備忘録として書き留めておくだけの内容です。過信はしないでください。

#define foo(...)

上のような省略記号を持つマクロの場合、省略記号の部分に指定した実引数を扱うには、__VA_ARGS__を使う必要があります。それ以前にマクロですので、関数原型のように型を指定できるわけでもなく、実引数の型を強制する方法は原則としてありません。それがよいところでもあり、悪いところでもあるのがマクロというものです。

今回は、そうしたマクロの特徴にあえて歯向かい、実引数の型を(関数と同程度に)強制しようという試みです。

やはり、CとC++の区別がつかない人は少なくないようだ

教えて!gooで「C言語での質問です。」というQ&Aを見て感じたのですが、CとC++の区別がついていない人が少なくないことを改めて認識しました。No.3の回答がそれです。

>char str[3] = "AAA"; は 間違いですね コンパイルしたら エラーがでました。

このように指摘しているわけですが、このコードをCコンパイラでコンパイルしてエラーが出るとすれば、そのコンパイラが非標準処理系であるか、不具合があるかのどちらかです。「C++プログラマのためのC言語入門」の「第9回 文字定数と文字列リテラル」でも解説しましたが、char型の配列を文字列リテラルで初期化する場合、ナル文字分の要素が足りなくても問題なくコンパイルできます。

フルスペックのprintf?

ときどきの雑記帖 i戦士篇で引用されていた2ちゃんねるのやり取りを見て、思わず画面に向かって突っ込んでしまったので、そのことを記録しておきます。printfを自前でフルスクラッチで(stdarg.h以外には依存せずに)書きたいということのようです。

結論からいえば不可能です。これは誰にでも簡単にわかるはずです。stdarg.hにしか依存できないのであれば、どうやって標準出力に書きこむのでしょうか? 少なくとも、fwrite、putc、fputc、putchar、fputs、fprintf、vprintf、vfprintfのどれかに依存しなければ標準出力への書き込みができません。趣旨を考えると、fwrite、putc、fputc、putcharのどれかを出力に使うべきかと思います。putchar以外を使う場合は、stdoutマクロも必要になります。

非標準処理系と移植性

C/C++の移植性を考える場合、一般論としては、非標準処理系や処理系の不具合は対象にすべきではありません。そうした話を持ち出すと、結局のところ標準規格は一切機能しなくなり、何が起きても不思議ではない状況が発生してしまいます。

ただ、現実的には、主要な処理系、あるいは大多数の処理系が規格にしたがっていないような場合もあります。例えば、C++のexportとか、国際文字名とかがそれにあたります。そうした場合には、「規格がこうだから」という話をいくらしても、現実には役に立ちません。つまり、こうした場合には、いわゆる一般論は通用しなくなるわけです。

もうひとつ、必ずしも主要とはいえないけれども、今自分が相手をしている処理系、あるいは今後相手をすると予想される処理系が、規格とは異なる振る舞いをする場合には、当然それらに対する配慮を行わなければ、必要な移植性を持たせることができなくなります。

「私は現在、職業訓練校にて勉強し始めた初心者です。」の回答への反論

アクセス解析を見ると、教えて!gooの「私は現在、職業訓練校にて勉強し始めた初心者です。」という質問の回答から当サイトへのリンクが貼られたようです。それ自体は何の問題もありませんが、回答が間違っているようです。間違った回答を補強してしまうとすれば、当サイトにも若干の責任がありますので、ここで反論しておきます。

C言語の厳密な仕様・規格等については詳しくありませんが、#1さんが
書かれているとおり、
  signed も unsigned も付けずに単に、char型として宣言した場合、
  符号付き(signed)となるか、符合無し(unsigned)となるかは、
  言語仕様としては【未定義】であり、
  その扱いは、処理系依存(コンパイラにより定義が異なる)
となるようです。(※下記参考サイト参照)

回答では上記のように記されています。字下げの部分はあたかも引用したように見えますが、当サイトも、もうひとつリンクを貼られたサイトにもこのような記述はなく、#1の回答にもこうした記述はありません。

未整理エントリー

ここには、「C++ろ組込み環境」に収録を検討しているけれども、まだ整理されていない記事を集めることにします。必然的に乱雑なものになりますし、内容の正確さについても十分な検証がなされていない可能性が高いと考えてください。

擬似プロセッサの新作を作ってみました。

まだテストもしきれていませんし、コードも雑なのですが、擬似プロセッサの新作を作ってみました。
http://www.kijineko.co.jp/files/pt2010-20100225.zip
他にやらなければならないことが山積なのですが、あいにく風邪で体調が悪く、混みいったことはできそうにないので、思いっきり現実逃避した結果の成果物です。

今回はC++で実装し、レジスタにはboolの配列を使いました。当然、加減算器は半加算器をもとに作った全加算器を組み合わせて使っています。ステータスフラグもあれば、マスク可能な割り込み(ただし優先度はなし)も作ってみました。外部I/Oはコールバック関数で処理するようにしましたので、どんな足回りでも外付けすることができます。まだドキュメントがありませんので、そのうち解説記事を書くようにします。

ところで、擬似プロセッサという呼称はいまいちよくなくて、本当は仮想プロセッサと呼ぶべきなのでしょうが、そう呼ぶと別のものを想像してしまったりするので、あえてこう読んでいます。

C++における構造体の初期化

「[迷信] とりあえず memset で初期化」へのアクセスは相変わらず多いのですが、解説があっさりしているために十分意図が伝わっていないことも少なくないようです。これまでも何度か補足的な解説を行ってきたのですが、今回もその一環として、補足解説を行うことにします。

一般的な話をすると、どうしても解説が抽象的になってしまい、その結果またしても十分に意図が伝わらないということが起きそうです。そこで、今回は解説の対象を絞り込み、できるだけ具体的な話をするつもりです。今回対象とするのは「C++の構造体の初期化」です。Cの話ではありません。また、「初期化」と書いていますが、いわゆる初期化子による初期化だけでなく、最初に値を設定する意味上の初期化も対象とします。

JIS X3014の値初期化の記述に悩まされる

すでに誰かが指摘していることなのだとは思いますが、JIS X3014:2003の「値初期化」の記述にさんざん悩まされました。今回は非常にデリケートな話なので、規格からの引用は転記でなく、キャプチャ画像を貼ることにします。

まず、順を追ってお話することにします。C互換型ではないクラス型の初期化について確認しようと思い、JIS X3014:2003の12.6 初期化に目を通しました。

12.6 初期化
 

これによると、初期化子が()の場合は「値初期化」されることになっています。つまり、std::string()のような記述を行った場合は値初期化されるわけです。

次に、値初期化について確認しようと思い、8.5 初期化子の記述に目を通しました。

[迷信] 引用符で囲んだヘッダ名はカレントディレクトリから探索する

あるディレクトリ(~/srcとします)に、foo.cとbar.hという二つのソースファイルがあったとします。bar.hは、foo.cから次のようにインクルードしています。

#include "bar.h"

ここで、別のディレクトリ(~/workとします)でfoo.cをコンパイルしたとしましょう。このとき、foo.cからインクルードしているbar.hは、期待通り~/src/bar.hを探し当てることができるでしょうか?

今度は、~/src/bar.hとは別に、~/work/bar.hというファイルを用意してみましょう。そして、先ほどと同じように、~/workで~/src/foo.cをコンパイルしてみます。このときに~/src/foo.cから取り込むbar.hは、~/src/bar.hでしょうか? それとも、~/work/bar.hでしょうか?

もし、引用符で囲まれたヘッダ名はカレントディレクトリから探索するのであれば、~/work/bar.hが取り込まれるはずですが、本当にそうなるでしょうか?

コンテンツ配信
このエントリーを含むはてなブックマーク