HiveBrain v1.2.0
Get Started
← Back to all entries
patterncMinor

Formatted print without the need to specify type matching specifiers using _Generic

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
withouttheneedtypeformatted_genericprintspecifiersusingspecify

Problem

This code allows printing in C with a style similar to C++ ostream. By using _Generic() (see GP()) to form a string specifier, the compiler handles the selection of printf() specifiers.

No more mis-matched specifiers!

Requested review goals:

-
Design concept: Ways to improve?

-
Implementation: What weakness & strengths exist?

4 parts:

-
main.c to show a sample use. GPrintf() must end in GP_eol or NULL

-
Output

-
GPrint.h Key macro GP()

-
GPrint.c void GPrintf(const char *format, ...)

main.c

#include "GPrint.h"
#include 

int main(void) {
  double q = 1000000.0 / 7;
  int i = 42;
  GPrintf("42 --> base 10:", GP(i), 
      GP_setbase(2), " 2:", GP(i), 
      GP_setbase(36), " 36:", GP(i), GP_eol);

  GPrintf("million/7 = ", GP(q), GP_eol);
  GPrintf("million/7 = ", GP_setprecision(3), GP(q), GP_eol);
  GPrintf("million/7 = ", GP_fixed, GP(q), GP_eol);
  GPrintf("million/7 = ", GP_fixed, GP_setprecision(3), GP(q), GP_eol);

  for (int y = 1; y < 9; y++) {
    int x = y * y;
    GPrintf(GP_setw(2), GP(y), ",", GP(x), ":", NULL);
    GPrintf(GP_repeat(x), GP((char)'*'), GP_eol);
  }
  GPrintf(GP_setw(5), GP(" "), ":", NULL);
  for (int x = 0; x < 70; x += 5)
    GPrintf("----+", NULL);
  GPrintf(GP_eol);

  return 0;
}


Output

42 --> base 10:42 2:101010 36:16
million/7 = 1.4285714285714287e+05
million/7 = 1.429e+05
million/7 = 142857.1428571428696159
million/7 = 142857.143
 1, 1:*
 2, 4:****
 3, 9:*********
 4,16:****************
 5,25:*************************
 6,36:************************************
 7,49:*************************************************
 8,64:****************************************************************
     :----+----+----+----+----+----+----+----+----+----+----+----+----+----+


GPrint.h

```
/*
* GPrint.c
*
* Created on: Dec 23, 2015
* Author: chux
*/

#ifndef GPRINT
#define GPRINT 1

#include
#include
#include

typedef enum {
GP_none,
GP__Bool,
GP_char,
GP_s

Solution

Technicality

Pedantically speaking, the GP_get_type function has undefined behavior if the pointer it gets isn't a pointer into (or one past the end of) GP_format (see §6.5.8/5 Relational operators).

Simple enough to fix: wrap all the arguments, including the first one and any string literal with GP.

You could enforce that with an assertion in GP_get_type (instead of the if, get rid of the default), but... you can't escape undefined behavior in that assertion :-)

I can't think of real-world circumstances on ordinary compilers/environments where this particular usage would be problematic though.

Context

StackExchange Code Review Q#115143, answer score: 7

Revisions (0)

No revisions yet.