Skip to content

Instantly share code, notes, and snippets.

@mickjc750
Last active April 2, 2024 23:52
Show Gist options
  • Save mickjc750/f0d10214da34e38aeb8a89b1da4316f1 to your computer and use it in GitHub Desktop.
Save mickjc750/f0d10214da34e38aeb8a89b1da4316f1 to your computer and use it in GitHub Desktop.
An example of how to extend printf() using a thread local arena to display custom data types.
/*
*/
#include "printf_ext.h"
//********************************************************************************************************
// Public functions
//********************************************************************************************************
int main(int argc, const char* argv[])
{
(void)argc;(void)argv;
complex float ca = 23.471 + 41.231*I;
complex float cb = -18.19 + 9.473*I;
printf_ext("\n\n\
This GIST demonstrates an easy way of extending printf\n\
Instead of calling printf() directly, call a wrapper function called printf_ext().\n\
printf_ext() uses vprintf(), but also resets a thread local string arena.\n\
This string arena can then be used by functions which describe whatever types you like.\n\
In this example, we can use prext_cplex_pol() and prext_cplex_rec() to display complex values in polar or rectangular form\n\
\n\
Some values in rectangular form:\n\
%s * %s = %s rectangular\n\
\n\
Some values in polar form:\n\
%s * %s = %s polar\n\
\n\
One of the benefits of extending printf in this way, is that %%s also takes a width parameter and offers left & right alignment\n\
so you can align your text. eg;\n\
%25s * %-25s = %s \n\
%25s * %-25s = %s \n\
\n"
, prext_cplex_rec(ca), prext_cplex_rec(cb), prext_cplex_rec(ca * cb)
, prext_cplex_pol(ca), prext_cplex_pol(cb), prext_cplex_pol(ca * cb)
, prext_cplex_rec(ca), prext_cplex_rec(cb), prext_cplex_rec(ca * cb)
, prext_cplex_pol(ca), prext_cplex_pol(cb), prext_cplex_pol(ca * cb)
);
return 0;
}
#----------------------------------------------------------------------------
#
# Target file name (without extension).
TARGET = demo
# List C source files here. (C dependencies are automatically generated.)
# To exclude certain files in a folder remove the $(wildcard) and
# list them seperated by spaces, ie src/main.c src/util.c
SRC = $(wildcard *.c)
# List any extra directories to look for include files here.
# Each directory must be seperated by a space.
# Use forward slashes for directory separators.
# For a directory that has spaces, enclose it in quotes.
EXTRAINCDIRS = .
# Object and list files directory
# To put .o and .lst files alongside .c files use a dot (.), do NOT make
# this an empty or blank macro!
# If source files are in sub directories, matching subdirectories must exist under this folder for the .o files
# This is a pain, if you can fix this, please do and share.
OBJLSTDIR = .
# Compiler flag to set the C Standard level.
# c89 = "ANSI" C
# gnu89 = c89 plus GCC extensions
# c99 = ISO C99 standard (not yet fully implemented)
# gnu99 = c99 plus GCC extensions
CSTANDARD = -std=gnu99
# Place -D or -U options here for C sources
CDEFS =
#---------------- Compiler Options C ----------------
# -g debug information
# -f...: tuning, see GCC manual and avr-libc documentation
# -Wall...: warning level
CFLAGS += $(CDEFS)
CFLAGS += -Wall
CFLAGS += -Wno-unused-function
CFLAGS += -Wno-unused-but-set-variable
CFLAGS += $(CSTANDARD)
CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
CFLAGS += -fsanitize=address
CFLAGS += -Wextra
CFLAGS += -fsanitize=undefined
# List any extra directories to look for libraries here.
# Each directory must be seperated by a space.
# Use forward slashes for directory separators.
# For a directory that has spaces, enclose it in quotes.
EXTRALIBDIRS = .
EXTRALIBS = -lm
#---------------- Linker Options ----------------
LDFLAGS = $(patsubst %,-L%,$(EXTRALIBDIRS))
LDFLAGS += $(EXTRALIBS)
#============================================================================
# Define programs and commands.
SHELL = sh
CC = gcc
REMOVE = rm -f
REMOVEDIR = rm -rf
COPY = cp
# Define Messages
# English
MSG_ERRORS_NONE = Errors: none
MSG_BEGIN = -------- begin --------
MSG_END = -------- end --------
MSG_LINKING = Linking:
MSG_COMPILING = Compiling C:
MSG_CLEANING = Cleaning project:
# Define all object files.
OBJ = $(SRC:%.c=$(OBJLSTDIR)/%.o)
# Compiler flags to generate dependency files.
GENDEPFLAGS = -MMD -MP -MF .dep/$(@F).d
# Combine all necessary flags and optional flags.
# Add target processor to flags.
ALL_CFLAGS = -I. $(CFLAGS) $(GENDEPFLAGS)
# Default target.
all: begin gccversion build end
build: tgt
tgt: $(TARGET)
# Eye candy.
# the following magic strings to be generated by the compile job.
begin:
@echo
@echo $(MSG_BEGIN)
end:
@echo $(MSG_END)
@echo
# Display compiler version information.
gccversion :
@$(CC) --version
# Link: create output file from object files.
.SECONDARY : $(TARGET)
.PRECIOUS : $(OBJ)
$(TARGET): $(OBJ)
@echo
@echo $(MSG_LINKING) $@
$(CC) $(ALL_CFLAGS) $^ --output $@ $(LDFLAGS)
# Compile: create object files from C source files.
$(OBJLSTDIR)/%.o : %.c
@echo
@echo $(MSG_COMPILING) $<
$(CC) -c $(ALL_CFLAGS) $< -o $@
# Target: clean project.
clean: begin clean_list end
clean_list :
@echo
@echo $(MSG_CLEANING)
$(REMOVE) $(SRC:%.c=$(OBJLSTDIR)/%.o)
$(REMOVE) $(SRC:%.c=$(OBJLSTDIR)/%.lst)
$(REMOVEDIR) .dep
# Create object files directory
$(shell mkdir $(OBJLSTDIR) 2>/dev/null)
# Include the dependency files.
-include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*)
# Listing of phony targets.
.PHONY : all begin end gccversion build tgt clean clean_list
/*
Extendable printf wrapper using a thread local string arena for the extensions.
*/
#include <assert.h>
#include <stdarg.h>
#include <complex.h>
#include <stdio.h>
#include "printf_ext.h"
//********************************************************************************************************
// Configurable defines
//********************************************************************************************************
#define PRINTF_EXT_ARENA_SIZE 1024
//********************************************************************************************************
// Local defines
//********************************************************************************************************
#define TERMINATOR_SIZE 1
struct arena_struct
{
char body[PRINTF_EXT_ARENA_SIZE];
char* ptr;
int remaining_size;
};
//********************************************************************************************************
// Private variables
//********************************************************************************************************
static __thread struct arena_struct arena = {.ptr = NULL};
//********************************************************************************************************
// Private prototypes
//********************************************************************************************************
static void arena_reset(struct arena_struct* arena);
static void arena_prepare(struct arena_struct* arena);
static char* arena_advance(struct arena_struct* arena, int char_count);
//********************************************************************************************************
// Public functions
//********************************************************************************************************
int printf_ext(const char* fmt, ...)
{
va_list va;
va_start(va, fmt);
const int retval = vprintf_ext(fmt, va);
va_end(va);
return retval;
}
int vprintf_ext(const char* fmt, va_list va)
{
const int retval = vprintf(fmt, va);
arena_reset(&arena);
return retval;
}
char* prext_cplex_pol(complex double c)
{
arena_prepare(&arena);
int char_count = snprintf(arena.ptr, arena.remaining_size, "(%.3f @%.3f)", cabs(c), carg(c));
return arena_advance(&arena, char_count);
}
char* prext_cplex_rec(complex double c)
{
arena_prepare(&arena);
int char_count = snprintf(arena.ptr, arena.remaining_size, "(%.3f, %.3f)", creal(c), cimag(c));
return arena_advance(&arena, char_count);
}
//********************************************************************************************************
// Private functions
//********************************************************************************************************
static void arena_reset(struct arena_struct* arena)
{
arena->ptr = NULL;
}
static void arena_prepare(struct arena_struct* arena)
{
if(arena->ptr == NULL)
{
arena->ptr = arena->body;
arena->remaining_size = PRINTF_EXT_ARENA_SIZE;
};
}
static char* arena_advance(struct arena_struct* arena, int char_count)
{
char* retval = arena->ptr;
int size = char_count + TERMINATOR_SIZE;
assert(size <= arena->remaining_size);
arena->ptr += size;
arena->remaining_size -= size;
return retval;
}
#include <stdarg.h>
#include <complex.h>
//********************************************************************************************************
// Public prototypes
//********************************************************************************************************
int printf_ext(const char* fmt, ...) __attribute__((format(printf, 1, 2)));
int vprintf_ext(const char* fmt, va_list va);
char* prext_cplex_pol(complex double c);
char* prext_cplex_rec(complex double c);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment