KaliVeda  1.12/06
Heavy-Ion Analysis Toolkit
KVParticleCondition.cpp
Go to the documentation of this file.
1 /*
2 $Id: KVParticleCondition.cpp,v 1.4 2007/03/26 10:14:56 franklan Exp $
3 $Revision: 1.4 $
4 $Date: 2007/03/26 10:14:56 $
5 */
6 
7 //Created by KVClassFactory on Thu Nov 16 14:20:38 2006
8 //Author: franklan
9 
10 #include "KVParticleCondition.h"
11 #include "TROOT.h"
12 #include "Riostream.h"
13 #include "TSystem.h"
14 #include "KVClassFactory.h"
15 #include "TPluginManager.h"
16 #include "TUUID.h"
17 #include <utility>
18 
19 using namespace std;
20 
22 
24 
25 
34 
36 {
37  //Set particle condition criteria.
38  //
39  //These must be valid C++ expressions using _NUC_ instead and in place of
40  //a pointer to the particle to be tested. Note that the methods used in the selection
41  //do not have to be limited to the KVNucleus class. The 'real' class of the object
42  //passed to Test() will be used to cast the base (KVNucleus) pointer up to the
43  //required pointer type at execution.
44 
45  Deprecate("Prefer to use lambda functions to define KVParticleCondition objects.");
46 
47  fCondition = cond;
48  Ssiz_t ind = fCondition.Index(";");
49  if (ind < 0) {
50  fCondition_raw = fCondition;
51  fCondition += ";"; //we add a ";" if there isn't already
52  }
53  else {
54  fCondition_raw = fCondition.Strip(TString::kTrailing, ';');
55  }
56  fCondition_brackets = "(" + fCondition_raw + ")";
57 }
58 
59 
60 
79 
81  : KVBase(cond, "KVParticleCondition")
82 {
83  //Create named object and set condition.
84  // This must be a valid C++ expression using `_NUC_` instead and in place of
85  // a `const KVNucleus*` pointer to the particle to be tested, for example
86  // ~~~~{.cpp}
87  // KVParticleCondition c1("_NUC_->GetZ()>2");
88  // KVParticleCondition c2("_NUC_->GetVpar()>0");
89  // ~~~~
90  // Note that the methods used in the selection
91  // do not have to be limited to the methods of the KVNucleus class.
92  // The 'real' class of the object
93  // passed to Test() can be used to cast the base pointer up (or is it down?) to the
94  // required pointer type at execution. In this case, you must call the method
95  // SetParticleClassName() with the name of the class to use in the cast.
96  //
97  // Note that the first call to Test() automatically causes the 'optimization' of the
98  // KVParticleCondition, which means that a class implementing the required condition is generated
99  // and compiled on the fly before continuing (see method Optimize()).
100  fOptimal = nullptr;
101  Set(cond);
102  cf = nullptr;
103  fOptOK = kFALSE;
104  fNUsing = 0;
105 }
106 
107 
108 
127 
129  : KVBase(cond, "KVParticleCondition")
130 {
131  //Create named object and set condition.
132  // This must be a valid C++ expression using `_NUC_` instead and in place of
133  // a `const KVNucleus*` pointer to the particle to be tested, for example
134  // ~~~~{.cpp}
135  // KVParticleCondition c1("_NUC_->GetZ()>2");
136  // KVParticleCondition c2("_NUC_->GetVpar()>0");
137  // ~~~~
138  // Note that the methods used in the selection
139  // do not have to be limited to the methods of the KVNucleus class.
140  // The 'real' class of the object
141  // passed to Test() can be used to cast the base pointer up (or is it down?) to the
142  // required pointer type at execution. In this case, you must call the method
143  // SetParticleClassName() with the name of the class to use in the cast.
144  //
145  // Note that the first call to Test() automatically causes the 'optimization' of the
146  // KVParticleCondition, which means that a class implementing the required condition is generated
147  // and compiled on the fly before continuing (see method Optimize()).
148  fOptimal = nullptr;
149  Set(cond);
150  cf = nullptr;
151  fOptOK = kFALSE;
152  fNUsing = 0;
153 }
154 
155 
156 
159 
161  : KVBase("KVParticleCondition", "Particle selection criteria")
162 {
163  //default ctor
164  fOptimal = nullptr;
165  cf = nullptr;
166  fOptOK = kFALSE;
167  fNUsing = 0;
168 }
169 
170 
171 
174 
176 {
177  //default dtor
178  if (fOptimal) {
179  // do not delete optimized condition unless we are the last to use it
180  --(fOptimal->fNUsing);
181  if (!(fOptimal->fNUsing)) {
183  delete fOptimal;
184  fOptimal = nullptr;
185  }
186  }
187  SafeDelete(cf);
188 }
189 
190 
191 
192 
195 
197 {
198  //Copy this to obj
199  KVBase::Copy(obj);
200  ((KVParticleCondition&) obj).fCondition = fCondition;
201  ((KVParticleCondition&) obj).fCondition_raw = fCondition_raw;
202  ((KVParticleCondition&) obj).fCondition_brackets = fCondition_brackets;
203 #ifdef USING_ROOT6
204  ((KVParticleCondition&) obj).fLambdaCondition = fLambdaCondition;
205  ((KVParticleCondition&) obj).fSavedLambda1 = fSavedLambda1;
206  ((KVParticleCondition&) obj).fSavedLambda2 = fSavedLambda2;
207  ((KVParticleCondition&) obj).fOpType = fOpType;
208 #endif
209  ((KVParticleCondition&) obj).fOptOK = fOptOK;
210  // force first call to Test to try optimization
211  // if existing optimized version exists in static list, pointer will be reset
212  ((KVParticleCondition&) obj).fOptimal = nullptr;
213  if (fClassName != "")((KVParticleCondition&) obj).SetParticleClassName(fClassName.Data());
214  if (cf) {
215  ((KVParticleCondition&) obj).SetClassFactory(cf);
216  }
217 }
218 
219 
220 
221 
225 
227 {
228  //PRIVATE METHOD
229  //Used by Copy
231  CF->Copy(*cf);
232 }
233 
234 
235 
236 
239 
241  : KVBase("KVParticleCondition", "Particle selection criteria")
242 {
243  // Copy constructor. Create new condition which is a copy of existing condition, obj.
244  fOptimal = nullptr;
245  cf = nullptr;
246  fOptOK = kFALSE;
247  fNUsing = 0;
248  obj.Copy(*this);
249 }
250 
251 
252 
253 
256 
258 {
259  // Set condition to be same as for existing KVParticleCondition object
260  if (&obj != this) obj.Copy(*this);
261  return (*this);
262 }
263 
264 
265 
266 
282 
284 {
285  // Set condition using pseudo-code in string (replacing any previous definition).
286  //
287  // This must be a valid C++ expression using `_NUC_` instead and in place of
288  // a `const KVNucleus*` pointer to the particle to be tested, for example
289  // ~~~~{.cpp}
290  // KVParticleCondition c1("_NUC_->GetZ()>2");
291  // KVParticleCondition c2("_NUC_->GetVpar()>0");
292  // ~~~~
293  // Note that the methods used in the selection
294  // do not have to be limited to the methods of the KVNucleus class.
295  // The 'real' class of the object
296  // passed to Test() can be used to cast the base pointer up (or is it down?) to the
297  // required pointer type at execution. In this case, you must call the method
298  // SetParticleClassName() with the name of the class to use in the cast.
299  Set(sel);
300  return (*this);
301 }
302 
303 
304 #ifdef USING_ROOT6
305 //KVParticleCondition::KVParticleCondition(KVParticleCondition&& other) noexcept :
306 // KVBase(other),
307 //#ifdef WITH_CPP14
308 // fNUsing(std::exchange(other.fNUsing, 0)),
309 //#endif
310 // fLambdaCondition(std::move(other.fLambdaCondition)),
311 // fSavedLambda1(std::move(other.fSavedLambda1)),
312 // fSavedLambda2(std::move(other.fSavedLambda2)),
313 // fOpType(other.fOpType),
314 // fCondition(std::move(other.fCondition)),
315 // fCondition_brackets(std::move(other.fCondition_brackets)),
316 //#ifdef WITH_CPP14
317 // fOptimal(std::exchange(other.fOptimal, nullptr)),
318 //#endif
319 // fClassName(std::move(other.fClassName)),
320 //#ifdef WITH_CPP14
321 // cf(std::exchange(other.cf, nullptr)),
322 //#endif
323 // fOptimizedClassName(std::move(other.fOptimizedClassName))
324 //#ifdef WITH_CPP14
325 // , fOptOK(std::exchange(other.fOptOK, false))
326 //#endif
327 //{
328 // // Move constructor
329 //#ifndef WITH_CPP14
330 // fNUsing = other.fNUsing;
331 // other.fNUsing = 0;
332 // fOptimal = other.fOptimal;
333 // other.fOptimal = nullptr;
334 // cf = other.cf;
335 // other.cf = nullptr;
336 // fOptOK = other.fOptOK;
337 // other.fOptOK = false;
338 //#endif
339 //}
340 
341 
344 
346 {
347  // Set condition using lambda expression (replacing any previous definition).
349  return (*this);
350 }
351 
352 
353 //KVParticleCondition& KVParticleCondition::operator=(KVParticleCondition&& other) noexcept
354 //{
355 // // Move assignment operator
356 // KVBase::operator=(other);
357 //#ifdef WITH_CPP14
358 // fNUsing = std::exchange(other.fNUsing, 0);
359 //#else
360 // fNUsing = other.fNUsing;
361 // other.fNUsing = 0;
362 //#endif
363 // fLambdaCondition = std::move(other.fLambdaCondition);
364 // fSavedLambda1 = std::move(other.fSavedLambda1);
365 // fSavedLambda2 = std::move(other.fSavedLambda2);
366 // fOpType = other.fOpType;
367 // fCondition = std::move(other.fCondition);
368 // fCondition_brackets = std::move(other.fCondition_brackets);
369 //#ifdef WITH_CPP14
370 // fOptimal = std::exchange(other.fOptimal, nullptr);
371 //#else
372 // fOptimal = other.fOptimal;
373 // other.fOptimal = nullptr;
374 //#endif
375 // fClassName = std::move(other.fClassName);
376 //#ifdef WITH_CPP14
377 // cf = std::exchange(other.cf, nullptr);
378 //#else
379 // cf = other.cf;
380 // other.cf = nullptr;
381 //#endif
382 // fOptimizedClassName = std::move(other.fOptimizedClassName);
383 //#ifdef WITH_CPP14
384 // fOptOK = std::exchange(other.fOptOK, false);
385 //#else
386 // fOptOK = other.fOptOK;
387 // other.fOptOK = false;
388 //#endif
389 // return (*this);
390 //}
391 #endif
392 
393 
394 
405 
407 {
408  //Perform boolean AND between the two selection conditions
409  //
410  //If SetParticleClassName() has been called for either of the two conditions,
411  //it will be called for the resulting condition with the same value
412  //
413  // Both conditions must be of same type, i.e. if one uses a lambda expression, the other
414  // must also use a lambda expression.
415  //
416  // If one or other of the conditions is not set, we just return the condition which has been set.
417 
418 #ifdef USING_ROOT6
421 #endif
422 
423  if (!(A.IsSet() && B.IsSet())) {
424  // one or both conditions is/are not set
425  if (!(A.IsSet() || B.IsSet())) {
426  // neither is set: return blank (unset) condition
427  return KVParticleCondition();
428  }
429  else if (A.IsSet()) return KVParticleCondition(A);
430  else return KVParticleCondition(B);
431  }
432 #ifdef USING_ROOT6
433  // if lambdas are used (error if not both ?)
434  if (A.IsLambda() || B.IsLambda()) {
435  if (A.IsLambda() && B.IsLambda()) {
440  tmp.SetName(Form("(%s) && (%s)", A.GetName(), B.GetName()));
441  return tmp;
442  }
443  else {
444  Error("operator&&", "Both KVParticleCondition objects must use lambda captures in order to do this");
445  return KVParticleCondition();
446  }
447  }
448 #endif
450  tmp.Set(A.fCondition_brackets + " && " + B.fCondition_brackets);
451  if (A.fClassName != "") tmp.SetParticleClassName(A.fClassName);
452  else if (B.fClassName != "") tmp.SetParticleClassName(B.fClassName);
453  return tmp;
454 }
455 
456 
457 
458 
469 
471 {
472  //Perform boolean OR between the two selection conditions
473  //
474  //If SetParticleClassName has been called for either of the two conditions,
475  //it will be called for the resulting condition with the same value
476  //
477  // Both conditions must be of same type, i.e. if one uses a lambda expression, the other
478  // must also use a lambda expression.
479  //
480  // If one or other of the conditions is not set, we just return the condition which has been set.
481 #ifdef USING_ROOT6
484 #endif
485 
486  if (!(A.IsSet() && B.IsSet())) {
487  // one or both conditions is/are not set
488  if (!(A.IsSet() || B.IsSet())) {
489  // neither is set: return blank (unset) condition
490  return KVParticleCondition();
491  }
492  else if (A.IsSet()) return KVParticleCondition(A);
493  else return KVParticleCondition(B);
494  }
495 #ifdef USING_ROOT6
496  // if lambdas are used (error if not both ?)
497  if (A.IsLambda() || B.IsLambda()) {
498  if (A.IsLambda() && B.IsLambda()) {
503  tmp.SetName(Form("(%s) || (%s)", A.GetName(), B.GetName()));
504  return tmp;
505  }
506  else {
507  Error("operator&&", "Both KVParticleCondition objects must use lambda captures in order to do this");
508  return KVParticleCondition();
509  }
510  }
511 #endif
513  tmp.Set(A.fCondition_brackets + " || " + B.fCondition_brackets);
514  if (A.fClassName != "") tmp.SetParticleClassName(A.fClassName);
515  else if (B.fClassName != "") tmp.SetParticleClassName(B.fClassName);
516  return tmp;
517 }
518 
519 
520 
526 
528 {
529  // Replace current condition with a logical 'OR' between itself and other
530  //
531  // Both conditions must be of same type, i.e. if one uses a lambda expression, the other
532  // must also use a lambda expression.
533 
534  KVParticleCondition tmp = *this || other;
535  tmp.Copy(*this);
536  return *this;
537 }
538 
539 
540 
546 
548 {
549  // Replace current condition with a logical 'AND' between itself and other
550  //
551  // Both conditions must be of same type, i.e. if one uses a lambda expression, the other
552  // must also use a lambda expression.
553 
554  KVParticleCondition tmp = *this && other;
555  tmp.Copy(*this);
556  return *this;
557 }
558 
559 
560 
561 
589 
591 {
592  //Optimisation of KVParticleCondition::Test() implies the automatic generation
593  //of a new class which implements the selection required by the user (see Optimize()).
594  //
595  //If the user's condition depends on objects of classes other than the family
596  //of particle classes (TLorentVector <- KVParticle <- KVNucleus ...) there will
597  //not be by default the necessary '#include' directive for the classes in question
598  //in the generated class; the required plugin for Test() to function will not
599  //load. In this case, the user should call this method with the name of each
600  //'#include' file to be added to the class implementation.
601  //
602  //Example:
603  //~~~~~{.cpp}
604  // KVParticleCondition p("_NUC_->GetVpar()>=gDataAnalyser->GetKinematics()->GetNucleus(1)->GetVpar()");
605  //~~~~~
606  //Optimization will not work, as:
607  // - gDataAnalyser pointer to current analysis manager (KVDataAnalyser object), defined in KVDataAnalyser.h
608  // - gDataAnalyser->GetKinematics() returns a pointer to a KV2Body object, defined in KV2Body.h
609  //
610  //Therefore, for this condition to work, the user must first call the methods :
611  //
612  //~~~~~{.cpp}
613  // p.AddExtraInclude("KVDataAnalyser.h");
614  // p.AddExtraInclude("KV2Body.h");
615  //~~~~~
616  //
617  //before the first call to p.Test() (when optimization occurs).
618 
620  cf->AddImplIncludeFile(inc_file);
621 }
622 
623 
624 
625 
628 
630 {
631  //Initialises KVClassFactory object used for optimization if it doesn't exist
632 
633  if (cf) return;
634 
635  // unique name for new class
636  TUUID unique;
637  KVString new_class = unique.AsString();
638  // only first 8 characters are unique
639  new_class.Remove(8);
640  new_class.Prepend("KVParticleCondition_");
641 
642  //create new class
643  cf = new KVClassFactory(new_class.Data(), "Particle condition to test", "KVParticleCondition");
644  cf->SetInheritAllConstructors(kFALSE); // avoid generating ctor with LambdaFunc argument!!!
645 }
646 
647 
648 
662 
664 {
665  //Generate a new class which inherits from KVParticleCondition but having a Test()
666  //method which tests explicitly the condition which is set by the user.
667  //
668  //If needed, the KVNucleus pointer argument will be upcasted to the type given to SetParticleClassName().
669  //
670  //The new class is added to the list of plugins of type KVParticleCondition,
671  //then an instance of the class is generated and a pointer to it stored in
672  //member KVParticleCondition::fOptimal.
673  //
674  //This object is then used in the Test() method of this object to test the condition.
675  //
676  //If compilation fails, the condition will evaluate to kFALSE for all subsequent calls.
677 
679  if (fOptimal) { /* check that the same condition has not already been optimized */
680  Info("Optimize", "Using existing optimized condition %p", fOptimal);
681  fOptimal->fNUsing++;
682  fOptOK = kTRUE;
683  return;
684  }
685  Info("Optimize", "Optimization of KVParticleCondition : %s", fCondition.Data());
686 
688  KVString created_class_name = cf->GetClassName();
689  //add Test() method
690  cf->AddMethod("optimized_test", "Bool_t", "public", false, true);
691  cf->AddMethodArgument("optimized_test", "const KVNucleus*", "nuc");
692  cf->AddHeaderIncludeFile("KVNucleus.h");
693 
694  //write body of method
695  KVString body(" //Optimized Test method for particle condition\n");
696  KVString pointer = "nuc";
697  if (fClassName != "") {
698  pointer.Form("((%s*)nuc)", fClassName.Data());
699  //upcasting pointer - we need to add corresponding #include to '.cpp' file
701  }
702  KVString tmp;
703  tmp = fCondition;
704  tmp.ReplaceAll("_NUC_", pointer.Data());
705  body += " return ";
706  body += tmp;
707 
708  cf->AddMethodBody("optimized_test", body);
709 
710  //generate .cpp and .h for new class
711  cf->GenerateCode();
712 
713  //add plugin for new class
714  gROOT->GetPluginManager()->AddHandler("KVParticleCondition", cf->GetClassName(), cf->GetClassName(),
715  Form("%s+", cf->GetImpFileName()), Form("%s()", cf->GetClassName()));
716  //load plugin
717  TPluginHandler* ph;
718  if (!(ph = LoadPlugin("KVParticleCondition", cf->GetClassName()))) {
719  Error("Optimize", " *** Optimization failed for KVParticleCondition : %s", fCondition.Data());
720  Error("Optimize", " *** Use method AddExtraInclude(const Char_t*) to give the names of all necessary header files for compilation of your condition.");
721  Fatal("Optimize", " *** THIS CONDITION WILL BE EVALUATED AS kFALSE FOR ALL PARTICLES!!!");
722  delete cf;
723  cf = 0;
724  //we set fOptimal to a non-zero value to avoid calling Optimize
725  //every time that Test() is called subsequently.
726  fOptimal = this;
727  fOptOK = kFALSE;
728  return;
729  }
730  fOptOK = kTRUE;
731  delete cf;
732  cf = 0;
733  //execute constructor
735 
736  Info("Optimize", "fOptimal = %p", fOptimal);
737  if (!fOptimal) {
738  Error("Optimize", " *** Optimization failed for KVParticleCondition : %s", fCondition.Data());
739  Error("Optimize", " *** Use method AddExtraInclude(const Char_t*) to give the names of all necessary header files for compilation of your condition.");
740  Fatal("Optimize", " *** THIS CONDITION WILL BE EVALUATED AS kFALSE FOR ALL PARTICLES!!!");
741  //we set fOptimal to a non-zero value to avoid calling Optimize
742  //every time that Test() is called subsequently.
743  fOptimal = this;
744  fOptOK = kFALSE;
745  }
746  // add to list of optimized conditions
747  const_cast<KVParticleCondition*>(fOptimal)->SetName(GetName());
748  const_cast<KVParticleCondition*>(fOptimal)->fOptimizedClassName = created_class_name;
750  fOptimal->fNUsing++;
751  Info("Optimize", "Success");
752 }
753 
754 
755 
758 
760 {
761  //Print informations on object
762  if (fCondition != "") {
763  Info("Print", "object name = %s, address = %p", GetName(), this);
764  cout << " * condition = " << fCondition.Data() << endl;
765  cout << " * classname = " << fClassName.Data() << endl;
766  cout << " * fOptimal = " << fOptimal << endl;
767  cout << " * fNUsing = " << fNUsing << endl;
768  if (cf) {
769  cout << " * classfactory :" << endl;
770  cf->Print();
771  }
772  }
773  else {
774  cout << GetName() << endl;
775  }
776 }
777 
778 
779 
KVParticleCondition operator||(const KVParticleCondition &A, const KVParticleCondition &B)
KVParticleCondition operator&&(const KVParticleCondition &A, const KVParticleCondition &B)
ClassImp(KVPartitionList) void KVPartitionList
Initialisation.
#define SafeDelete(p)
#define f(i)
int Ssiz_t
char Char_t
const Bool_t kFALSE
const Bool_t kTRUE
const char Option_t
#define gROOT
char * Form(const char *fmt,...)
Base class for KaliVeda framework.
Definition: KVBase.h:135
virtual void Copy(TObject &) const
Make a copy of this object.
Definition: KVBase.cpp:397
static TPluginHandler * LoadPlugin(const Char_t *base, const Char_t *uri="0")
Definition: KVBase.cpp:756
Factory class for generating skeleton files for new classes.
void Print(Option_t *opt="") const
Print infos on object.
void GenerateCode()
Generate header and implementation file for currently-defined class.
void SetInheritAllConstructors(Bool_t yes=kTRUE)
const Char_t * GetImpFileName() const
void AddHeaderIncludeFile(const Char_t *filename)
void Copy(TObject &obj) const
Copy the state of this KVClassFactory to the one referenced by 'obj'.
void AddImplIncludeFile(const Char_t *filename)
const Char_t * GetClassName() const
void AddMethodBody(const Char_t *method_name, const KVString &body)
void AddMethodArgument(const Char_t *method_name, const Char_t *argument_type, const Char_t *argument_name="", const Char_t *default_value="")
KVClassMethod * AddMethod(const Char_t *name, const Char_t *return_type, const Char_t *access="public", Bool_t isVirtual=kFALSE, Bool_t isConst=kFALSE)
Extended version of ROOT THashList.
Definition: KVHashList.h:28
Handles particle selection criteria for data analysis classes ,.
LambdaFunc fSavedLambda2
used by || and &&
virtual ~KVParticleCondition()
default dtor
static KVHashList fgOptimized
list of optimized particle conditions
Bool_t fOptOK
false if optimisation failed (can't load generated code)
void logical_operator_lambda_condition_test() const
void CreateClassFactory() const
Initialises KVClassFactory object used for optimization if it doesn't exist.
KVClassFactory * cf
used to generate code for optimisation
Int_t fNUsing
number of classes using this as an optimized condition
void AddExtraInclude(const Char_t *inc_file)
KVParticleCondition & operator|=(const KVParticleCondition &)
enum KVParticleCondition::LogOp fOpType
KVParticleCondition()
default ctor
KVString fOptimizedClassName
name of generated class used for optimisation
KVParticleCondition & operator&=(const KVParticleCondition &)
KVString fCondition
string containing selection criteria with ";" at end
void Copy(TObject &) const
Copy this to obj.
KVString fCondition_raw
'raw' condition, i.e. no ';'
void SetClassFactory(KVClassFactory *CF)
void SetParticleClassName(const Char_t *cl)
KVParticleCondition & operator=(const KVParticleCondition &)
Set condition to be same as for existing KVParticleCondition object.
std::function< bool(const KVNucleus *)> LambdaFunc
KVString fCondition_brackets
condition with '(' and ')' around it
void Set(const KVString &name, const LambdaFunc &F)
const KVParticleCondition * fOptimal
void Print(Option_t *opt="") const
Print informations on object.
virtual void Add(TObject *obj)
virtual TObject * Remove(TObject *obj)
Remove object from list.
virtual TObject * FindObject(const char *name) const
Extension of ROOT TString class which allows backwards compatibility with ROOT v3....
Definition: KVString.h:72
virtual const char * GetName() const
virtual void SetName(const char *name)
virtual void Error(const char *method, const char *msgfmt,...) const
virtual void Fatal(const char *method, const char *msgfmt,...) const
virtual void Info(const char *method, const char *msgfmt,...) const
Longptr_t ExecPlugin(int nargs, const T &... params)
const char * Data() const
TString & Prepend(char c, Ssiz_t rep=1)
void Form(const char *fmt,...)
TString & Remove(EStripType s, char c)
TString & ReplaceAll(const char *s1, const char *s2)
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
const char * AsString() const
void Error(const char *location, const char *va_(fmt),...)