Question on TString::Form()

Dear ROOTers,

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.

Regards,
Simon

1 Like

Hi Simon,

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 :slight_smile:

Cheers,
Kim

Hi Kim,

Thank you for your answer. Looking at the TString header, it seems that Form is even defined as a global function (not a static member of TString).

extern char *Form(const char *fmt, ...)

It could be useful to mention it in the documentation of either TString::Form or TString::Format.

Cheers,
Simon

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()));

Cheers,
Philippe.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.