ITPub博客

首页 > 数据库 > 数据库开发技术 > 放弃printf,使用println优雅打印

放弃printf,使用println优雅打印

原创 数据库开发技术 作者:Rust嵌入式 时间:2024-01-13 19:03:33 0 删除 编辑


为什么要替换printf

在rust中,有个println!的宏,类似C语言的printf函数,能根据指定格式打印变量的内容。println!的使用如下:

fn main() {
    let v1 = 123;
    let str = "hello world";
    let b: bool = true;
 // 输出: v1: 123, str: hello world, b: true
    println!("v1: {}, str: {}, b: {}", v1, str, b);
}

然而在C的printf函数中,对于数据的型号需要根据%d%f%s等之类的格式化符先指定类型,使用非常不便,如果数据类型指定错误,则可能打印出错误的值甚至引起软件崩溃。

自定义println宏的测试效果

本文则根据C语言的宏来生成一个println的打印接口,数据的打印格式不再依赖指定的符号,而是根据数据本身的类型自动识别打印格式。测试代码如下所示,main 中定义了C语言常见的基础类型,打印的效果良好。话不多少,直接看测试:

#include <stdio.h>
#include <stdint.h>

int main(void)
{
  uint8_t v_u8 = 0;
  int8_t v_i8 = -1;
  uint16_t v_u16 = 0x1234;
  int16_t v_i16 = -1234;
  uint32_t v_u32 = 0x12345678;
  int32_t v_i32 = -12345678;
  uint64_t v_u64 = 0x1111222233334444;
  int64_t v_i64 = -1234567890;
  float v_f32 = 3.1415;
  double v_f64 = -99999.123;
  uint8_t arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

  printf("***************************** start *********************************\n");
  println(v_u8);
  println(v_u8, v_i8);
  println(v_u16, v_i16, 123456, -123456);
  println(v_u32, v_i32, v_u64, v_i64);
  println(v_f32, v_f64);
  println(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
  println(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7],
          arr[8], arr[9]);
  printf("***************************** end **********************************\n");
  return 0;
}

运行结果如下:

宏的实现源码

#include <stdio.h>
#include <stdint.h>

#define IS_SAME_TYPE(T1, T2) \
  __builtin_types_compatible_p(__typeof__(T1), __typeof__(T2))

#define likely(x) __builtin_expect(!!(x), 1)
#define unlinkely(x) __builtin_expect(!!(x), 1)
#define __NAME2_HELPER(a, b) a##b
#define NAME2(a, b) __NAME2_HELPER(a, b)
#define UNIQUE_NAME(prefix) NAME2(NAME2(prefix, _), __LINE__)
#define IS_ARRAY(A) !IS_SAME_TYPE((A), &(A)[0])
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
#define FOREACH_ARRAY_ELEMENT(arr, item_ptr, closure) \
  do                                                  \
  {                                                   \
    for (size_t i = 0; i < ARRAY_SIZE(arr); i++)      \
    {                                                 \
      typeof(arr[0]) *item_ptr = arr + i;             \
      {                                               \
        closure                                       \
      }                                               \
    }                                                 \
  } while (0)
#define _STR(s) #s
#define STR(s) _STR(s)

//
#define PRINTF printf

#define IF_LIKELY(match, s, closure) \
  if (IS_SAME_TYPE(match, s))        \
  {                                  \
    closure                          \
  }

typedef enum _type_id
{
  type_char = 0,
  type_i8 = 1,
  type_i16 = 2,
  type_i32 = 3,
  type_i64 = 4,
  type_u8 = 5,
  type_u16 = 6,
  type_u32 = 7,
  type_u64 = 8,
  type_f32 = 9,
  type_f64 = 10,
  type_custom,
} type_id_t;

#define IF_LIKEY_EQ(a, b, closure) \
  if (likely(a == b))              \
  {                                \
    closure                        \
  }
#define PRINT_FMT(fmt, ty, var)                                            \
  do                                                                       \
  {                                                                        \
    typeof(var) _v = var;                                                  \
    PRINTF("[%s: " STR(% fmt) "] ", STR(var), (ty) * (ty *)(void *)(&_v)); \
  } while (0)
#define PRINT_PARSE(type_id, var)                                \
  do                                                             \
  {                                                              \
    IF_LIKEY_EQ(type_id, type_char, PRINT_FMT(c, char, var);)    \
    IF_LIKEY_EQ(type_id, type_u8, PRINT_FMT(u, uint8_t, var);)   \
    IF_LIKEY_EQ(type_id, type_u16, PRINT_FMT(u, uint16_t, var);) \
    IF_LIKEY_EQ(type_id, type_u32, PRINT_FMT(u, uint32_t, var);) \
    IF_LIKEY_EQ(type_id, type_u64, PRINT_FMT(lx, uint64_t, var);) \
    IF_LIKEY_EQ(type_id, type_i8, PRINT_FMT(d, int8_t, var);)    \
    IF_LIKEY_EQ(type_id, type_i16, PRINT_FMT(d, int16_t, var);)  \
    IF_LIKEY_EQ(type_id, type_i32, PRINT_FMT(d, int32_t, var);)  \
    IF_LIKEY_EQ(type_id, type_i64, PRINT_FMT(ld, int64_t, var);) \
    IF_LIKEY_EQ(type_id, type_f32, PRINT_FMT(f, float, var);)    \
    IF_LIKEY_EQ(type_id, type_f64, PRINT_FMT(lf, double, var);)  \
  } while (0)

#define TYPE_PARSE(x) _Generic((x), \
    char: type_char,                \
    int8_t: type_i8,                \
    int16_t: type_i16,              \
    int32_t: type_i32,              \
    int64_t: type_i64,              \
    uint8_t: type_u8,              \
    uint16_t: type_u16,             \
    uint32_t: type_u32,             \
    uint64_t: type_u64,             \
    float: type_f32,                \
    double: type_f64,               \
    default: type_u32)

#define PRINT_VAR(s)                 \
  do                                 \
  {                                  \
    type_id_t _type = TYPE_PARSE(s); \
    PRINT_PARSE(_type, s);           \
  } while (0)

#define PRINT_PTR(s)                 \
  do                                 \
  {                                  \
    PRINTF("[%s: 0x%p]", STR(s), s); \
  } while (0)

#define PRINT_ARR(fmt, s)                                     \
  do                                                          \
  {                                                           \
    PRINTF("[%s: ", STR(s));                                  \
    FOREACH_ARRAY_ELEMENT(s, pch, PRINTF(STR(% fmt), *pch);); \
    PRINTF("]");                                              \
  } while (0)

#define _PRINT_1(s1) PRINT_VAR(s1)
#define _PRINT_2(s1, s2) \
  do                     \
  {                      \
    PRINT_VAR(s1);       \
    PRINT_VAR(s2);       \
  } while (0)
#define _PRINT_3(s1, s2, s3) \
  do                         \
  {                          \
    _PRINT_2(s1, s2);        \
    _PRINT_1(s3);            \
  } while (0)
#define _PRINT_4(s1, s2, s3, s4) \
  do                             \
  {                              \
    _PRINT_3(s1, s2, s3);        \
    _PRINT_1(s4);                \
  } while (0)
#define _PRINT_5(s1, s2, s3, s4, s5) \
  do                                 \
  {                                  \
    _PRINT_4(s1, s2, s3, s4);        \
    _PRINT_1(s5);                    \
  } while (0)
#define _PRINT_6(s1, s2, s3, s4, s5, s6) \
  do                                 \
  {                                  \
    _PRINT_5(s1, s2, s3, s4, s5);    \
    _PRINT_1(s6);                    \
  } while (0)

#define _PRINT_7(s1, s2, s3, s4, s5, s6, s7) \
  do                                 \
  {                                  \
    _PRINT_6(s1, s2, s3, s4, s5, s6);    \
    _PRINT_1(s7);                    \
  } while (0)

#define _PRINT_8(s1, s2, s3, s4, s5, s6, s7, s8) \
  do                                 \
  {                                  \
    _PRINT_7(s1, s2, s3, s4, s5, s6, s7);    \
    _PRINT_1(s8);                    \
  } while (0)

#define _PRINT_9(s1, s2, s3, s4, s5, s6, s7, s8, s9) \
  do                                 \
  {                                  \
    _PRINT_8(s1, s2, s3, s4, s5, s6, s7, s8);    \
    _PRINT_1(s9);                    \
  } while (0)

#define _PRINT_10(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10) \
  do                                 \
  {                                  \
    _PRINT_9(s1, s2, s3, s4, s5, s6, s7, s8, s9);    \
    _PRINT_1(s10);                    \
  } while (0)

#define _ARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, __N, ...) __N
#define VA_NUM_ARGS(...) _ARGS_IMPL(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
#define _PRINT_(...) NAME2(_PRINT_, VA_NUM_ARGS(__VA_ARGS__))(__VA_ARGS__)

#define PRINTLN(args...) \
  _PRINT_(args);         \
  PRINTF("\r\n")

#define println(args...)  PRINTF("|%s +%d| : ", __FILE__, __LINE__);  PRINTLN(args)


int main(void)
{
  uint8_t v_u8 = 0;
  int8_t v_i8 = -1;
  uint16_t v_u16 = 0x1234;
  int16_t v_i16 = -1234;
  uint32_t v_u32 = 0x12345678;
  int32_t v_i32 = -12345678;
  uint64_t v_u64 = 0x1111222233334444;
  int64_t v_i64 = -1234567890;
  float v_f32 = 3.1415;
  double v_f64 = -99999.123;
  uint8_t arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

  printf("***************************** start *********************************\n");
  println(v_u8);
  println(v_u8, v_i8);
  println(v_u16, v_i16, 123456, -123456);
  println(v_u32, v_i32, v_u64, v_i64);
  println(v_f32, v_f64);
  println(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
  println(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6], arr[7],
          arr[8], arr[9]);
  printf("***************************** end **********************************\n");
  return 0;
}

读者可以修改PRINTF来指定输出目标位置,如文件、串口设备等。

原文作者:Rust嵌入式;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2023-11-13

  • 博文量
    64
  • 访问量
    45582