6

ネストされた関数/ブロックを使用して移植可能な C コードを作成することは可能ですか?

gcc は非標準拡張機能としてネストされた関数のみをサポートし、clang はブロックのみをサポートすることを理解していますが、MACROS を使用して標準 C を使用して両方でコンパイルするコードを作成する方法はありますか?

それが不可能な場合、最善の回避策は何ですか? 例として、次のようなパラメーターを受け取る移植可能なバージョンをどのように実装しますか? GCC での簡単な例:

int main(int argc, char*[] argv)
{
  char reverse = 0;

  int cmp_func(const void *a, const void *b)
  {
    const int* aa = (const int)a;
    const int* bb = (const int)b;
    return (reverse) ? aa - bb : bb - aa;
  }

  int list[8] = {1,2,3,4,5,20,100,200};
  qsort(list, 8, sizeof(int), &cmp_func);
}

同様の例は、Clang の Blocks を使用してまとめることができます。理想的には、ソリューションはスレッドセーフであるべきです (そのため、グローバル変数は避けてください)。

編集:明確にするために、「標準」はC99を意味すると仮定しましょう。上記は些細な例です。私が求めているのは、いくつかのパラメーターを必要とする並べ替えに対する C99 のアプローチです。ここではブール値として char を使用していますが、複数の整数などを取るソリューションを求めています。これは、グローバル変数なしでは不可能なようです。

編集 2:関数ポインターと共に void ポインターを渡すと、ネストされた関数で実行できるすべてのことを実行できることに気付きました。qsort_r提案してくれた@Quuxplusoneに感謝しqsort_sます。と に移植可能なラッパーをまとめようとしましqsort_rqsort_s。コンパレーター関数と状態を格納する void ポインターを使用するため、複雑な並べ替えアルゴリズムのネストされた関数への依存がなくなります。したがって、GCC と Clang の両方でコンパイルできます。

typedef struct
{
  void *arg;
  int (*compar)(const void *a1, const void *a2, void *aarg);
} SortStruct;

int cmp_switch(void *s, const void *aa, const void *bb)
{
  SortStruct *ss = (SortStruct*)s;
  return (ss->compar)(aa, bb, ss->arg);
}

void sort_r(void *base, size_t nel, size_t width,
            int (*compar)(const void *a1, const void *a2, void *aarg), void *arg)
{
  #if (defined _GNU_SOURCE || defined __GNU__ || defined __linux__)

    qsort_r(base, nel, width, compar, arg);

  #elif (defined __APPLE__ || defined __MACH__ || defined __DARWIN__ || \
         defined __FREEBSD__ || defined __BSD__ || \
         defined OpenBSD3_1 || defined OpenBSD3_9)

    SortStruct tmp = {arg, compar};
    qsort_r(base, nel, width, &tmp, &cmp_switch);

  #elif (defined _WIN32 || defined _WIN64 || defined __WINDOWS__)

    SortStruct tmp = {arg, compar};
    qsort_s(*base, nel, width, &cmp_switch, &tmp);

  #else
    #error Cannot detect operating system
  #endif
}

注: 多くのプラットフォームでこれをテストしていないため、バグが見つかった場合はお知らせください。お使いのマシンでは機能しません。

使用例として、選択した回答と同じ並べ替えを実装しました。

int sort_r_cmp(const void *aa, const void *bb, void *arg)
{
  const int *a = aa, *b = bb, *p = arg;
  int cmp = *a - *b;
  int inv_start = p[0], inv_end = p[1];
  char norm = (*a < inv_start || *a > inv_end || *b < inv_start || *b > inv_end);

  return norm ? cmp : -cmp;
}

int arr[18] = {1, 5, 28, 4, 3, 2, 10, 20, 18, 25, 21, 29, 34, 35, 14, 100, 27, 19};
int p[] = {20, 30};
sort_r(arr, 18, sizeof(int), sort_r_cmp, p);
4

5 に答える 5

12

楽しみのために(そして元の質問に答えるために)、マクロシステムを使用して標準準拠のC99でネストされた関数を記述して、コードのネストされたバージョンを「もつれを解く」ことは完全に可能です。可能な実装の 1 つを次に示します: https://github.com/Leushenko/C99-Lambda

これを使用すると、次のように忌まわしきものを書くことができます。

typedef int(* fptr)(int);
func(fptr, someFunc, (void) {
    return fn(int, (int a), {
        fptr f = fn(int, (int b), { return b * 6; });
        return a * f(a + 1);
    });
})

ただし、はっきりさせておきますが、これは C でこの種のコードを記述するには絶対最悪の方法です。実際にマクロ ライブラリを使用してこのようなコードを記述する必要がある場合は、次のように仕事を辞めてください。プログラマーになって農家に。これを本番環境で使用すると、睡眠中に同僚に殺害される可能性があります。

また、おもしろいことに、技術的には標準に準拠しているにもかかわらず、その多くのマクロの膨大な重量を処理できるプリプロセッサを備えた唯一のコンパイラは、とにかく GCC と Clang です。

于 2013-02-08T21:28:45.160 に答える
4

C標準ではネストされた関数が許可されていないため、Cでネストされた関数を作成する移植可能な方法はありません。
ここでは、マクロはあまり役に立ちません。マクロはプリプロセッサによって評価され、コンパイラは関数をネストしてエラーをフラグするコードを引き続き確認するからです。

于 2012-08-31T11:57:40.500 に答える
1

ここで@Kirilenkoの提案に従って、グローバル変数とミューテックスを使用してパラメーターをソートコンパレーター関数に渡すソリューションを考え出しました。このアプローチはスレッドセーフであり、ネストされた関数で実現されるすべてのことを実行でき、コンパイラ間で移植可能である必要があります。

この例では、整数のリストを並べ替えますが、特定の領域の並べ替えを逆にします。

// define lock for sort parameters
pthread_mutex_t lock;

// Parameters used in sort funciton - invert region (inclusive)
int invert_start, invert_end;

// Comparitor that uses global variables (invert_start, invert_end) as paramaters
int cmp_func(const void *a, const void *b)
{
  const int aa = *(const int*)a;
  const int bb = *(const int*)b;

  if(aa < invert_start || aa > invert_end ||
     bb < invert_start || bb > invert_end)
  {
    return aa - bb;
  }
  else
  {
    return bb - aa;
  }
}

void sort_things(int* arr, int arr_len, int inv_start, int inv_end)
{
  // Mutex lock
  pthread_mutex_lock(&lock);

  // Set params
  invert_start = inv_start;
  invert_end = inv_end;

  // do sort
  qsort(arr, arr_len, sizeof(*arr), &cmp_func);

  // Mutex free
  pthread_mutex_unlock(&lock);
}

結果の例:

input: 1 5 28 4 3 2 10 20 18 25 21 29 34 35 14 100 27 19
invert_start = 20, invert_end = 30
output: 1 2 3 4 5 10 14 18 19 29 28 27 25 21 20 34 35 100
于 2012-09-05T14:52:00.450 に答える
0

入れ子関数とグローバルの問題に取り組むのはなぜですか?100%ポータブル(K&Rでも)ソリューションは、2つの異なる関数を使用するだけです。1つは通常の順序で並べ替え、もう1つは逆の順序で並べ替えてから、次のように呼び出します。

qsort(list, 8, sizeof(int), reverse ? cmp_func_reverse : cmp_func);

注:。を使用して関数のアドレスを取得する必要はありません&

于 2012-09-14T12:34:11.777 に答える
0

ネストされた関数は C 標準にはありませんが、gcc拡張機能です (フラグ-fnested-functionsがアクティブな場合)。また、静的関数 ( cmp_func) と追加パラメーター ( reverse) を使用して同じことを行うこともできます。

于 2012-08-31T12:12:14.407 に答える