Introduction
Many nowadays applications with user interface are using string manipulation routines for sure. It is a programmer’s right to choose the best and most convenience tool for a particular situation. I guess that the oldest one should be standard library’s function called “printf” and its derivations. Why yet another printf like function? Because, it is fun and should be useful for particular situations. Interested? Read bellow…
Alternatives
You can choose whatever you want to format your string for particular situation. My best guess that you already know at least one that is right for you. I can write a short list that I know and am using for daily work: STL (streams), MFC/WTL (CString), CRT/libc(printf), boost::format, there are few articles like this in http://www.codeproject.com too. Now, I will add my format function to this list.
I don’t like streams because if you need to add something to it with particular format, you need to call many additional functions like “width, setf” and so on. It is not very convenience if you have large text to stream, many formats to change. So, the alternative for this would be “CString” class and/or “printf” functions, because you “call” all these functions with format string and argument list. “CString” class is quite good, but it is very specific, so if you write programs for many platforms, you might not be using this. Function “printf” is good, but you have to know how many symbols it will pop-out, you will not have dynamic buffer here. Are there any alternatives? Library boost, but I did not like its speed. I will confess, I didn’t test it, but I saw results on the net, I didn’t like them. What are other alternatives? Write your own and be happy with it.
The beginning
Safety is #1 at programming world. Well, you will not get this with “printf” and with my format function. They both use the same “stdarg” mechanism which is good if you know what you are doing. But if you want to compromise application, you can easily change format string and/or argument order. You should consider that with safety you loose speed and choose the right method for you.
So, let’s look closer, what kind of reinvented wheel do we have? I have read much about string formats and made decision: my format library will be very close with printf “standards”. Why? Because it is very convenience and it is already in my blood, and it is very hard to convince other programmers to use your library with new format. It is not wise. So, here it goes: old format with some weird enhancements:
%[<|><{>][flags][:padchar][width][.precision]type-char[<|><}>]
[flags] optional flag.
'-': left alignment (default is right alignment)
'=': centered alignment
'+': show sign
'!': don't print int that is *0* or string that is *null*
'L': use long instead of int
'LL': use long long instead of long
'0': pad with pad char (if pad char is not specified, then fill with 0's)
'#': show base. o- 0, x- 0x, X- 0X
[:padchar] optional flag. Default value depends on [flags].
[width] optional flag. Specifies a minimal width for the string resulting form the conversion.
[.precision] optional flag. When outputting a floatting type number, it sets the maximum number of digits. When used with type-char s or S the conversion string is truncated to the precision first chars.
type-char:
'b': binary output
'o': octal output
'i', 'u', 'd': decimal output (signed, unsigned)
'x': hexadecimal output ('X' upper case)
'f': fixed float format
's', 'S': string output
'c', 'C': char output
'%': print '%'
I guess if you use printf like formatting string, you will find familiar things in the format above. The new thing is “padchar”. I didn’t find feature like this (except STL streams), so I silently added this.
Using the code
To use my function, you need to include vformat.h file. The main function and all structs goes under "frmt" namespace. It is your right to add line "using namespace frmt;" or call function with "frmt::" prefix. To make things easier and more WindowsAPI alike you can make additional functions:
size_t formatA (string& sOut, const char *cpcFormat, ... )
{
va_list vargs;
va_start (vargs, cpcFormat);
size_t iRes = vformat<char> (sOut, cpcFormat, vargs);
va_end (vargs);
return iRes;
}
size_t formatW (wstring& sOut, const wchar_t *cpwcFormat, ... )
{
va_list vargs;
va_start (vargs, cpwcFormat);
size_t iRes = vformat<wchar_t> (sOut, cpwcFormat, vargs);
va_end (vargs);
return iRes;
}
It is wise to check "iRes" variable in debug versions, because it shows where error in format string lies (0- on success).
Points of Interest
I bet you want to see how fast my format function is? Well, I have to disappoint you: it is not as fast as I or you wanted, but it is promising. In some cases it is very similar with CRT/libc printf speed, but in most cases it is a bit slower. Look at the speed table and you will understand (D stands for debug, R for release) (the time is in ms):
Format string
| FORMAT (D)
| printf (D)
| FORMAT (R)
| printf (R)
|
%i
| 4844
| 1109
| 421
| 688
|
%-#4x %+#012x %+#012o %#8x
| 14813
| 4203
| 2906
| 2563
|
%-#4x
| 5359
| 1110
| 547
| 656
|
%+#012x
| 5625
| 1312
| 640
| 782
|
%+#012o
| 5438
| 1234
| 547
| 703
|
%0#-+3.5s %0#-+5.3s %0#-+s
| 9688
| 2078
| 1343
| 1094
|
Well, numbers are numbers, they speak for their selves. Debug version is slow for sure, I cannot do anything with that, but release version is promising. As complexity of format string grows, my function’s time is very close to printf time. Also, please note very interesting thing: when format string grows, the time of printf function grows much faster than mine function’s. That is very interesting and funny results.
Final words
This function is small experience in my life of reinventing wheel. Will I use it? Yes! I wanted to write something similar with STL string, because I missed that feature in STL very much. Finally, I have done this and more over, I put this on the internet for mass judgment. Please, write me a short note if you start using my function and are happy of it. You are free to use it in any product, but don’t claim, that you wrote it.
History
- A small bug-fix in string format: 'c' and 'C'.
- Added additional ifdef's to make code compile under linux systems.