18

引数をvoidポインターのリストに保存することにより、関数呼び出しを(関数ラッパーを使用して)延期しようとしています:

void *args[]
int argt[]

argt は、void * の場所に格納されているデータ型を記憶するために使用されます。

後で、延期された関数を呼び出す必要があります。

function(args[0], args[1])

しかし問題は、それらの型を正しく指定しなければならないことです。

次のようなマクロを使用します。

#define ARGTYPE(arg, type) type == CHARP ? (char *) arg : (type == LONGLONG ? *((long long *) arg) : NULL)

関数呼び出しは次のようになります。

function(ARGTYPE(args[0], argt[0]), ARGTYPE(args[1], argt[1]))

2 つの問題があります。

1) 警告: マクロ定義によって生成された条件式のポインター/整数型の不一致 (2) を参照してください)

2) 本当の問題: long long 引数が正しく渡されません (毎回 0 になります)

明らかに何かが欠けているので、マクロが正しく機能しない理由を (詳細に) 説明したり、別のアプローチを提案したりできますか?

EDIT: ここに引数の保存部分を追加します (関連する詳細、va_list を解析します)。形式指定子に基づいて型を取得します。

while (*format)
{
    switch(*format)
    {
        case 's':
            saved_arguments[i] = strdup(arg);
            break;
        case 'l':
            saved_arguments[i] = malloc(sizeof(long long));
            *((long long *) saved_arguments[i]) = arg;
            break;
    }
    i++;
    format++;
}
4

6 に答える 6

3

演算子の true および false 結果オペランドは?:互換性のある型である必要があるため、試行は失敗しています。

考えられるすべての組み合わせで引数を展開する関数呼び出しラッパー マクロを作成するという私の最初の提案は、実際には 2 つの型と 2 つの引数以上をサポートしたい場合、実行可能な解決策ではありません。

私はあなたが電話を利用swapcontext()setcontext()て延期することができると思いました. 基本的に、引数をデータ構造に隠しておき、隠し引数をアンパックする将来の呼び出しのために印刷関数から戻るのではなくswapcontext()、印刷を再開できるようになるまで、引き継ぎたい関数にジャンプするために使用します。前後にフリップするだけであれば、必要なコンテキストは 2 つだけです。

struct execution_state {
    /*...*/
    ucontext_t main_ctx_;
    ucontext_t alt_ctx_;
    char alt_stack_[32*1024];
} es;

印刷関数は次のようになります。

void deferred_print (const char *fmt, ...) {
    va_list ap;
    while (need_to_defer()) {
        /*...*/
        swapcontext(&es.main_ctx_, &es.alt_ctx_);
    }
    va_start(ap, fmt);
    vprintf(fmt, ap);
    va_end(ap);
}

wherealt_ctx_は、印刷が再開できるまで実行を引き継ぐランデブー関数に初期化されます。印刷が再開できる場合、印刷コンテキストは次のように復元されます。

    setcontext(&es.main_ctx_);

おもちゃの例をコード化しました。ここで実際の動作を確認できます。

于 2013-08-06T14:40:54.237 に答える
1

次のようなものを使用できます。

#include <stdio.h>
#include <string.h>
#include <stdarg.h>

enum e_type {
    CHAR = 0,
    INT,
    LONG,
    CHARPTR
};

struct type {
    enum e_type type;
    union {
        char c;
        int i;
        long l;
        char *s;
    } value;
};

#define convert(t) (t.type == CHAR ? t.value.c : (t.type == INT ? t.value.i : (t.type == LONG ? t.value.l : t.value.s)))

void test_fun(int argc, ...)
{
    va_list args;
    int i = 0, curr = 0;
    struct type t;

    va_start(args, argc);

    while (i++ < argc)
    {
        t = va_arg(args, struct type);

        switch (t.type) {
            case CHAR:
                printf("%c, ", convert(t));
                break;

            case INT:
                printf("%d, ", convert(t));
                break;

            case LONG:
                printf("%ld, ", convert(t));
                break;

            case CHARPTR:
                printf("%s, ", convert(t));
                break;
        }
    }
    printf("\n");
    va_end(args);
}

void test_fun2(char c, long l, char *s)
{
    printf("%c, %ld, %s\n", c, l, s);
}

int main()
{
    struct type t1, t2, t3;
    t1.type = CHAR;
    t1.value.c = 0x61;

    t2.type = LONG;
    t2.value.l = 0xFFFF;

    t3.type = CHARPTR;
    t3.value.s = "hello";

    test_fun(3, t1, t2, t3);
    test_fun2(convert(t1), convert(t2), convert(t3));

    return 0;
}

ここでの秘密は、ユニオンを使用することです。

コンパイラはマクロから返された値の型を正しく判断できないため、このコードは多くの警告を出します。

上記のコードは正しく出力されます:

a, 65535, hello, 
a, 65535, hello

(Linux で gcc と clang でテスト済み)

于 2013-08-19T19:21:15.403 に答える
0

これを解決するには、次の方法をお勧めします。まず、関数呼び出し中の引数の型チェックを取り除きましょう。

#include <stdio.h>

int function(int a, long long b)
{
    printf("a = %d\n", a);
    printf("b = %lld\n", b);
    return 0;
}

int function2(double c, char *d)
{
    printf("c = %f\n", c);
    printf("d = %s\n", d);
    return 0;
}

typedef int (*ftype)(); // The type of function which can take undefined number of arguments and return 'int'

int main(int argc, char *argv[])
{
    ftype f1, f2;

    f1 = (ftype)function;
    f2 = (ftype)function2;
    f1(10, 100500);
    f2(2.3, "some string");
    return 0;
}

次に、関数呼び出しを正しく実行する「ディスパッチャ」を実装できます。

int dispatch(void **args, int call_type, ftype function)
{
    int ret_val;
    switch(call_type)
    {
        0: ret_val = function(*(int*)args[0], *(double*)args[1]);
           break;
        1: ret_val = function(*(long long*)args[0], *(int*)args[1]);
           break;

        etc etc...
    }
}

このアプローチの主な欠点は、ディスパッチャーに多くのケースを実装する必要があることです。そしてもちろん、これらのすべてのケースがアプリオリに定義されている場合にのみ機能します。

最後に、これは非常に危険な実装であると言わざるを得ません。奇妙で危険なエラーの原因になりやすいのです。

于 2013-08-06T16:53:09.573 に答える
-1

使わない理由g_timeout_add_seconds()

デフォルトの優先度 G_PRIORITY_DEFAULT で定期的に呼び出される関数を設定します。関数は FALSE を返すまで繰り返し呼び出され、その時点でタイムアウトは自動的に破棄され、関数は再度呼び出されなくなります。

于 2013-08-06T14:06:25.837 に答える