在宏重新定义printf中将字符连接到__VA_ARGS__

时间:2017-12-09 00:15:27

标签: c macros

我正在开发一个嵌入式应用程序,它使用库与SPI NAND存储器芯片连接。提供的库使用打印功能,作为应用程序开发人员,我必须覆盖我的特定平台。

例如,他们在代码中广泛使用函数printf_

我重新定义它,以便通过串行接口打印打印功能

#define printf_(...) serial.printf(__VA_ARGS__)

它工作正常,但在此平台上,您需要为换行提供回车符(" \ r")。所以现在,当调用库时,格式化很糟糕。

我想追加一个" \ r"到提供给打印功能的任何内容的末尾。喜欢

#define printf_(...) serial.printf(__VA_ARGS__##"\r")

显然这不起作用。

我真的不熟悉,并且很难理解C语言中的可变函数,所以如果我能弄清楚这一点,这将为我提供一点学习机会,或者至少我会学习如果这是不可能的。

我也无法使用va_listvsprintf来包装该函数,因为我的平台不支持此功能(我宁愿在宏中重新定义它,因为个人表演/风格选择。

有办法做到这一点吗?

2 个答案:

答案 0 :(得分:1)

也许你可以在调用serial.printf后输出'\r'

#define printf_(...) do { serial.printf(__VA_ARGS__); serial.printf("\r"); } while(0)

答案 1 :(得分:0)

我同意其他用户的说法,你在 LF之后你的CR的要求是非常奇怪的,我怀疑你是CRLF,而不是LFCR。无论如何,我会帮助您完成将CR附加到字符串的原始要求。

在单线程宏中执行此操作将非常困难。或者,您可以使用看起来很讨厌的多行宏:

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

#define printf_(format, args...)                                        \
do {                                                                    \
    int _len_orig = strlen(format);                                     \
    /* strlen doesn't consider newline. Check for that. */              \
    if ( format[_len_orig] == '\n' )                                    \
        _len_orig++;                                                    \
                                                                        \
    /* len_orig + 2 to make space for null terminator and '\r' */       \
    unsigned _new_size = _len_orig + 2;                                 \
    char* _new_str  = calloc(_new_size, 1);                             \
    strncpy( _new_str, format, _len_orig );                             \
    _new_str[_new_size-2] = '\r';                                       \
                                                                        \
    /* Print out the raw hex of the string to prove it worked */        \
    int _i;                                                             \
    for( _i = 0; _i < _new_size; _i++)                                  \
        printf("%02x ", _new_str[_i]);                                  \
    printf("\n");                                                       \
    free(_new_str);                                                     \
} while(0)


int main(){

    char* str = "@@@@\n";
    printf_( str );

    return 0;
}

在我的宏中,我没有使用你的serial.printf函数,而是制作了一些简短的代码,打印出连接字符串的十六进制值,以证明逻辑有效。

解释

  • 要求宏显示为C预处理器的单行。在每个宏行的末尾添加\会转义换行符。

  • gcc allows for named arguments in your macro。这里,format描述了您通常传递给printf的格式字符串,args...是&#34;其他所有&#34;的变量参数名称。你会传入printf。我在这个例子中没有引用args,但你肯定想在你的最终代码中修改我的例子以包含它。您对printf的调用看起来像serial.printf( _new_str, args);

  • 多行宏是一种令人讨厌的东西,你需要做一些技巧才能让它们作为人类程序员的正常功能出现。需要在do-while is nicely discussed here中包含宏。链接页面适用于C ++,但它也适用于C.

如果你复制我的代码,编译并运行它,这就是输出:

[clipp@h2ologin3:~]$ ./a.out
40 40 40 40 0a 0d 00

此处,此ASCII转储向我们显示复制了4个@个符号,然后是\n\r,最后是\0

还有一个强有力的例子,可以将其写为内联C函数。你说你的系统不支持va_list等所以如果这是真的,那么宏可能是唯一的选择。

QED。