Well, it seems the String::formatted is still broken by last change to git, so I propose this class, providing the main advantages I see to printf like functions:
[list]
[]String format not linked to the data to format[/]
[]Stateless format specifications for argument[/]
[]Translatable format with no source code modification[/][/list]
Here it is, that’s advanced pseudo code, since I’m not allowed to provide my code for this:
// Could be a inner class of String, so it could be used as String::Format() in the code
class Format
{
public:
enum Justification
{
Left,
Centered,
Right,
};
enum Type
{
Integer,
VoidPointer,
String,
FloatingPoint,
FloatingPointScientific,
};
private:
struct FormatList
{
const Type type;
const int position;
const int lengthSpecifier;
const int precision;
const Justification justification;
const int base;
FormatList(const Type type, const int position, const int lengthSpecifier = -1, const int precision = -1, const Justification justification = Center, const int base = 10) :
type(type), position(position), lengthSpecifier(lengthSpecifier), precision(precision), justification(justification), base(base) {}
};
// The main formatting logic is here
struct Formatter
{
/** The format to follow */
const FormatList & format;
/** Should be overridden for each type, of course.
If the compiler stops here, you probably need to cast the object you're passing to the
String::Format to a basic type, or provide your own template specialization for your object */
virtual String format() const = 0;
Formatter(const FormatList & format) : format(format) {}
virtual ~Formatter() {}
};
template<class Param>
struct FormatListItem;
OwnedArray<Formatter> arguments;
Array<FormatList> expectedArguments;
StringArray pieces;
String formattedString;
void prepareExpectedList()
{
// Printf like parser here, I can send you the code for this part by email, but you'll need adaptation to the current string class
// Basically, at the end of this function, "expectedArguments" array is filled, and so is "pieces" containing all the pieces between each parameter
};
/** This is used to figure out of position format for the i-th argument */
const FormatList & getExpectedArgumentForPosition(const int position)
{ // Could be improved if the array was sorted by position first
for (int i = 0; i < expectedArguments.size(); i++)
if (expectedArguments.getUnchecked(i).position == position) return expectedArguments.getUnchecked(i);
return expectedArguments[expectedArguments.size()];
}
/** Compute the final formatted string */
void computeString()
{
if (formattedString) return;
// If you use this method, you must provide the number of arguments you've said you will in your format chain.
jassert(pieces.size() == expectedArguments.size() + 1);
for (int i = 0; i < expectedArguments.size(); i++)
formattedString = pieces.getUnchecked(i) + expectedArguments.getUnchecked(i)->format();
formattedString += pieces.getUnchecked(expectedArguments.size());
}
public:
Format(const String & formatString)
{
prepareExpectedList(formatString);
}
~Format() { computeString(); }
template <typename Param>
Format & operator % (Param param)
{
arguments.add(new FormatListItem<Param>(param, getExpectedArgumentsForPosition(arguments.size()));
return *this;
}
operator String() { computeString(); return formattedString; }
};
Then you can use it like this:
// Example sprintf code
sprintf(buffer, "There is %d egg in the basket", eggsCount);
// Equivalent Juce code, with support for internationalization
String ret = String::Format(TRANS("There is %d egg in the basket")) % eggsCount;
// Another harder example, self explicit
String ret = String::Format(TRANS("Pointer %p contains %4d objects : %08X") % objPtr % objPtr->size() % (*objPtr)[0];
// This one show very convinient method for argument position
String blue = TRANS("blue");
String car = TRANS("car");
String ret = String::Format(TRANS("I love the %s %s")) % blue % car; // Translation text could be "J'aime la %2$s %1$s" giving, at runtime the correct: "J'aime la voiture bleue"
This class could prove very useful, is typesafe (that is, you won’t crash if you pass a String instead of a const char*), doesn’t compile if you pass in object that doesn’t match the supported format types (but you can provide your own format if you want, provided you specialize a template)