17

たとえば、配列に何らかの形で格納された引数のリストがあるとします。

関数 pointer が与えられた場合、格納されている引数のリストを渡して関数を呼び出すにはどうすればよいですか?

配列を引数として渡そうとはしていません。わかりましたか?その各要素を引数として渡したい。配列は単なる例です。引数をタプル構造に格納できます。また、手元に関数ポインターがあり、文字列形式の署名がある可能性があることを確認してください。可変長リストを処理できる関数を定義しようとしているだけではありません。

私がそれを行う唯一の方法は、アセンブリ(by __asm pushet al。)またはこれを使用することです:

void (*f)(...);

int main()
{
    f = <some function pointer>;
    int args[]; <stored in a array, just to illustrate>
    int num_args = <some value>;

    switch(num_args)
    {
        case 0:
            f();
        break;

        case 1:
            f(args[0]);
        break;

        case 2:
            f(args[0], args[1]);
        break;

        /* etc */
    }

    return 0;
}

私はこのアプローチがあまり好きではありません...

別のポータブルで短い形式はありますか?

いくつかのスクリプト言語は、C 関数を呼び出すことができます。

Python や Ruby などのスクリプト言語はどのようにそれを行うのでしょうか? 移植可能な方法でどのように実装しますか? 最終的に、いくつかのプラットフォームまたは上記のアセンブリを使用するだけですか?

スクリプト言語から C へのパラメータ マーシャリングやその他の詳細については質問していないようです。最終的に内部的に、スクリプト言語による C 関数の呼び出しがどのように構築されるかだけに関心があります。

編集

質問のタイトルはそのままにしますが、質問のより良い方法は次のとおりだと思います。

実行時にのみ使用可能なポインターとシグネチャーを使用して C 関数を呼び出す方法は?

アップデート

PLT スキームの外部インターフェイスから:

コールアウトは通常の関数呼び出しです。動的設定では、(バイナリ) 入力/出力タイプを指定する「呼び出しインターフェイス」オブジェクトを作成します。このオブジェクトを任意の関数ポインタおよび入力値の配列とともに使用して、関数へのコールアウトを実行し、その結果を取得できます。これを行うには、スタックを操作し、関数がどのように呼び出されるかを知る必要があります。これらは、libffiが処理する詳細です。

@AnttiHaapala にlibffiを検索、検出、および指定していただきありがとうございます。それは私が探していたものであり、多くのスクリプト言語で使用されており、いくつかのアーキテクチャとコンパイラにまたがって実装されたポータブル ライブラリです。

4

4 に答える 4

12

指定された数の引数で任意の関数ポインタを呼び出す移植可能な方法は何かと尋ねました。正解は、そのような方法はありません

たとえば、python は ctypes モジュールを介して C 関数を呼び出すことができますが、これは正確なプロトタイプと呼び出し規則を知っている限り移植可能です。C で同じことを達成する最も簡単な方法は、コンパイル時に関数ポインターのプロトタイプを知ることです。

アップデート

python / ctypes の例では、ctypes モジュールが有効になっている各プラットフォームで、python は特定の引数セットの呼び出しスタックを記述する方法を認識しています。たとえば、Windows では、Python は 2 つの標準呼び出し規則を認識しています。スタック上の C 順序のパラメーターを持つ cdecl と、「パスカル スタイルの順序」を持つ stdcall です。Linux では、32 ビットと 64 ビットのどちらの共有オブジェクトを呼び出すかなどについて考慮する必要があります。Python が別のプラットフォームにコンパイルされている場合、ctypes も変更する必要があります。ctypes モジュールの C コードは、移植性がありません。

更新 2

Python の場合、魔法はここにあります: ctypes source code . 特に、 http://sourceware.org/libffi/にリンクしているようです。これは、まさに必要なものかもしれません。

于 2012-09-04T14:43:10.377 に答える
7

私はlibffiの作者です。それはあなたが求めていることをします。

于 2012-09-04T19:54:07.557 に答える
6

@AnttiHaapalaはlibffiを指摘しました。これに関するいくつかの情報があります:

libffiとは何ですか?

一部のプログラムは、コンパイル時にどの引数が関数に渡されるかを知らない場合があります。たとえば、インタプリタは、実行時に、特定の関数を呼び出すために使用される引数の数とタイプについて通知される場合があります。このようなプログラムで「libffi」を使用して、インタプリタプログラムからコンパイル済みコードへのブリッジを提供できます。

'libffi'ライブラリは、さまざまな呼び出し規約へのポータブルで高水準のプログラミングインターフェイスを提供します。これにより、プログラマーは実行時に呼び出しインターフェースの説明で指定された任意の関数を呼び出すことができます。

FFIは、ForeignFunctionInterfaceの略です。外部関数インターフェースは、ある言語で記述されたコードが別の言語で記述されたコードを呼び出すことを可能にするインターフェースの一般的な名前です。'libffi'ライブラリは、実際には、フル機能の外部関数インターフェイスの最下位のマシン依存レイヤーのみを提供します。2つの言語間で渡される値の型変換を処理するレイヤーは、「libffi」の上に存在する必要があります。

'libffi'は、呼び出したい関数へのポインターがあり、それを渡す引数の数とタイプ、および関数の戻りタイプを知っていることを前提としています。


歴史的背景

もともとAnthonyGreen(SOユーザー: anthony -green)によって開発されたlibffiは、SiliconGraphicsのGencallライブラリに触発されました。Gencallは、アドレスによる関数への呼び出しを許可し、特定の呼び出し規約の呼び出しフレームを作成する目的で、Gianni Marianiによって開発され、SGIによって採用されました。Anthony Greenはアイデアを洗練し、他のアーキテクチャや呼び出し規約、オープンソーシングlibffiに拡張しました。


libffiで捕虜を呼び出す

#include <stdio.h>
#include <math.h>
#include <ffi.h>

int main()
{
  ffi_cif     call_interface;
  ffi_type    *ret_type;
  ffi_type    *arg_types[2];

  /* pow signature */
  ret_type = &ffi_type_double;
  arg_types[0] = &ffi_type_double;
  arg_types[1] = &ffi_type_double;

  /* prepare pow function call interface */
  if (ffi_prep_cif(&call_interface, FFI_DEFAULT_ABI, 2, ret_type, arg_types) == FFI_OK)
  {
    void *arg_values[2];
    double x, y, z;

    /* z stores the return */
    z = 0;

    /* arg_values elements point to actual arguments */
    arg_values[0] = &x;
    arg_values[1] = &y;

    x = 2;
    y = 3;

    /* call pow */
    ffi_call(&call_interface, FFI_FN(pow), &z, arg_values);

    /* 2^3=8 */
    printf("%.0f^%.0f=%.0f\n", x, y, z);
  }

  return 0;
}

私がlibffiを主張できるのは、私が求めたことを実行するための移植可能な方法だと思います。これは、AnttiHaapalaがそのような方法はないと主張しているのとは対照的です。libffiをポータブルテクノロジーと呼ぶことができない場合、コンパイラーとアーキテクチャー間でどれだけ移植/実装されているか、そしてどのインターフェイスがC標準に準拠しているかを考えると、Cなどをポータブルと呼ぶこともできません。

から抽出された情報と履歴:

https://github.com/atgreen/libffi/blob/master/doc/libffi.info

http://en.wikipedia.org/wiki/Libffi

于 2012-09-04T16:12:27.603 に答える
2

安全のために、変数を送信する前にアンパックする必要があります。アセンブラーを使用してパラメーター スタックをハックすると、コンパイラー間で移植できない場合があります。呼び出し規約は異なる場合があります。

Ruby について話すことはできませんが、Perl および Python への C インターフェイスを使用してかなりの数のプログラムを作成しました。Perl および Python の変数は、C の変数と直接比較することはできませんが、より多くの機能を備えています。たとえば、Perl のスカラーには文字列と数値の 2 つの値があり、一度に有効なのはそのうちの 1 つだけです。

Perl/Python 変数と C の間の変換は、 ( Python のモジュールで) packandを使用して行われます。C インターフェイスでは、型に応じて特定の API を呼び出して変換を行う必要があります。したがって、これは単純なポインター転送ではなく、アセンブラーを必要としません。unpackstruct

于 2012-09-04T13:26:03.710 に答える