2012-01-01 21:55:05 +01:00
|
|
|
/*
|
|
|
|
* Cppcheck - A tool for static C/C++ code analysis
|
2018-10-13 18:20:31 +02:00
|
|
|
* Copyright (C) 2007-2018 Cppcheck team.
|
2012-01-01 21:55:05 +01:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
#ifndef templatesimplifierH
|
|
|
|
#define templatesimplifierH
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
2017-05-27 04:33:47 +02:00
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <ctime>
|
2012-01-02 22:05:27 +01:00
|
|
|
#include <list>
|
2018-11-23 11:36:09 +01:00
|
|
|
#include <map>
|
2017-05-27 04:33:47 +02:00
|
|
|
#include <set>
|
2012-01-02 21:53:13 +01:00
|
|
|
#include <string>
|
2012-01-03 22:35:06 +01:00
|
|
|
#include <vector>
|
2012-01-01 21:55:05 +01:00
|
|
|
|
2012-05-06 10:38:55 +02:00
|
|
|
class ErrorLogger;
|
2012-01-09 20:33:11 +01:00
|
|
|
class Settings;
|
2017-05-27 04:33:47 +02:00
|
|
|
class Token;
|
2018-09-01 11:26:10 +02:00
|
|
|
class Tokenizer;
|
2017-05-27 04:33:47 +02:00
|
|
|
class TokenList;
|
2012-01-01 21:55:05 +01:00
|
|
|
|
|
|
|
/// @addtogroup Core
|
|
|
|
/// @{
|
|
|
|
|
|
|
|
/** @brief Simplify templates from the preprocessed and partially simplified code. */
|
2012-06-10 14:19:09 +02:00
|
|
|
class CPPCHECKLIB TemplateSimplifier {
|
2012-04-23 20:45:36 +02:00
|
|
|
public:
|
2019-01-07 06:55:22 +01:00
|
|
|
explicit TemplateSimplifier(Tokenizer *tokenizer);
|
2018-09-01 11:26:10 +02:00
|
|
|
~TemplateSimplifier();
|
2012-01-01 21:55:05 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Used after simplifyTemplates to perform a little cleanup.
|
|
|
|
* Sometimes the simplifyTemplates isn't fully successful and then
|
|
|
|
* there are function calls etc with "wrong" syntax.
|
|
|
|
*/
|
2018-09-01 11:26:10 +02:00
|
|
|
void cleanupAfterSimplify();
|
2012-01-02 21:37:32 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
*/
|
2018-09-01 11:26:10 +02:00
|
|
|
void checkComplicatedSyntaxErrorsInTemplates();
|
2012-01-02 21:43:38 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* is the token pointing at a template parameters block
|
|
|
|
* < int , 3 > => yes
|
|
|
|
* \param tok start token that must point at "<"
|
|
|
|
* \return number of parameters (invalid parameters => 0)
|
|
|
|
*/
|
|
|
|
static unsigned int templateParameters(const Token *tok);
|
2012-01-02 21:46:43 +01:00
|
|
|
|
2017-12-26 10:55:18 +01:00
|
|
|
/**
|
|
|
|
* Token and its full scopename
|
|
|
|
*/
|
|
|
|
struct TokenAndName {
|
2019-01-14 20:36:23 +01:00
|
|
|
/**
|
|
|
|
* Constructor used for instantiations.
|
|
|
|
* \param tok template instantiation name token "name<...>"
|
|
|
|
* \param scope full qualification of template
|
|
|
|
*/
|
|
|
|
TokenAndName(Token *tok, const std::string &s);
|
|
|
|
/**
|
|
|
|
* Constructor used for declarations.
|
|
|
|
* \param tok template declaration token "template < ... >"
|
|
|
|
* \param scope full qualification of template
|
|
|
|
* \param nt template name token "template < ... > class name"
|
|
|
|
* \param pe template parameter end token ">"
|
|
|
|
*/
|
|
|
|
TokenAndName(Token *tok, const std::string &s, const Token *nt, const Token *pe);
|
2018-12-21 13:51:45 +01:00
|
|
|
TokenAndName(const TokenAndName& otherTok);
|
|
|
|
~TokenAndName();
|
|
|
|
|
2018-09-14 14:16:34 +02:00
|
|
|
bool operator == (const TokenAndName & rhs) const {
|
2019-01-14 08:29:32 +01:00
|
|
|
return token == rhs.token && scope == rhs.scope && name == rhs.name && fullName == rhs.fullName &&
|
2019-01-07 06:55:22 +01:00
|
|
|
nameToken == rhs.nameToken && paramEnd == rhs.paramEnd && flags == rhs.flags;
|
2018-09-14 14:16:34 +02:00
|
|
|
}
|
2017-12-26 10:55:18 +01:00
|
|
|
Token *token;
|
2017-12-30 09:00:19 +01:00
|
|
|
std::string scope;
|
2017-12-26 10:55:18 +01:00
|
|
|
std::string name;
|
2019-01-14 08:29:32 +01:00
|
|
|
std::string fullName;
|
2018-11-23 11:36:09 +01:00
|
|
|
const Token *nameToken;
|
2018-12-30 21:16:11 +01:00
|
|
|
const Token *paramEnd;
|
2019-01-07 06:55:22 +01:00
|
|
|
unsigned int flags;
|
|
|
|
|
|
|
|
enum {
|
2019-01-18 21:12:39 +01:00
|
|
|
fIsClass = (1 << 0), // class template
|
|
|
|
fIsFunction = (1 << 1), // function template
|
|
|
|
fIsVariable = (1 << 2), // variable template
|
|
|
|
fIsAlias = (1 << 3), // alias template
|
|
|
|
fIsSpecialized = (1 << 4), // user specialized template
|
|
|
|
fIsForwardDeclaration = (1 << 5), // forward declaration
|
2019-01-07 06:55:22 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
bool isClass() const {
|
|
|
|
return getFlag(fIsClass);
|
|
|
|
}
|
|
|
|
void isClass(bool state) {
|
|
|
|
setFlag(fIsClass, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isFunction() const {
|
|
|
|
return getFlag(fIsFunction);
|
|
|
|
}
|
|
|
|
void isFunction(bool state) {
|
|
|
|
setFlag(fIsFunction, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isVariable() const {
|
|
|
|
return getFlag(fIsVariable);
|
|
|
|
}
|
|
|
|
void isVariable(bool state) {
|
|
|
|
setFlag(fIsVariable, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isAlias() const {
|
|
|
|
return getFlag(fIsAlias);
|
|
|
|
}
|
|
|
|
void isAlias(bool state) {
|
|
|
|
setFlag(fIsAlias, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isSpecialized() const {
|
|
|
|
return getFlag(fIsSpecialized);
|
|
|
|
}
|
|
|
|
void isSpecialized(bool state) {
|
|
|
|
setFlag(fIsSpecialized, state);
|
|
|
|
}
|
|
|
|
|
2019-01-18 21:12:39 +01:00
|
|
|
bool isForwardDeclaration() const {
|
|
|
|
return getFlag(fIsForwardDeclaration);
|
|
|
|
}
|
|
|
|
void isForwardDeclaration(bool state) {
|
|
|
|
setFlag(fIsForwardDeclaration, state);
|
|
|
|
}
|
|
|
|
|
2019-01-07 06:55:22 +01:00
|
|
|
/**
|
|
|
|
* Get specified flag state.
|
|
|
|
* @param flag flag to get state of
|
|
|
|
* @return true if flag set or false in flag not set
|
|
|
|
*/
|
|
|
|
bool getFlag(unsigned int flag) const {
|
|
|
|
return ((flags & flag) != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set specified flag state.
|
|
|
|
* @param flag flag to set state
|
|
|
|
* @param state new state of flag
|
|
|
|
*/
|
|
|
|
void setFlag(unsigned int flag, bool state) {
|
|
|
|
flags = state ? flags | flag : flags & ~flag;
|
|
|
|
}
|
2017-12-26 10:55:18 +01:00
|
|
|
};
|
|
|
|
|
2012-01-02 22:05:27 +01:00
|
|
|
/**
|
2018-09-01 11:26:10 +02:00
|
|
|
* Match template declaration/instantiation
|
|
|
|
* @param instance template instantiation
|
|
|
|
* @param numberOfArguments number of template arguments
|
|
|
|
* @param patternAfter pattern that must match the tokens after the ">"
|
|
|
|
* @return match => true
|
2012-01-02 22:05:27 +01:00
|
|
|
*/
|
2018-09-01 11:26:10 +02:00
|
|
|
static bool instantiateMatch(const Token *instance, const std::size_t numberOfArguments, const char patternAfter[]);
|
2012-01-02 22:05:27 +01:00
|
|
|
|
|
|
|
/**
|
2018-09-01 11:26:10 +02:00
|
|
|
* Match template declaration/instantiation
|
|
|
|
* @param tok The ">" token e.g. before "class"
|
|
|
|
* @return -1 to bail out or positive integer to identity the position
|
|
|
|
* of the template name.
|
2012-01-02 22:05:27 +01:00
|
|
|
*/
|
2019-01-18 21:12:39 +01:00
|
|
|
static int getTemplateNamePosition(const Token *tok);
|
2018-12-22 10:05:10 +01:00
|
|
|
|
|
|
|
/**
|
2019-01-18 21:12:39 +01:00
|
|
|
* Get function template name position
|
2018-12-22 10:05:10 +01:00
|
|
|
* @param tok The ">" token e.g. before "class"
|
|
|
|
* @param namepos return offset to name
|
|
|
|
* @return true if name found, false if not
|
|
|
|
* */
|
2019-01-07 06:55:22 +01:00
|
|
|
static bool getTemplateNamePositionTemplateFunction(const Token *tok, int &namepos);
|
2012-01-03 22:35:06 +01:00
|
|
|
|
2019-01-18 21:12:39 +01:00
|
|
|
/**
|
|
|
|
* Get variable template name position
|
|
|
|
* @param tok The ">" token
|
|
|
|
* @param namepos return offset to name
|
|
|
|
* @return true if name found, false if not
|
|
|
|
* */
|
|
|
|
static bool getTemplateNamePositionTemplateVariable(const Token *tok, int &namepos);
|
|
|
|
|
2012-01-03 22:35:06 +01:00
|
|
|
/**
|
2018-09-01 11:26:10 +02:00
|
|
|
* Simplify templates
|
|
|
|
* @param maxtime time when the simplification should be stopped
|
|
|
|
* @param codeWithTemplates output parameter that is set if code contains templates
|
2012-01-03 22:35:06 +01:00
|
|
|
*/
|
2018-09-01 11:26:10 +02:00
|
|
|
void simplifyTemplates(
|
|
|
|
const std::time_t maxtime,
|
|
|
|
bool &codeWithTemplates);
|
2012-01-03 22:35:06 +01:00
|
|
|
|
2017-12-29 22:47:07 +01:00
|
|
|
/**
|
2018-09-01 11:26:10 +02:00
|
|
|
* Simplify constant calculations such as "1+2" => "3"
|
|
|
|
* @param tok start token
|
|
|
|
* @return true if modifications to token-list are done.
|
|
|
|
* false if no modifications are done.
|
2017-12-29 22:47:07 +01:00
|
|
|
*/
|
2018-10-14 16:57:07 +02:00
|
|
|
static bool simplifyNumericCalculations(Token *tok);
|
2017-12-29 22:47:07 +01:00
|
|
|
|
2012-01-03 22:35:06 +01:00
|
|
|
/**
|
2018-09-01 11:26:10 +02:00
|
|
|
* Simplify constant calculations such as "1+2" => "3".
|
|
|
|
* This also performs simple cleanup of parentheses etc.
|
|
|
|
* @return true if modifications to token-list are done.
|
|
|
|
* false if no modifications are done.
|
2012-01-03 22:35:06 +01:00
|
|
|
*/
|
2019-01-18 21:12:39 +01:00
|
|
|
bool simplifyCalculations(Token* frontToken = nullptr, Token *backToken = nullptr);
|
2012-01-03 22:35:06 +01:00
|
|
|
|
2018-09-01 11:26:10 +02:00
|
|
|
private:
|
2012-01-03 22:49:50 +01:00
|
|
|
/**
|
2018-09-01 11:26:10 +02:00
|
|
|
* Get template declarations
|
2018-11-10 16:41:14 +01:00
|
|
|
* @return true if code has templates.
|
2012-01-03 22:49:50 +01:00
|
|
|
*/
|
2018-11-10 16:41:14 +01:00
|
|
|
bool getTemplateDeclarations();
|
2012-01-05 21:45:19 +01:00
|
|
|
|
2017-12-26 22:34:39 +01:00
|
|
|
/**
|
2018-09-01 11:26:10 +02:00
|
|
|
* Get template instantiations
|
2017-12-26 22:34:39 +01:00
|
|
|
*/
|
2018-09-01 11:26:10 +02:00
|
|
|
void getTemplateInstantiations();
|
2012-01-09 20:33:11 +01:00
|
|
|
|
2018-10-24 14:38:59 +02:00
|
|
|
/**
|
|
|
|
* Fix forward declared default argument values by copying them
|
|
|
|
* when they are not present in the declaration.
|
|
|
|
*/
|
|
|
|
void fixForwardDeclaredDefaultArgumentValues();
|
|
|
|
|
2014-11-01 22:07:24 +01:00
|
|
|
/**
|
2018-09-01 11:26:10 +02:00
|
|
|
* simplify template instantiations (use default argument values)
|
2014-11-01 22:07:24 +01:00
|
|
|
*/
|
2018-09-01 11:26:10 +02:00
|
|
|
void useDefaultArgumentValues();
|
|
|
|
|
2018-12-29 11:19:53 +01:00
|
|
|
/**
|
|
|
|
* Try to locate a matching declaration for each user defined
|
|
|
|
* specialization.
|
|
|
|
*/
|
|
|
|
void getUserDefinedSpecializations();
|
|
|
|
|
2018-09-01 11:26:10 +02:00
|
|
|
/**
|
|
|
|
* simplify template aliases
|
|
|
|
*/
|
|
|
|
void simplifyTemplateAliases();
|
2014-11-01 22:07:24 +01:00
|
|
|
|
2012-01-09 20:33:11 +01:00
|
|
|
/**
|
2013-02-10 07:43:09 +01:00
|
|
|
* Simplify templates : expand all instantiations for a template
|
2012-01-09 20:33:11 +01:00
|
|
|
* @todo It seems that inner templates should be instantiated recursively
|
2017-12-26 10:55:18 +01:00
|
|
|
* @param templateDeclaration template declaration
|
2017-12-30 22:14:48 +01:00
|
|
|
* @param specializations template specializations (list each template name token)
|
2016-02-12 12:05:32 +01:00
|
|
|
* @param maxtime time when the simplification will stop
|
2012-01-09 20:33:11 +01:00
|
|
|
* @param expandedtemplates all templates that has been expanded so far. The full names are stored.
|
2012-07-29 16:01:05 +02:00
|
|
|
* @return true if the template was instantiated
|
2012-01-09 20:33:11 +01:00
|
|
|
*/
|
2018-09-01 11:26:10 +02:00
|
|
|
bool simplifyTemplateInstantiations(
|
2017-12-26 10:55:18 +01:00
|
|
|
const TokenAndName &templateDeclaration,
|
2017-12-30 22:14:48 +01:00
|
|
|
const std::list<const Token *> &specializations,
|
2016-02-12 12:05:32 +01:00
|
|
|
const std::time_t maxtime,
|
2012-01-09 20:33:11 +01:00
|
|
|
std::set<std::string> &expandedtemplates);
|
|
|
|
|
2018-11-05 19:55:21 +01:00
|
|
|
/**
|
|
|
|
* Simplify templates : add namespace to template name
|
|
|
|
* @param templateDeclaration template declaration
|
|
|
|
* @param tok place to insert namespace
|
|
|
|
*/
|
|
|
|
void addNamespace(const TokenAndName &templateDeclaration, const Token *tok);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Simplify templates : check if namespace already present
|
|
|
|
* @param templateDeclaration template declaration
|
|
|
|
* @param tok place to start looking for namespace
|
|
|
|
* @return true if namespace already present
|
|
|
|
*/
|
2019-01-07 06:55:22 +01:00
|
|
|
static bool alreadyHasNamespace(const TokenAndName &templateDeclaration, const Token *tok);
|
2018-11-05 19:55:21 +01:00
|
|
|
|
2018-09-01 11:26:10 +02:00
|
|
|
/**
|
|
|
|
* Expand a template. Create "expanded" class/function at end of tokenlist.
|
2018-10-26 14:20:13 +02:00
|
|
|
* @param templateDeclaration Template declaration information
|
|
|
|
* @param templateInstantiation Full name of template
|
2018-09-01 11:26:10 +02:00
|
|
|
* @param typeParametersInDeclaration The type parameters of the template
|
|
|
|
* @param newName New name of class/function.
|
2018-10-14 16:57:07 +02:00
|
|
|
* @param copy copy or expand in place
|
2018-09-01 11:26:10 +02:00
|
|
|
*/
|
|
|
|
void expandTemplate(
|
2018-10-26 14:20:13 +02:00
|
|
|
const TokenAndName &templateDeclaration,
|
|
|
|
const TokenAndName &templateInstantiation,
|
2018-09-01 11:26:10 +02:00
|
|
|
const std::vector<const Token *> &typeParametersInDeclaration,
|
|
|
|
const std::string &newName,
|
2018-10-14 16:57:07 +02:00
|
|
|
bool copy);
|
2018-09-01 11:26:10 +02:00
|
|
|
|
2017-06-08 00:16:43 +02:00
|
|
|
/**
|
|
|
|
* Replace all matching template usages 'Foo < int >' => 'Foo<int>'
|
2019-01-14 20:36:23 +01:00
|
|
|
* @param instantiation Template instantiation information.
|
2017-12-26 22:34:39 +01:00
|
|
|
* @param typeStringsUsedInTemplateInstantiation template parameters. list of token strings.
|
2017-06-08 00:16:43 +02:00
|
|
|
* @param newName The new type name
|
|
|
|
*/
|
2019-01-14 20:36:23 +01:00
|
|
|
void replaceTemplateUsage(const TokenAndName &instantiation,
|
2018-09-01 11:26:10 +02:00
|
|
|
const std::list<std::string> &typeStringsUsedInTemplateInstantiation,
|
2018-10-14 16:57:07 +02:00
|
|
|
const std::string &newName);
|
2012-08-20 18:10:32 +02:00
|
|
|
|
|
|
|
/**
|
2018-09-01 11:26:10 +02:00
|
|
|
* @brief TemplateParametersInDeclaration
|
|
|
|
* @param tok template < typename T, typename S >
|
|
|
|
* ^ tok
|
|
|
|
* @param typeParametersInDeclaration template < typename T, typename S >
|
|
|
|
* ^ [0] ^ [1]
|
|
|
|
* @return template < typename T, typename S >
|
|
|
|
* ^ return
|
2012-01-09 20:33:11 +01:00
|
|
|
*/
|
2018-10-14 16:57:07 +02:00
|
|
|
static const Token * getTemplateParametersInDeclaration(
|
2018-09-01 11:26:10 +02:00
|
|
|
const Token * tok,
|
|
|
|
std::vector<const Token *> & typeParametersInDeclaration);
|
2012-07-29 16:01:05 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove a specific "template < ..." template class/function
|
|
|
|
*/
|
2019-01-07 06:55:22 +01:00
|
|
|
static bool removeTemplate(Token *tok);
|
2012-07-29 16:01:05 +02:00
|
|
|
|
2015-06-23 20:53:57 +02:00
|
|
|
/** Syntax error */
|
|
|
|
static void syntaxError(const Token *tok);
|
|
|
|
|
2018-10-14 16:57:07 +02:00
|
|
|
static bool matchSpecialization(
|
2018-09-01 11:26:10 +02:00
|
|
|
const Token *templateDeclarationNameToken,
|
|
|
|
const Token *templateInstantiationNameToken,
|
|
|
|
const std::list<const Token *> & specializations);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Same as Token::eraseTokens() but tries to fix up lists with pointers to the deleted tokens.
|
|
|
|
* @param begin Tokens after this will be erased.
|
|
|
|
* @param end Tokens before this will be erased.
|
|
|
|
*/
|
2019-01-07 06:55:22 +01:00
|
|
|
static void eraseTokens(Token *begin, const Token *end);
|
2018-09-01 11:26:10 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete specified token without invalidating pointer to following token.
|
|
|
|
* tok will be invalidated.
|
|
|
|
* @param tok token to delete
|
|
|
|
*/
|
2018-10-14 16:57:07 +02:00
|
|
|
static void deleteToken(Token *tok);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the new token name.
|
2018-12-21 21:23:03 +01:00
|
|
|
* @param tok2 name token
|
2018-10-14 16:57:07 +02:00
|
|
|
* @param &typeStringsUsedInTemplateInstantiation type strings use in template instantiation
|
|
|
|
* @return new token name
|
|
|
|
*/
|
|
|
|
std::string getNewName(
|
|
|
|
Token *tok2,
|
|
|
|
std::list<std::string> &typeStringsUsedInTemplateInstantiation);
|
2018-09-01 11:26:10 +02:00
|
|
|
|
2019-01-14 08:29:32 +01:00
|
|
|
void printOut(
|
|
|
|
const TokenAndName &tokenAndName,
|
|
|
|
const std::string &indent = " ") const;
|
|
|
|
void printOut(const std::string &text = "") const;
|
|
|
|
|
2018-09-01 11:26:10 +02:00
|
|
|
TokenList &mTokenList;
|
|
|
|
const Settings *mSettings;
|
|
|
|
ErrorLogger *mErrorLogger;
|
|
|
|
|
|
|
|
std::list<TokenAndName> mTemplateDeclarations;
|
2018-11-10 16:41:14 +01:00
|
|
|
std::list<TokenAndName> mTemplateForwardDeclarations;
|
2018-11-23 11:36:09 +01:00
|
|
|
std::map<Token *, Token *> mTemplateForwardDeclarationsMap;
|
2018-12-29 11:19:53 +01:00
|
|
|
std::map<Token *, Token *> mTemplateUserSpecializationMap;
|
2018-09-01 11:26:10 +02:00
|
|
|
std::list<TokenAndName> mTemplateInstantiations;
|
|
|
|
std::list<TokenAndName> mInstantiatedTemplates;
|
2018-09-14 14:16:34 +02:00
|
|
|
std::list<TokenAndName> mMemberFunctionsToDelete;
|
2018-12-21 13:51:45 +01:00
|
|
|
std::vector<TokenAndName> mExplicitInstantiationsToDelete;
|
|
|
|
std::vector<TokenAndName> mTypesUsedInTemplateInstantiation;
|
2012-01-01 21:55:05 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/// @}
|
|
|
|
//---------------------------------------------------------------------------
|
2013-09-04 20:59:49 +02:00
|
|
|
#endif // templatesimplifierH
|