Top/article/Things-only-macros-can-do
English | Japanese
English | Japanese

Menu

  • Top
  • Akihiko Yamaguchi 山口 明彦
  • Project プロジェクト
  • Text テキスト
  • Recent articles 最近の記事
  • Articles 記事一覧 (a to z)
  • Search 検索

Tags タグ †

  • [c++][bash][python][latex][php]
  • [linux][windows][mac][android]
  • [math][algorithm][idea][trick]
  • [liboctave][opencv][git][ros]
  • [setting][bug][general]
↑

Recent articles 最近の記事 †

2019-07-02
  • article/Display-Unix-Time
  • article/Synchronize-Linux-Time-to-Remote
2018-09-27
  • article/python-multimode-singleton
2018-09-02
  • article/rosinstall-git-default-remote
2017-07-28
  • article/SubMenu
2017-03-05
  • article/Import-a-different-version-of-OpenCV-in-Python
2015-08-17
  • article/DRC-finals-2015
2015-01-05
  • article/Upgrade-Android-to-Lollipop
2015-01-01
  • article/Kernel-panic-of-Linux-when-using-Xtion
  • article/Do-not-skip-freeing-data-when-using-tri-mesh-in-ODE
Access: 1/2321 - Admin
These search terms have been highlighted:[c++]

Things only macros can do

When should we use macros?

マクロにしかできないこと ~C++でマクロを使うべきな場面~

[c++][trick]
2008-12-17

C++では, #define で定数を定義するな, const TYPE によるグローバル変数(もしくは適当な名前空間に内包されたグローバル変数)を使え, #define でマクロ関数を定義するな,代わりに inline 関数を使え,みたいなことが言われる.これは確かにその通りだ.

1項 #define ではなく, const と inline を使おう
Scott Meyers (スコット・メイヤーズ): Effective C++ (吉川訳, アスキー出版局, 1998)

に書いてあるように, プリプロセッサよりコンパイラに仕事させるべき だ.でもマクロにしかできないことも多々あるわけで.

case 1: 配列のサイズを取得 †

#define SIZE_OF_ARRAY(array)  (sizeof(array)/sizeof((array)[0]))

SIZE_OF_ARRAY(配列) とすれば,配列の要素数に置き換えられる.これは, inline 関数ではできない.関数の引数に配列を使うと,自動的にポインタとして扱われてしまうからだ.

↑

case 2: The # operator (マクロ引数の文字列化) †

例えば変数名を文字列として取得したい場合, inline 関数では逆立ちしてもできない.しかし,マクロの # を使えばできる.

#define print(var) std::cout<<#var"= "<<var<<std::endl

これは, print(x) のように使うと

std::cout<<"x""= "<<x<<std::endl

のように置き換えられるマクロだ*1.マクロ引数 (var) に # をつけると (#var),文字列として置換される.文字列化 # が必要な理由は,

#define print(var) std::cout<<"var= "<<var<<std::endl

とマクロを定義すると, print(x) を

std::cout<<"var= "<<x<<std::endl

のように置き換えるだけだからだ.

↑

case 3: The ## operator (連結) †

例えば x が代入されているマクロ引数 HOGE と _0 をくっつけて x_0 のような識別子を生成した場合に使う.こういう連結による識別子の生成も, inline 関数では絶対できない.

#include <iostream>
#include <cmath>
using namespace std;
#define print(var) std::cout<<#var"= "<<var<<std::endl
  // case 2 の print の使用例
int main(int argc, char**argv)
{
  const double angle[]= {0.5*M_PI, 0.25*M_PI, M_PI/3.0};
  #define defc(_j) c##_j= std::cos(angle[_j])
  const double defc(0),defc(1),defc(2);
  print(c0);print(c1);print(c2);
  #undef defc  // ここでしか使わないマクロは undef しておく方がいい
  return 0;
}

この例では defc(0)... の部分は

  const double c0= std::cos(angle[0]), c1= std::cos(angle[1]), c2= std::cos(angle[2]);

のように置換される.タイプミスなどで c の後の番号と配列のインデクスを異なったものにしてしまうと,発見しにくいバグになるから,結構便利なことがわかる.つまり,似たような変数を生成するときに,ミスを減らせる.

例えば Octave のソース mx-op-defs.h では,キャストなどの演算子を生成するためのマクロを定義して,コードを共通化しているが,ここでも連結 ## の使用例がみられる.

↑

おまけ: その他のプリプロセッサ・ディレクティブ †

#define 以外のプリプロセッサ・ディレクティブについても,適当に紹介しておく.

↑

#include †

#include <iostream>

あまりにも当り前だが,これはプリプロセッサにしか実現できない.

↑

#ifdef/#ifndef ~ #endif によるコードの切替え †

例えば C と C++ の両方から include するヘッダで,

#ifdef __cplusplus
extern "C"
{
#endif

こういうプリプロセッサ・ディレクティブを使うことはよくある.

ライブラリのヘッダを2重に読まないようにするために,

#ifndef hogehoge_h
#define hogehoge_h
...
#endif

みたいなコードを書くことも.

↑

#error †

#ifndef HOGEHOGE
  #error ERROR!!!!
#endif

これは HOGEHOGE が #define されていない場合に ERROR!!!! というエラーを吐いてコンパイルを中止する,プリプロセッサ・ディレクティブ.

↑

#pragma †

処理系(コンパイラ)が自由に決めていいプリプロセッサ・ディレクティブ.ソースから警告を抑制する場合などに使われる.

↑

参考文献 †

  • ISO/IEC 14882:2003 (E): "16 Preprocessing directives" (C++ の標準規格)

*1 C++では連続する文字列 "hoge" "hehe" は連結されて "hogehehe" となることに注意しよう.



Last-modified:2014-12-30 (Tue) 23:02:52 (3840d)
Site admin: Akihiko Yamaguchi.
Written by: Akihiko Yamaguchi.
System: PukiWiki 1.5.0. PHP 5.2.17. HTML conversion time: 0.014 sec.