Logo ROOT  
Reference Guide
TClingBaseClassInfo.cxx
Go to the documentation of this file.
1// @(#)root/core/meta:$Id$
2// Author: Paul Russo 30/07/2012
3
4/*************************************************************************
5 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12/** \class TClingBaseClassInfo
13
14Emulation of the CINT BaseClassInfo class.
15
16The CINT C++ interpreter provides an interface to metadata about
17the base classes of a class through the BaseClassInfo class. This
18class provides the same functionality, using an interface as close
19as possible to BaseClassInfo but the base class metadata comes from
20the Clang C++ compiler, not CINT.
21*/
22
23#include "TClingBaseClassInfo.h"
24
25#include "TClingClassInfo.h"
26#include "TDictionary.h"
27#include "TClingUtils.h"
28
29#include "TError.h"
30
31#include "cling/Interpreter/Interpreter.h"
32#include "cling/Interpreter/Transaction.h"
33
34
35#include "clang/AST/ASTContext.h"
36#include "clang/AST/Decl.h"
37#include "clang/AST/DeclCXX.h"
38#include "clang/AST/PrettyPrinter.h"
39#include "clang/AST/RecordLayout.h"
40#include "clang/AST/Type.h"
41#include "clang/AST/CXXInheritance.h"
42
43
44#include "llvm/Support/Casting.h"
45#include "llvm/Support/raw_ostream.h"
46#include "llvm/ExecutionEngine/ExecutionEngine.h"
47#include "llvm/IR/Module.h"
48
49#include <string>
50#include <sstream>
51
52using namespace llvm;
53using namespace clang;
54using namespace std;
55
58 : fInterp(interp), fClassInfo(0), fFirstTime(true), fDescend(false),
59 fDecl(0), fIter(0), fBaseInfo(0), fOffset(0L), fClassInfoOwnership(true)
60{
61 // Constructs a base class iterator on ci; ci == 0 means global scope (which
62 // is meaningless). The derived class info passed in as ci is copied.
63 if (!ci) {
64 fClassInfo = new TClingClassInfo(interp);
65 return;
66 }
67 fClassInfo = new TClingClassInfo(*ci);
68 if (!fClassInfo->GetDecl()) {
69 return;
70 }
71 const clang::CXXRecordDecl* CRD =
72 llvm::dyn_cast<clang::CXXRecordDecl>(fClassInfo->GetDecl());
73 if (!CRD) {
74 // We were initialized with something that is not a class.
75 // FIXME: We should prevent this from happening!
76 return;
77 }
78 fDecl = CRD;
79 {
80 // In particular if the base are templated, this might deserialize.
81 cling::Interpreter::PushTransactionRAII RAII(fInterp);
82 fIter = CRD->bases_begin();
83 }
84}
85
87 TClingClassInfo* derived,
88 TClingClassInfo* base)
89 : fInterp(interp), fClassInfo(0), fFirstTime(true), fDescend(false),
90 fDecl(0), fIter(0), fBaseInfo(0), fOffset(0L), fClassInfoOwnership(false)
91{
92 // Constructs a single base class base (no iterator) of derived; derived must be != 0.
93 // The derived class info is referenced during the lifetime of the TClingBaseClassInfo.
94 if (!derived->GetDecl()) {
95 return;
96 }
97 const clang::CXXRecordDecl* CRD =
98 llvm::dyn_cast<clang::CXXRecordDecl>(derived->GetDecl());
99 const clang::CXXRecordDecl* BaseCRD =
100 llvm::dyn_cast<clang::CXXRecordDecl>(base->GetDecl());
101 if (!CRD || !BaseCRD) {
102 // We were initialized with something that is not a class.
103 // FIXME: We should prevent this from happening!
104 return;
105 }
106
107 fClassInfo = derived;
108 fDecl = CRD;
109 //CRD->isDerivedFrom(BaseCRD, Paths);
110 // Check that base derives from derived.
111 clang::CXXBasePaths Paths;
112
113 // CXXRecordDecl::isDerivedFrom can trigger deserialization.
114 cling::Interpreter::PushTransactionRAII RAII(fInterp);
115
116 if (!CRD->isDerivedFrom(BaseCRD, Paths)) {
117 //Not valid fBaseInfo = 0.
118 return;
119 }
120
121 fBaseInfo = new TClingClassInfo(*base);
122 fIter = CRD->bases_end();
123}
124
126 : fInterp(rhs.fInterp), fClassInfo(0), fFirstTime(rhs.fFirstTime),
127 fDescend(rhs.fDescend), fDecl(rhs.fDecl), fIter(rhs.fIter), fBaseInfo(0),
128 fIterStack(rhs.fIterStack), fOffset(rhs.fOffset), fClassInfoOwnership(true)
129{
130 // Copies a base class info including the base and derived class infos.
133}
134
136 const TClingBaseClassInfo& rhs)
137{
138 if (this != &rhs) {
139 fInterp = rhs.fInterp;
141 delete fClassInfo;
144 fDescend = rhs.fDescend;
145 fDecl = rhs.fDecl;
146 fIter = rhs.fIter;
147 delete fBaseInfo;
150 fOffset = rhs.fOffset;
151 fClassInfoOwnership = true;
152 }
153 return *this;
154}
155
157{
158 if (!IsValid()) {
159 return 0;
160 }
161 return fBaseInfo;
162}
163
166 TClingClassInfo* toBaseClass,
167 void* address, bool isDerivedObject) const
168{
169 // Generate a function at run-time that would calculate the offset
170 // from the parameter derived class to the parameter toBase class for the
171 // address.
172
173 // rootcling can trigger this, too, and without CodeGen we cannot use any
174 // offset calculation function.
175 if (fInterp->isInSyntaxOnlyMode())
176 return 0;
177
178 // Get the dedcls for the two classes.
179 const clang::RecordDecl* fromDerivedDecl
180 = dyn_cast<clang::RecordDecl>(fromDerivedClass->GetDecl());
181 if (!fromDerivedDecl) {
182 ::Error("TClingBaseClassInfo::GenerateBaseOffsetFunction",
183 "Offset of non-class %s is ill-defined!", fromDerivedClass->Name());
184 return 0;
185 }
186 const clang::RecordDecl* toBaseDecl
187 = dyn_cast<clang::RecordDecl>(toBaseClass->GetDecl());
188 if (!toBaseDecl) {
189 ::Error("TClingBaseClassInfo::GenerateBaseOffsetFunction",
190 "Offset of non-class %s is ill-defined!", toBaseClass->Name());
191 return 0;
192 }
193
194 // Make the wrapper name.
195 string wrapper_name;
196 {
197 ostringstream buf;
198 buf << "h" << fromDerivedDecl;
199 buf << '_';
200 buf << "h" << toBaseDecl;
201 wrapper_name = buf.str();
202 }
203 string code;
204 // Check whether the function was already generated.
205 if (!fInterp->getAddressOfGlobal(wrapper_name)) {
206 // Get the class or namespace name.
207 string fromDerivedClassName;
208 clang::QualType QTDerived(fromDerivedClass->GetType(), 0);
210 QTDerived, *fInterp);
211 string toBase_class_name;
212 clang::QualType QTtoBase(toBaseClass->GetType(), 0);
214 QTtoBase, *fInterp);
215 // Write the wrapper code.
216 llvm::raw_string_ostream buf(code);
217 buf << "extern \"C\" long " + wrapper_name + "(void* address, bool isDerivedObject) {\n"
218 // If the object is not derived, will downcast to toBase first.
219 << " " << fromDerivedClassName << " *fromDerived;"
220 << " if (isDerivedObject) {"
221 << " fromDerived = (" << fromDerivedClassName << "*)address;\n"
222 << " } else {\n"
223 << " fromDerived = dynamic_cast<" << fromDerivedClassName << "*>((" << toBase_class_name << "*)address);\n"
224 << " }\n"
225 << " if (!fromDerived) {\n"
226 << " return -1; \n"
227 << " }\n"
228 << " " << toBase_class_name << " *toBase = fromDerived;\n"
229 << " return ((long)toBase - (long)fromDerived);\n}\n";
230 }
231
232 // If we have a GV then compileFunction will use it; empty code is enough.
233 void* f = fInterp->compileFunction(wrapper_name, code, true /*ifUnique*/,
234 false /*withAccessControl*/);
235 if (!f) {
236 ::Error("TClingBaseClassInfo::GenerateBaseOffsetFunction",
237 "Compilation failed!");
238 return 0;
239 }
240
241 return (OffsetPtrFunc_t) f;
242}
243
245{
246 return
247 // inited with a valid class, and
248 fClassInfo->IsValid() &&
249 // the base class we are iterating over is valid, and
250 fDecl &&
251 // our current base has a TClingClassInfo, and
252 fBaseInfo &&
253 // our current base is a valid class
255}
256
258{
259 // Exit early if the iterator is already invalid.
260 if (!fDecl || !fIter ||
261 (fIter == llvm::dyn_cast<clang::CXXRecordDecl>(fDecl)->bases_end())) {
262 return 0;
263 }
264
265 // Advance to the next valid base.
266 while (1) {
267 // Advance the iterator.
268 if (fFirstTime) {
269 // The cint semantics are strange.
270 fFirstTime = false;
271 }
272 else if (!onlyDirect && fDescend) {
273 // We previously processed a base class which itself has bases,
274 // now we process the bases of that base class.
275
276 // At least getASTRecordLayout() might deserialize.
277 cling::Interpreter::PushTransactionRAII RAII(fInterp);
278 fDescend = false;
279 const clang::RecordType *Ty = fIter->getType()->
280 getAs<clang::RecordType>();
281 // Note: We made sure this would work when we selected the
282 // base for processing.
283 clang::CXXRecordDecl *Base = llvm::cast<clang::CXXRecordDecl>(
284 Ty->getDecl()->getDefinition());
285 clang::ASTContext &Context = Base->getASTContext();
286 const clang::RecordDecl *RD = llvm::dyn_cast<clang::RecordDecl>(fDecl);
287 const clang::ASTRecordLayout &Layout = Context.getASTRecordLayout(RD);
288 int64_t offset = Layout.getBaseClassOffset(Base).getQuantity();
289 fOffset += static_cast<long>(offset);
290 fIterStack.push_back(std::make_pair(std::make_pair(fDecl, fIter),
291 static_cast<long>(offset)));
292 fDecl = Base;
293 fIter = Base->bases_begin();
294 }
295 else {
296 // Simple case, move on to the next base class specifier.
297 ++fIter;
298 }
299 // Fix it if we went past the end.
300 while (
301 (fIter == llvm::dyn_cast<clang::CXXRecordDecl>(fDecl)->bases_end()) &&
302 fIterStack.size()
303 ) {
304 // All done with this base class.
305 fDecl = fIterStack.back().first.first;
306 fIter = fIterStack.back().first.second;
307 fOffset -= fIterStack.back().second;
308 fIterStack.pop_back();
309 ++fIter;
310 }
311 // Check for final termination.
312 if (fIter == llvm::dyn_cast<clang::CXXRecordDecl>(fDecl)->bases_end()) {
313 // We have reached the end of the direct bases, all done.
314 delete fBaseInfo;
315 fBaseInfo = 0;
316 // Iterator is now invalid.
317 return 0;
318 }
319 // Check if current base class is a dependent type, that is, an
320 // uninstantiated template class.
321 const clang::TagType *Ty = fIter->getType()->getAs<clang::TagType>();
322 if (!Ty) {
323 // A dependent type (uninstantiated template), skip it.
324 continue;
325 }
326 // Check if current base class has a definition.
327 const clang::CXXRecordDecl *Base =
328 llvm::cast_or_null<clang::CXXRecordDecl>(Ty->getDecl()->
329 getDefinition());
330 if (!Base) {
331 // No definition yet (just forward declared), skip it.
332 continue;
333 }
334 // Now that we are going to return this base, check to see if
335 // we need to examine its bases next call.
336 if (!onlyDirect && Base->getNumBases()) {
337 fDescend = true;
338 }
339 // Update info for this base class.
340 delete fBaseInfo;
341 clang::QualType bType = ROOT::TMetaUtils::ReSubstTemplateArg(fIter->getType(),fClassInfo->GetType());
342 fBaseInfo = new TClingClassInfo(fInterp, *bType);
343 // Iterator is now valid.
344 return 1;
345 }
346}
347
348int TClingBaseClassInfo::Next(int onlyDirect)
349{
350 return InternalNext(onlyDirect);
351}
352
354{
355 return Next(1);
356}
357
358// This function is updating original one on http://clang.llvm.org/doxygen/CGExprCXX_8cpp_source.html#l01647
359// To fit the needs.
360static clang::CharUnits computeOffsetHint(clang::ASTContext &Context,
361 const clang::CXXRecordDecl *Src,
362 const clang::CXXRecordDecl *Dst,
363 cling::Interpreter* interp)
364{
365 clang::CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
366 /*DetectVirtual=*/false);
367
368 // If Dst is not derived from Src we can skip the whole computation below and
369 // return that Src is not a public base of Dst. Record all inheritance paths.
370 if (!Dst->isDerivedFrom(Src, Paths))
371 return clang::CharUnits::fromQuantity(-2);
372
373 unsigned NumPublicPaths = 0;
374 clang::CharUnits Offset;
375
376 // Now walk all possible inheritance paths.
377 for (clang::CXXBasePaths::paths_iterator I = Paths.begin(), E = Paths.end();
378 I != E; ++I) {
379
380 ++NumPublicPaths;
381
382 for (clang::CXXBasePath::iterator J = I->begin(), JE = I->end(); J != JE; ++J) {
383 // If the path contains a virtual base class we can't give any hint.
384 // -1: no hint.
385 if (J->Base->isVirtual())
386 return clang::CharUnits::fromQuantity(-1);
387
388 if (NumPublicPaths > 1) // Won't use offsets, skip computation.
389 continue;
390
391 // Accumulate the base class offsets.
392 cling::Interpreter::PushTransactionRAII RAII(interp);
393 const clang::ASTRecordLayout &L = Context.getASTRecordLayout(J->Class);
394 Offset += L.getBaseClassOffset(J->Base->getType()->getAsCXXRecordDecl());
395 }
396 }
397
398 // -2: Src is not a public base of Dst.
399 if (NumPublicPaths == 0)
400 return clang::CharUnits::fromQuantity(-2);
401
402 // -3: Src is a multiple public base type but never a virtual base type.
403 if (NumPublicPaths > 1)
404 return clang::CharUnits::fromQuantity(-3);
405
406 // Otherwise, the Src type is a unique public nonvirtual base type of Dst.
407 // Return the offset of Src from the origin of Dst.
408 return Offset;
409 }
410
411ptrdiff_t TClingBaseClassInfo::Offset(void * address, bool isDerivedObject) const
412{
413 // Compute the offset of the derived class to the base class.
414
415 if (!IsValid()) {
416 return -1;
417 }
418 // Check if current base class has a definition.
419 const clang::CXXRecordDecl* Base =
420 llvm::cast_or_null<clang::CXXRecordDecl>(fBaseInfo->GetDecl());
421 if (!Base) {
422 // No definition yet (just forward declared), invalid.
423 return -1;
424 }
425 // If the base class has no virtual inheritance.
426 if (!(Property() & kIsVirtualBase)) {
427 clang::ASTContext& Context = Base->getASTContext();
428 const clang::CXXRecordDecl* RD = llvm::dyn_cast<clang::CXXRecordDecl>(fDecl);
429 if (!RD) {
430 // No RecordDecl for the class.
431 return -1;
432 }
433 long clang_val = computeOffsetHint(Context, Base, RD, fInterp).getQuantity();
434 if (clang_val == -2 || clang_val == -3) {
435 TString baseName;
436 TString derivedName;
437 {
438 // Need TNormalizedCtxt otherwise...
439 // Note: should we really be issuing a message here? Shouldn't
440 // the caller check and issue the message?
441 std::string buf;
442 PrintingPolicy Policy(fBaseInfo->GetDecl()->getASTContext().
443 getPrintingPolicy());
444 llvm::raw_string_ostream stream(buf);
445 ((const clang::NamedDecl*)fBaseInfo->GetDecl())
446 ->getNameForDiagnostic(stream, Policy, /*Qualified=*/true);
447 stream.flush();
448 baseName = buf;
449
450 buf.clear();
451 ((const clang::NamedDecl*)fClassInfo->GetDecl())
452 ->getNameForDiagnostic(stream, Policy, /*Qualified=*/true);
453 stream.flush();
454 derivedName = buf;
455 }
456 if (clang_val == -2) {
457 ::Error("TClingBaseClassInfo::Offset",
458 "The class %s does not derive from the base %s.",
459 derivedName.Data(), baseName.Data());
460 } else {
461 // clang_val == -3
462 ::Error("TClingBaseClassInfo::Offset",
463 "There are multiple paths from derived class %s to base class %s.",
464 derivedName.Data(), baseName.Data());
465 }
466 clang_val = -1;
467 }
469 return clang_val;
470 }
471 // Verify the address of the instantiated object
472 if (!address) {
473 ::Error("TClingBaseClassInfo::Offset", "The address of the object for virtual base offset calculation is not valid.");
474 return -1;
475 }
476
477 // Virtual inheritance case
478 OffsetPtrFunc_t executableFunc = GenerateBaseOffsetFunction(fClassInfo, fBaseInfo, address, isDerivedObject);
479 if (executableFunc) {
481 return (*executableFunc)(address, isDerivedObject);
482 }
483
484 return -1;
485}
486
487
489{
490 if (!IsValid()) {
491 return 0L;
492 }
493 long property = 0L;
494
495 if (fDecl == fClassInfo->GetDecl()) {
496 property |= kIsDirectInherit;
497 }
498
499 const clang::CXXRecordDecl* CRD
500 = llvm::dyn_cast<CXXRecordDecl>(fDecl);
501 const clang::CXXRecordDecl* BaseCRD
502 = llvm::dyn_cast<CXXRecordDecl>(fBaseInfo->GetDecl());
503 if (!CRD || !BaseCRD) {
504 ::Error("TClingBaseClassInfo::Property",
505 "The derived class or the base class do not have a CXXRecordDecl.");
506 return property;
507 }
508
509 clang::CXXBasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/true,
510 /*DetectVirtual=*/true);
511 if (!CRD->isDerivedFrom(BaseCRD, Paths)) {
512 // Error really unexpected here, because construction / iteration guarantees
513 //inheritance;
514 ::Error("TClingBaseClassInfo", "Class not derived from given base.");
515 }
516 if (Paths.getDetectedVirtual()) {
517 property |= kIsVirtualBase;
518 }
519
520 clang::AccessSpecifier AS = clang::AS_public;
521 // Derived: public Mid; Mid : protected Base: Derived inherits protected Base?
522 for (clang::CXXBasePaths::const_paths_iterator IB = Paths.begin(), EB = Paths.end();
523 AS != clang::AS_private && IB != EB; ++IB) {
524 switch (IB->Access) {
525 // keep AS unchanged?
526 case clang::AS_public: break;
527 case clang::AS_protected: AS = clang::AS_protected; break;
528 case clang::AS_private: AS = clang::AS_private; break;
529 case clang::AS_none: break;
530 }
531 }
532 switch (AS) {
533 case clang::AS_public:
534 property |= kIsPublic;
535 break;
536 case clang::AS_protected:
537 property |= kIsProtected;
538 break;
539 case clang::AS_private:
540 property |= kIsPrivate;
541 break;
542 case clang::AS_none:
543 // IMPOSSIBLE
544 break;
545 }
546 return property;
547}
548
550{
551 if (!IsValid()) {
552 return -1L;
553 }
554 return fBaseInfo->Tagnum();
555}
556
558{
559 if (!IsValid()) {
560 output.clear();
561 return;
562 }
563 fBaseInfo->FullName(output,normCtxt);
564}
565
566const char* TClingBaseClassInfo::Name() const
567{
568 if (!IsValid()) {
569 return 0;
570 }
571 return fBaseInfo->Name();
572}
573
575{
576 if (!IsValid()) {
577 return 0;
578 }
579 return fBaseInfo->TmpltName();
580}
581
#define f(i)
Definition: RSha256.hxx:104
static clang::CharUnits computeOffsetHint(clang::ASTContext &Context, const clang::CXXRecordDecl *Src, const clang::CXXRecordDecl *Dst, cling::Interpreter *interp)
ptrdiff_t(* OffsetPtrFunc_t)(void *, bool)
@ kIsPublic
Definition: TDictionary.h:75
@ kIsPrivate
Definition: TDictionary.h:77
@ kIsProtected
Definition: TDictionary.h:76
@ kIsVirtualBase
Definition: TDictionary.h:89
@ kIsDirectInherit
Definition: TDictionary.h:83
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition: TError.cxx:187
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t property
Emulation of the CINT BaseClassInfo class.
const char * TmpltName() const
const char * Name() const
const clang::Decl * fDecl
ptrdiff_t Offset(void *address=0, bool isDerivedObject=true) const
TClingClassInfo * fBaseInfo
TClingClassInfo * fClassInfo
OffsetPtrFunc_t GenerateBaseOffsetFunction(TClingClassInfo *derivedClass, TClingClassInfo *targetClass, void *address, bool isDerivedObject) const
TClingBaseClassInfo(cling::Interpreter *, TClingClassInfo *)
void FullName(std::string &output, const ROOT::TMetaUtils::TNormalizedCtxt &normCtxt) const
TClingBaseClassInfo & operator=(const TClingBaseClassInfo &)
cling::Interpreter * fInterp
std::vector< std::pair< std::pair< const clang::Decl *, clang::CXXRecordDecl::base_class_const_iterator >, long > > fIterStack
int InternalNext(int onlyDirect)
clang::CXXRecordDecl::base_class_const_iterator fIter
TClingClassInfo * GetBase() const
Emulation of the CINT ClassInfo class.
void FullName(std::string &output, const ROOT::TMetaUtils::TNormalizedCtxt &normCtxt) const
const char * TmpltName() const
void AddBaseOffsetValue(const clang::Decl *decl, ptrdiff_t offset)
const clang::Type * GetType() const
Longptr_t Tagnum() const
void AddBaseOffsetFunction(const clang::Decl *decl, OffsetPtrFunc_t func)
virtual const char * Name() const
virtual bool IsValid() const
virtual const clang::Decl * GetDecl() const
Basic string class.
Definition: TString.h:136
const char * Data() const
Definition: TString.h:369
RooCmdArg Layout(double xmin, double xmax=0.99, double ymin=0.95)
RooCmdArg Offset(bool flag=true)
#define I(x, y, z)
#define Dst
Definition: mesh.h:158
clang::QualType ReSubstTemplateArg(clang::QualType input, const clang::Type *instance)
Check if 'input' or any of its template parameter was substituted when instantiating the class templa...
void GetFullyQualifiedTypeName(std::string &name, const clang::QualType &type, const cling::Interpreter &interpreter)
static constexpr double L
constexpr Double_t E()
Base of natural log: .
Definition: TMath.h:93
Definition: TString.h:850
static void output()