EAStdC provides a portable version of the C printf family of functions which improves on the C family in the following ways:
The primary disadvantages of EAStdC's printf are:
EAStdC doesn't attempt to solve the locale formatting problem because users really are better off using serious locale libraries for such things rather than using the meager support provided by the C standard library. The EALocale library attempts to provide such functionality.
EAStdC provides the following functions in 8, 16, and 32 bit versions:
int Cprintf(WriteFunction8 pWriteFunction, void* pContext, const char8_t* pFormat, ...);
int Fprintf(FILE* pFile, const char8_t* pFormat, ...);
int Printf(const char8_t* pFormat, ...);
int Sprintf(char8_t* pDestination, const char8_t* pFormat, ...);
int Snprintf(char8_t* pDestination, size_t n, const char8_t* pFormat, ...);
int Vcprintf(WriteFunction8 pWriteFunction8, void* pContext, const char8_t* pFormat, va_list arguments);
int Vfprintf(FILE* pFile, const char8_t* pFormat, va_list arguments);
int Vprintf(const char8_t* pFormat, va_list arguments);
int Vsprintf(char8_t* pDestination, const char8_t* pFormat, va_list arguments);
int Vsnprintf(char8_t* pDestination, size_t n, const char8_t* pFormat, va_list arguments);
int Vscprintf(const char8_t* EA_RESTRICT pFormat, va_list arguments);
Also there are:
template <typename String> int StringPrintf(String& s, const typename String::value_type* EA_RESTRICT pFormat, ...);
template <typename String> int StringVcprintf(String& s, const typename String::value_type* EA_RESTRICT pFormat, va_list arguments);
The EASprintf 'n' functions (Snprintf and Vsnprintf) follow the C99 standard for return value, which is different from some C standard library implementations such as VC++. These functions return the number of characters that would have been written had n been sufficiently large, not counting the terminating null character, or a negative value if an encoding error occurred. Thus, the null-terminated output has been completely written if and only if the returned value is nonnegative and less than n. Another way of saying it is that the return value equal to the strlen of the intended output. See the examples below.
The sprintf family of functions are very convenient but offer numerous opportunities for incorrect usage and security problems. Here is a list of some of the most common issues.
Printf provides extended format functionality not found in the C99 standard but which is useful nevertheless:
| Format/modifier | Description | Example | Example output |
| b | Binary output field type (joins d, i, x, o, etc.). | printf("0b%b", 255); printf("%#b", 255); printf("%#B", 255); |
0b11111111 0b11111111 0B11111111 |
| I8 | 8 bit integer field modifier. | printf("%I8d", 0xff); | -1 |
| I16 | 16 bit integer field modifier. | printf("%I16u", 0xffff); | 65535 |
| I32 | 32 bit integer field modifier. | printf("%I32d", 0xffffffff); | -1 |
| I64 | 64 bit integer field modifier. | printf("%I64u", 0xffffffffffffffff); | 18446744073709551615 |
| I128 | 128 bit integer field modifier. | printf("%I128d", 0xffffffffffffffffffffffffffffffff); | -1 |
| ' | Display a thousands separator. | printf("%'I16u", 0xffff); | 65,535 |
EAStdC also provides an ordered sprintf
Ordered printf is like printf except it works on "ordered" printf specifiers. This means that instead of using "%4d %f" we give an order to the arguments via an index and colon, as with "%1:4d %2:f". The point, however, is that you can reorder the indexes, as with "%2:f %1:4d". This is particularly useful for formatted string localization, as different locales use subjects and verbs in different orders.
User indexes can start at either 0 or 1. Oprintf detects which is in use as it goes. So the following have identical effect:
OPrintf("%1:s" %0:f", 3.0, "hello");
OPrintf("%2:s" %1:f", 3.0, "hello");
// User indexes must be contiguous and the behaviour of Oprintf is undefined
// if the ordering is non-contiguous. There are debug checks to validate
// contiguity, so debug tests should catch mistakes. The following is an
// example of non-contiguous ordering, as it is missing a "3:" format:
// OPrintf("%1:d" %0:d %3:d", 17, 18, 19, 20);
Example usage:
char16_t buffer[80];
// The module provides ordered versions of the printf family of functions:
// int OCprintf(WriteFunction pFunction, void* pContext, const char_t* pFormat, ...);
// int OFprintf(FILE* pFile, const char_t* pFormat, ...);
// int OPrintf(cconst char_t* pFormat, ...);
// int OSprintf(char_t* pDestination, const char_t* pFormat, ...);
// int OSnprintf(char_t* pDestination, size_t n, const char_t* pFormat, ...);
//
// int OVcprintf(WriteFunction pFunction, void* pContext, const char_t* pFormat, va_list arguments);
// int OVfprintf(FILE* pFile, const char_t* pFormat, va_list arguments);
// int OVprintf(const char_t* pFormat, va_list arguments);
// int OVsprintf(char_t* pDestination, const char_t* pFormat, va_list arguments);
// int OVsnprintf(char_t* pDestination, size_t n, const char_t* pFormat, va_list arguments);
// int OVscprintf(const char* pFormat, va_list arguments);
//
// Ordered printf is like printf except it works on "ordered" printf specifiers.
// This means that instead of using "%4d %f" we give an order to the arguments via
// an index and colon, as with "%1:4d %2:f". The point, however, is that you can
// reorder the indexes, as with "%2:f %1:4d". This is particularly useful for
// formatted string localization, as different locales use subjects and verbs
// in different orders.
//
// User indexes can start at either 0 or 1. Oprintf detects which is in use as
// it goes. So the following have identical effect:
// OPrintf("%1:s" %0:f", 3.0, "hello");
// OPrintf("%2:s" %1:f", 3.0, "hello");
//
// User indexes must be contiguous and the behaviour of Oprintf is undefined
// if the ordering is non-contiguous. There are debug checks to validate
// contiguity, so debug tests should catch mistakes. The following is an
// example of non-contiguous ordering, as it is missing a "3:" format:
// OPrintf("%1:d" %0:d %3:d", 17, 18, 19, 20);
/////////////////////////////////////////////////////////////////////////////
All examples presume the #include of EASprintf.h.
Snprintf usage:
char16_t buffer[80];
int nStrlen = Snprintf16(buffer, 80, "Columbus arrived in %d.", 1492);
if((unsigned)nStrlen < 80) // Cast to unsigned in order to make any negative values be positive and > 80.
puts("success");
How to write a function that takes variable arguments (e.g. ...) and passes them to EASprintf:
#include <stdarg.h>
void CustomFormattedOutput(const char* pFormat, ...)
{
va_list arguments;
va_start(arguments, pFormat);
Vprintf8(pFormat, arguments);
va_end(arguments);
}
How to write a function that does custom formatted output to a buffer that is resized as needed for it. This is useful for the implementation of fail-safe utility functions and for debug tracing systems, among other things.
#include <stdarg.h>
#include <string>
// This is a generic function for doing a sprintf into a C++ std::string object.
// If the string object lacks enough space for the output, then the string will
// be resized to exactly fit the output.
size_t StringSprintf(std::string& s, const char* pFormat, ...)
{
va_list arguments;
va_start(arguments, pFormat);
if(s.size() < s.capacity())
s.resize(s.capacity());
const int nStrlen = Vsnprintf8(const_cast<char*>(s.data()), s.capacity() + 1, pFormat, arguments);
va_end(arguments);
if(nStrlen > (int)s.size())
{
s.resize(nStrlen);
Vsnprintf8(const_cast<char*>(s.data()), s.capacity() + 1, pFormat, arguments);
return (size_t)nStrlen;
}
Write to the C stderr stream:
#include <stdio.h>
Fprintf8(stderr, "Columbus arrived in %d.", 1492);
Vsscanf usage. A complete discussion of the ins and outs of vsscanf are currently beyond the scope of this document. The scanf found in EAPrintf acts the same as with the C++ standard, so you can read about that elsewhere for the time being.
char16_t buffer[64] = EAText16("Columbus arrived in 1492");
int year;
Vsscanf16(buffer, "Columbus arrived in %d.", &year);
Printf family
int Fprintf8(FILE* pFile, const char8_t* pFormat, ...);
int Printf8(const char8_t* pFormat, ...);
int Sprintf8(char8_t* pDestination, const char8_t* pFormat, ...);
int Snprintf8(char8_t* pDestination, size_t n, const char8_t* pFormat, ...);
int Fprintf16(FILE* pFile, const char16_t* pFormat, ...);
int Printf16(const char16_t* pFormat, ...);
int Sprintf16(char16_t* pDestination, const char16_t* pFormat, ...);
int Snprintf16(char16_t* pDestination, size_t n, const char16_t* pFormat, ...);
Vprintf family
/// Note that vsnprintf was not added to the C standard until C99.
/// Here we follow the C99 standard and have the return value of vsnprintf
/// return the number of required characters to complete the formatted string.
/// This is more useful than the way some libraries implement vsnprintf by
/// returning -1 if there isn't enough space. The return value is -1 if there
/// was an "encoding error" or the implementation was unable to return a
/// value > n upon lack of sufficient space as per the C99 standard.
///
/// Specification:
/// The vsnprintf function is equivalent to snprintf, with the variable
/// argument list replaced by arguments, which shall have been initialized
/// by the va_start macro (and possibly subsequent va_arg calls).
/// The vsnprintf function does not invoke the va_end macro. If copying
/// takes place between objects that overlap, the behavior is undefined.
///
/// The vsnprintf function returns the number of characters that would
/// have been written had n been sufficiently large, not counting the
/// terminating null character, or a neg ative value if an encoding error
/// occurred. Thus, the null-terminated output has been completely written
/// if and only if the returned value is nonnegative and less than n.
///
int Vfprintf8(FILE* pFile, const char8_t* pFormat, va_list arguments);
int Vprintf8(const char8_t* pFormat, va_list arguments);
int Vsprintf8(char8_t* pDestination, const char8_t* pFormat, va_list arguments);
int Vsnprintf8(char8_t* pDestination, size_t n, const char8_t* pFormat, va_list arguments);
int Vfprintf16(FILE* pFile, const char16_t* pFormat, va_list arguments);
int Vprintf16(const char16_t* pFormat, va_list arguments);
int Vsprintf16(char16_t* pDestination, const char16_t* pFormat, va_list arguments);
int Vsnprintf16(char16_t* pDestination, size_t n, const char16_t* pFormat, va_list arguments);