I leave my solution here if someone comes across this thread and is still interested in an answer to the original question:
template <typename... ArgumentTypes>
FString MyPrintf(const FString& Format, ArgumentTypes... Args)
{
// Note that for some reason the format parameter of 'FString::Printf' does not work with other variables only with
// char[] literals. This is enforced by static assertions. We can use this static buffer though to copy the pattern
// over. Not sure what the rationale behind this limitation is.
//
static TCHAR FormatBuffer[128];
// Make sure to check that the format string is not longer than the buffer.
// We use '<' instead of '<=' because 'Len' does not include the '\0' char. See implementation of 'FString::Len()'.
if (!ensureMsgf(Format.Len() < UE_ARRAY_COUNT(FormatBuffer),
TEXT("Format string '%s' (length %d) exceeds the maximum size of %d"), *Format, Format.Len(),
UE_ARRAY_COUNT(FormatBuffer)))
{
return FString();
}
// Copy the format string to the buffer.
TCString<TCHAR>::Strcpy<UE_ARRAY_COUNT(FormatBuffer)>(FormatBuffer, *Format);
return FString::Printf(FormatBuffer, Args...);
}
Note that this function requires a constant maximum length of the format string. In my case 128. If you have longer format strings you need to use a larger buffer.
Example usage:
FColor Color = FColor::Cyan;
FString FormatString = TEXT("(R=%d,G=%d,B=%d,A=%d)");
FString Result = MyPrintf(FormatString, Color.R, Color.G, Color.B, Color.A);