8

私の問題vsprintfは、入力引数を直接取得できないことです。最初に入力を1つずつ取得して保存し、次にこれをにvoid**渡す必要があります。これはすべてWindowsで問題ありませんが、64ビットLinuxの場合、gccはコンパイルできません。からへの変換は許可されていないため、Linuxでこれを行う方法を教えてくれる人はいますか?void**vsprintf()void**va_list

GCCでva_listを動的に作成できますか?

void getInputArgs(char* str, char* format, ...)
{
    va_list args;
    va_start(args, format);
    vsprintf(str, format, args);
    va_end(args);
}  

void process(void)
{
    char s[256];
    double tempValue;
    char * tempString = NULL;
    void ** args_ptr = NULL;
    ArgFormatType format;   //defined in the lib I used in the code
    int numOfArgs = GetNumInputArgs();  // library func used in my code

    if(numOfArgs>1)
    {
        args_ptr = (void**) malloc(sizeof(char)*(numOfArgs-1));
        for(i=2; i<numOfArgs; i++)
        {
            format = GetArgType();    //library funcs

            switch(format)
            {
                case ArgType_double:
                    CopyInDoubleArg(i, TRUE, &tempValue);   //lib func
                    args_ptr[i-2] = (void*) (int)tempValue;    
                    break;

                case ArgType_char:
                    args_ptr[i-2]=NULL;
                    AllocInCharArg(i, TRUE, &tempString);  //lib func
                    args_ptr[i-2]= tempString;
                break;
            }
        }
    }

    getInputArgs(s, formatString, (va_list) args_ptr);   //Here 
           // is the location where gcc cannot compile, 
           // Can I and how if I can create a va_list myself?
}
4

5 に答える 5

6

これを行う方法はいくつかありますが、これはLinuxに固有のものです。gccLinux(テスト済み)では、32ビットビルドと64ビットビルドの両方で機能します。

免責事項:私はこのコードの使用を推奨していません。それは持ち運びができず、ハックであり、ことわざの綱渡りで非常に率直に言って不安定なバランスの象です。私は、元の質問が求めていた、 va_listusingを動的に作成できることを示しているにすぎません。gcc

そうは言っても、次の記事ではva_list、amd64 ABIとの連携方法について詳しく説明しています:Amd64およびVa_arg

構造体の内部構造の知識があれば、マクロをva_listだまして、自分で作成したものから読み取ることができます。va_argva_list

#if (defined( __linux__) && defined(__x86_64__))
// AMD64 byte-aligns elements to 8 bytes
#define VLIST_CHUNK_SIZE 8
#else
#define VLIST_CHUNK_SIZE 4
#define _va_list_ptr _va_list
#endif

typedef struct  {
    va_list _va_list;
#if (defined( __linux__) && defined(__x86_64__))
    void* _va_list_ptr;
#endif
} my_va_list;

void my_va_start(my_va_list* args, void* arg_list)
{
#if (defined(__linux__) && defined(__x86_64__))
    /* va_args will read from the overflow area if the gp_offset
       is greater than or equal to 48 (6 gp registers * 8 bytes/register)
       and the fp_offset is greater than or equal to 304 (gp_offset +
       16 fp registers * 16 bytes/register) */
    args->_va_list[0].gp_offset = 48;
    args->_va_list[0].fp_offset = 304;
    args->_va_list[0].reg_save_area = NULL;
    args->_va_list[0].overflow_arg_area = arg_list;
#endif
    args->_va_list_ptr = arg_list;
}

void my_va_end(my_va_list* args)
{
    free(args->_va_list_ptr);
}

typedef struct {
    ArgFormatType type; // OP defined this enum for format
    union {
        int i;
        // OTHER TYPES HERE
        void* p;
    } data;
} va_data;

これで、メソッドまたは次のva_listようなものを使用して、ポインター(64ビットビルドと32ビットビルドの両方で同じ)を生成できます。process()

void* create_arg_pointer(va_data* arguments, unsigned int num_args) {
    int i, arg_list_size = 0;
    void* arg_list = NULL;

    for (i=0; i < num_args; ++i)
    {
        unsigned int native_data_size, padded_size;
        void *native_data, *vdata;

        switch(arguments[i].type)
        {
            case ArgType_int:
                native_data = &(arguments[i].data.i);
                native_data_size = sizeof(arguments[i]->data.i);
                break;
            // OTHER TYPES HERE
            case ArgType_string:
                native_data = &(arguments[i].data.p);
                native_data_size = sizeof(arguments[i]->data.p);
                break;
            default:
                // error handling
                continue;
        }

        // if needed, pad the size we will use for the argument in the va_list
        for (padded_size = native_data_size; 0 != padded_size % VLIST_CHUNK_SIZE; padded_size++);

        // reallocate more memory for the additional argument
        arg_list = (char*)realloc(arg_list, arg_list_size + padded_size);

        // save a pointer to the beginning of the free space for this argument
        vdata = &(((char *)(arg_list))[arg_list_size]);

        // increment the amount of allocated space (to provide the correct offset and size for next time)
        arg_list_size += padded_size;

        // set full padded length to 0 and copy the actual data into the location
        memset(vdata, 0, padded_size);
        memcpy(vdata, native_data, native_data_size);
    }

    return arg_list;
}

そして最後に、それを使用することができます:

va_data data_args[2];
data_args[0].type = ArgType_int;
data_args[0].data.i = 42;

data_args[1].type = ArgType_string;
data_args[1].data.p = "hello world";

my_va_list args;
my_va_start(&args, create_arg_pointer(data_args, 2));

vprintf("format string %d %s", args._va_list);

my_va_end(&args);

そして、あなたはそれを持っています。これは通常のマクロとほとんど同じように機能しますが、呼び出し規約に依存してスタックフレームを設定する代わりに、動的に生成されたバイトアラインされた独自のポインターを渡して使用できます。va_startva_end

于 2015-06-03T23:45:35.217 に答える
3

どこかで述べたようにlibffiを使ってみましたが、うまくいきました。以下はリンクです。同様の問題を抱えている他の人に役立つことを願っています。私がここに来たすべての助けにもう一度感謝します!

リンク: http ://www.atmark-techno.com/~yashi/libffi.html-http://www.swig.org/Doc1.3/Varargs.htmlの 簡単な例 -printf ()およびその他の例与えられた

于 2012-07-31T13:05:52.550 に答える
2

のタイプはva_listvoid **64ビットgcc(Intel x86 / 64マシンの場合)とは異なります。Mac OS X10.7.4とRHEL5の両方で、にヘッダーはありませstdarg.h/usr/include。次のコードを検討してください。

#include <stdarg.h>
#include <stdio.h>
int main(void)
{
    printf("sizeof(va_list) = %zu\n", sizeof(va_list));
    return 0;
}

64ビットコンパイルを使用したRHEL5とMacOSX10.7の両方での出力は次のとおりです。

sizeof(va_list) = 24

32ビットコンパイルでは、各プラットフォームでの出力は次のようになります。

sizeof(va_list) = 4

(32ビットバージョンと64ビットバージョンの間にこれほど多くの不一致があることに驚いたと思うかもしれません。32ビットバージョンでは12から24の値を期待していました。)

したがって、タイプは不透明です。何かを教えてくれるヘッダーさえ見つかりません。また、64ビットマシンの単一のポインターよりもはるかに大きくなります。

コードが一部のマシンで機能する場合でも、どこでも機能することは保証されていません。

GCC 4.7.1マニュアルには、実行時にビルドできる機能については記載されていませんva_list

于 2012-07-27T23:31:35.480 に答える
0

次のクラスは私のために働きます:

class VaList
{
    va_list     _arguments;

public:

    explicit inline VaList(const void * pDummy, ...)
    {
        va_start(_arguments, pDummy);
    }

    inline operator va_list &()
    {
        return _arguments;
    }

    inline operator const va_list &() const
    {
        return _arguments;
    }

    inline ~VaList()
    {
        va_end(_arguments);
    }
};

そしてそれはこのように使用することができます:

void v(const char * format, const va_list & arguments)
{
    vprintf(format, const_cast<va_list &>(arguments));
}

...

    v("%d\n", VaList("", 1)); // Uses VaList::operator va_list &()
    v("%d %d\n", VaList(nullptr, 2, 3)); // Uses VaList::operator va_list &()
    vprintf("%s %s %s\n", VaList("", "Works", "here", "too!"));

    const VaList args(NULL, 4, 5, "howdy", "there");
    v("%d %d %s %s\n", args); // Uses VaList::operator const va_list &() const

最初のダミーパラメータは任意の種類のポインタにすることができ、次の引数のアドレスを計算するためにのみ使用されます。

もちろん、Cでも同じことができますが、それほど巧妙ではありません(参照の代わりにポインターを使用してください)。

VaListを使用して動的va_listを作成する簡単な例:

static void VectorToVaList(const std::vector<int> & v, va_list & t)
{
    switch (v.size())
    {
        case 1: va_copy(t, VaList("", v[0])); return;
        case 2: va_copy(t, VaList("", v[0], v[1])); return;
        case 3: va_copy(t, VaList("", v[0], v[1], v[2])); return;
        case 4: va_copy(t, VaList("", v[0], v[1], v[2], v[3])); return;
        // etc
    }

    throw std::out_of_range("Out of range vector size!");
}

と使用法:

    va_list t;
    VectorToVaList(std::vector<int>{ 1, 2, 3, 4 }, t);
    vprintf("%d %d %d %d\n", t);
于 2018-08-15T07:39:34.307 に答える
-1

解決しようとしている問題が、va_listスタイル内の関数に任意の型を渡すことを挿入することである場合は、次の使用を検討してunionください。

#include <iostream>
#include <cstdarg>

union ARG
{
    int d;
    char* s;
    double f;
};

int main()
{
    printf("%d %s %f \n", 1, "two", 3.1415 );
    // Output: 1 two 3.141500

    char format[ 1024 ] = "%d %s %f\n";
    ARG args[ 5 ] = { };
    args[ 0 ].d = 1;
    args[ 1 ].s = "two";
    args[ 2 ].f = 3.1415;
    printf( format, args[ 0 ], args[ 1 ], args[ 2 ], args[ 3 ], args[ 4 ] );
    // Output: 1 two 3.141500

    return 0;
}

私の解決策についてあなたが注意するいくつかのこと:

  • 正しい数の引数を生成する試みは行われません。つまり、引数を過剰に供給しますが、ほとんどの関数は最初のパラメーターを調べて、残りのパラメーター(つまりフォーマット)を処理する方法を決定します。
  • フォーマットを動的に作成する必要はありませんでしたが、動的にデータを入力するルーチンを作成するのは簡単な作業formatですargs

これをテストしました:-Ubuntu、g ++-Android NDK

さらにテストを行い、この回答が倍精度で機能しないことについて@PeterCoordesのコメントを確認しました。

于 2019-12-02T15:59:09.543 に答える