可変長再帰プリプロセッサマクロ-可能ですか?
-
05-07-2019 - |
質問
ちょっとした理論上の問題に遭遇しました。私が管理しているコードの一部には、次のようなマクロのセットがあります
#define MAX_OF_2(a, b) (a) > (b) ? (a) : (b)
#define MAX_OF_3(a, b, c) MAX_OF_2(MAX_OF_2(a, b), c)
#define MAX_OF_4(a, b, c, d) MAX_OF_2(MAX_OF_3(a, b, c), d)
...etc up to MAX_OF_8
私がやりたいのは、それらを次のようなものに置き換えることです:
/* Base case #1, single input */
#define MAX_OF_N(x) (x)
/* Base case #2, two inputs */
#define MAX_OF_N(x, y) (x) > (y) ? (x) : (y)
/* Recursive definition, arbitrary number of inputs */
#define MAX_OF_N(x, ...) MAX_OF_N(x, MAX_OF_N(__VA_ARGS__))
...もちろん、これは有効なプリプロセッサコードではありません。
この特定のケースはおそらくプリプロセッサマクロではなく関数を使用して解決する必要があることを無視、可変長のMAX_OF_N()マクロを定義することは可能ですか?
わかりやすくするために、最終結果は、任意の数のパラメーターを取り、それらの最大値に評価される単一のマクロでなければなりません。私はこれが可能だという奇妙な気持ちを持っていますが、どのように見えるのかわかりません。
解決
いいえ。プリプロセッサは「スワイプ」を1つしか受け取らないため、ファイルで。マクロを再帰的に定義する方法はありません。
このようなことを行うのを見た唯一のコードは not variadicでしたが、ユーザーが渡す必要があるデフォルト値を使用しました:
x = MAX_OF_8 (a, b, -1, -1, -1, -1, -1, -1)
すべての値が負でないと仮定します。
インライン関数は、少なくともC ++については同じものを提供するはずです。述べているように、おそらく printf()
に似た可変引数を持つ関数に任せた方が良いでしょう。
他のヒント
呼び出された引数の数を評価するマクロを書くことは可能です。 (最初に見た場所へのリンクを見つけることができませんでした。)したがって、希望どおりに機能するMAX_OF_N()を書くことができますが、制限があるまですべての番号付きマクロが必要です:
#define MAX_OF_1(a) (a)
#define MAX_OF_2(a,b) max(a, b)
#define MAX_OF_3(a,...) MAX_OF_2(a,MAX_OF_2(__VA_ARGS__))
#define MAX_OF_4(a,...) MAX_OF_2(a,MAX_OF_3(__VA_ARGS__))
#define MAX_OF_5(a,...) MAX_OF_2(a,MAX_OF_4(__VA_ARGS__))
...
#define MAX_OF_64(a,...) MAX_OF_2(a,MAX_OF_63(__VA_ARGS__))
// NUM_ARGS(...) evaluates to the literal number of the passed-in arguments.
#define _NUM_ARGS2(X,X64,X63,X62,X61,X60,X59,X58,X57,X56,X55,X54,X53,X52,X51,X50,X49,X48,X47,X46,X45,X44,X43,X42,X41,X40,X39,X38,X37,X36,X35,X34,X33,X32,X31,X30,X29,X28,X27,X26,X25,X24,X23,X22,X21,X20,X19,X18,X17,X16,X15,X14,X13,X12,X11,X10,X9,X8,X7,X6,X5,X4,X3,X2,X1,N,...) N
#define NUM_ARGS(...) _NUM_ARGS2(0, __VA_ARGS__ ,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)
#define _MAX_OF_N3(N, ...) MAX_OF_ ## N(__VA_ARGS__)
#define _MAX_OF_N2(N, ...) _MAX_OF_N3(N, __VA_ARGS__)
#define MAX_OF_N(...) _MAX_OF_N2(NUM_ARGS(__VA_ARGS__), __VA_ARGS__)
Now MAX_OF_N(a、b、c、d、e)
は max(a、max(b、max(c、max(d、e))))と評価されます
。 (gcc 4.2.1でテストしました。)
ベースケース( MAX_OF_2
)が展開で引数を複数回繰り返さないことが重要であることに注意してください(この例では max
を配置する理由です) )。そうしないと、すべてのレベルで展開の長さが倍になるため、64個の引数で何が起こるか想像できます:)
この不正行為は、再帰的ではなく、プリプロセッサでの作業を行わないため、考慮することができます。また、GCC拡張機能を使用します。そして、それは1つのタイプに対してのみ機能します。ただし、可変長のMAX_OF_Nマクロです:
#include <iostream>
#include <algorithm>
#define MAX_OF_N(...) ({\
int ra[] = { __VA_ARGS__ }; \
*std::max_element(&ra[0], &ra[sizeof(ra)/sizeof(int)]); \
})
int main() {
int i = 12;
std::cout << MAX_OF_N(1,3,i,6);
}
ああ、そして初期化子リストに潜在的な変数式があるため、これに相当するもの(独自の関数を使用してstd :: max_elementを回避する)がC89で機能するとは思いません。しかし、可変長マクロがC89にもあるかどうかはわかりません。
ここでは、「1つのタイプのみ」を回避する方法を紹介します。制限。しかし、少し毛むくじゃらになっています:
#include <iostream>
#include <algorithm>
#define MAX_OF_N(x, ...) ({\
typeof(x) ra[] = { (x), __VA_ARGS__ }; \
*std::max_element(&ra[0], &ra[sizeof(ra)/sizeof(ra[0])]); \
})
int main() {
int i = 12;
std::cout << MAX_OF_N(i+1,1,3,6,i);
}
マクロを再帰的に展開できたとしても、効率の面でアプローチに1つの小さな問題があると思います...マクロが展開されるとき、 MAX_OF_ [N-1]
の方が大きい場合は、最初からもう一度評価する必要があります。
これはおそらく誰もxDが好きではないという愚かで愚かな答えです
ファイル&quot; source.c&quot;
#include "my_macros.h"
...
ファイル&quot; Makefile&quot;
myprogram: source.c my_macros.h
gcc source.c -o myprogram
my_macros.h: make_macros.py
python make_macros.py > my_macros.h
ファイル&quot; make_macros.py&quot;
def split(l):
n = len(l)
return l[:n/2], l[n/2:]
def gen_param_seq(n):
return [chr(i + ord("A")) for i in range(n)]
def make_max(a, b):
if len(a) == 1:
parta = "("+a[0]+")"
else:
parta = make_max(*split(a))
if len(b) == 1:
partb = "("+b[0]+")"
else:
partb = make_max(*split(b))
return "("+parta +">"+partb+"?"+parta+":"+partb+")"
for i in range(2, 9):
p = gen_param_seq(i)
print "#define MAX_"+str(i)+"("+", ".join(p)+") "+make_max(*split(p))
その後、これらのきれいなマクロが定義されます:
#define MAX_2(A, B) ((A)>(B)?(A):(B))
#define MAX_3(A, B, C) ((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C)))
#define MAX_4(A, B, C, D) (((A)>(B)?(A):(B))>((C)>(D)?(C):(D))?((A)>(B)?(A):(B)):((C)>(D)?(C):(D)))
#define MAX_5(A, B, C, D, E) (((A)>(B)?(A):(B))>((C)>((D)>(E)?(D):(E))?(C):((D)>(E)?(D):(E)))?((A)>(B)?(A):(B)):((C)>((D)>(E)?(D):(E))?(C):((D)>(E)?(D):(E))))
#define MAX_6(A, B, C, D, E, F) (((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C)))>((D)>((E)>(F)?(E):(F))?(D):((E)>(F)?(E):(F)))?((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C))):((D)>((E)>(F)?(E):(F))?(D):((E)>(F)?(E):(F))))
#define MAX_7(A, B, C, D, E, F, G) (((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C)))>(((D)>(E)?(D):(E))>((F)>(G)?(F):(G))?((D)>(E)?(D):(E)):((F)>(G)?(F):(G)))?((A)>((B)>(C)?(B):(C))?(A):((B)>(C)?(B):(C))):(((D)>(E)?(D):(E))>((F)>(G)?(F):(G))?((D)>(E)?(D):(E)):((F)>(G)?(F):(G))))
#define MAX_8(A, B, C, D, E, F, G, H) ((((A)>(B)?(A):(B))>((C)>(D)?(C):(D))?((A)>(B)?(A):(B)):((C)>(D)?(C):(D)))>(((E)>(F)?(E):(F))>((G)>(H)?(G):(H))?((E)>(F)?(E):(F)):((G)>(H)?(G):(H)))?(((A)>(B)?(A):(B))>((C)>(D)?(C):(D))?((A)>(B)?(A):(B)):((C)>(D)?(C):(D))):(((E)>(F)?(E):(F))>((G)>(H)?(G):(H))?((E)>(F)?(E):(F)):((G)>(H)?(G):(H))))
そしてそれについての最高のことは...それが機能するということです^ _ ^
C ++でこの道を進む場合は、をご覧ください。テンプレートのメタプログラミング。きれいではなく、正確な問題を解決できないかもしれませんが、再帰を処理します。
最初に、マクロは再帰的に展開しません。ただし、再帰レベルごとにマクロを作成し、再帰レベルを推測することにより、マクロにリエントラントを設定できます。ただし、この繰り返しと推論の推論はすべて、 Boost.Preprocessor ライブラリ。したがって、最大次数を計算するために高次の折り畳みマクロを使用できます:
#define MAX_EACH(s, x, y) BOOST_PP_IF(BOOST_PP_GREATER_EQUAL(x, y), x, y)
#define MAX(...) BOOST_PP_SEQ_FOLD_LEFT(MAX_EACH, 0, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
MAX(3, 6, 8) //Outputs 8
MAX(4, 5, 9, 2) //Outputs 9
今、これは0から256までのリテラル数を理解します。 CプリプロセッサはC ++を理解しないため、C ++変数または式では機能しません。その単なる純粋なテキスト置換。しかし、C ++は&quot; function&quot;と呼ばれる機能を提供します。これはC ++式で機能し、それを使用して最大値を計算できます。
template<class T>
T max(T x, T y)
{
return x > y ? x : y;
}
template<class X, class... T>
auto max(X x, T ... args) -> decltype(max(x, max(args...)))
{
return max(x, max(args...));
}
現在、上記のコードにはC ++ 11コンパイラが必要です。 C ++ 03を使用している場合、可変引数をシミュレートするために、関数の複数のオーバーロードを作成できます。さらに、プリプロセッサを使用して、この反復コードを生成できます(それが目的です)。 C ++ 03では、次のように記述できます。
template<class T>
T max(T x, T y)
{
return x > y ? x : y;
}
#define MAX_FUNCTION(z, n, data) \
template<class T> \
T max(T x, BOOST_PP_ENUM_PARAMS(n, T x)) \
{ \
return max(x, max(BOOST_PP_ENUM_PARAMS(n, x)));\
}
BOOST_PP_REPEAT_FROM_TO(2, 64, MAX_FUNCTION, ~)