MIN
とMAX
はCのどこに定義されていますか?
できる限り総称的かつ安全に入力するために、これらを実装するための最良の方法は何ですか? (主流コンパイラ用のコンパイラ拡張/組み込みが推奨されています。)
MIN
とMAX
はCのどこに定義されていますか?
そうではありません。
可能な限り一般的かつタイプセーフなものとしてこれらを実装するための最良の方法は何ですか(メインストリームコンパイラ用のコンパイラ拡張/組み込みが推奨されます)。
機能として。私は#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
のようなマクロを使用しません、特にあなたがあなたのコードをデプロイすることを計画しているならば。あなた自身のものを書くか、標準の fmax
または fmin
のようなものを使うか、 GCCのtypeof を使ってマクロを修正してください(あなたもタイプセーフボーナスを得ます):
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
誰もが「ああ、二重評価について知っている、それは問題ない」と言い、数ヶ月後には、何時間もの間最も過酷な問題をデバッグすることになるでしょう。
typeof
の代わりに__typeof__
を使用していることに注意してください。
ISO Cプログラムにインクルードしたときに動作しなければならないヘッダファイルを書いているなら、
typeof
の代わりに__typeof__
を書いてください。
これはGNU libc(Linux)およびFreeBSD版のsys/param.hでも提供されており、dreamlaxによって提供された定義も持っています。
Debianについて
$ uname -sr
Linux 2.6.11
$ cat /etc/debian_version
5.0.2
$ egrep 'MIN\(|MAX\(' /usr/include/sys/param.h
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
$ head -n 2 /usr/include/sys/param.h | grep GNU
This file is part of the GNU C Library.
FreeBSDの場合:
$ uname -sr
FreeBSD 5.5-STABLE
$ egrep 'MIN\(|MAX\(' /usr/include/sys/param.h
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
ソースリポジトリは次のとおりです。
C++にはstd::min
とstd::max
がありますが、残念ながら、C標準ライブラリには同等のものはありません。次のようなマクロを使って自分でそれらを定義できます。
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
あなたがMAX(++a, ++b)
のような何かを書く場合しかし、これは問題を引き起こします。
非標準のコンパイラ拡張を避けて、純粋な標準C(ISO 9899:2011)で完全に型保証されたマクロとしてそれを実装してください。
解決策
#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y))
#define ENSURE_int(i) _Generic((i), int: (i))
#define ENSURE_float(f) _Generic((f), float: (f))
#define MAX(type, x, y) \
(type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))
使用法
MAX(int, 2, 3)
説明
マクロMAXは、type
パラメータに基づいて別のマクロを作成します。この制御マクロは、指定された型に対して実装されている場合、両方のパラメータが正しい型であることを確認するために使用されます。 type
がサポートされていない場合は、コンパイラエラーが発生します。
Xまたはyのどちらかが正しい型ではない場合、ENSURE_
マクロにコンパイラエラーがあります。より多くの型がサポートされている場合は、このようなマクロをさらに追加することができます。算術型(整数、浮動小数点数、ポインタなど)だけが使用され、構造体や配列などは使用されないと想定しました。
すべての型が正しい場合は、GENERIC_MAXマクロが呼び出されます。 Cマクロを書くときの通常の標準予防策として、各マクロパラメータを囲む余分な括弧が必要です。
それから、Cには暗黙の型の昇格に関する通常の問題があります。?:
operatorは、2番目と3番目のオペランドを互いにバランスさせます。たとえば、GENERIC_MAX(my_char1, my_char2)
の結果はint
になります。マクロがそのような潜在的に危険な型の昇格を行わないようにするために、意図した型にキャストされた最終型が使用されました。
理論的根拠
マクロの両方のパラメータを同じ型にします。 ?:
のような演算子が暗黙の型の昇格をもたらすので、それらのうちの1つが異なる型のものである場合、マクロはもう型安全ではありません。そしてそれがそうであるので、私達はまた常に上で説明されたように意図されたタイプに最終結果をキャストする必要があります。
たった1つのパラメータを持つマクロはもっと簡単な方法で書かれているかもしれません。ただし、2つ以上のパラメータを使用する場合は、追加の型パラメータを含める必要があります。残念ながらこのようなことは不可能です。
// this won't work
#define MAX(x, y) \
_Generic((x), \
int: GENERIC_MAX(x, ENSURE_int(y)) \
float: GENERIC_MAX(x, ENSURE_float(y)) \
)
問題は、上記のマクロがint
を2つ持つMAX(1, 2)
として呼び出されても、_Generic
アソシエーションリストのすべての可能なシナリオをマクロ展開しようとすることです。そのため、int
には関係ありませんが、ENSURE_float
マクロも拡張されます。そしてそのマクロは意図的にfloat
型のみを含んでいるので、コードはコンパイルされません。
これを解決するために、##演算子を使用して、代わりにプリプロセッサフェーズでマクロ名を作成したので、誤ってマクロが展開されることはありません。
例
#include <stdio.h>
#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y))
#define ENSURE_int(i) _Generic((i), int: (i))
#define ENSURE_float(f) _Generic((f), float: (f))
#define MAX(type, x, y) \
(type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))
int main (void)
{
int ia = 1, ib = 2;
float fa = 3.0f, fb = 4.0f;
double da = 5.0, db = 6.0;
printf("%d\n", MAX(int, ia, ib)); // ok
printf("%f\n", MAX(float, fa, fb)); // ok
//printf("%d\n", MAX(int, ia, fa)); compiler error, one of the types is wrong
//printf("%f\n", MAX(float, fa, ib)); compiler error, one of the types is wrong
//printf("%f\n", MAX(double, fa, fb)); compiler error, the specified type is wrong
//printf("%f\n", MAX(float, da, db)); compiler error, one of the types is wrong
//printf("%d\n", MAX(unsigned int, ia, ib)); // wont get away with this either
//printf("%d\n", MAX(int32_t, ia, ib)); // wont get away with this either
return 0;
}
私はそれらが標準化されたマクロだとは思わない。浮動小数点のための標準化された関数、fmax
とfmin
(そしてfloatのためのfmaxf
、そして倍精度のためのfmaxl
)がすでにあります。
副作用/二重評価の問題を認識していれば、それらをマクロとして実装することができます。
#define MAX(a,b) ((a) > (b) ? a : b)
#define MIN(a,b) ((a) < (b) ? a : b)
ほとんどの場合、コンパイラに任せて、実行しようとしていることを判断し、可能な限り最適化することができます。これがMAX(i++, j++)
のように使われるとき問題を引き起こしますが、私は一度にインクリメントされた値の最大値をチェックすることに今までに多くの必要性があることを疑います。最初にインクリメントしてから確認してください。
かなり最近の開発のため、これは遅い答えです。 OPは移植性のないGCC(およびclang)拡張typeof
- または 'clean' ISO Cの場合は__typeof__
に頼る答えを受け入れたので、 gcc-4.9 から利用可能なより良い解決策があります。
#define max(x,y) ( \
{ __auto_type __x = (x); __auto_type __y = (y); \
__x > __y ? __x : __y; })
この拡張の明らかな利点は、__typeof__
ソリューションとは異なり、各マクロ引数が一度だけ展開されることです。
__auto_type
はC++ 11のauto
の限定形式です。 C++ 11を使用するときにauto
の優れた型推論機能を使用しない理由はありませんが、C++コードでは使用できません(または使用すべきではありません)。
そうは言っても、私は 仮定 マクロがextern "C" { ... }
スコープに含まれている場合、この構文を使用しても問題はありません。たとえば、Cヘッダーから。私の知る限り、この拡張機能はその方法で検出されていませんinfo clang
私はこれを書いた バージョン MSVC、GCC、C、およびC++で動作します。
#if defined(__cplusplus) && !defined(__GNUC__)
# include <algorithm>
# define MIN std::min
# define MAX std::max
//# define TMIN(T, a, b) std::min<T>(a, b)
//# define TMAX(T, a, b) std::max<T>(a, b)
#else
# define _CHOOSE2(binoper, lexpr, lvar, rexpr, rvar) \
({ \
decltype(lexpr) lvar = (lexpr); \
decltype(rexpr) rvar = (rexpr); \
lvar binoper rvar ? lvar : rvar; \
})
# define _CHOOSE_VAR2(prefix, unique) prefix##unique
# define _CHOOSE_VAR(prefix, unique) _CHOOSE_VAR2(prefix, unique)
# define _CHOOSE(binoper, lexpr, rexpr) \
_CHOOSE2( \
binoper, \
lexpr, _CHOOSE_VAR(_left, __COUNTER__), \
rexpr, _CHOOSE_VAR(_right, __COUNTER__) \
)
# define MIN(a, b) _CHOOSE(<, a, b)
# define MAX(a, b) _CHOOSE(>, a, b)
#endif
高価な分岐を避けるために最小値/最大値が必要な場合は、3項演算子を使用しないでください。コンパイルするとジャンプします。以下のリンクは、分岐せずに最小/最大関数を実装するための便利な方法を説明しています。
http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
min
とmax
を次のような三次関数で定義すれば、それを指摘する価値があります。
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
fmin(-0.0,0.0)
とfmax(-0.0,0.0)
の特別な場合に同じ結果を得るためには、引数を交換する必要があります。
fmax(a,b) = MAX(a,b)
fmin(a,b) = MIN(b,a)
私はその男が "C"と言ったことを知っています...しかしもしあなたがチャンスがあれば、C++テンプレートを使います:
template<class T> T min(T a, T b) { return a < b ? a : b; }
タイプセーフで、他のコメントに記載されている++に関する問題はありません。
Windef.h
(a la #include <windows.h>
)はmax
とmin
(小文字)マクロを持っているように見えますが、それらも "二重評価"の難しさに苦しんでいますが、それらは自分自身を再ロールバックしたくない人のためにあります:)
a
とb
の2つの整数の最大値は(int)(0.5((a+b)+abs(a-b)))
です。これは、doubleに対して(double)
とfabs(a-b)
を使っても動作します(フロートに対しても同様です)。
に関連してBrett Haleのコメント 、clang
は2016年頃に__auto_type
のサポートを開始しました( patch を参照)。