{"id":2448,"date":"2011-09-25T02:01:49","date_gmt":"2011-09-25T00:01:49","guid":{"rendered":"https:\/\/iguanademos.com\/Jare\/wp\/?page_id=2448"},"modified":"2011-09-25T02:03:04","modified_gmt":"2011-09-25T00:03:04","slug":"positional-printf","status":"publish","type":"page","link":"https:\/\/iguanademos.com\/Jare\/wp\/?page_id=2448","title":{"rendered":"Positional Printf"},"content":{"rendered":"<p>(Note: this article was originally found at <a href=\"http:\/\/www.flipcode.com\/cgi-bin\/msg.cgi?showThread=COTD-posprintf&#038;forum&#038;forum=cotd&#038;id=-1\" target=\"_blank\">Code Of The Day<\/A> in <a href=\"http:\/\/www.flipcode.com\" target=\"_blank\">Flipcode<\/a>. The C++ to HTML formating comes from Kurt&#8217;s internal tools)<\/font><\/p>\n<p>Printf-style formatting is well known, flexible and very useful in general. All the worries about unchecked parameters and so on have proven to be a non-\u00edssue in my experience. However, it lacks parameter positioning, the way .NET allows. Why is this important for games? Any modern game needs to have a good localisation system; most game translators understand printf-style format strings and know what to do with them when they are among the text strings to be localised. There is a subtle kind of localised strings that can&#8217;t be easily translated most of the times: strings in which parameterized data changes position depending on the language. A good example is the date format, dd\/mm\/yy as we use in Europe as opposed to the mm\/dd\/yy format in the US. The format string would be &#8220;%d\/%d\/%d&#8221;, and the call could be something like:\n<\/p>\n<p><i>printf(LTEXT_DATE_STRING, day, month, year);<\/i>\n<\/p>\n<p>How would you localise a string like that to the US without modifying the code? Using system locale functions defeats the point I&#8217;m making; another example are strings like &#8220;Rick&#8217;s Bar&#8221; as opposed to &#8220;El Bar de Rick&#8221;, where both &#8220;Rick&#8221; and &#8220;Bar&#8221; would change positions if they are parameters.\n<\/p>\n<p>The function I present here wraps printf with an extension to format specifiers, which allows for parameter placement. In the given example, the US string would be &#8220;%{1}d\/%{0}d\/%{2}d&#8221;, where the number in parenthesis specifies which parameter should be used in that place of the format string. If no placement modifier is included, it assumes the next parameter from the previous format specifier. This way, the function will work fine with regular printf format strings.\n<\/p>\n<p>The code will need modifications to be a full-blown function to use in your own application, and is only presented as an example of the technique. Check it, read the comments and modify at will. It is templatized in order to support both 8-bit chars and 16-bit wchar_t strings.\n<\/p>\n<p>Caveats:\n<\/p>\n<blockquote>\n<li>Asterisks (&#8216;*&#8217;) in the format string can&#8217;t be supported this way.\n<li>It only supports &#8220;%c&#8221;, &#8220;%d&#8221;, &#8220;%g&#8221; and &#8220;%s&#8221; format strings (but will work with all format modifiers except the mentioned asterisk). Extending this support is trivial.\n<li>Portions of the code are system-dependent. This example currently works in Microsoft&#8217;s .NET compiler, but it shouldn&#8217;t be hard to modify for your own platform.\n<\/p><\/blockquote>\n<table bgcolor=\"#ffffff\" width=\"98%\" cellspacing=0 cellpadding=10 border=1>\n<tr>\n<td width=\"100%%\" bgcolor=\"#ffffff\">\n<pre><font size=2 face=\"Courier, Courier New\" color=\"#000000\"><font color=\"#007f00\">\/\/ -----------------------------------------------\r\n<\/font><font color=\"#007f00\">\/\/ PositionalPrintfTest.cpp\r\n\r\n<\/font>\r\n<font color=\"#0000ff\">#define<\/font> UNICODE\r\n<font color=\"#0000ff\">#include<\/font> &lt;tchar.h>\r\n<font color=\"#0000ff\">#include<\/font> &lt;stdio.h>\r\n\r\n<font color=\"#0000ff\">#include<\/font> \"PositionalPrintf.h\"\r\n\r\n<font color=\"#007f00\">\/\/ Try different things with the fancy printf function\r\n<\/font><font color=\"#0000ff\">int<\/font> _tmain(<font color=\"#0000ff\">int<\/font> argc, _TCHAR* argv[])\r\n{\r\n  printf(_T(\"%s %g %d %c\\n\"), _T(\"Hola\"), 3.4f, 34, _T('3'));\r\n\r\n  PositionalPrintf(\"%s %g %d %c\\n\", \"Hola\", 3.4f, 34, '3');\r\n  PositionalPrintf(L\"%s %g %d %c\\n\", L\"Hola\", 3.4f, 34, L'3');\r\n  PositionalPrintf(_T(\"%s %g %d %c\\n\"), _T(\"Hola\"), 3.4f, 34, _T('3'));\r\n\r\n  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);\r\n}\r\n\r\n\r\n\r\n<font color=\"#007f00\">\/\/ -----------------------------------------------\r\n<\/font><font color=\"#007f00\">\/\/ PositionalPrintf.h\r\n<\/font>\r\n<font color=\"#007f00\">\/\/ Only two versions of this template are instantiated, char and wchar_t\r\n<\/font><font color=\"#007f00\">\/\/ Any other attempt to use this function will result in a link error.\r\n<\/font><font color=\"#0000ff\">template<\/font>&lt;<font color=\"#0000ff\">typename<\/font> T>\r\n<font color=\"#0000ff\">void<\/font> PositionalPrintf(<font color=\"#0000ff\">const<\/font> T *pszFmt, ...);\r\n\r\n<font color=\"#007f00\">\/\/ -----------------------------------------------\r\n<\/font><font color=\"#007f00\">\/\/ PositionalPrintf.cpp\r\n\r\n\r\n<\/font>\r\n<font color=\"#0000ff\">#include<\/font> &lt;stdarg.h>\r\n<font color=\"#0000ff\">#include<\/font> &lt;stdlib.h>\r\n<font color=\"#0000ff\">#include<\/font> &lt;stdio.h>\r\n<font color=\"#0000ff\">#include<\/font> &lt;string.h>\r\n\r\n\r\n<font color=\"#007f00\">\/\/ Templated functions for char\/wchar_t operations.\r\n<\/font><font color=\"#0000ff\">template<\/font> &lt;<font color=\"#0000ff\">typename<\/font> T> <font color=\"#0000ff\">int<\/font> TempAtoi(<font color=\"#0000ff\">const<\/font> T *p);\r\n\r\n\r\n\r\n<font color=\"#0000ff\">template<\/font>&lt;> <font color=\"#0000ff\">int<\/font> TempAtoi(<font color=\"#0000ff\">const<\/font> <font color=\"#0000ff\">char<\/font> *p) { <font color=\"#0000ff\">return<\/font> atoi(p); }\r\n<font color=\"#0000ff\">template<\/font>&lt;> <font color=\"#0000ff\">int<\/font> TempAtoi(<font color=\"#0000ff\">const<\/font> wchar_t *p) { <font color=\"#0000ff\">return<\/font> _wtoi(p); }\r\n\r\n\r\n\r\n<font color=\"#0000ff\">template<\/font> &lt;<font color=\"#0000ff\">typename<\/font> T> <font color=\"#0000ff\">void<\/font> TempVPrintf(<font color=\"#0000ff\">const<\/font> T *p, va_list va);\r\n\r\n<font color=\"#0000ff\">template<\/font>&lt;> <font color=\"#0000ff\">void<\/font> TempVPrintf(<font color=\"#0000ff\">const<\/font> <font color=\"#0000ff\">char<\/font> *p, va_list va)     { vprintf(p, va); }\r\n\r\n\r\n<font color=\"#0000ff\">template<\/font>&lt;> <font color=\"#0000ff\">void<\/font> TempVPrintf(<font color=\"#0000ff\">const<\/font> wchar_t *p, va_list va)  { vwprintf(p, va); }\r\n\r\n<font color=\"#007f00\">\/\/ The actual function\r\n<\/font><font color=\"#0000ff\">template<\/font>&lt;<font color=\"#0000ff\">typename<\/font> T>\r\n<font color=\"#0000ff\">void<\/font> PositionalPrintf(<font color=\"#0000ff\">const<\/font> T *pszFmt, ...)\r\n{\r\n  <font color=\"#0000ff\">enum<\/font> TParam\r\n  {\r\n    TP_UNK,\r\n    TP_CHAR,\r\n    TP_INT,\r\n    TP_STR,\r\n    TP_FLOAT,\r\n  };\r\n  <font color=\"#0000ff\">static<\/font> <font color=\"#0000ff\">const<\/font> <font color=\"#0000ff\">int<\/font> MAX_PARAMS = 100;\r\n\r\n  <font color=\"#007f00\">\/\/ Here we store the parameters we should be expecting on the stack\r\n\r\n\r\n<\/font>  <font color=\"#007f00\">\/\/ We decide this based on the format string.\r\n<\/font>  <font color=\"#0000ff\">int<\/font> nParams  = 0;\r\n  <font color=\"#0000ff\">int<\/font> nextParam = 0;\r\n  TParam aParams[MAX_PARAMS];\r\n\r\n  <font color=\"#007f00\">\/\/ Here we store each '%' format element's data from the format string.\r\n<\/font>  <font color=\"#0000ff\">struct<\/font>\r\n  {\r\n    TParam param;\r\n    <font color=\"#0000ff\">int<\/font>    pos;\r\n  } aFormats[MAX_PARAMS];\r\n  <font color=\"#0000ff\">int<\/font> nFormats = 0;\r\n\r\n  <font color=\"#0000ff\">for<\/font> (<font color=\"#0000ff\">int<\/font> i = 0; i &lt; MAX_PARAMS; ++i)\r\n    aParams[i] = TP_UNK;\r\n\r\n  <font color=\"#007f00\">\/\/ Scan the format string to detect expected parameters and the way to extract them from the stack.\r\n\r\n\r\n<\/font>  <font color=\"#0000ff\">bool<\/font> bError = <font color=\"#0000ff\">false<\/font>;\r\n  <font color=\"#0000ff\">for<\/font> (<font color=\"#0000ff\">const<\/font> T *s = pszFmt; *s; ++s)\r\n  {\r\n    <font color=\"#0000ff\">if<\/font> (*s == T('%'))\r\n    {\r\n      <font color=\"#007f00\">\/\/ Found a format element. First thing to do is identify the position of the parameter\r\n<\/font>      <font color=\"#007f00\">\/\/ on the stack.\r\n\r\n\r\n<\/font>      <font color=\"#0000ff\">int<\/font> pos;\r\n      <font color=\"#0000ff\">if<\/font> (*(s+1) == T('{'))\r\n      {\r\n        <font color=\"#007f00\">\/\/ Explicit position modifier\r\n<\/font>        pos = TempAtoi(s+2);\r\n        nextParam = pos+1;\r\n      }\r\n      <font color=\"#0000ff\">else<\/font>\r\n        pos = nextParam++; <font color=\"#007f00\">\/\/ Just se the next position.\r\n<\/font>      <font color=\"#0000ff\">while<\/font> (*s && *s != T('c') && *s != T('d') && *s != T('g') && *s != T('s'))\r\n      {\r\n        <font color=\"#007f00\">\/\/ Here we could detect '*' parameters (which won't work), missing '}', etc.\r\n\r\n\r\n<\/font>        s++;\r\n      }\r\n      <font color=\"#0000ff\">if<\/font> (!*s)\r\n        <font color=\"#0000ff\">break<\/font>;\r\n\r\n      <font color=\"#0000ff\">if<\/font> (pos >= MAX_PARAMS)\r\n      {\r\n        <font color=\"#007f00\">\/\/ error\r\n<\/font>        printf(\"ERROR! Parameter %d out of range.\\n\", pos);\r\n        bError = <font color=\"#0000ff\">true<\/font>;\r\n      }\r\n      <font color=\"#0000ff\">else<\/font> \r\n      {\r\n        <font color=\"#007f00\">\/\/ Identifica el tipo de par\u00e1metro que es\r\n\r\n\r\n<\/font>        <font color=\"#007f00\">\/\/ Aqui podemos extender esto un mont\u00f3n para cubrir todos los tipos de formato\r\n<\/font>        TParam p;\r\n        <font color=\"#0000ff\">if<\/font> (*s == T('c'))      p = TP_CHAR;\r\n        <font color=\"#0000ff\">else<\/font> <font color=\"#0000ff\">if<\/font> (*s == T('d')) p = TP_INT;\r\n        <font color=\"#0000ff\">else<\/font> <font color=\"#0000ff\">if<\/font> (*s == T('s')) p = TP_STR;\r\n        <font color=\"#0000ff\">else<\/font>                   p = TP_FLOAT;\r\n\r\n        <font color=\"#0000ff\">if<\/font> (aParams[pos] != TP_UNK && aParams[pos] != p)\r\n        {\r\n          <font color=\"#007f00\">\/\/ error\r\n\r\n\r\n<\/font>          printf(\"ERROR! Parameter %d used with different format specifiers (%d and %d).\\n\", pos, aParams[pos], p);\r\n          bError = <font color=\"#0000ff\">true<\/font>;\r\n        }\r\n\r\n        <font color=\"#007f00\">\/\/ Store the parameter type to be expected at position 'pos' on the stack.\r\n<\/font>        aParams[pos] = p;\r\n        <font color=\"#0000ff\">if<\/font> (nParams &lt;= pos)\r\n          nParams = pos+1;\r\n\r\n        <font color=\"#007f00\">\/\/ Store the parameter type and position on the stack, for this format element.\r\n<\/font>        <font color=\"#0000ff\">if<\/font> (nFormats >= MAX_PARAMS)\r\n        {\r\n          <font color=\"#007f00\">\/\/ error\r\n\r\n\r\n<\/font>          printf(\"ERROR! Too many format elements (%d)!\\n\", nFormats);\r\n          bError = <font color=\"#0000ff\">true<\/font>;\r\n        }\r\n        <font color=\"#0000ff\">else<\/font>\r\n        {\r\n          aFormats[nFormats].param = p;\r\n          aFormats[nFormats].pos   = pos;\r\n          nFormats++;\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  <font color=\"#007f00\">\/\/ Verify that all parameters on the stack are referenced. (optional)\r\n<\/font>  <font color=\"#0000ff\">for<\/font> (<font color=\"#0000ff\">int<\/font> i = 0; i &lt; nParams; i++)\r\n  {\r\n    <font color=\"#0000ff\">if<\/font> (aParams[i] == TP_UNK)\r\n    {\r\n      <font color=\"#007f00\">\/\/ error\r\n\r\n\r\n<\/font>      <font color=\"#007f00\">\/\/ Benign if we assume that unused parameters are of INT size.\r\n<\/font><font color=\"#007f00\">\/\/      printf(\"ERROR! Parameter %d undefined.\\n\", i);\r\n<\/font><font color=\"#007f00\">\/\/      bError = true;\r\n<\/font>    }\r\n  }\r\n  <font color=\"#0000ff\">if<\/font> (bError)\r\n    <font color=\"#0000ff\">return<\/font>;\r\n\r\n  <font color=\"#007f00\">\/\/ Build a new format string removing the {n} modifiers\r\n<\/font>  T szNewFmt[3000];\r\n  {\r\n    T *p = szNewFmt;\r\n\r\n    szNewFmt[0] = T(0);\r\n\r\n    <font color=\"#0000ff\">for<\/font> (<font color=\"#0000ff\">const<\/font> T *s = pszFmt; *s; s++)\r\n    {\r\n      *p++ = *s;\r\n      <font color=\"#0000ff\">if<\/font> (*s == T('%'))\r\n      {\r\n        <font color=\"#0000ff\">if<\/font> (*(s+1) == T('{'))\r\n        {\r\n          s += 2;\r\n          <font color=\"#0000ff\">while<\/font> (*s && *s != T('}'))\r\n            s++;\r\n          <font color=\"#0000ff\">if<\/font> (!*s)\r\n            <font color=\"#0000ff\">break<\/font>;\r\n        }\r\n      }\r\n    }\r\n    *p = T(0); <font color=\"#007f00\">\/\/ Zero-end the new format string\r\n\r\n\r\n<\/font>  }\r\n  \r\n  <font color=\"#007f00\">\/\/ Copy the parameters to the parameter buffer in correct order.\r\n<\/font>  <font color=\"#007f00\">\/\/ Here comes the system-dependent part\r\n<\/font>  <font color=\"#0000ff\">char<\/font> aParamBuf[3000];\r\n  {\r\n    <font color=\"#0000ff\">char<\/font> *p = aParamBuf;\r\n\r\n    <font color=\"#0000ff\">for<\/font> (<font color=\"#0000ff\">int<\/font> i = 0; i &lt; nFormats; ++i)\r\n    {\r\n      va_list va;\r\n      va_start(va, pszFmt);\r\n\r\n      <font color=\"#007f00\">\/\/ Skip stack parameters until the one we're looking for.\r\n\r\n\r\n<\/font>      <font color=\"#007f00\">\/\/ System-dependent: assumes that anything that is not of type double is of INT size\r\n<\/font>      <font color=\"#0000ff\">for<\/font> (<font color=\"#0000ff\">int<\/font> j = 0; j &lt; aFormats[i].pos; ++j)\r\n      {\r\n        <font color=\"#0000ff\">if<\/font> (aParams[j] == TP_FLOAT)\r\n          va_arg(va, <font color=\"#0000ff\">double<\/font>);\r\n        <font color=\"#0000ff\">else<\/font>\r\n\r\n          va_arg(va, <font color=\"#0000ff\">int<\/font>);\r\n      }\r\n\r\n      <font color=\"#007f00\">\/\/ Copy the parameter into the new parameter buffer\r\n<\/font>      <font color=\"#007f00\">\/\/ System-dependent: the size thing again.\r\n<\/font>      <font color=\"#007f00\">\/\/ System-dependent: assumes things about the order in which parameters are stored on the stack.\r\n<\/font>      <font color=\"#0000ff\">if<\/font> (aParams[j] == TP_FLOAT)\r\n      {\r\n        <font color=\"#0000ff\">double<\/font> d = va_arg(va, <font color=\"#0000ff\">double<\/font>);\r\n        memcpy(p, &d, <font color=\"#0000ff\">sizeof<\/font>(d));\r\n        p += <font color=\"#0000ff\">sizeof<\/font>(d);\r\n      }\r\n      <font color=\"#0000ff\">else<\/font>\r\n\r\n      {\r\n        <font color=\"#0000ff\">int<\/font> d = va_arg(va, <font color=\"#0000ff\">int<\/font>);\r\n        memcpy(p, &d, <font color=\"#0000ff\">sizeof<\/font>(d));\r\n        p += <font color=\"#0000ff\">sizeof<\/font>(d);\r\n      }\r\n    }\r\n  }\r\n\r\n  <font color=\"#007f00\">\/\/ System-dependent: assumes that aParamBuf can be cast straight to va_list.\r\n<\/font>  TempVPrintf(szNewFmt, (va_list)aParamBuf);\r\n}\r\n\r\n<font color=\"#007f00\">\/\/ Instantiate the char and wchar_t versions of the function.\r\n\r\n<\/font><font color=\"#0000ff\">template<\/font> <font color=\"#0000ff\">void<\/font> PositionalPrintf(<font color=\"#0000ff\">const<\/font> <font color=\"#0000ff\">char<\/font> *pszFmt, ...);\r\n\r\n<font color=\"#0000ff\">template<\/font> <font color=\"#0000ff\">void<\/font> PositionalPrintf(<font color=\"#0000ff\">const<\/font> wchar_t *pszFmt, ...);\r\n\r\n\r\n<\/font><\/pre>\n<\/td>\n<\/tr>\n<\/table>\n","protected":false},"excerpt":{"rendered":"<p>(Note: this article was originally found at Code Of The Day in Flipcode. The C++ to HTML formating comes from Kurt&#8217;s internal tools) Printf-style formatting is well known, flexible and very useful in general. All the worries about unchecked parameters &hellip; <a href=\"https:\/\/iguanademos.com\/Jare\/wp\/?page_id=2448\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"parent":2432,"menu_order":0,"comment_status":"open","ping_status":"open","template":"","meta":{"footnotes":""},"class_list":["post-2448","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/iguanademos.com\/Jare\/wp\/index.php?rest_route=\/wp\/v2\/pages\/2448","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/iguanademos.com\/Jare\/wp\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/iguanademos.com\/Jare\/wp\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/iguanademos.com\/Jare\/wp\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/iguanademos.com\/Jare\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2448"}],"version-history":[{"count":3,"href":"https:\/\/iguanademos.com\/Jare\/wp\/index.php?rest_route=\/wp\/v2\/pages\/2448\/revisions"}],"predecessor-version":[{"id":2451,"href":"https:\/\/iguanademos.com\/Jare\/wp\/index.php?rest_route=\/wp\/v2\/pages\/2448\/revisions\/2451"}],"up":[{"embeddable":true,"href":"https:\/\/iguanademos.com\/Jare\/wp\/index.php?rest_route=\/wp\/v2\/pages\/2432"}],"wp:attachment":[{"href":"https:\/\/iguanademos.com\/Jare\/wp\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2448"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}