6

最小限の労力でシンタックス シュガーとして C の高次関数 (HOF) を実装したいと考えています。たとえば、次のコードの場合

function add(int x) {
  return int(int y) {
    return x + y;
  };
}

int main() {
  function add1 = add(1);
  return add1(2);
}

次のように純粋な C にトランスコンパイルされます。

#include <stdlib.h>

typedef struct {
  void *ctx;
  void* (*fun)(void *arg, void *ctx);
} function;

function new_function(void *ctx, void* (*fun)(void *, void *)) {
  function f = {.ctx=ctx, .fun=fun};
  return f;
}

void* apply(function f, void *arg) {
  return (*(f.fun))(arg, f.ctx);
}

typedef struct {
  int x;
} context$1;

void* new_context$1(int x) {
  context$1 *ctx = malloc(sizeof(context$1));
  ctx->x = x;
  return ctx;
}

void* function$1(void *arg, void *ctx) {
  int y = (int)arg;
  int x = ((context$1*)ctx)->x;
  return (void*)(x + y);
}

function add(int x) {
  return new_function(new_context$1(x), function$1);
}

int main() {
  function add1 = add(1);
  return (int)apply(add1, (void*)2);
}

この (手動で) トランスコンパイルされたバージョンを実行しましたが、正常に動作します。実装には、AST 操作とラムダ リフティングで十分だと思います。

私のアプローチに潜在的な欠陥はありますか? HOF のより簡単な方法はありますか、または実装を容易にするためにアプローチを改善できますか?

4

2 に答える 2

2

これまでに2つの明らかな問題があります。

  • void *を使用してパラメータを表し、任意のタイプの戻り値を使用すると、最終的には機能しなくなります。ia-32の「longlong」パラメーター、または値によって渡される構造体を検討してください。
  • ガベージコレクションなしで高階関数を有用にすることは(可能であれば)困難です。context $ 1がmallocされているが、決して解放されていない独自の例からそれを見ることができます。ベームGCはここで役立つかもしれません。
于 2013-01-04T16:25:18.540 に答える
1

生成されたコードが未定義の動作を引き起こすことに注意してください。いくつかの場所で、直接キャストを介して整数型とポインター型を変換しています。これは C では合法ではありません。ポインターと anintが同じサイズであるという保証や、値を変更または破損することなくそれらの間でキャストできるという保証はありません。コードは偶然にも特定のシステムで機能する場合がありますが、他の多くのシステムでは機能しません。

ポインターと整数型の両方 (さらに言えば構造体も) を汎用的に処理できる唯一の方法は、可変長の引数リストを使用してパラメーターを渡すことです。そうすれば、基になるデータ型のサイズに関係なく、各引数を抽出できます。

もう 1 つのオプションは、一般的な構造を削除functionして、コード内の各 HOF 用にカスタマイズされた構造を作成することです。関数ポインターは、必要なパラメーターを汎用インターフェイスの背後で抽象化しようとする代わりに、すべての必要なパラメーターを直接リストすることができます。これにより、問題のあるキャストが排除され、使用されるデータ型に関係なく、コードが期待どおりに動作できるようになります。

ユーザビリティに関する限り、HOF の戻り値の型が宣言されている行で指定される (または少なくともリストされる) ように、インターフェイスを変更することを検討してください。C オブジェクトは、常にその型を宣言にリストします。あなたの例では、宣言はfunction add1 = add(1);. この関数が返すデータ型を見つけるには、HOF の定義を掘り下げる必要があります。これは、サンプル コードでは難しい作業ではありませんが、より複雑なコードでは簡単ではない可能性があります。ライブラリからのものである場合、HOF のコードがまったくない可能性があります。おそらく、次のようなものfunction(int) add1 = add(1);がより明確になる可能性があります。

static余談ですが、モジュール間の名前の衝突を防ぐために、自動生成された関数を定義することをお勧めします。

于 2013-01-04T19:04:27.423 に答える