C言語マクロで配列をフィルターする
概要
久しぶりのC言語との邂逅でマクロに触れることができた。
配列(begin, end)に対してfirstとfilterしてみたメモ。
実装
first
配列を捜査して条件にマッチする最初の要素のポインタを取得します。
_Type
には型名が入ります。_begin
, _end
は配列の先頭と末尾の次を指すポインタです。C++のイテレータと捉えていただければ。
_first
には結果が格納されます。型は_Type**
になります。残りの_Match
と_arg
は要素のチェックに利用します。
bb_algorithm.h
#define BB_list_first(_Type, _begin, _end, _first, _Match, _arg) \
do { \
_Type* _ptr; \
for (_ptr = _begin; _ptr < _end; ++_ptr) { \
if (_Match(_ptr, _arg)) { \
*_first = _ptr; \
break; \
} \
} \
} while (0)
このマクロで
static int test_first(int* lhs, void const* arg) {
return *lhs >= *(int*)arg ? 1 : 0;
}
int main(int argc, const char * argv[]) {
int list[5] = { 2, 3, 5, 7, 11 };
int* first = NULL;
int arg = 5;
BB_list_first(int, &list[0], &list[sizeof(list)/sizeof(*list)], &first, test_first, &arg);
if (first) {
printf(" index: %d\n", (int)(first - list));
}
return 0;
}
すると
index: 2
になります。(GitHubのコードから簡略化してます)
_Match
にブロックスやラムダを利用したいのですが関数ポインタで妥協します。マクロに正しい引数を渡せるようになるまでコンパイラーと格闘が必要です。
filter
基本はfirstと同じ。条件にマッチする要素を_filtered
が指す配列に格納していく。
bb_algorithm.h
#define BB_list_filter(_Type, _begin, _end, _filtered, _Match, _arg) \
do { \
_Type* _ptr; \
for (_ptr = _begin; _ptr < _end; ++_ptr) { \
if (_Match(_ptr, _arg)) { \
*_filtered = *_ptr; \
++_filtered; \
} \
} \
} while (0)
このマクロで
static int test_apple_filter(char const** lhs, void const* arg) {
return strstr(*lhs, "apple") != NULL ? 1 : 0;
}
int main(int argc, const char * argv[]) {
char const* list[4] = { "I have a pen", "I have an apple", "ugh", "apple pen" };
char const* filtered[4];
char const** filtered_end = filtered;
BB_list_filter(char const*, &list[0], &list[sizeof(list)/sizeof(*list)], filtered_end, test_apple_filter, NULL);
{
char const** ptr;
for (ptr = filtered; ptr < filtered_end; ++ptr) {
printf(" %s\n", *ptr);
}
}
return 0;
}
すると
I have an apple
apple pen
になります。(GitHubのコードから簡略化してます)
firstよりもマクロの引数が難しいです。filtered_end
を初期化してから渡すことが要求されてます。
まとめ
マクロをC++のテンプレートのように利用したかったのですが、コンパイルエラーを解消するのが難しいです。