30#pragma GCC diagnostic push
31#pragma GCC diagnostic ignored "-Woverloaded-virtual"
32#pragma GCC diagnostic ignored "-Wshadow"
36#pragma GCC diagnostic pop
66struct ParsedExpression {
76static bool IsStrInVec(
const std::string &str,
const std::vector<std::string> &vec)
78 return std::find(vec.cbegin(), vec.cend(), str) != vec.cend();
81static const std::string &ResolveAlias(
const std::string &col,
const std::map<std::string, std::string> &aliasMap)
83 const auto it = aliasMap.find(col);
84 if (it != aliasMap.end())
90static ColumnNames_t FindUsedColumns(
const std::string &expr,
const ColumnNames_t &treeBranchNames,
91 const ColumnNames_t &customColNames,
const ColumnNames_t &dataSourceColNames,
92 const std::map<std::string, std::string> &aliasMap)
96 lexertk::generator tokens;
97 const auto tokensOk = tokens.process(expr);
99 const auto msg =
"Failed to tokenize expression:\n" + expr +
"\n\nMake sure it is valid C++.";
100 throw std::runtime_error(msg);
104 const auto nTokens = tokens.size();
105 const auto kSymbol = lexertk::token::e_symbol;
106 for (
auto i = 0u; i < nTokens; ++i) {
107 const auto &tok = tokens[i];
109 if (tok.type != kSymbol || tok.value ==
"&" || tok.value ==
"|") {
117 auto dotChainKeepsGoing = [&](
unsigned int _i) {
118 return _i + 2 <= nTokens && tokens[_i + 1].value ==
"." && tokens[_i + 2].type == kSymbol;
120 while (dotChainKeepsGoing(i)) {
121 potentialColNames.emplace_back(potentialColNames.back() +
"." + tokens[i + 2].value);
128 auto isRDFColumn = [&](
const std::string &columnOrAlias) {
129 const auto &col = ResolveAlias(columnOrAlias, aliasMap);
130 if (IsStrInVec(col, customColNames) || IsStrInVec(col, treeBranchNames) || IsStrInVec(col, dataSourceColNames))
134 const auto longestRDFColMatch = std::find_if(potentialColNames.crbegin(), potentialColNames.crend(), isRDFColumn);
136 if (longestRDFColMatch != potentialColNames.crend() && !IsStrInVec(*longestRDFColMatch, usedCols)) {
138 usedCols.emplace_back(*longestRDFColMatch);
145static ParsedExpression ParseRDFExpression(
const std::string &expr,
const ColumnNames_t &treeBranchNames,
146 const ColumnNames_t &customColNames,
const ColumnNames_t &dataSourceColNames,
147 const std::map<std::string, std::string> &aliasMap)
149 const auto usedColsAndAliases = FindUsedColumns(expr, treeBranchNames, customColNames, dataSourceColNames, aliasMap);
151 auto escapeDots = [](
const std::string &s) {
154 dot.Substitute(ss,
"\\.",
"g");
155 return std::string(std::move(ss));
161 for (
const auto &colOrAlias : usedColsAndAliases) {
162 const auto col = ResolveAlias(colOrAlias, aliasMap);
164 if (!IsStrInVec(col, usedCols)) {
165 usedCols.emplace_back(col);
166 varIdx = varNames.size();
167 varNames.emplace_back(
"var" + std::to_string(varIdx));
171 varIdx = std::distance(usedCols.begin(), std::find(usedCols.begin(), usedCols.end(), col));
173 TPRegexp replacer(
"\\b" + escapeDots(colOrAlias) +
"\\b");
174 replacer.Substitute(exprWithVars, varNames[varIdx],
"g");
177 return ParsedExpression{std::string(std::move(exprWithVars)), std::move(usedCols), std::move(varNames)};
187static std::unordered_map<std::string, std::string> &GetJittedExprs() {
188 static std::unordered_map<std::string, std::string> jittedExpressions;
189 return jittedExpressions;
193BuildLambdaString(
const std::string &expr,
const ColumnNames_t &vars,
const ColumnNames_t &varTypes)
195 R__ASSERT(vars.size() == varTypes.size());
198 const bool hasReturnStmt = re.Match(expr) == 1;
200 static const std::vector<std::string> fundamentalTypes = {
233 std::stringstream ss;
235 for (
auto i = 0u; i < vars.size(); ++i) {
236 std::string fullType;
237 const auto &
type = varTypes[i];
238 if (std::find(fundamentalTypes.begin(), fundamentalTypes.end(),
type) != fundamentalTypes.end()) {
240 fullType =
"const " +
type +
" ";
244 fullType =
type +
"& ";
246 ss << fullType << vars[i] <<
", ";
249 ss.seekp(-2, ss.cur);
255 ss << expr <<
"\n;}";
262static std::string DeclareLambda(
const std::string &expr,
const ColumnNames_t &vars,
const ColumnNames_t &varTypes)
266 const auto lambdaExpr = BuildLambdaString(expr, vars, varTypes);
267 auto &exprMap = GetJittedExprs();
268 const auto exprIt = exprMap.find(lambdaExpr);
269 if (exprIt != exprMap.end()) {
271 const auto lambdaName = exprIt->second;
276 const auto lambdaBaseName =
"lambda" + std::to_string(exprMap.size());
277 const auto lambdaFullName =
"__rdf::" + lambdaBaseName;
279 const auto toDeclare =
"namespace __rdf {\nauto " + lambdaBaseName +
" = " + lambdaExpr +
";\nusing " +
280 lambdaBaseName +
"_ret_t = typename ROOT::TypeTraits::CallableTraits<decltype(" +
281 lambdaBaseName +
")>::ret_type;\n}";
285 exprMap.insert({lambdaExpr, lambdaFullName});
287 return lambdaFullName;
292static std::string RetTypeOfLambda(
const std::string &lambdaName)
294 const auto dt =
gROOT->GetType((lambdaName +
"_ret_t").c_str());
296 const auto type = dt->GetFullTypeName();
300static void GetTopLevelBranchNamesImpl(
TTree &t, std::set<std::string> &bNamesReg, ColumnNames_t &bNames,
301 std::set<TTree *> &analysedTrees,
const std::string friendName =
"")
303 if (!analysedTrees.insert(&t).second) {
309 for (
auto branchObj : *branches) {
311 if (bNamesReg.insert(
name).second) {
312 bNames.emplace_back(
name);
313 }
else if (!friendName.empty()) {
317 const auto longName = friendName +
"." +
name;
318 if (bNamesReg.insert(longName).second)
319 bNames.emplace_back(longName);
329 for (
auto friendTreeObj : *friendTrees) {
330 auto friendElement =
static_cast<TFriendElement *
>(friendTreeObj);
331 auto friendTree = friendElement->
GetTree();
332 const std::string frName(friendElement->GetName());
333 GetTopLevelBranchNamesImpl(*friendTree, bNamesReg, bNames, analysedTrees, frName);
337static bool IsValidCppVarName(
const std::string &var)
341 const char firstChar = var[0];
344 auto isALetter = [](
char c) {
return (
c >=
'A' &&
c <=
'Z') || (
c >=
'a' &&
c <=
'z'); };
345 const bool isValidFirstChar = firstChar ==
'_' || isALetter(firstChar);
346 if (!isValidFirstChar)
350 auto isANumber = [](
char c) {
return c >=
'0' &&
c <=
'9'; };
351 auto isValidTok = [&isALetter, &isANumber](
char c) {
return c ==
'_' || isALetter(
c) || isANumber(
c); };
352 for (
const char c : var)
369 std::set<std::string> bNamesSet;
371 std::set<TTree *> analysedTrees;
372 GetTopLevelBranchNamesImpl(t, bNamesSet, bNames, analysedTrees);
384 std::string tname(tn);
392 const auto theRegexSize = columnNameRegexp.size();
393 std::string theRegex(columnNameRegexp);
395 const auto isEmptyRegex = 0 == theRegexSize;
397 if (theRegexSize > 0 && theRegex[0] !=
'^')
398 theRegex =
"^" + theRegex;
399 if (theRegexSize > 0 && theRegex[theRegexSize - 1] !=
'$')
400 theRegex = theRegex +
"$";
407 for (
auto &&colName : colNames) {
409 selectedColumns.emplace_back(colName);
413 if (selectedColumns.empty()) {
414 std::string
text(callerName);
415 if (columnNameRegexp.empty()) {
416 text =
": there is no column available to match.";
418 text =
": regex \"" + std::string(columnNameRegexp) +
"\" did not match any column.";
420 throw std::runtime_error(
text);
422 return selectedColumns;
426 const std::map<std::string, std::string> &aliasMap,
const ColumnNames_t &dataSourceColumns)
428 const std::string definedColStr(definedCol);
430 if (!IsValidCppVarName(definedColStr)) {
431 const auto msg =
"Cannot define column \"" + definedColStr +
"\": not a valid C++ variable name.";
432 throw std::runtime_error(msg);
435 if (treePtr !=
nullptr) {
437 const auto branch = treePtr->
GetBranch(definedColStr.c_str());
438 if (branch !=
nullptr) {
439 const auto msg =
"branch \"" + definedColStr +
"\" already present in TTree";
440 throw std::runtime_error(msg);
444 if (std::find(customCols.begin(), customCols.end(), definedCol) != customCols.end()) {
445 const auto msg =
"Redefinition of column \"" + definedColStr +
"\"";
446 throw std::runtime_error(msg);
450 const auto aliasColNameIt = aliasMap.find(definedColStr);
451 if (aliasColNameIt != aliasMap.end()) {
452 const auto msg =
"An alias with name " + definedColStr +
" pointing to column " +
453 aliasColNameIt->second +
" is already existing.";
454 throw std::runtime_error(msg);
458 if (!dataSourceColumns.empty()) {
459 if (std::find(dataSourceColumns.begin(), dataSourceColumns.end(), definedCol) != dataSourceColumns.end()) {
460 const auto msg =
"Redefinition of column \"" + definedColStr +
"\" already present in the data-source";
461 throw std::runtime_error(msg);
468 if (nTemplateParams != nColumnNames) {
469 std::string err_msg =
"The number of template parameters specified is ";
470 err_msg += std::to_string(nTemplateParams);
471 err_msg +=
" while ";
472 err_msg += std::to_string(nColumnNames);
473 err_msg +=
" columns have been specified.";
474 throw std::runtime_error(err_msg);
484 if (defaultNames.size() < nRequiredNames)
485 throw std::runtime_error(
486 std::to_string(nRequiredNames) +
" column name" + (nRequiredNames == 1 ?
" is" :
"s are") +
487 " required but none were provided and the default list has size " + std::to_string(defaultNames.size()));
489 return ColumnNames_t(defaultNames.begin(), defaultNames.begin() + nRequiredNames);
492 if (names.size() != nRequiredNames) {
493 auto msg = std::to_string(nRequiredNames) +
" column name" + (nRequiredNames == 1 ?
" is" :
"s are") +
494 " required but " + std::to_string(names.size()) + (names.size() == 1 ?
" was" :
" were") +
496 for (
const auto &
name : names)
497 msg +=
" \"" +
name +
"\",";
499 throw std::runtime_error(msg);
509 for (
auto &column : requiredCols) {
510 const auto isBranch = std::find(datasetColumns.begin(), datasetColumns.end(), column) != datasetColumns.end();
513 const auto isDefine = std::find(definedCols.begin(), definedCols.end(), column) != definedCols.end();
516 const auto isDataSourceColumn =
517 std::find(dataSourceColumns.begin(), dataSourceColumns.end(), column) != dataSourceColumns.end();
518 if (isDataSourceColumn)
520 unknownColumns.emplace_back(column);
522 return unknownColumns;
525std::vector<std::string>
GetFilterNames(
const std::shared_ptr<RLoopManager> &loopManager)
527 return loopManager->GetFiltersNames();
533 std::string_view dirName =
"";
534 std::string_view treeName = fullTreeName;
535 const auto lastSlash = fullTreeName.rfind(
'/');
536 if (std::string_view::npos != lastSlash) {
537 dirName = treeName.substr(0, lastSlash);
538 treeName = treeName.substr(lastSlash + 1, treeName.size());
540 return {std::string(treeName), std::string(dirName)};
547 s << std::hex << std::showbase << reinterpret_cast<size_t>(addr);
552 std::shared_ptr<RDFDetail::RNodeBase> *prevNodeOnHeap, std::string_view
name,
553 std::string_view expression,
const std::map<std::string, std::string> &aliasMap,
557 const auto &dsColumns = ds ? ds->GetColumnNames() :
ColumnNames_t{};
559 const auto parsedExpr =
560 ParseRDFExpression(std::string(expression), branches, customCols.
GetNames(), dsColumns, aliasMap);
561 const auto exprVarTypes =
563 const auto lambdaName = DeclareLambda(parsedExpr.fExpr, parsedExpr.fVarNames, exprVarTypes);
564 const auto type = RetTypeOfLambda(lambdaName);
566 std::runtime_error(
"Filter: the following expression does not evaluate to bool:\n" + std::string(expression));
575 std::stringstream filterInvocation;
576 filterInvocation <<
"ROOT::Internal::RDF::JitFilterHelper(" << lambdaName <<
", new const char*["
577 << parsedExpr.fUsedCols.size() <<
"]{";
578 for (
const auto &col : parsedExpr.fUsedCols)
579 filterInvocation <<
"\"" << col <<
"\", ";
580 if (!parsedExpr.fUsedCols.empty())
581 filterInvocation.seekp(-2, filterInvocation.cur);
586 filterInvocation <<
"}, " << parsedExpr.fUsedCols.size() <<
", \"" <<
name <<
"\", "
587 <<
"reinterpret_cast<std::weak_ptr<ROOT::Detail::RDF::RJittedFilter>*>("
589 <<
"reinterpret_cast<std::shared_ptr<ROOT::Detail::RDF::RNodeBase>*>(" << prevNodeAddr <<
"),"
590 <<
"reinterpret_cast<ROOT::Internal::RDF::RBookedDefines*>(" << definesOnHeapAddr <<
")"
593 auto lm = jittedFilter->GetLoopManagerUnchecked();
594 lm->ToJitExec(filterInvocation.str());
601 std::shared_ptr<RNodeBase> *upcastNodeOnHeap)
605 const auto &dsColumns = ds ? ds->GetColumnNames() :
ColumnNames_t{};
607 const auto parsedExpr =
608 ParseRDFExpression(std::string(expression), branches, customCols.
GetNames(), dsColumns, aliasMap);
609 const auto exprVarTypes =
611 const auto lambdaName = DeclareLambda(parsedExpr.fExpr, parsedExpr.fVarNames, exprVarTypes);
612 const auto type = RetTypeOfLambda(lambdaName);
618 std::stringstream defineInvocation;
619 defineInvocation <<
"ROOT::Internal::RDF::JitDefineHelper(" << lambdaName <<
", new const char*["
620 << parsedExpr.fUsedCols.size() <<
"]{";
621 for (
const auto &col : parsedExpr.fUsedCols) {
622 defineInvocation <<
"\"" << col <<
"\", ";
624 if (!parsedExpr.fUsedCols.empty())
625 defineInvocation.seekp(-2, defineInvocation.cur);
630 defineInvocation <<
"}, " << parsedExpr.fUsedCols.size() <<
", \"" <<
name
631 <<
"\", reinterpret_cast<ROOT::Detail::RDF::RLoopManager*>(" <<
PrettyPrintAddr(&lm)
632 <<
"), reinterpret_cast<std::weak_ptr<ROOT::Detail::RDF::RJittedDefine>*>("
634 <<
"), reinterpret_cast<ROOT::Internal::RDF::RBookedDefines*>(" << definesAddr
635 <<
"), reinterpret_cast<std::shared_ptr<ROOT::Detail::RDF::RNodeBase>*>("
645 const std::type_info &helperArgType,
const std::type_info &at,
void *helperArgOnHeap,
647 RDataSource *ds, std::weak_ptr<RJittedAction> *jittedActionOnHeap)
651 if (!helperArgClass) {
652 std::string exceptionText =
"An error occurred while inferring the result type of an operation.";
653 throw std::runtime_error(exceptionText.c_str());
655 const auto helperArgClassName = helperArgClass->GetName();
659 if (!actionTypeClass) {
660 std::string exceptionText =
"An error occurred while inferring the action type of the operation.";
661 throw std::runtime_error(exceptionText.c_str());
663 const std::string actionTypeName = actionTypeClass->GetName();
664 const std::string actionTypeNameBase = actionTypeName.substr(actionTypeName.rfind(
':') + 1);
671 std::stringstream createAction_str;
672 createAction_str <<
"ROOT::Internal::RDF::CallBuildAction<" << actionTypeName;
673 const auto columnTypeNames =
675 for (
auto &colType : columnTypeNames)
676 createAction_str <<
", " << colType;
679 createAction_str <<
">(reinterpret_cast<std::shared_ptr<ROOT::Detail::RDF::RNodeBase>*>("
680 <<
PrettyPrintAddr(prevNode) <<
"), new const char*[" << cols.size() <<
"]{";
681 for (
auto i = 0u; i < cols.size(); ++i) {
683 createAction_str <<
", ";
684 createAction_str <<
'"' << cols[i] <<
'"';
686 createAction_str <<
"}, " << cols.size() <<
", " << nSlots <<
", reinterpret_cast<" << helperArgClassName <<
"*>("
688 <<
"), reinterpret_cast<std::weak_ptr<ROOT::Internal::RDF::RJittedAction>*>("
690 <<
"), reinterpret_cast<ROOT::Internal::RDF::RBookedDefines*>(" << definesAddr <<
"));";
691 return createAction_str.str();
696 for (
const auto &s : strings) {
703std::shared_ptr<RNodeBase>
UpcastNode(std::shared_ptr<RNodeBase> ptr)
717 auto selectedColumns =
SelectColumns(nColumns, columns, defaultColumns);
719 const auto unknownColumns =
722 if (!unknownColumns.empty()) {
724 std::stringstream unknowns;
725 std::string delim = unknownColumns.size() > 1 ?
"s: " :
": ";
726 for (
auto &unknownColumn : unknownColumns) {
727 unknowns << delim << unknownColumn;
730 throw std::runtime_error(
"Unknown column" + unknowns.str());
735 auto aliasMapEnd = aliasMap.end();
737 for (
auto idx :
ROOT::TSeqU(selectedColumns.size())) {
738 const auto &colName = selectedColumns[idx];
739 const auto aliasColumnNameIt = aliasMap.find(colName);
740 if (aliasMapEnd != aliasColumnNameIt) {
741 selectedColumns[idx] = aliasColumnNameIt->second;
745 return selectedColumns;
749 RDataSource *ds,
const std::string &context,
bool vector2rvec)
751 auto toCheckedArgType = [&](
const std::string &
c) {
754 if (colType.rfind(
"CLING_UNKNOWN_TYPE", 0) == 0) {
756 "The type of custom column \"" +
c +
"\" (" + colType.substr(19) +
757 ") is not known to the interpreter, but a just-in-time-compiled " + context +
758 " call requires this column. Make sure to create and load ROOT dictionaries for this column's class.";
759 throw std::runtime_error(msg);
763 std::vector<std::string> colTypes;
764 colTypes.reserve(colNames.size());
765 std::transform(colNames.begin(), colNames.end(), std::back_inserter(colTypes), toCheckedArgType);
774 const auto nColumns = requestedCols.size();
775 std::vector<bool> mustBeDefined(nColumns,
false);
776 for (
auto i = 0u; i < nColumns; ++i)
777 mustBeDefined[i] = std::find(definedCols.begin(), definedCols.end(), requestedCols[i]) == definedCols.end();
778 return mustBeDefined;
R__EXTERN TVirtualMutex * gROOTMutex
#define R__LOCKGUARD(mutex)
The head node of a RDF computation graph.
const std::map< std::string, std::string > & GetAliasMap() const
const ColumnNames_t & GetBranchNames()
Return all valid TTree::Branch names (caching results for subsequent calls).
void ToJitExec(const std::string &) const
const std::map< std::string, std::vector< void * > > & GetDSValuePtrs() const
const ColumnNames_t & GetDefaultColumnNames() const
Return the list of default columns – empty if none was provided when constructing the RDataFrame.
unsigned int GetNSlots() const
Encapsulates the columns defined by the user.
bool HasName(std::string_view name) const
Check if the provided name is tracked in the names list.
const RDefineBasePtrMap_t & GetColumns() const
Returns the list of the pointers to the defined columns.
ColumnNames_t GetNames() const
Returns the list of the names of the defined columns.
RDataSource defines an API that RDataFrame can use to read arbitrary data formats.
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
virtual const char * GetName() const
Return name of this collection.
A TFriendElement TF describes a TTree object TF in a file.
virtual TTree * GetTree()
Return pointer to friend TTree.
Int_t Match(const TString &s, const TString &mods="", Int_t start=0, Int_t nMaxMatch=10, TArrayI *pos=0)
The number of matches is returned, this equals the full match + sub-pattern matches.
A TTree represents a columnar dataset.
virtual TBranch * GetBranch(const char *name)
Return pointer to the branch with the given name in this tree or its friends.
virtual TObjArray * GetListOfBranches()
virtual TList * GetListOfFriends() const
std::vector< std::string > ColumnNames_t
const ColumnNames_t SelectColumns(unsigned int nRequiredNames, const ColumnNames_t &names, const ColumnNames_t &defaultNames)
Choose between local column names or default column names, throw in case of errors.
ParsedTreePath ParseTreePath(std::string_view fullTreeName)
std::string ColumnName2ColumnTypeName(const std::string &colName, TTree *tree, RDataSource *ds, RDefineBase *define, bool vector2rvec)
Return a string containing the type of the given branch.
std::shared_ptr< RNodeBase > UpcastNode(std::shared_ptr< RNodeBase > ptr)
std::vector< std::string > GetFilterNames(const std::shared_ptr< RLoopManager > &loopManager)
std::string PrettyPrintAddr(const void *const addr)
ColumnNames_t GetTopLevelBranchNames(TTree &t)
Get all the top-level branches names, including the ones of the friend trees.
void CheckTypesAndPars(unsigned int nTemplateParams, unsigned int nColumnNames)
std::string DemangleTypeIdName(const std::type_info &typeInfo)
bool AtLeastOneEmptyString(const std::vector< std::string_view > strings)
std::string JitBuildAction(const ColumnNames_t &cols, std::shared_ptr< RDFDetail::RNodeBase > *prevNode, const std::type_info &helperArgType, const std::type_info &at, void *helperArgOnHeap, TTree *tree, const unsigned int nSlots, const RDFInternal::RBookedDefines &customCols, RDataSource *ds, std::weak_ptr< RJittedAction > *jittedActionOnHeap)
ColumnNames_t GetValidatedColumnNames(RLoopManager &lm, const unsigned int nColumns, const ColumnNames_t &columns, const ColumnNames_t &validDefines, RDataSource *ds)
Given the desired number of columns and the user-provided list of columns:
ColumnNames_t FindUnknownColumns(const ColumnNames_t &requiredCols, const ColumnNames_t &datasetColumns, const ColumnNames_t &definedCols, const ColumnNames_t &dataSourceColumns)
bool IsInternalColumn(std::string_view colName)
void InterpreterDeclare(const std::string &code)
void CheckDefine(std::string_view definedCol, TTree *treePtr, const ColumnNames_t &customCols, const std::map< std::string, std::string > &aliasMap, const ColumnNames_t &dataSourceColumns)
ColumnNames_t ConvertRegexToColumns(const ColumnNames_t &colNames, std::string_view columnNameRegexp, std::string_view callerName)
std::vector< std::string > GetValidatedArgTypes(const ColumnNames_t &colNames, const RBookedDefines &defines, TTree *tree, RDataSource *ds, const std::string &context, bool vector2rvec)
std::vector< bool > FindUndefinedDSColumns(const ColumnNames_t &requestedCols, const ColumnNames_t &definedCols)
Return a bitset each element of which indicates whether the corresponding element in selectedColumns ...
void BookFilterJit(const std::shared_ptr< RJittedFilter > &jittedFilter, std::shared_ptr< RDFDetail::RNodeBase > *prevNodeOnHeap, std::string_view name, std::string_view expression, const std::map< std::string, std::string > &aliasMap, const ColumnNames_t &branches, const RDFInternal::RBookedDefines &customCols, TTree *tree, RDataSource *ds)
std::shared_ptr< RJittedDefine > BookDefineJit(std::string_view name, std::string_view expression, RLoopManager &lm, RDataSource *ds, const RDFInternal::RBookedDefines &customCols, const ColumnNames_t &branches, std::shared_ptr< RNodeBase > *upcastNodeOnHeap)
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
TSeq< unsigned int > TSeqU
char * DemangleTypeIdName(const std::type_info &ti, int &errorCode)
Demangle in a portable way the type id name.