评估编译时间的恒定表达

发布于 2025-02-06 12:36:54 字数 3726 浏览 2 评论 0 原文

我正在尝试编写一个预处理器宏 myprintf(x,...),该应该用 printf 来工作,但带有附加长度的指定器 w w 的定义应该取决于操作系统,但在Linux上,它等效于 l

该代码正常工作,但是当 x 是恒定表达式时,我在评估表达式时遇到问题。例如,如果我

long int x = 13;
MYPRINTF("%wd\n", x);

在Linux系统上编写,我希望编译器在编译时间中将其转换为

long int x = 13;
printf("%ld\n", x);

查看装配代码,我可以说这不是发生的事情。

这是我正在运行的代码

#include <stdio.h>

#define SET_CHAR(ptr, cr)                       \
    do                                          \
    {                                           \
        *(ptr) = (cr);                          \
        (ptr)++;                                \
    } while (0)

#define SET_WORD_FMT(x)                         \
    do                                          \
    {                                           \
        SET_CHAR(x, 'l');                       \
    } while (0)

#define IS_FORMAT(c) ((c) == 'c' || (c) == 's' || (c) == 'd' || (c) == 'i' || (c) == 'o' || (c) == 'x' || (c) == 'X' || (c) == 'u' || (c) == 'f' || (c) == 'F' || (c) == 'e' || (c) == 'E' || (c) == 'a' || (c) == 'A' || (c) == 'g' || (c) == 'G' || (c) == 'n' || (c) == 'p')

#define MYPRINTF(x, ...)                        \
    do                                          \
    {                                           \
        char _str[512];                         \
        char * _strptr = _str;                  \
        const char * _xptr = (x);               \
        while (*_xptr != '\0')                  \
        {                                       \
            if (*_xptr != '%')                  \
            {                                   \
                SET_CHAR(_strptr, *_xptr);      \
                _xptr++;                        \
                continue;                       \
            }                                   \
                                                \
            SET_CHAR(_strptr, '%');             \
            _xptr++;                            \
                                                \
            if (*_xptr == '%')                  \
            {                                   \
                SET_CHAR(_strptr, '%');         \
                _xptr++;                        \
                continue;                       \
            }                                   \
            else while (!IS_FORMAT(*_xptr))     \
            {                                   \
                SET_CHAR(_strptr, *_xptr);      \
                _xptr++;                        \
            }                                   \
                                                \
            if (_strptr[-1] == 'w')             \
            {                                   \
                _strptr--;                      \
                SET_WORD_FMT(_strptr);          \
            }                                   \
                                                \
            SET_CHAR(_strptr, *_xptr);          \
            _xptr++;                            \
        }                                       \
        *_strptr = '\0';                        \
        printf(_str, __VA_ARGS__);              \
    } while (0)

int
main()
{
    long int slx = 18273817628731872;
    int x = 13;
    int lx = 7128172;
    long long int llx = 1928398172938791872;

    MYPRINTF("hello %wd, %d, %ld, %% and %lld\n", slx, x, lx, llx);
}

,该代码是使用gcc 12.1.0与flags -o3 -march =本机进行编译的。

是否可以在编译期间评估这一点?如果是这样,怎么样?

I am trying to write a preprocessor macro MYPRINTF(x, ...) that should work as printf but with an additional length specifier w. The definition of w is supposed to be operating system dependent, but on Linux it is equivalent to l.

The code works fine, but I'm having problems with evaluating expressions during compile time when x is a constant expression. As an example, if I write

long int x = 13;
MYPRINTF("%wd\n", x);

on a Linux system, I would like the compiler to convert it during compile time to

long int x = 13;
printf("%ld\n", x);

Looking at the assembly code, I can say that this is not what is happening.

Here is the code that I'm running

#include <stdio.h>

#define SET_CHAR(ptr, cr)                       \
    do                                          \
    {                                           \
        *(ptr) = (cr);                          \
        (ptr)++;                                \
    } while (0)

#define SET_WORD_FMT(x)                         \
    do                                          \
    {                                           \
        SET_CHAR(x, 'l');                       \
    } while (0)

#define IS_FORMAT(c) ((c) == 'c' || (c) == 's' || (c) == 'd' || (c) == 'i' || (c) == 'o' || (c) == 'x' || (c) == 'X' || (c) == 'u' || (c) == 'f' || (c) == 'F' || (c) == 'e' || (c) == 'E' || (c) == 'a' || (c) == 'A' || (c) == 'g' || (c) == 'G' || (c) == 'n' || (c) == 'p')

#define MYPRINTF(x, ...)                        \
    do                                          \
    {                                           \
        char _str[512];                         \
        char * _strptr = _str;                  \
        const char * _xptr = (x);               \
        while (*_xptr != '\0')                  \
        {                                       \
            if (*_xptr != '%')                  \
            {                                   \
                SET_CHAR(_strptr, *_xptr);      \
                _xptr++;                        \
                continue;                       \
            }                                   \
                                                \
            SET_CHAR(_strptr, '%');             \
            _xptr++;                            \
                                                \
            if (*_xptr == '%')                  \
            {                                   \
                SET_CHAR(_strptr, '%');         \
                _xptr++;                        \
                continue;                       \
            }                                   \
            else while (!IS_FORMAT(*_xptr))     \
            {                                   \
                SET_CHAR(_strptr, *_xptr);      \
                _xptr++;                        \
            }                                   \
                                                \
            if (_strptr[-1] == 'w')             \
            {                                   \
                _strptr--;                      \
                SET_WORD_FMT(_strptr);          \
            }                                   \
                                                \
            SET_CHAR(_strptr, *_xptr);          \
            _xptr++;                            \
        }                                       \
        *_strptr = '\0';                        \
        printf(_str, __VA_ARGS__);              \
    } while (0)

int
main()
{
    long int slx = 18273817628731872;
    int x = 13;
    int lx = 7128172;
    long long int llx = 1928398172938791872;

    MYPRINTF("hello %wd, %d, %ld, %% and %lld\n", slx, x, lx, llx);
}

which is compiled using GCC 12.1.0 with flags -O3 -march=native.

Is it possible to evaluate this during compile time? If so, how?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

寄人书 2025-02-13 12:36:54

是否可以在编译时间进行评估?

不,这是不可能的。

例如,为62个字符串的恒定表达strlen编写非常非常内存消耗,并且您的编译器可以轻松地击中千兆字节的内存(主要是因为生成的预处理器代码需要很多)。为每种可能的情况编写恒定表达的字符串解析功能是不可行的,并且在实践中不可汇编。

如果您想要这样的功能,请移至其他编程语言或通过您的代码运行预处理器。例如,使用M4预处理器预处理您的代码。

// run with m4 -P
m4_define(`MYPRINTF', `printf(m4_patsubst(`$1', `%wd', `%ld'), m4_shift($@))')
MYPRINTF("%wd", a)   // expands to printf("%ld", a)

应该用作printf,但具有额外的长度说明符W

glibc的额外长度指定符W,您可以重新定义%d 转换并添加 w flag。

Is it possible to evaluate this during compile time?

No, it is not possible.

For example, writing of a constant-expression strlen for strings up to 62 is very memory consuming and your compiler can easily hit gigabytes of memory (mostly, because the generated preprocessor code takes so much). Writing a constant-expression string parsing function for every possible case is just not feasible and not compilable in practice.

If you want such functionality, move to a different programming language or run a preprocessor through your code. For example, preprocess your code with M4 preprocessor.

// run with m4 -P
m4_define(`MYPRINTF', `printf(m4_patsubst(`$1', `%wd', `%ld'), m4_shift($@))')
MYPRINTF("%wd", a)   // expands to printf("%ld", a)

that should work as printf but with an additional length specifier w

With glibc you can redefine %d conversion and add w flag. https://www.gnu.org/software/libc/manual/html_node/Registering-New-Conversions.html

新一帅帅 2025-02-13 12:36:54

这样做的方法是以OS依赖性方式定义具有长度说明符的单独宏。然后,您可以在需要的地方使用它。

#ifdef __linux__
#define WIDE "l"
#else
#define WIDE "L"
#endif

...

printf("%" WIDE "d\n", x);

请注意,这利用了连续的字符串文字的自动组合。

The way to do this is to define a separate macro with the length specifier in an OS dependent way. Then you can use that where you want.

#ifdef __linux__
#define WIDE "l"
#else
#define WIDE "L"
#endif

...

printf("%" WIDE "d\n", x);

Note that this takes advantage of automatic combination of consecutive string literals.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文