ggformat

A string formatting library for C++
Log | Files | Refs | README

ggformat.h (6061B)


      1 /*
      2  * ggformat v1.0
      3  *
      4  * Copyright (c) 2017 Michael Savage <mike@mikejsavage.co.uk>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 
     19 #pragma once
     20 
     21 #include <stdio.h>
     22 #include <stdlib.h>
     23 
     24 /*
     25  * prototypes of the functions you should be calling
     26  */
     27 
     28 /*
     29  * `ggformat` writes at most `len` bytes to `buf`, and that always includes a
     30  * null terminator. It returns the number of bytes that would have been written
     31  * if `buf` were large enough, not including the null terminator, and can be
     32  * larger than `len` (just like sprintf).
     33 */
     34 template< typename... Rest >
     35 size_t ggformat( char * buf, size_t len, const char * fmt, const Rest & ... rest );
     36 
     37 /*
     38  * `ggprint_to_file` does what you would expect, and `ggprint` writes to
     39  * standard output. Both return `true` on success, or `false` if the write
     40  * fails.
     41  */
     42 template< typename... Rest >
     43 bool ggprint_to_file( FILE * file, const char * fmt, const Rest & ... rest );
     44 
     45 template< typename... Rest >
     46 bool ggprint( const char * fmt, const Rest & ... rest );
     47 
     48 /*
     49  * structures and functions used for formatting specific data types
     50  */
     51 
     52 struct FormatOpts {
     53 	enum NumberFormat { DECIMAL, HEX, BINARY };
     54 
     55 	int width = -1;
     56 	int precision = -1;
     57 	bool plus_sign = false;
     58 	bool left_align = false;
     59 	bool zero_pad = false;
     60 	NumberFormat number_format = DECIMAL;
     61 };
     62 
     63 struct FormatBuffer {
     64 	FormatBuffer( char * b, size_t c ) {
     65 		buf = b;
     66 		capacity = c;
     67 		len = 0;
     68 	}
     69 
     70 	char * buf;
     71 	size_t capacity;
     72 	size_t len;
     73 };
     74 
     75 /*
     76  * format implementations for primitive types
     77  */
     78 
     79 void format( FormatBuffer * fb, signed char x, const FormatOpts & opts );
     80 void format( FormatBuffer * fb, short x, const FormatOpts & opts );
     81 void format( FormatBuffer * fb, int x, const FormatOpts & opts );
     82 void format( FormatBuffer * fb, long x, const FormatOpts & opts );
     83 void format( FormatBuffer * fb, long long x, const FormatOpts & opts );
     84 void format( FormatBuffer * fb, unsigned char x, const FormatOpts & opts );
     85 void format( FormatBuffer * fb, unsigned short x, const FormatOpts & opts );
     86 void format( FormatBuffer * fb, unsigned int x, const FormatOpts & opts );
     87 void format( FormatBuffer * fb, unsigned long x, const FormatOpts & opts );
     88 void format( FormatBuffer * fb, unsigned long long x, const FormatOpts & opts );
     89 void format( FormatBuffer * fb, double x, const FormatOpts & opts );
     90 void format( FormatBuffer * fb, bool x, const FormatOpts & opts );
     91 void format( FormatBuffer * fb, char x, const FormatOpts & opts );
     92 void format( FormatBuffer * fb, const char * x, const FormatOpts & opts = FormatOpts() );
     93 
     94 /*
     95  * nasty implementation details that have to go in the header
     96  */
     97 
     98 #define GGFORMAT_ASSERT( p ) \
     99 	do { \
    100 		if( !( p ) ) { \
    101 			fprintf( stderr, "assertion failed: %s\n", #p ); \
    102 			abort(); \
    103 		} \
    104 	} while( 0 )
    105 
    106 #if defined( _MSC_VER )
    107 #  define GGFORMAT_COMPILER_MSVC 1
    108 #elif defined( __clang__ )
    109 #  define GGFORMAT_COMPILER_CLANG 1
    110 #elif defined( __GNUC__ )
    111 #  define GGFORMAT_COMPILER_GCC 1
    112 #else
    113 #  error new compiler
    114 #endif
    115 
    116 // this is optional but helps compile times
    117 #if GGFORMAT_COMPILER_MSVC
    118 #  define GGFORMAT_DISABLE_OPTIMISATIONS() __pragma( optimize( "", off ) )
    119 #  define GGFORMAT_ENABLE_OPTIMISATIONS() __pragma( optimize( "", on ) )
    120 #elif GGFORMAT_COMPILER_GCC
    121 #  define GGFORMAT_DISABLE_OPTIMISATIONS() \
    122 	_Pragma( "GCC push_options" ) \
    123 	_Pragma( "GCC optimize (\"O0\")" )
    124 #  define GGFORMAT_ENABLE_OPTIMISATIONS() _Pragma( "GCC pop_options" )
    125 #elif GGFORMAT_COMPILER_CLANG
    126 #  define GGFORMAT_DISABLE_OPTIMISATIONS() _Pragma( "clang optimize off" )
    127 #  define GGFORMAT_ENABLE_OPTIMISATIONS() _Pragma( "clang optimize on" )
    128 #else
    129 #  error new compiler
    130 #endif
    131 
    132 FormatOpts parse_formatopts( const char * fmt, size_t len );
    133 void ggformat_impl( FormatBuffer * fb, const char * fmt );
    134 bool ggformat_find( const char * str, size_t * start, size_t * one_past_end );
    135 void ggformat_literals( FormatBuffer * fb, const char * literals, size_t len );
    136 
    137 GGFORMAT_DISABLE_OPTIMISATIONS();
    138 
    139 template< typename T, typename... Rest >
    140 void ggformat_impl( FormatBuffer * fb, const char * fmt, const T & first, const Rest & ... rest ) {
    141 	size_t start, one_past_end;
    142 	bool has_fmt = ggformat_find( fmt, &start, &one_past_end );
    143 	GGFORMAT_ASSERT( has_fmt );
    144 
    145 	ggformat_literals( fb, fmt, start );
    146 
    147 	FormatOpts opts = parse_formatopts( fmt + start + 1, one_past_end - start - 1 );
    148 	format( fb, first, opts );
    149 
    150 	ggformat_impl( fb, fmt + one_past_end + 1, rest... );
    151 }
    152 
    153 template< typename... Rest >
    154 size_t ggformat( char * buf, size_t len, const char * fmt, const Rest & ... rest ) {
    155 	FormatBuffer fb( buf, len );
    156 	ggformat_impl( &fb, fmt, rest... );
    157 	return fb.len;
    158 }
    159 
    160 template< typename... Rest >
    161 bool ggprint_to_file( FILE * file, const char * fmt, const Rest & ... rest ) {
    162 	char buf[ 4096 ];
    163 	FormatBuffer fb( buf, sizeof( buf ) );
    164 	ggformat_impl( &fb, fmt, rest... );
    165 
    166 	if( fb.len < fb.capacity ) {
    167 		size_t written = fwrite( buf, 1, fb.len, file );
    168 		return written == fb.len;
    169 	}
    170 
    171 	char * large_buf = ( char * ) malloc( fb.len + 1 );
    172 	GGFORMAT_ASSERT( large_buf != NULL );
    173 	FormatBuffer new_fb( large_buf, fb.len + 1 );
    174 	ggformat_impl( &new_fb, fmt, rest... );
    175 	size_t written = fwrite( large_buf, 1, fb.len, file );
    176 	free( large_buf );
    177 
    178 	return written == fb.len;
    179 }
    180 
    181 template< typename... Rest >
    182 bool ggprint( const char * fmt, const Rest & ... rest ) {
    183 	return ggprint_to_file( stdout, fmt, rest... );
    184 }
    185 
    186 GGFORMAT_ENABLE_OPTIMISATIONS();