gpt4 book ai didi

c++ - 在 C/C++ 中存储 va_list 供以后使用的最佳方法

转载 作者:可可西里 更新时间:2023-11-01 18:09:55 25 4
gpt4 key购买 nike

我正在使用 va_list 构建一个呈现的字符串。

void Text2D::SetText(const char *szText, ...)

一切都很好,但现在用户可以在应用程序运行时更改语言。我需要重新生成所有文本字符串并在初始化后重新缓存文本位图。我想存储 va_list 并在需要生成文本时使用它。

为了提供更多背景信息,这需要在我正在翻译的 key 字符串中包含动态数据的情况下发生。

"Player Score:%d"

那是我需要翻译的关键字符串。我想保留 va_list 中提供的数字供以后使用(在初始化文本的函数范围之外),以防在初始化后需要重新翻译。我最好保留一份 va_list 的拷贝,以便与 vsnprintf 一起使用。

我对此进行了一些研究,并找到了一些方法。其中一些我质疑它是否是一种合适的方法(就稳定和便携而言)。

最佳答案

这个问题确实激起了我的兴趣。我自己的工作中也会遇到类似的问题,所以这里设计的解决方案也可能对我有帮助。

简而言之,我编写了概念验证代码,它缓存变量参数供以后使用——您可以在下面找到它。

我能够让下面的代码在 Windows 和基于英特尔的 Linux 上正常工作。我在 Linux 上用 gcc 编译,在 Windows 上用 MSVC 编译。关于从 gcc 中滥用 va_start() 有一个重复两次的警告——您可以在您的 makefile 中禁用该警告。

我很想知道这段代码是否适用于 Mac 编译器。可能需要稍作调整才能编译。

我意识到这段代码是:

  • 极端滥用 ANSI C 标准定义的 va_start()。
  • 老式的面向字节的 C。
  • 理论上不可移植,因为它使用 va_list 变量作为指针。

我对 malloc() 和 free() 的使用是经过深思熟虑的,因为 va_list 宏来自 C 标准,而不是 C++ 特性。我知道你的问题标题提到了 C++,但除了使用一些 C++ 风格的注释之外,我还试图生成一个完全与 C 兼容的解决方案。

此代码在格式字符串处理方面也无疑存在一些错误或不可移植性。我提供这个作为概念证明,我在两个小时内一起破解,而不是准备好供专业使用的完成代码示例。

该免责声明说,我希望您能像我一样发现结果令人愉快!这是一个可爱的问题,可以深入探讨。结果的病态和扭曲性质让我大笑起来。 ;)

#include <stdio.h>#include <stdarg.h>#include <stdlib.h>#include <string.h>#define VERBOSE 0#ifdef WINDOWS#define strdup _strdup#endif/* * struct cached_printf_args * * This is used as the pointer type of the dynamically allocated * memory which holds a copy of variable arguments.  The struct * begins with a const char * which recieves a copy of the printf() * format string. * * The purpose of ending a struct with a zero-length array is to * allow the array name to be a symbol to the data which follows * that struct.  In this case, additional memory will always be * allocted to actually contain the variable args, and cached_printf_args->args * will name the start address of that additional buffer space. * */struct cached_printf_args{    const char * fmt;    char  args[0];};/* * copy_va_args -- Accepts a printf() format string and va_list *                 arguments. * *                 Advances the va_list pointer in *p_arg_src in *                 accord with the specification in the format string. * *                 If arg_dest provided is not NULL, each argument *                 is copied from *p_arg_src to arg_dest according *                 to the format string. * */int copy_va_args(const char * fmt, va_list * p_arg_src, va_list arg_dest){    const char * pch = fmt;    int processing_format = 0;    while (*pch)    {        if (processing_format)        {            switch (*pch)            {            //case '!': Could be legal in some implementations such as FormatMessage()            case '0':            case '1':            case '2':            case '3':            case '4':            case '5':            case '6':            case '7':            case '8':            case '9':            case '.':            case '-':                // All the above characters are legal between the % and the type-specifier.                // As the have no effect for caching the arguments, here they are simply                // ignored.                break;            case 'l':            case 'I':            case 'h':                printf("Size prefixes not supported yet.\n");                exit(1);            case 'c':            case 'C':                // the char was promoted to int when passed through '...'            case 'x':            case 'X':            case 'd':            case 'i':            case 'o':            case 'u':                if (arg_dest)                {                     *((int *)arg_dest) = va_arg(*p_arg_src, int);                     va_arg(arg_dest, int);                }                else                    va_arg(*p_arg_src, int);#if VERBOSE                printf("va_arg(int), ap = %08X, &fmt = %08X\n", *p_arg_src, &fmt);#endif                processing_format = 0;                break;            case 's':            case 'S':            case 'n':            case 'p':                if (arg_dest)                {                    *((char **)arg_dest) = va_arg(*p_arg_src, char *);                    va_arg(arg_dest, char *);                }                else                    va_arg(*p_arg_src, char *);#if VERBOSE                printf("va_arg(char *), ap = %08X, &fmt = %08X\n", *p_arg_src, &fmt);#endif                processing_format = 0;                break;            case 'e':            case 'E':            case 'f':            case 'F':            case 'g':            case 'G':            case 'a':            case 'A':                if (arg_dest)                {                    *((double *)arg_dest) = va_arg(*p_arg_src, double);                    va_arg(arg_dest, double);                }                else                    va_arg(*p_arg_src, double);#if VERBOSE                printf("va_arg(double), ap = %08X, &fmt = %08X\n", *p_arg_src, &fmt);#endif                processing_format = 0;                break;            }        }        else if ('%' == *pch)        {            if (*(pch+1) == '%')                pch ++;            else                processing_format = 1;        }        pch ++;    }    return 0;}/* * printf_later -- Accepts a printf() format string and variable *                 arguments. * *                 Returns NULL or a pointer to a struct which can *                 later be used with va_XXX() macros to retrieve *                 the cached arguments. * *                 Caller must free() the returned struct as well as *                 the fmt member within it. * */struct cached_printf_args * printf_later(const char *fmt, ...){    struct cached_printf_args * cache;    va_list ap;    va_list ap_dest;    char * buf_begin, *buf_end;    int buf_len;    va_start(ap, fmt);#if VERBOSE     printf("va_start, ap = %08X, &fmt = %08X\n", ap, &fmt);#endif    buf_begin = (char *)ap;    // Make the 'copy' call with NULL destination.  This advances    // the source point and allows us to calculate the required    // cache buffer size.    copy_va_args(fmt, &ap, NULL);    buf_end = (char *)ap;    va_end(ap);    // Calculate the bytes required just for the arguments:    buf_len = buf_end - buf_begin;    if (buf_len)    {        // Add in the "header" bytes which will be used to fake        // up the last non-variable argument.  A pointer to a        // copy of the format string is needed anyway because        // unpacking the arguments later requires that we remember        // what type they are.        buf_len += sizeof(struct cached_printf_args);        cache = malloc(buf_len);        if (cache)        {            memset(cache, 0, buf_len);            va_start(ap, fmt);            va_start(ap_dest, cache->fmt);            // Actually copy the arguments from our stack to the buffer            copy_va_args(fmt, &ap, ap_dest);            va_end(ap);            va_end(ap_dest);            // Allocate a copy of the format string            cache->fmt = strdup(fmt);            // If failed to allocate the string, reverse allocations and            // pointers            if (!cache->fmt)            {                free(cache);                cache = NULL;            }        }    }    return cache;}/* * free_printf_cache - frees the cache and any dynamic members * */void free_printf_cache(struct cached_printf_args * cache){    if (cache)        free((char *)cache->fmt);    free(cache);}/* * print_from_cache -- calls vprintf() with arguments stored in the *                     allocated argument cache * * * In order to compile on gcc, this function must be declared to * accept variable arguments.  Otherwise, use of the va_start() * macro is not allowed.  If additional arguments are passed to * this function, they will not be read. */int print_from_cache(struct cached_printf_args * cache, ...){    va_list arg;    va_start(arg, cache->fmt);    vprintf(cache->fmt, arg);    va_end(arg);}int main(int argc, char *argv){    struct cached_printf_args * cache;    // Allocates a cache of the variable arguments and copy of the format string.    cache = printf_later("All %d of these arguments will be %s fo%c later use, perhaps in %g seconds.", 10, "stored", 'r', 2.2);    // Demonstrate the time-line with some commentary to the output.    printf("This statement intervenes between creation of the cache and its journey to the display.\n"    // THIS is the call which actually displays the output from the cached printf.    print_from_cache(cache);    // Don't forget to return dynamic memory to the free store    free_printf_cache(cache);    return 0;}

关于c++ - 在 C/C++ 中存储 va_list 供以后使用的最佳方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1562992/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com