README.md (6848B)
1 # ggformat v1.0 2 3 ggformat is a liberally licensed string formatting library for C++ that 4 supports user defined types without blowing up your compile times. It is 5 meant to be used as a replacement for printf and friends. 6 7 ggformat saves you time by reducing the amount of tedious boilerplate 8 code you have to write, without adding that time back onto the build. 9 ggformat has a trivial API and does not mind being mixed with other 10 formatting libraries, so it's easy to incrementally integrate with 11 existing software. In particular, it's easy to integrate ggformat with 12 your favourite string class, adding an extra convenience API on top of 13 the functionality you already use. ggformat does not allocate memory 14 unless you want it to, and it's easy to use your own allocators, making 15 it appropriate for use in long-lived applications and games where memory 16 fragmentation is a concern. 17 18 ggformat requires C++11 (variadic templates), and supports VS2015, GCC 19 and clang out of the box. It should also work with VS2013 and VS2017 but 20 I don't test against them. 21 22 I wrote ggformat because the existing string formatting options for C++ 23 either do not support user defined types or bloat compile times too 24 much. printf doesn't support user defined types. Streams and std::string 25 are slow to compile and IO manipulators are unreadable. Other C++ 26 formatting libraries include STL headers and also hurt compile times. 27 28 29 ## Version history 30 31 - __v1.0 29th Oct 2017__: variadic arguments are now passed by const 32 reference. You can now use ggformat with types that have deleted copy 33 constructors/assignment operators. 34 35 36 ## Usage 37 38 ```cpp 39 size_t ggformat( char * buf, size_t len, const char * fmt, ... ); 40 bool ggprint_to_file( FILE * file, const char * fmt, ... ); 41 bool ggprint( const char * fmt, ... ); 42 ``` 43 44 In short, `ggformat` replaces s(n)printf, `ggprint_to_file` replaces 45 fprintf, and `ggprint` replaces printf. 46 47 `ggformat` writes at most `len` bytes to `buf`, and that always includes 48 a null terminator. It returns the number of bytes that would have been 49 written if `buf` were large enough, _not including the null terminator_, 50 and can be larger than `len` (just like sprintf). 51 52 `ggprint_to_file` does what you would expect, and `ggprint` writes to 53 standard output. Both return `true` on success, or `false` if the write 54 fails. 55 56 `ggformat` does not allocate memory, the other functions allocate if you 57 print lots of data. All functions abort if you mess up the formatting 58 string or don't pass the right number of arguments. You should not pass 59 user defined strings as format strings, and I believe it's more helpful 60 to fail hard on programmer typos. 61 62 Basic usage looks like this: 63 64 ```cpp 65 #include "ggformat.h" 66 67 int main() { 68 ggprint( "hello {}\n", 1.23 ); // hello 1.23000 69 return 0; 70 } 71 ``` 72 73 and you can see more examples in basic_examples.cc and 74 string_examples.cc. 75 76 77 ## Format options 78 79 You can add format specifiers between the braces to change how things 80 are printed. The following options are supported: 81 82 - Plus sign (`{+}`): Prints a leading + for positive numeric types. 83 - Width (`{x}`): left pads the output with spaces to be `x` characters 84 wide. When used on floats, it left pads the output so the _left side 85 of the decimal point_ is `x` characters wide (I chose this because I 86 think it makes `{x.y}` more intuitive). If the output is already wider 87 than `x` characters, it doesn't do anything. 88 - Width with zero padding (`{0x}`): as above, but pads with zeroes 89 instead of spaces. 90 - Width with left alignment (`{-x}` or `{-0x}`): same again but puts the 91 spaces/zeroes on the right. 92 - Precision (`{.x}`): specifies the number of digits that appear after 93 the decimal point when printing floats. 94 - Number format (`{x}` or `{b}`): prints integers in hexadecimal/binary. 95 96 These can all be combined, but should be kept in the order they were 97 just listed in. 98 99 If you want to print a literal { or }, use {{ and }}. 100 101 102 ## User defined types 103 104 If you want to print your own types with ggformat, you need to define 105 `void format( FormatBuffer * fb, T x, const FormatOpts & opts );`. 106 `FormatBuffer` is a wrapper around a `char *` and length and its exact 107 definition is not important. `FormatOpts` holds parsed format options 108 and is defined as: 109 110 ```cpp 111 struct FormatOpts { 112 enum NumberFormat { DECIMAL, HEX, BINARY }; 113 114 int width = -1; 115 int precision = -1; 116 bool plus_sign = false; 117 bool left_align = false; 118 bool zero_pad = false; 119 NumberFormat number_format = DECIMAL; 120 }; 121 ``` 122 123 `format` implementations are typically quite simple: 124 125 ```cpp 126 #include "ggformat.h" 127 128 struct v3 { 129 explicit v3( float x_, float y_, float z_ ) { x = x_; y = y_; z = z_; } 130 float x, y, z; 131 }; 132 133 v3 operator+( const v3 & lhs, const v3 & rhs ) { 134 return v3( lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z ); 135 } 136 137 void format( FormatBuffer * fb, const v3 & v, const FormatOpts & opts ) { 138 format( fb, "v3(" ); 139 format( fb, v.x, opts ); 140 format( fb, ", " ); 141 format( fb, v.y, opts ); 142 format( fb, ", " ); 143 format( fb, v.z, opts ); 144 format( fb, ")" ); 145 } 146 147 int main() { 148 v3 a = v3( 1, 2, 3 ); 149 v3 b = v3( 4, 5, 6 ); 150 // a = v3(1.00000, 2.00000, 3.00000). b = v3( 4.00, 5.00, 6.00). 151 // a + b = v3(+5.00000, +7.00000, +9.00000) 152 ggprint( "a = {}. b = {2.2}.\na + b = {+}\n", a, b, a + b ); 153 154 return 0; 155 } 156 ``` 157 158 If you have a huge type and don't feel like writing a wall of `format`, 159 see `Thing` in basic_examples.cc. 160 161 162 ## Dynamic allocation (std::string, asprintf, etc) 163 164 `ggformat( NULL, 0, ... );` returns the number of bytes required to hold 165 the formatted string. With that it's easy to integrate ggformat with 166 your favourite dynamic string solution. For example, ggformat with 167 std::string: 168 169 ```cpp 170 template< typename... Rest > 171 std::string ggformat_to_string( const char * fmt, const Rest & ... rest ) { 172 size_t space_required = ggformat( nullptr, 0, fmt, rest... ); 173 174 if( space_required + 1 < space_required ) 175 throw std::overflow_error( "formatted string is too long" ); 176 177 std::string result; 178 result.resize( space_required + 1 ); // + 1 so there's space for the null terminator... 179 ggformat( &result[ 0 ], space_required + 1, fmt, rest... ); 180 result.resize( space_required ); // ...and then trim it off 181 182 return result; 183 } 184 ``` 185 186 187 ## Other stuff 188 189 Since this is C++ you can and should wrap `ggformat` in a string class 190 to make it more convenient to use. You can see an example in 191 string_examples.cc. 192 193 ggformat uses sprintf under the hood. sprintf can be pretty slow at 194 runtime, but compiles quickly. 195 196 In general ggformat is short enough that you can easily modify it to fit 197 your needs, and will be updated infrequently enough that doing so isn't 198 a huge pain. For example, it's very easy to replace malloc/free with 199 your own allocators, and if you don't like aborting on errors it's 200 pretty easy to change that too.