fork download
  1. #include <string>
  2.  
  3. //Overload or specialize for custom types.
  4. template<typename Type>
  5. std::string ToString(const Type &type)
  6. {
  7. return std::to_string(type);
  8. }
  9.  
  10. //Dummy 'conversion' for arguments that are already strings.
  11. std::string ToString(const std::string &str) { return str; }
  12. std::string ToString(const char *str) { return std::string(str); }
  13. std::string ToString(char c) { return std::string(1, c); }
  14.  
  15. //====================================
  16.  
  17. //Counts (at compile-time) the number of 'decimal places' in an integer (e.g. 17 = 2 symbols, 555 = 3 symbols, 12345 = 5 symbols, 0 = 1 symbol).
  18. template<unsigned int base = 10, unsigned int result = 1>
  19. constexpr unsigned int NumSymbolsAsString(unsigned int value)
  20. {
  21. static_assert(base >= 2, "'base' must be 2 or greater.");
  22. static_assert(result <= 32, "Whatever base you are using, you'll need to add an ending function for it, or it'll compile infinitely.");
  23.  
  24. return ((value < base)? result : NumSymbolsAsString<base, result+1>(value/base));
  25. }
  26.  
  27. //Set a limit on how many functions to generate at compile-time, or it'd compile infinitely.
  28. //A max of 32 for binary, but 10 symbols max for base-10 (4294967295 = 10 chars), and only 8 for base-16 (FFFF-FFFF = 8 symbols).
  29. template<> constexpr unsigned int NumSymbolsAsString<2, 32>(unsigned int value) { return 32; }
  30. template<> constexpr unsigned int NumSymbolsAsString<8, 11>(unsigned int value) { return 11; }
  31. template<> constexpr unsigned int NumSymbolsAsString<10, 10>(unsigned int value) { return 10; }
  32. template<> constexpr unsigned int NumSymbolsAsString<16, 8>(unsigned int value) { return 8; }
  33.  
  34.  
  35. //====================================
  36.  
  37. //Ends the variadic template.
  38. template<unsigned int argIndex>
  39. void priv_Format(std::string &input, char symbol, char escapeSymbol)
  40. {
  41. return;
  42. }
  43.  
  44. template<unsigned int argIndex, typename Arg, typename... Args>
  45. void priv_Format(std::string &input, char symbol, char escapeSymbol, const Arg &arg, Args &&... args)
  46. {
  47. //Check how many chars are needed to represent the number 'argIndex'
  48. unsigned int indexCharSize = NumSymbolsAsString(argIndex);
  49.  
  50. //Convert the argument to a string.
  51. std::string argumentAsText = ToString(arg);
  52.  
  53. //Iterate over the input string.
  54. char prevChar = '\0';
  55. for(size_t i = 0; i < (input.size() - indexCharSize); ++i)
  56. {
  57. //Check for the character 'symbol', as long as it's not escaped.
  58. if(input[i] == symbol && prevChar != escapeSymbol)
  59. {
  60. //Make sure this symbol's number is the same index as our argument.
  61. if(std::stoi(std::string(input, i+1, indexCharSize)) == argIndex)
  62. {
  63. //We're replacing the symbol and the number after it.
  64. size_t replacementSize = (1 + indexCharSize);
  65.  
  66. //Replace the symbol with the argument.
  67. input.replace(i, replacementSize, argumentAsText);
  68.  
  69. //Adjust our iterator to compensate for the removal + insertion.
  70. i -= replacementSize;
  71. i += argumentAsText.size();
  72. }
  73. }
  74.  
  75. prevChar = input[i];
  76. }
  77.  
  78. //Go on to the next argument.
  79. priv_Format<argIndex+1>(input, symbol, escapeSymbol, std::forward<Args>(args)...);
  80. }
  81.  
  82. //Iterates over 'input' replacing every occurance of '%n' with the nth argument.
  83. //
  84. //Speed concerns for the next time I'm bored:
  85. // Iterates over the entire string once per argument.
  86. // Calls std::string::replace (potentially reallocating the string) once for every symbol
  87. // in 'input' that has a matching argument (e.g. once for every %n if an argument actually exists for 'n').
  88. template<typename... Args>
  89. std::string Format(const std::string &input, Args &&... args)
  90. {
  91. std::string output(input);
  92. priv_Format<1>(output, '%', '\\', std::forward<Args>(args)...);
  93.  
  94. return output;
  95. }
  96.  
  97. //=============================
  98. #include <iostream>
  99.  
  100. struct CustomType
  101. {
  102. int x;
  103. int y;
  104. };
  105.  
  106. std::string ToString(const CustomType &type)
  107. {
  108. return std::string("(") + ToString(type.x) + ", " + ToString(type.y) + ")";
  109. }
  110.  
  111. int main(void)
  112. {
  113. std::cout << Format("Test") << std::endl;
  114. std::cout << Format("Today is %2/%1/%3", 9, 4, 2014) << std::endl; //Arguments don't have to be in order (useful for localization).
  115. std::cout << Format("Today is %2 %1, %3", std::string("9th"), "April", 2014) << std::endl; //Mix and match argument types.
  116. std::cout << Format("%1 %2 %3 %4 %3 %2 %1", 'W', 'X', 'Y', 'Z') << std::endl; //Arguments can be repeated in the string.
  117. std::cout << Format("Position: %2 at a distance of %1", 4.057f, CustomType{357,100}) << std::endl; //Support for custom types by overloading ToString().
  118.  
  119. return 0;
  120. }
  121.  
Success #stdin #stdout 0s 3484KB
stdin
Standard input is empty
stdout
Test
Today is 4/9/2014
Today is April 9th, 2014
W X Y Z Y X W
Position: (357, 100) at a distance of 4.057000