日々

link permalink

C, C++ の可変引数マクロ

C99, C++11 の可変引数マクロでは、


#define AAA(a, ...)

のように省略記号の前にパラメータがあるマクロを定義したとき、


AAA(hoge, fuga);

という呼び出しはマクロのパラメータ数より、引数の数が多いので well-formed つまり正しいけれども、


AAA(hoge);

という呼び出しはマクロのパラメータ数と引数の数が同じなので ill-formed つまり不正になるようです。

なので、C99, C++11 に準拠して書こうと思うと、可変引数なしのマクロと、可変引数ありのマクロを準備して、呼び分けなければなりません。非常に面倒くさいです…。


#define AAA(a)
#define BBB(a, ...)

AAA(hoge);
//AAA(hoge, fuga); //ill-formed

//BBB(hoge); //ill-formed
BBB(hoge, fuga);

具体的に規格のどこに違反するのかよくわかりませんが、下記の文章ですかね?私の翻訳はあやしいので、原文も併せて載せます。

N3337 Working Draft, Standard for Programming Language C++

16.3 Macro replacement [cpp.replace]
...
12. If there is a ... in the identifier-list in the macro definition, then the trailing arguments, including any separating comma preprocessing tokens, are merged to form a single item: the variable arguments. The number of arguments so combined is such that, following merger, the number of arguments is one more than the number of parameters in the macro definition (excluding the ...).

(参考訳)
もし省略記号(...)がマクロ定義内の identifier-list に存在する場合、後続の引数(カンマで区切られた全ての preprocessing-token)は一つの項目としてまとめられる。これが可変引数である。
このようにまとめられた後の引数の数は、マクロ定義内の省略記号(...)を除いたパラメータ数より一つ多くなる。

何言ってんだお前?と思わなくもない文章ですが…、定義が #define AAA(a, b, ...) のマクロを AAA(i1, i2, i3, i4) と 4つの引数でマクロを呼ぶと、省略記号の部分がまとめられ i1, i2, (残り全部) の 3つになるから、マクロ定義内のパラメータ a, b の数である 2つに比べて 1個多くなる、くらいの意味でしょうか。

しかし、可変引数を空にしてマクロを呼ぶと、引数の数が同じになってしまって項目 12 の既定に反するので、文法違反とせざるを得ない、という解釈なのでしょう。

あと identifier-list は 16 Preprocessing directives [cpp] に、preprocessing-token は 2.5 Preprocessing Tokens [lex.pptoken] に規定されている構文要素を指していると思われるので、訳していません。

GNU 拡張構文 その 1

ただし、GCC は AAA(hoge); と書いても何も警告してきません。これは「可変引数が空っぽのマクロ呼び出しを許す」つまり AAA(hoge); という呼び出しを許す GCC の拡張構文らしいです。

ちなみに gcc -pedantic-errors とすると拡張構文の使用箇所をエラー扱いするようになりますので、

error: ISO C99 requires rest arguments to be used
AAA("hoge");

と怒ってきます。

GNU 拡張構文 その 2

可変引数マクロを使ってオレオレ printf マクロを作れますが、その時に必ずぶつかる問題があって、

可変引数マクロでオレオレ printf を作る(問題あり版)

#include <cstdio>

#define DEBUG_LOG(fmt, ...) std::printf(fmt, __VA_ARGS__)

int main(int argc, char *argv[])
{
    DEBUG_LOG("Hello World.\n");
    DEBUG_LOG("Hello World %d.\n", 2);

    return 0;
}

せっかく GNU 拡張構文を使って、最初の呼び出し(引数が 1つだけ)を可能にしたのに、いざコンパイルしてみると、引数が無いよ?と怒られてしまう問題です。

オレオレ printf の引数が 1つだと怒られる
$ g++ -Wall --std=c++0x variadic_macros.cpp
variadic_macros.cpp: In function ‘int main(int, char**)’:
variadic_macros.cpp:3:57: error: expected primary-expression before ‘)’ token
 #define DEBUG_LOG(fmt, ...) std::printf(fmt, __VA_ARGS__)
                                                         ^
variadic_macros.cpp:7:5: note: in expansion of macro ‘DEBUG_LOG’
     DEBUG_LOG("Hello World.
");

もちろんこの問題は GNU の人たちはわかっていて、解決策もあります。そうです GNU 拡張構文です。先ほどのマクロ定義を下記のように変えます。

可変引数マクロでオレオレ printf を作る(解決版)

#include <cstdio>

#define DEBUG_LOG(fmt, ...) std::printf(fmt, ##__VA_ARGS__)

int main(int argc, char *argv[])
{
    DEBUG_LOG("Hello World.\n");
    DEBUG_LOG("Hello World %d.\n", 2);

    return 0;
}

違いは、__VA_ARGS__ の前に ## が付いていることです。## はトークン連結演算子といって、プリプロセッサ時点で、## の前後を結合して一つのトークンにする演算子です。

オレオレ printf の引数が 1つでも怒られなくなった
$ g++ -Wall --std=c++0x variadic_macros.cpp

$ ./a.out
Hello World.
Hello World 2.

この拡張構文の仕様を正確に説明できませんが、カンマと __VA_ARGS__ をトークン連結する際に、可変引数が空(つまり __VA_ARGS__ が空)ならカンマを消し去る、という動作をしているようです。

これら 2つの GNU 拡張構文「可変引数が空っぽのマクロ呼び出しを許す」と「カンマと __VA_ARGS__ をトークン連結する」のおかげで、printf 風のマクロが自然に書けるようになります。

安心してください、clang でも使えます

GNU 独自の拡張構文とは言いますが、実は clang や Visual Studio でも使えます。そのため PC 向けのコードであれば移植性をあまり気にせず使える機能となっています。

皆さん同じ問題に困っていたのでしょう。もう C, C++ 規格側を変えた方が良い気がするんですけど…、何か互換性の問題があるのでしょうか。

(追記)省略記号のみではダメな理由

コメントでも指摘いただきましたが、上記の例だと具体的に何が問題か伝わらないので追記します。

下記のように書けば、引数が 1個でも文法的に OK で、先頭に固定されたヘッダ名を足すこともできます。

可変引数マクロでオレオレ printf を作る(解決版?)

#include <cstdio>

#define DEBUG_LOG(...) std::printf("debug: " __VA_ARGS__)

int main(int argc, char *argv[])
{
    DEBUG_LOG("Hello World.\n"); //well-formed
    DEBUG_LOG("Hello World %d.\n", 2); //well-formed

    return 0;
}
$ g++ -Wall --std=c++0x variadic_macros.cpp -pedantic-errors

$ ./a.out
debug: Hello World.
debug: Hello World 2.

ところがもう少し凝ったこと、例えばヘッダの文字列を場合によって Error: に変えたい、関数名を出したいなど、何かしら引数を渡さなければならない場合は、上記の定義では実現できません。

省略記号のみでは記述できない例

#include <cstdio>

//やりたいこと: 
//  この呼び出しを…
//    DEBUG_LOG("Hello World.\n");
//  このように展開したい
//    printf("%s: " "Hello World.\n", __func__);

//このマクロ定義なら記述可能
#define DEBUG_LOG(fmt, ...)    printf("%s: " fmt, __func__, ##__VAR_ARGS__)

//このマクロ定義では記述不可能、__func__ を書ける場所が無いため
//#define DEBUG_LOG(...)    printf("%s: " __VA_ARGS__ ...?

int main(int argc, char *argv[])
{
    DEBUG_LOG("Hello World.\n");
    DEBUG_LOG("Hello World %d.\n", 2);

    return 0;
}
$ g++ -Wall --std=c++0x variadic_macros.cpp

$ ./a.out
main: Hello World.
main: Hello World 2.

GNU 拡張構文のありがたみが身に沁みますね。

[編集者: すずき]
[更新: 2016年 1月 10日 02:46]
link 編集する

コメント一覧

  • hdk 
    #define DEBUG_LOG(...) std::printf(__VA_ARGS__)
    ですべて解決、かと思いましたが、第一引数の先頭に "%s:" とかをつけて __func__ を追加する、というような使い方の時に困るんですね。なるほど。 
    (2016年01月10日 01:22:00)
  • すずき 
    >hdk さん

    はい。そうなんです。コードの例が悪くて伝わらないですね…。
    例を追記しておきます。 
    (2016年01月10日 02:13:30)
open/close この記事にコメントする



link permalink

Chrome で見ると文字が異常に小さい

スマホで見たときに、コードとか実行結果の表示に使っている <pre> タグと <code> タグの中の文字が異常に小さく表示されることに気づきました。

コード表示に使っているのは <pre> と <code> の組み合わせです。

Hello World

#include <stdio.h>

int main(int main, char *argv[])
{
    printf("Hello, World!\n");
    return 0;
}

実行結果の表示に使っているのは <pre> だけですが、これも小さく表示されていました。

Hello World の実行結果
$ gcc -Wall hello_world.c

$ ./a.out
Hello, World!

スマホだけおかしいのか?と思って PC の Chrome で見ても、多少はマシであるもののやはり字が小さいです。

ググって見つけた、このサイト「等幅フォントが使われる要素の扱いがブラウザー間でまちまちな問題」のおかげでかなりマシになりましたが、完全に同じ見た目にはならないようです。

普段は SeaMonkey でしか確認していなかったので、気づきませんでした。たまには IE や Chrome でも見てみるべきですね。

[編集者: すずき]
[更新: 2016年 1月 9日 18:34]
link 編集する

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

録画大国日本

4K/8K無料放送は「録画禁止」に? NexTV-F 発表の規定が大きな波紋 - Phile-Webを読んで。

ご存知の通り日本は録画大国でして、Netflix や Hulu といったネット映像配信サービスがあまり流行らず、代わりにレコーダがガンガン売れる、不思議の国です。

参考: 2014年民生用電子機器国内出荷統計 - JEITA
(2014年: テレビ 549万台、BD レコーダ 247万台)

テレビ局にしてみれば、録画され CM を飛ばされるのが面白くないのはわかるんですが、録画禁止規定を強めたら、テレビ放送をリアルタイムで見るようになるか?と言ったら絶対見ないでしょう。

レコーダが不人気の北米や欧州でテレビ放送が復権した話は聞いたことがありません。代わりに聞こえるのは Netflix や Hulu のようなネット映像配信の躍進だけです。

参考: 示したかったのですが、世界の出荷台数が見つからず…

日本からレコーダを滅ぼしたところで、テレビ局の滅びは止まりません。止まらないどころか、ネット映像配信サービスへの移行を加速してしまう可能性すらあるんじゃないでしょうか?

テレビ局の歴々がこんなことに気づかないとは、到底思えないのですが。テレビは栄光の時代が長すぎて、頭がおかしくなってるのかなあ…。

メモ: 技術系の話は Facebook から転記しておくことにした。

[編集者: すずき]
[更新: 2016年 1月 10日 14:54]
link 編集する

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

オススメのプログラミング環境は?

最近よく見る「今年こそ、プログラミングを勉強したい」広告を見ていて思ったのですが、プログラミング初めての人に教えることになったら、何を選ぶでしょう?

もちろん、何がしたいのか?教えるスキルがあるか?にも依りますが、私の場合は、

  • ハード: ノート PC
  • OS: Windows or Ubuntu Linux
  • 言語: Java

になりそうです。古典的ですね。

メモ: 技術系の話は Facebook から転記しておくことにした。

[編集者: すずき]
[更新: 2016年 1月 10日 14:57]
link 編集する

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

favicon 乱立

あらゆる種類の favicon を自動生成してくれる Favicon Generator for all platforms というサイトが面白かったので、このサイトにも PC ブラウザ向けの favicon を作成してみました。

生成に使ったのは、昔、旭山動物園に行ったときに私が撮ったペンギンの写真です。

生成された favicon を全部設定するのは嫌だったので(ミラーサイトのトップに favicon 置きたくない)、昔からある favicon と iOS, Chrome 用の favicon だけ設定しておきました。しかし、そんな手抜き設定をするだけでも 13種類もの大きさがあるんです。

プラットフォームやブラウザが増えるごとに、どんどん favicon の亜流が増えていけば、必ずサイト側の対応が追い付かなくなって破綻します。破綻します、というより、既に破綻しています、と言った方が良いのか…(現時点で favicon は 20種類を超えている)。

収拾の手立ては?

事態を収拾するには favicon の規格を統一し、各プラットフォーム、ブラウザ、各サイトが地道に対応する方法がありますが、難しそうです。

オリジナルの favicon 自体 IE 5 が勝手に作った機能で、規格もへったくれもないですし、規格にしたところで、ここまで亜流だらけになれば、もはや統一は上手くいかないでしょう。

このまま破綻して誰も扱いきれなくなって廃れるか、どこかで一発リセットが掛かるか。どっちかなあ…。

[編集者: すずき]
[更新: 2016年 1月 17日 21:54]
link 編集する

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

一太郎 2015

一太郎 2015 特別優待版を買いました。Amazon で 8000円くらいでした。元々 Word や一太郎のようなワープロはあまり使わないので、一太郎と一緒に入っている ATOK 2015 をメインに使うことになりそうです。

なぜ 2016 を買わないのか?という突っ込みはごもっともですが、まだ一太郎 2016 は出ていないのです。

2015年は一太郎の 30周年記念で「らくらく画面カッター」と「はかどる数式メーカー」というソフトが記念に付属されていました。画面カッターはわかりませんが、数式メーカーは使うかなあ。

ATOK 2015

以前使っていたのは ATOK 2010 だったので、軽く 5年ほど経っていますが、使い勝手が激変しないところが ATOK の良いところです。

使い始めてすぐに気になったことは、なぜか Google の検索ボックスが激重になったことです。なんだこりゃ?

他にも気づいた点があれば後日にでも書きます。

[編集者: すずき]
[更新: 2016年 1月 17日 19:06]
link 編集する

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

DDR3-2133 で動くはずなのに

デスクトップで使っている CPU(AMD A10-7800)、マザーボード(ASUS A88XM-PLUS)、メモリ(G.Skill F3-2133C10Q-32GZM)の全てが、DDR3-2133 対応を謳っている製品にも関わらず、UEFI の自動設定だとなぜか DDR3-1600 になってしまいます。

勿体ないので、DDR3-2133 の設定に変更したところ、UEFI まではご機嫌に立ち上がりますが、Windows10 が起動するかしないかのところで、画面が真緑&ノイズだらけになってしまい、使い物になりませんでした。

A10 内蔵の Radeon がおかしい?もしくは誰かが DDR3-2133 に対応していないウソつきなのか…?

動いたように見せかけて動かない

ASUS のサイトから UEFI の最新版を持ってきてバージョンアップし、グラフィクスの設定を Turbo Mode とやらに変更したところ、Windows10 のログイン画面まで出ました。

が、ここまで動いたと思わせておいて、やはり真緑になって死亡しました。ダメだこりゃ…。

メモ: 技術系の話は Facebook から転記しておくことにした。

[編集者: すずき]
[更新: 2016年 1月 17日 18:43]
link 編集する

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

いい人

あなたのチームの「いい人」は機能していますか? - SlideShareを読んで。

会社で「システム班」と呼ばれた人たちがほぼ全員辞めていった理由が全て書いてありました。

私もここ 1年くらい Wiki や Redmine で「情報の抱え込みをやめて、共有する文化」の普及に挑戦しましたが、挫折気味です。

やってくれる人は言ったその日から継続的にやってくれるのですが、やってくれないオジサン達って「共有は大事」と言いつつ、三日坊主 or ガン無視するんですよね…。

どうしたら良いんだろね?

メモ: 技術系の話は Facebook から転記しておくことにした。

[編集者: すずき]
[更新: 2016年 1月 21日 00:08]
link 編集する

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link permalink

紆余曲折だった C++11 の override と final

最近 cpprefjp(リンクはこちら)のコミット権をいただいたので、小物ネタから書いてはコントリビュートしていますが、一見簡単そうに見えた C++11 の override と final の経緯を調べていたら、土日が終わってしまいました。

実は override と final は簡単ですが、同時に検討されていた hiding が鬼門でした…。

以下の例のように「間違って基底クラスの仮想メンバ関数を隠した(hiding)ときエラーにしたい」という提案から始まりました。

hiding を明示的に宣言するコンテキスト依存キーワード 'new'

class A {
    virtual void func();
};

// check hiding and override
class B explicit : public A {
    // NG, 間違って A::func() を隠していないか?
    void func(int a);
    // OK, あえて A::func() を隠すと宣言している
    void func(double b) new;
};

でも事はそう単純ではありませんでした。override はメンバ関数同士でしか発生しませんが、hiding はメンバ関数とメンバ関数以外でも発生するため、考慮するケースが多いからです。

C++ 標準化委員会のペーパーには、問題となる例が挙げられていますが、あえて私見で「あ、これダメだ」と思ったヤツを 1つ挙げると、以下の例です。

hiding を明示するキーワード new の文法上の問題

class A {
    virtual void N();
};
class B explicit : public A {
    // B::N は A::N() を隠すから new を宣言すべき、でも new を書く文法は?
    enum { N };
};

初見の方は new を使い回すなよと思うでしょう。実際、最初は hiding という名前で提案されました。しかし問題がありました。

hiding をコンテキスト依存キーワードにしたときの悩み

struct A {};
struct B {
    // これは構造体の宣言?変数の宣言?
    struct A hiding;
};

これらの問題を回避するために enum の中の N やら M やらにまで、一々 new と書く文法を新たに作るのも、不細工極まりないです。

他にも using、多重継承など、かなり広範に渡って hiding が起きるため、これはダメだと判断されたようで explicit と new (hiding を宣言するための) は、C++11 策定の最後辺りで Drop されました。

C++14 にも採用されていないように見えるけど、次の C++ 規格で入るのかな…??

メモ: 技術系の話は Facebook から転記しておくことにした。

[編集者: すずき]
[更新: 2016年 1月 25日 23:00]
link 編集する

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



こんてんつ

open/close wiki
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報