// ----------------------------------------------- // PositionalPrintfTest.cpp #define UNICODE #include #include #include "PositionalPrintf.h" // Try different things with the fancy printf function int _tmain(int argc, _TCHAR* argv[]) { printf(_T("%s %g %d %c\n"), _T("Hola"), 3.4f, 34, _T('3')); PositionalPrintf("%s %g %d %c\n", "Hola", 3.4f, 34, '3'); PositionalPrintf(L"%s %g %d %c\n", L"Hola", 3.4f, 34, L'3'); PositionalPrintf(_T("%s %g %d %c\n"), _T("Hola"), 3.4f, 34, _T('3')); PositionalPrintf("%{3}c %g - %{0}s's %{1}s\n%{3}c %{4}g - El %{1}s de %{0}s", "Rick", "Bar", 'S', 'E', 3.1415f); } // ----------------------------------------------- // PositionalPrintf.h // Only two versions of this template are instantiated, char and wchar_t // Any other attempt to use this function will result in a link error. template void PositionalPrintf(const T *pszFmt, ...); // ----------------------------------------------- // PositionalPrintf.cpp #include #include #include #include // Templated functions for char/wchar_t operations. template int TempAtoi(const T *p); template<> int TempAtoi(const char *p) { return atoi(p); } template<> int TempAtoi(const wchar_t *p) { return _wtoi(p); } template void TempVPrintf(const T *p, va_list va); template<> void TempVPrintf(const char *p, va_list va) { vprintf(p, va); } template<> void TempVPrintf(const wchar_t *p, va_list va) { vwprintf(p, va); } // The actual function template void PositionalPrintf(const T *pszFmt, ...) { enum TParam { TP_UNK, TP_CHAR, TP_INT, TP_STR, TP_FLOAT, }; static const int MAX_PARAMS = 100; // Here we store the parameters we should be expecting on the stack // We decide this based on the format string. int nParams = 0; int nextParam = 0; TParam aParams[MAX_PARAMS]; // Here we store each '%' format element's data from the format string. struct { TParam param; int pos; } aFormats[MAX_PARAMS]; int nFormats = 0; for (int i = 0; i < MAX_PARAMS; ++i) aParams[i] = TP_UNK; // Scan the format string to detect expected parameters and the way to extract them from the stack. bool bError = false; for (const T *s = pszFmt; *s; ++s) { if (*s == T('%')) { // Found a format element. First thing to do is identify the position of the parameter // on the stack. int pos; if (*(s+1) == T('{')) { // Explicit position modifier pos = TempAtoi(s+2); nextParam = pos+1; } else pos = nextParam++; // Just se the next position. while (*s && *s != T('c') && *s != T('d') && *s != T('g') && *s != T('s')) { // Here we could detect '*' parameters (which won't work), missing '}', etc. s++; } if (!*s) break; if (pos >= MAX_PARAMS) { // error printf("ERROR! Parameter %d out of range.\n", pos); bError = true; } else { // Identifica el tipo de parámetro que es // Aqui podemos extender esto un montón para cubrir todos los tipos de formato TParam p; if (*s == T('c')) p = TP_CHAR; else if (*s == T('d')) p = TP_INT; else if (*s == T('s')) p = TP_STR; else p = TP_FLOAT; if (aParams[pos] != TP_UNK && aParams[pos] != p) { // error printf("ERROR! Parameter %d used with different format specifiers (%d and %d).\n", pos, aParams[pos], p); bError = true; } // Store the parameter type to be expected at position 'pos' on the stack. aParams[pos] = p; if (nParams <= pos) nParams = pos+1; // Store the parameter type and position on the stack, for this format element. if (nFormats >= MAX_PARAMS) { // error printf("ERROR! Too many format elements (%d)!\n", nFormats); bError = true; } else { aFormats[nFormats].param = p; aFormats[nFormats].pos = pos; nFormats++; } } } } // Verify that all parameters on the stack are referenced. (optional) for (int i = 0; i < nParams; i++) { if (aParams[i] == TP_UNK) { // error // Benign if we assume that unused parameters are of INT size. // printf("ERROR! Parameter %d undefined.\n", i); // bError = true; } } if (bError) return; // Build a new format string removing the {n} modifiers T szNewFmt[3000]; { T *p = szNewFmt; szNewFmt[0] = T(0); for (const T *s = pszFmt; *s; s++) { *p++ = *s; if (*s == T('%')) { if (*(s+1) == T('{')) { s += 2; while (*s && *s != T('}')) s++; if (!*s) break; } } } *p = T(0); // Zero-end the new format string } // Copy the parameters to the parameter buffer in correct order. // Here comes the system-dependent part char aParamBuf[3000]; { char *p = aParamBuf; for (int i = 0; i < nFormats; ++i) { va_list va; va_start(va, pszFmt); // Skip stack parameters until the one we're looking for. // System-dependent: assumes that anything that is not of type double is of INT size for (int j = 0; j < aFormats[i].pos; ++j) { if (aParams[j] == TP_FLOAT) va_arg(va, double); else va_arg(va, int); } // Copy the parameter into the new parameter buffer // System-dependent: the size thing again. // System-dependent: assumes things about the order in which parameters are stored on the stack. if (aParams[j] == TP_FLOAT) { double d = va_arg(va, double); memcpy(p, &d, sizeof(d)); p += sizeof(d); } else { int d = va_arg(va, int); memcpy(p, &d, sizeof(d)); p += sizeof(d); } } } // System-dependent: assumes that aParamBuf can be cast straight to va_list. TempVPrintf(szNewFmt, (va_list)aParamBuf); } // Instantiate the char and wchar_t versions of the function. template void PositionalPrintf(const char *pszFmt, ...); template void PositionalPrintf(const wchar_t *pszFmt, ...);