C ansi格式宏全局字符[]覆盖

prdp8dxp  于 2023-02-03  发布在  其他
关注(0)|答案(2)|浏览(111)

我有一个必须用C语言完成的编码任务。我偏离了主题,决定尝试制作一个格式宏,可以用ANSI代码对文本进行样式化。我的代码如下:
<format.h>

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

/**
 * global buffer to store format arguments for prints
 */
char format_buffer[128];

/**
 * Count the number of char* in variadic arguments, auxiliary for FMT macro
 */
#define FARGS(...)  (sizeof((char*[]){__VA_ARGS__})/sizeof(char*))

/**
 * Automatically fills in parameters buffer and argc for the format function. Buffer is a global variable, therefore it is never out of scope.
 */
#define FMT(...) format(format_buffer, FARGS(__VA_ARGS__), __VA_ARGS__)

#define A_START "\x1B["
#define A_END "m"

/**
 * creates a format string based on input parameters.
 * @param argc the number of ansi flags to use
 * @param buffer the buffer to write the format string into. A size of 64 should be enough for most reasonable uses
 * @param ... the ansi flags to use. Examples include RED, BLUE, DASH, etc
 * @return the same pointer buffer, for ease of use in printf functions
 */
char* format(char* buffer, int argc, ...);

// ansi macros for formatting, all of the style ";NN"

<format.c>

#include "format.h"

char format_buffer[128] = {};

char* format(char* buffer, int argc, ...){
    buffer[0] = '\0';

    va_list argv;
    int i;

    // initialize argv to get data from the variable arguments
    va_start(argv, argc);

    strcat(buffer, A_START);
    /* access all the arguments assigned to valist */
    for (i = 0; i < argc; i++) {
        strcat(buffer, va_arg(argv, char*));
    }
    strcat(buffer, A_END);

    /* clean memory reserved for valist */
    va_end(argv);

    return buffer;

}

使用它,我可以如下调用宏,这就是我想要的:
printf("%sHello!\n", FMT(RED, BOLD)) //prints <Hello!\n> in red and bold
∮问题是
我遇到的问题是,当我试图在同一个print语句中使用多个调用时:
printf("%sHello, %sWorld!\n", FMT(RED, BOLD), FMT(YELLOW)) //prints <Hello, World!\n> in yellow
我肯定它没有像预期的那样工作,因为FMT(...)总是返回相同的全局char *,但我不知道如何更改它,以便:

  • 我可以调用如上所示的格式宏:printf("%sHello!\n", FMT(RED, BOLD)).
  • 我可以在同一个print语句中使用多个FMT调用,例如printf("%sHello, %sWorld!\n", FMT(RED, BOLD), FMT(YELLOW))应该用红色和粗体打印〈Hello,〉,用黄色打印〈World!\n〉。

作为最后一点说明,我宁愿编写解决方案,而不是使用已经实现了它的库或头文件。
我第一次尝试在format函数中创建一个新的char [],但是它会存储在堆栈中,所以我认为这是一个更糟糕的结果。

hc2pp10m

hc2pp10m1#

只需将宏更改为:

#define FMT(...) format((char[128]){}, FARGS(__VA_ARGS__), __VA_ARGS__)

https://godbolt.org/z/KvWWcYfPb

aamkag61

aamkag612#

在阅读了Jonathan Leffler的注解和0___________的答案后,我将文件更改为:
<format.h>

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

/**
 * Count the number of char* in variadic arguments, auxiliary for FMT macro
 */
#define FARGS(...)  (sizeof((char*[]){__VA_ARGS__})/sizeof(char*))

#define FMT_BUF_SIZE 64

/**
 * Automatically fills in parameters buffer and argc for the format function. 
 */
#define FMT(...) format((char[FMT_BUF_SIZE]){""}, FARGS(__VA_ARGS__), __VA_ARGS__)

#define FMT_START "\x1B["
#define FMT_END "m"

// ansi flags for text styling, there's a lot of them

//black
#define BLACK ";30"
//red
#define RED ";31"
//green
#define GREEN ";32"
//yellow
#define YELLOW ";33"
//blue
#define BLUE ";34"
//magenta
#define MAGENTA ";35"
//cyan
#define CYAN ";36"
//bright gray
#define GRAY_B ";37"

//black background
#define BLACK_BG ";40"
//red background
#define RED_BG ";41"
//green background
#define GREEN_BG ";42"
//yellow background
#define YELLOW_BG ";43"
//blue background
#define BLUE_BG ";44"
//magenta background
#define MAGENTA_BG ";45"
//cyan background
#define CYAN_BG ";46"
//bright gray background
#define GRAY_BG_B ";47"

//gray
#define GRAY ";90"
//bright red
#define RED_B ";91"
//bright green
#define GREEN_B ";92"
//bright yellow
#define YELLOW_B ";93"
//bright blue
#define BLUE_B ";94"
//bright magenta
#define MAGENTA_B ";95"
//bright cyan
#define CYAN_B ";96"
//white
#define WHITE ";97"

//gray background
#define GRAY_BG ";100"
//bright red background
#define RED_BG_B ";101"
//bright green background
#define GREEN_BG_B ";102"
//bright yellow background
#define YELLOW_BG_B ";103"
//bright blue background
#define BLUE_BG_B ";104"
//bright magenta background
#define MAGENTA_BG_B ";105"
//bright cyan background
#define CYAN_BG_B ";106"
//white background
#define WHITE_BG ";107"

#define DASH ";9"

#define BOLD ";1"
#define ITALICS ";3"
#define STRIKETHROUGH ";9"
// I can't explain this one
#define REVERSE ";7"
#define CLEAR ";0"

// underline light
#define UNDERLINE_L ";4"
// underline heavy
#define UNDERLINE_H ";21"

/**
 * creates a format string based on input parameters.
 * @param argc the number of ansi flags to use
 * @param buffer the buffer to write the format string into. A size of 64 should be enough for most reasonable uses
 * @param ... the ansi flags to use. Examples include RED, BLUE, DASH, etc
 * @return the same pointer buffer, for ease of use in printf functions
 */
char* format(char* buffer, int argc, ...);

<format.c>

#include "format.h"

char* format(char* buffer, int argc, ...){
    buffer[0] = '\0';

    va_list argv;
    int i;

    // calculate available memory in buffer available for ANSI codes
    int buffer_len = FMT_BUF_SIZE - strlen(FMT_START) - strlen(FMT_END) - 1;

    // initialize argv to get data from the variable arguments
    va_start(argv, argc);

    strcat(buffer, FMT_START);
    /* access all the arguments assigned to valist */
    for (i = 0; i < argc; i++) {

        // retrieve ansi code and substract length to available length to prevent overflows
        char* code = va_arg(argv, char*);
        buffer_len -= strlen(code);

        // concatenate ansi code if there's space available in the buffer, else break out of loop
        if(buffer_len >= 0) strcat(buffer, code);
        else break;
    }
    strcat(buffer, FMT_END);

    /* clean memory reserved for valist */
    va_end(argv);

    return buffer;

}

宏按预期工作,如果超过格式缓冲区容量,格式函数现在将提前停止阅读参数(缓冲区的长度必须至少为4,我相信最长的单个ANSI代码是4个字符长)。

相关问题