Given that TString::Form(const char* fmt, ...) is not a static function and has return type void, I don’t understand how code like this does not break and actually produces the formatted output:
#include <iostream>
#include "TString.h"
void testTString(){
int i = 1;
std::string s(Form("Test %d", i));
std::cout << s << std::endl;
}
The code above does run and outputs “Test 1”, both in interpreted and compiled modes (with ROOT 5 and 6, using clang or gcc). The syntax std::string s = Form("Test %d", i); or const char* cs = Form("Test %d", i); also work.
This really puzzles me. It seems like my understanding of the C++ language is the problem here.
I wonder if the above syntax is safe, as using it instead of std::string s = TString::Format( ... ).Data() would produce more concise and readable code.
There are multiple functions called TString::Form, one which is an member method and one which is a static method. The member method does indeed have a return type of void since it modifies the TString e.g
TString str ("");
str.Form("Some format string");
The second, static method, is the one that is called in your example. It has a return type of char * and is used as TString str = Form("Some string");
Unfortunately the documentation for the latter seems to be missing from the reference documentation. A sibling function TString::Format is available and is very similar to the TString::Form function; They differ only in return type.
You can check TString.h if you want to go to the bottom of this yourself
To summarize. There are 4 similar functions. 2 are member functions of the class TString and 2 are in the global namespace.
TString::Form : static function returns a temporary TString object
TString::Format : member function modifying a TString object
::Form, ::Format : different in argument only, return a const char* that is inside a thread local character buffer.
The usage of TString::Form, ::Form and ::Format is similar. Using the later two, you need to make sure that the result is not being passed to a function that itself will call one of these two functions as it would ‘overwrite’ the buffer while it is being used. These two functions date back to a time when creating temporary object and especially copying them (i.e. no move semantic) was very expansive.
Nowadays the recommendation is simple, just use TString::Form or TString::Format. For example:
TString formatted;
formatted.Form("%s in <%s>: %s", type, location, msg);
lines.emplace_back(TString::Format("Welcome to ROOT %s%%shttp://root.cern.ch",
gROOT->GetVersion()));