ENH: add low-level polymorphic/boxed exprValue

- primarily for handling expression results,
  but can also be used as a universal value holder.

  Has some characteristics suitable for type-less IO:
  eg, is_integral(), nComponents()

ENH: add is_pointer() check for expression scanToken
This commit is contained in:
Mark Olesen 2023-06-30 18:43:41 +02:00
parent fc2760ab9c
commit 5635e14f81
13 changed files with 1243 additions and 90 deletions

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2021 OpenCFD Ltd.
Copyright (C) 2021-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
@ -17,8 +17,9 @@ Description
#include "IOstreams.H"
#include "ITstream.H"
#include "exprTraits.H"
#include "uLabel.H"
#include "exprTraits.H"
#include "error.H"
#include "stringList.H"
#include "exprScanToken.H"
@ -27,16 +28,18 @@ using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
template<class T>
template<class Type>
void printTraits()
{
const auto typeCode = exprTypeTraits<T>::value;
const auto typeCode = exprTypeTraits<Type>::value;
Info<< "type " << pTraits<T>::typeName
<< " code:" << int(typeCode)
<< " name:" << exprTypeTraits<T>::name;
Info<< "Type '" << pTraits<Type>::typeName
<< "' = code:" << int(typeCode)
<< " rank:" << exprTypeTraits<Type>::rank
<< " cmpt:" << exprTypeTraits<Type>::nComponents
<< " name:" << exprTypeTraits<Type>::name;
if (pTraits<T>::typeName != word(exprTypeTraits<T>::name))
if (pTraits<Type>::typeName != word(exprTypeTraits<Type>::name))
{
Info<< " (UNSUPPORTED)";
}
@ -45,6 +48,17 @@ void printTraits()
}
void print(const expressions::scanToken& tok)
{
Info<< " type:" << int(tok.type_);
if (tok.is_pointer())
{
Info<< " ptr:" << Foam::name(tok.name_);
}
Info<< nl;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main()
@ -56,6 +70,7 @@ int main()
printTraits<bool>();
printTraits<label>();
printTraits<scalar>();
printTraits<complex>();
printTraits<vector>();
printTraits<tensor>();
printTraits<symmTensor>();
@ -71,33 +86,27 @@ int main()
Info<< "Name of typeCode: "
<< getName(expressions::valueTypeCode::type_bool) << nl;
{
expressions::scanToken tok;
expressions::scanToken tok2;
expressions::scanToken tok(expressions::scanToken::null());
expressions::scanToken tok2(expressions::scanToken::null());
Info<< nl << "sizeof(scanToken): "
<< sizeof(tok) << nl;
Info<< " type:" << int(tok.type_) << nl;
Info<< " ptr:" << Foam::name(tok.name_) << nl;
Info<< " type:" << int(tok2.type_) << nl;
Info<< " ptr:" << Foam::name(tok2.name_) << nl;
print(tok);
print(tok2);
tok.setWord("hello");
Info<< " type:" << int(tok.type_) << nl;
Info<< " ptr:" << Foam::name(tok.name_) << nl;
print(tok);
tok2 = tok;
Info<< " type:" << int(tok2.type_) << nl;
Info<< " ptr:" << Foam::name(tok2.name_) << nl;
print(tok2);
tok2.destroy();
Info<< " type:" << int(tok2.type_) << nl;
Info<< " ptr:" << Foam::name(tok2.name_) << nl;
print(tok); // Not a leak, but old rubbish
print(tok2);
}
Info<< nl << "Done" << nl;

View File

@ -0,0 +1,3 @@
Test-exprValue.C
EXE = $(FOAM_USER_APPBIN)/Test-exprValue

View File

@ -0,0 +1,2 @@
/* EXE_INC = */
/* EXE_LIBS = */

View File

@ -0,0 +1,141 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2021-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
Application
Test-exprValue
Description
Test low-level polymorphic value container (exprValue)
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "IOstreams.H"
#include "ITstream.H"
#include "exprValue.H"
using namespace Foam;
void printInfo(const expressions::exprValue& val)
{
Info<< "Boxed type:" << int(val.typeCode())
<< " (" << val.valueTypeName() << ") good:"
<< val.good() << " => " << val << nl;
}
expressions::exprValue tryParse(const std::string& str)
{
expressions::exprValue val, val2;
ITstream is(str);
const bool ok = val.read(is);
Info<< "read " << Foam::name(val.typeCode()) << " from " << str;
if (ok)
{
Info<< " trailing tokens:" << is.nRemainingTokens() << nl
<< "value: " << val << nl;
}
else
{
Info<< " FAILED" << nl;
}
if (ok)
{
Info<< "Direct from string: ";
if (expressions::exprValue::read(str, val2))
{
Info<< "good" << nl;
}
else
{
Info<< "bad" << nl;
}
}
return val;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::noBanner();
argList::noParallel();
#include "setRootCase.H"
// Aborts
// expressions::exprValue value(std::string(""));
{
expressions::exprValue value;
// Nothing
printInfo(value);
value.set(scalar(100));
printInfo(value);
value.set(vector(1,2,3));
printInfo(value);
value = vector(4,5,6);
printInfo(value);
value = Zero;
printInfo(value);
value.clear();
printInfo(value);
value = 100 * vector(1,0,0);
printInfo(value);
}
{
Info<< nl << "Test parsing" << nl << nl;
for
(
const auto& input :
stringList
({
"()", // bad
"( 1 2 ", // also bad
"( ", // really bad
"(1 16 12)",
"(1 bad)",
"(5)",
"1.2345",
"5.678 trailing",
"true",
"false",
" 1 ",
" yes no "
})
)
{
(void) tryParse(input);
}
}
return 0;
}
// ************************************************************************* //

View File

@ -183,6 +183,7 @@ $(expr)/exprTools/exprTools.C
$(expr)/scanToken/exprScanToken.C
$(expr)/traits/exprTraits.C
$(expr)/value/exprValue.C
$(expr)/exprDriver/exprDriver.C
$(expr)/exprDriver/exprDriverFields.C

View File

@ -24,7 +24,7 @@ License
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Namespace
Class
Foam::expressions::exprString
Description

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2021 OpenCFD Ltd.
Copyright (C) 2021-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -32,7 +32,7 @@ License
Foam::expressions::scanToken Foam::expressions::scanToken::null()
{
scanToken tok;
tok.type_ = LABEL;
tok.type_ = tokenType::LABEL;
tok.labelValue = 0;
return tok;
@ -41,12 +41,12 @@ Foam::expressions::scanToken Foam::expressions::scanToken::null()
void Foam::expressions::scanToken::destroy()
{
if (type_ == VECTOR)
if (type_ == tokenType::VECTOR)
{
delete vectorPtr;
vectorPtr = nullptr;
}
else if (type_ == WORD)
else if (type_ == tokenType::WORD)
{
delete wordPtr;
wordPtr = nullptr;
@ -56,35 +56,35 @@ void Foam::expressions::scanToken::destroy()
void Foam::expressions::scanToken::setLabel(label val)
{
type_ = LABEL;
type_ = tokenType::LABEL;
labelValue = val;
}
void Foam::expressions::scanToken::setScalar(scalar val)
{
type_ = SCALAR;
type_ = tokenType::SCALAR;
scalarValue = val;
}
void Foam::expressions::scanToken::setVector(scalar x, scalar y, scalar z)
{
type_ = VECTOR;
type_ = tokenType::VECTOR;
vectorPtr = new Foam::vector(x, y, z);
}
void Foam::expressions::scanToken::setVector(const vector& val)
{
type_ = VECTOR;
type_ = tokenType::VECTOR;
vectorPtr = new Foam::vector(val);
}
void Foam::expressions::scanToken::setWord(const word& val)
{
type_ = WORD;
type_ = tokenType::WORD;
wordPtr = new Foam::word(val);
}

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2019-2021 OpenCFD Ltd.
Copyright (C) 2019-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -33,13 +33,15 @@ Description
\*---------------------------------------------------------------------------*/
#ifndef expressions_scanToken_H
#define expressions_scanToken_H
#ifndef Foam_expressions_scanToken_H
#define Foam_expressions_scanToken_H
#include "scalar.H"
#include "vector.H"
#include "word.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace expressions
@ -80,7 +82,7 @@ struct scanToken
// Member Functions
//- Return a null token - in lieu of a default constructor
//- Return a null token (label = 0) - in lieu of a default constructor
static scanToken null();
//- Assign type/value to be LABEL. Does not call destroy().
@ -98,6 +100,12 @@ struct scanToken
//- Assign type/value to be WORD (name). Does not call destroy().
void setWord(const word& val);
//- True if a pointer type
bool is_pointer() const noexcept
{
return (type_ == tokenType::VECTOR || type_ == tokenType::WORD);
}
//- Manual deletion of pointer types
void destroy();
};

View File

@ -27,45 +27,82 @@ License
#include "exprTraits.H"
//TBD: handle complex?
#undef FOR_ALL_EXPR_TYPE_CODES
#define FOR_ALL_EXPR_TYPE_CODES(Macro, ...) \
Macro(bool, __VA_ARGS__) \
Macro(label, __VA_ARGS__) \
Macro(scalar, __VA_ARGS__) \
Macro(vector, __VA_ARGS__) \
Macro(sphericalTensor, __VA_ARGS__) \
Macro(symmTensor, __VA_ARGS__) \
Macro(tensor, __VA_ARGS__)
// * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
Foam::expressions::valueTypeCode
Foam::expressions::valueTypeCodeOf(const word& dataTypeName)
Foam::direction
Foam::expressions::Detail::nComponents
(
const expressions::valueTypeCode typeCode
) noexcept
{
#undef stringToTypeCode
#define stringToTypeCode(Type) \
\
if (dataTypeName == exprTypeTraits<Type>::name) \
{ \
return expressions::valueTypeCode::type_##Type; \
switch (typeCode)
{
case expressions::valueTypeCode::NONE :
case expressions::valueTypeCode::INVALID :
{
break;
}
#undef doLocalCode
#define doLocalCode(Type, UnusedParam) \
\
case expressions::valueTypeCode::type_##Type : \
{ \
return pTraits<Type>::nComponents; \
}
FOR_ALL_EXPR_TYPE_CODES(doLocalCode);
#undef doLocalCode
}
return 0;
}
Foam::expressions::valueTypeCode
Foam::expressions::valueTypeCodeOf
(
const word& dataTypeName,
const expressions::valueTypeCode deflt
)
{
if (!dataTypeName.empty())
{
stringToTypeCode(bool);
stringToTypeCode(label);
stringToTypeCode(scalar);
stringToTypeCode(vector);
stringToTypeCode(tensor);
stringToTypeCode(sphericalTensor);
stringToTypeCode(symmTensor);
}
#undef stringToTypeCode
// Could compare with pTraits<Type>::typeName instead of
// exprTypeTraits<Type>::name, but then we might miss
// possible typos.
return expressions::valueTypeCode::INVALID;
#undef doLocalCode
#define doLocalCode(Type, UnusedParam) \
\
if (dataTypeName == exprTypeTraits<Type>::name) \
{ \
return expressions::valueTypeCode::type_##Type; \
}
FOR_ALL_EXPR_TYPE_CODES(doLocalCode);
#undef doLocalCode
}
return deflt;
}
Foam::word Foam::name(const expressions::valueTypeCode typeCode)
{
#undef case_typeCodeToString
#define case_typeCodeToString(Type) \
\
case expressions::valueTypeCode::type_##Type : \
{ \
return exprTypeTraits<Type>::name; \
}
switch (typeCode)
{
case expressions::valueTypeCode::NONE :
@ -75,22 +112,27 @@ Foam::word Foam::name(const expressions::valueTypeCode typeCode)
case expressions::valueTypeCode::INVALID :
{
// ie, ""
// returns ""
break;
}
case_typeCodeToString(bool);
case_typeCodeToString(label);
case_typeCodeToString(scalar);
case_typeCodeToString(vector);
case_typeCodeToString(tensor);
case_typeCodeToString(sphericalTensor);
case_typeCodeToString(symmTensor);
#undef doLocalCode
#define doLocalCode(Type, UnusedParam) \
case expressions::valueTypeCode::type_##Type : \
{ \
return exprTypeTraits<Type>::name; \
}
FOR_ALL_EXPR_TYPE_CODES(doLocalCode);
#undef doLocalCode
}
#undef case_typeCodeToString
return word();
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#undef FOR_ALL_EXPR_TYPE_CODES
// ************************************************************************* //

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2021 OpenCFD Ltd.
Copyright (C) 2021-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -28,18 +28,33 @@ Class
Description
Simple type identifiers for polymorphic expression values.
The definitions are similar to std::integral_constant in that they
provide value, value_type (and name).
SourceFiles
exprTraits.C
Namespace
Foam::expressions
Description
A namespace for expression-related classes/traits etc.
Namespace
Foam::expressions::Detail
Description
A namespace for implementation details related to expressions.
\*---------------------------------------------------------------------------*/
#ifndef expressions_exprTraits_H
#define expressions_exprTraits_H
#ifndef Foam_expressions_exprTraits_H
#define Foam_expressions_exprTraits_H
// Regular field types
#include "label.H"
#include "scalar.H"
//TBD: #include "complex.H"
#include "vector.H"
#include "sphericalTensor.H"
#include "symmTensor.H"
@ -56,7 +71,7 @@ namespace expressions
//- An enumeration of known and expected expression value types.
// Do not rely on the enumeration values for any direct coding.
//
// \note NONE use used when initializing types, whereas INVALID is used
// \note NONE used when initializing types, whereas INVALID is used
// for unsupported types (never as a stored type).
// This avoids false positives when testing.
//
@ -72,6 +87,7 @@ enum class valueTypeCode : unsigned char
type_bool, //!< Type is 'bool'
type_label, //!< Type is 'label'
type_scalar, //!< Type is 'scalar'
//TBD: type_complex, //!< Type is 'complex'
// Rank 1 types
type_vector, //!< Type is 'vector'
@ -86,7 +102,23 @@ enum class valueTypeCode : unsigned char
// Global Functions
//- From string to valueTypeCode (if any)
valueTypeCode valueTypeCodeOf(const word& dataTypeName);
valueTypeCode valueTypeCodeOf
(
const word& dataTypeName,
// Fallback for unknown
const expressions::valueTypeCode deflt = expressions::valueTypeCode::INVALID
);
// Some implementation detail
namespace Detail
{
//- The number of components associated with given valueTypeCode
::Foam::direction nComponents(const expressions::valueTypeCode) noexcept;
} // End namespace Detail
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -101,45 +133,58 @@ valueTypeCode valueTypeCodeOf(const word& dataTypeName);
template<class Type>
struct exprTypeTraits
{
// The value type
typedef Type value_type;
// The type name (similar to pTraits typeName)
static constexpr const char* const name = "";
// The enumeration number associated with the type
static constexpr
::Foam::expressions::valueTypeCode value =
::Foam::expressions::valueTypeCode::INVALID;
// The rank of the type
static constexpr ::Foam::direction rank = 0;
// The number of components
static constexpr ::Foam::direction nComponents = 0;
};
#undef defineExprTypeTraits
#define defineExprTypeTraits(Type, Name) \
#define defineExprTypeTraits(Type, Name, Rank, NumCmpts) \
/*! \brief \c Name expression traits */ \
template<> \
struct exprTypeTraits<Type> \
{ \
typedef Type value_type; \
static constexpr const char* const name = #Name; \
static constexpr const char* const name = #Name ; \
static constexpr \
::Foam::expressions::valueTypeCode value = \
::Foam::expressions::valueTypeCode::type_##Name; \
::Foam::expressions::valueTypeCode::type_##Name ; \
static constexpr ::Foam::direction rank = Rank ; \
static constexpr ::Foam::direction nComponents = NumCmpts ; \
};
// Define with "name" to match regular pTraits typeName
defineExprTypeTraits(bool, bool);
defineExprTypeTraits(::Foam::label, label);
defineExprTypeTraits(::Foam::scalar, scalar);
defineExprTypeTraits(::Foam::vector, vector);
defineExprTypeTraits(::Foam::tensor, tensor);
defineExprTypeTraits(::Foam::sphericalTensor, sphericalTensor);
defineExprTypeTraits(::Foam::symmTensor, symmTensor);
// Define with name to match pTraits::typeName, with rank/nComponents
defineExprTypeTraits(bool, bool, 0, 1);
defineExprTypeTraits(::Foam::label, label, 0, 1);
defineExprTypeTraits(::Foam::scalar, scalar, 0, 1);
//TBD: defineExprTypeTraits(::Foam::complex, complex, 0, 2);
defineExprTypeTraits(::Foam::vector, vector, 1, 3);
defineExprTypeTraits(::Foam::tensor, tensor, 2, 9);
defineExprTypeTraits(::Foam::sphericalTensor, sphericalTensor, 2, 1);
defineExprTypeTraits(::Foam::symmTensor, symmTensor, 2, 6);
#undef defineExprTypeTraits
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
//- A word representation of a valueTypeCode. Empty for INVALID
//- A word representation of a valueTypeCode.
//- Empty for expressions::valueTypeCode::INVALID
word name(const expressions::valueTypeCode typeCode);
//- A word representation of a valueTypeCode. Empty for INVALID
//- A word representation of a valueTypeCode.
//- Empty for expressions::valueTypeCode::INVALID
template<>
struct nameOp<expressions::valueTypeCode>
{

View File

@ -0,0 +1,341 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2021-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM 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.
OpenFOAM 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 OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "exprValue.H"
#include "ITstream.H"
#include "Switch.H"
#include <cstring> // For memcpy, memset
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace Foam
{
template<class Type>
static void fillTokens(const Type& val, tokenList& toks)
{
const direction nCmpt = pTraits<Type>::nComponents;
const direction nParen = 2*(pTraits<Type>::rank || (nCmpt > 1) ? 1 : 0);
toks.resize_nocopy(nCmpt + nParen);
auto iter = toks.begin();
if (nParen)
{
*iter = token::BEGIN_LIST;
++iter;
}
for (direction cmpt = 0; cmpt < nCmpt; ++cmpt)
{
*iter = component(val, cmpt);
++iter;
}
if (nParen)
{
*iter = token::END_LIST;
++iter;
}
}
//- Specialized for bool
template<>
void fillTokens<bool>(const bool& val, tokenList& toks)
{
toks.resize_nocopy(1);
toks.front() = token::boolean(val);
}
//- Specialized for label
template<>
void fillTokens<label>(const label& val, tokenList& toks)
{
toks.resize_nocopy(1);
toks.front() = val;
}
//- Specialized for scalar
template<>
void fillTokens<scalar>(const scalar& val, tokenList& toks)
{
toks.resize_nocopy(1);
toks.front() = val;
}
} // End namespace Foam
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
Foam::expressions::valueTypeCode
Foam::expressions::exprValue::peekType(const ITstream& is)
{
expressions::valueTypeCode whichCode(expressions::valueTypeCode::INVALID);
const token& tok0 = is.peek();
if (tok0.isPunctuation(token::BEGIN_LIST))
{
// Expecting "( content )" - eg, (x y z), (xx xy ...)
// First component starts after the opening '('.
// Can use the current index if the '(' actually came from
// the putBack.
const label firstCmpti = (is.tokenIndex() + (is.hasPutback() ? 0 : 1));
// Search for closing ')', require all components to be numbers
for (label endCmpti = firstCmpti; endCmpti < is.size(); ++endCmpti)
{
const token& tok = is[endCmpti];
///InfoErr
/// << "check token: " << (endCmpti-firstCmpti) << " : "
/// << is[endCmpti].name() << nl;
if (tok.isPunctuation(token::END_LIST))
{
// Select based on the number of components
// cf. pTraits<Type>::nComponents
switch (endCmpti - firstCmpti)
{
case 1: // pTraits<sphericalTensor>::nComponents
whichCode = exprTypeTraits<sphericalTensor>::value;
break;
// FUTURE?
// case 2: // pTraits<complex>::nComponents
// whichCode = exprTypeTraits<complex>::value;
// break;
case 3: // pTraits<vector>::nComponents
whichCode = exprTypeTraits<vector>::value;
break;
case 6: // pTraits<symmTensor>::nComponents
whichCode = exprTypeTraits<symmTensor>::value;
break;
case 9: // pTraits<tensor>::nComponents
whichCode = exprTypeTraits<tensor>::value;
break;
}
// Closing ')' terminates peeking
break;
}
else if (!tok.isNumber())
{
// All components should be numeric
break;
}
}
}
else if (tok0.good())
{
/// InfoErr<< "check token: " << tok0.info() << nl;
if (tok0.isScalar())
{
whichCode = exprTypeTraits<scalar>::value;
}
else if (tok0.isLabel())
{
whichCode = exprTypeTraits<label>::value;
}
else if (Switch(tok0).good())
{
whichCode = exprTypeTraits<bool>::value;
}
}
return whichCode;
}
bool Foam::expressions::exprValue::read
(
const std::string& str,
exprValue& val
)
{
ITstream is(str);
// No trailing non-whitespace!
return (val.read(is) && !is.nRemainingTokens());
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::expressions::exprValue::clear()
{
std::memset(static_cast<void*>(this), '\0', sizeof(*this));
// Redundant: typeCode_ = expressions::valueTypeCode::NONE;
}
void Foam::expressions::exprValue::deepCopy(const exprValue& rhs)
{
if (this != &rhs)
{
// Self-assignment is a no-op
std::memcpy(static_cast<void*>(this), &rhs, sizeof(*this));
}
}
Foam::tokenList Foam::expressions::exprValue::tokens() const
{
tokenList toks;
switch (typeCode_)
{
#undef doLocalCode
#define doLocalCode(Type, UnusedParam) \
\
case expressions::valueTypeCode::type_##Type : \
{ \
const Type* dataPtr = data_.get<Type>(); \
if (dataPtr) \
{ \
fillTokens<Type>(*dataPtr, toks); \
} \
break; \
}
FOR_ALL_EXPR_VALUE_TYPES(doLocalCode);
#undef doLocalCode
// exprValue may only be a subset of valueTypeCode types
default: break;
}
return toks;
}
void Foam::expressions::exprValue::print(Ostream& os) const
{
switch (typeCode_)
{
#undef doLocalCode
#define doLocalCode(Type, UnusedParam) \
\
case expressions::valueTypeCode::type_##Type : \
{ \
const Type* dataPtr = data_.get<Type>(); \
if (dataPtr) \
{ \
os << *dataPtr; \
} \
break; \
}
FOR_ALL_EXPR_VALUE_TYPES(doLocalCode);
#undef doLocalCode
// exprValue may only be a subset of valueTypeCode types
default: break;
}
}
bool Foam::expressions::exprValue::read(ITstream& is)
{
clear(); // type: none, value: zero
const valueTypeCode whichCode(exprValue::peekType(is));
switch (whichCode)
{
#undef doLocalCode
#define doLocalCode(Type, UnusedParam) \
\
case expressions::valueTypeCode::type_##Type : \
{ \
data_.set<Type>(pTraits<Type>(is)); \
typeCode_ = whichCode; \
return true; \
}
FOR_ALL_EXPR_VALUE_TYPES(doLocalCode);
#undef doLocalCode
// exprValue may only be a subset of valueTypeCode types
default: break;
}
return false;
}
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
Foam::Ostream& Foam::operator<<
(
Ostream& os,
const expressions::exprValue& val
)
{
val.print(os);
return os;
}
template<>
Foam::Ostream& Foam::operator<<
(
Ostream& os,
const InfoProxy<expressions::exprValue>& iproxy
)
{
const auto& val = *iproxy;
if (val.good())
{
os << val.valueTypeName() << ": ";
val.print(os);
}
else
{
// typeCode_ is *never* set to INVALID,
// so NONE is the only remaining non-good type
os << "none";
}
return os;
}
// ************************************************************************* //

View File

@ -0,0 +1,348 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2021-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM 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.
OpenFOAM 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 OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::expressions::exprValue
Description
A polymorphic typed union of simple primitive and VectorSpace types.
It uses a 'fatter' representation that includes standard VectorSpace
types, which avoids heap allocations at the expense of more storage.
This is mostly not an issue since lists and fields would box/unbox
an entire field, not individual values.
SourceFiles
exprValue.C
exprValueI.H
\*---------------------------------------------------------------------------*/
#ifndef Foam_expressions_exprValue_H
#define Foam_expressions_exprValue_H
#include "exprTraits.H"
#include "error.H"
#include "tokenList.H"
#include "InfoProxy.H"
#include <typeinfo>
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Execute macro for known exprValue types, with more probably ones first
#undef FOR_ALL_EXPR_VALUE_TYPES
#define FOR_ALL_EXPR_VALUE_TYPES(Macro, ...) \
Macro(scalar, __VA_ARGS__) \
Macro(vector, __VA_ARGS__) \
Macro(tensor, __VA_ARGS__) \
Macro(symmTensor, __VA_ARGS__) \
Macro(sphericalTensor, __VA_ARGS__) \
Macro(label, __VA_ARGS__) \
Macro(bool, __VA_ARGS__)
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward Declarations
class ITstream;
namespace expressions
{
namespace Detail
{
/*---------------------------------------------------------------------------*\
Class exprValueUnion Declaration
\*---------------------------------------------------------------------------*/
//- The data content (as a union).
// The stem of the member names correspond identically to the data types
// to enable macro definitions (naming similar to Foam::token)
union exprValueUnion
{
// Member Data
#undef declareUnionMember
#define declareUnionMember(Type, UnusedParam) \
\
/*! Define Type data member */ \
Type Type##Value;
FOR_ALL_EXPR_VALUE_TYPES(declareUnionMember);
#undef declareUnionMember
// Member Functions
//- Runtime 'assert' for unimplemented generic methods
// This seems to be the best way to catch programming errors
// since static_assert does not help here.
// The 'noexcept' is slightly misleading (needed for the caller)
static void notSpecialized(const std::string& msg) noexcept
{
FatalErrorInFunction
<< "non-specialized: " << msg.c_str() << endl
<< abort(FatalError);
}
//- Return read pointer to typed union member,
//- which is nullptr for unspecialized versions
template<class Type>
inline const Type* get() const noexcept { return nullptr; }
//- No generic set(), only specialized methods
template<class Type>
inline void set(const Type& val) noexcept
{
notSpecialized("set<" + std::string(typeid(Type).name()) + '>');
}
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Type-specific get/set (definitions)
#undef defineMultiTypeValueUnionMethods
#define defineMultiTypeValueUnionMethods(Type, UnusedParam) \
\
/*! Return read pointer to Type union member */ \
template<> \
inline const Type* exprValueUnion::get<Type>() const noexcept \
{ \
return &(Type##Value); \
} \
\
/*! Set Type value */ \
template<> \
inline void exprValueUnion::set<Type>(const Type& val) noexcept \
{ \
Type##Value = val; \
}
// Type-specific methods
FOR_ALL_EXPR_VALUE_TYPES(defineMultiTypeValueUnionMethods);
#undef defineMultiTypeValueUnionMethods
} // End namespace Detail
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
/*---------------------------------------------------------------------------*\
Class exprValue Declaration
\*---------------------------------------------------------------------------*/
class exprValue
{
// Private Data
//- The data content (as a union)
Detail::exprValueUnion data_;
//- The data type (as a type-code)
expressions::valueTypeCode typeCode_ = valueTypeCode::NONE;
// Private Member Functions
//- Assign zero, preserving the data type
inline void fill_zero();
//- Copy assignment
void deepCopy(const exprValue& rhs);
public:
//- Runtime type information
ClassName("exprValue");
// Constructors
//- Default construct (zero-initialized) as 'none'
inline exprValue();
//- Copy construct (deep copy)
inline exprValue(const exprValue& rhs);
//- Construct from Type. Fatal for unsupported types
template<class Type>
inline explicit exprValue(const Type& val);
// Static Methods (valueTypeCode)
//- True if valueTypeCode is not none/invalid
inline static bool good
(
const expressions::valueTypeCode
) noexcept;
//- True if valueTypeCode is bool/label
inline static bool is_integral
(
const expressions::valueTypeCode
) noexcept;
//- The number of components associated with the valueTypeCode
inline static direction nComponents
(
const expressions::valueTypeCode
) noexcept;
//- True if the specified type is supported
template<class Type>
inline static bool supportedType();
// Static Methods
//- Detect the type from the available tokens.
// Possible ambiguity between label and scalar.
// \return INVALID for unsupported type
static expressions::valueTypeCode peekType(const ITstream& is);
//- Read entire string as a exprValue,
//- skipping leading/trailing whitespace.
// \return True if successful.
static bool read(const std::string& str, exprValue& val);
// Member Functions
//- The value type code
expressions::valueTypeCode typeCode() const noexcept
{
return typeCode_;
}
//- True if the value type is not none/invalid
inline bool good() const noexcept;
//- True if the value type is an integral (bool or label)
inline bool is_integral() const noexcept;
//- The number of components associated with the value type
inline direction nComponents() const noexcept;
//- The name for the value type. Similar to pTraits typeName
inline word valueTypeName() const;
//- The name for the compound token (for a List of values).
//- Eg, \c List\<scalar\>, \c List\<vector\> ...
inline word listCompoundName() const;
//- Reset to 'none'
void clear();
//- Guess type and read tokens (if possible)
// \return True on success
bool read(ITstream& is);
// Typed Access
//- Return non-null pointer to the data element (if types match).
//- Can also be tested as a bool.
template<class Type>
inline const Type* isA() const noexcept;
//- Assign from type.
//- Returns false and sets to 'none' for unsupported types
template<class Type>
inline bool set(const Type& val);
//- Return the value. Return pTraits zero if the types mismatch
template<class Type>
inline const Type& get() const;
// Type-specific methods, operators
#undef defineUnionMethods
#define defineUnionMethods(Type, UnusedParam) \
\
/*! True if value is a Type */ \
bool is_##Type() const noexcept \
{ \
return (typeCode_ == exprTypeTraits<Type>::value); \
} \
\
/*! Assign from Type */ \
void operator=(const Type& val) { this->set<Type>(val); }
FOR_ALL_EXPR_VALUE_TYPES(defineUnionMethods)
#undef defineUnionMethods
//- Copy assignment
void operator=(const exprValue& rhs) { deepCopy(rhs); }
//- Assign from zero. Changes value but not type
void operator=(const Foam::zero) { fill_zero(); }
// Output
//- Return info proxy for printing information to a stream
InfoProxy<exprValue> info() const { return *this; }
//- The value as tokens. An empty list is returned for NONE.
tokenList tokens() const;
//- Print the (type-specific) content
void print(Ostream& os) const;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace expressions
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
//- Write value to output stream in ASCII format
Ostream& operator<<(Ostream& os, const expressions::exprValue& val);
template<>
Ostream& operator<<(Ostream& os, const InfoProxy<expressions::exprValue>&);
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "exprValueI.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,213 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2021-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM 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.
OpenFOAM 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 OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "exprValue.H"
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
inline bool Foam::expressions::exprValue::good
(
const expressions::valueTypeCode valTypeCode
) noexcept
{
return
(
valTypeCode != expressions::valueTypeCode::NONE
&& valTypeCode != expressions::valueTypeCode::INVALID
);
}
inline bool Foam::expressions::exprValue::is_integral
(
const expressions::valueTypeCode valTypeCode
) noexcept
{
return
(
valTypeCode == expressions::valueTypeCode::type_bool
|| valTypeCode == expressions::valueTypeCode::type_label
);
}
inline Foam::direction Foam::expressions::exprValue::nComponents
(
const expressions::valueTypeCode valTypeCode
) noexcept
{
return expressions::Detail::nComponents(valTypeCode);
}
template<class Type>
inline bool Foam::expressions::exprValue::supportedType()
{
return
(
expressions::valueTypeCode::INVALID != exprTypeTraits<Type>::value
);
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
inline void Foam::expressions::exprValue::fill_zero()
{
// Preserve the type
const auto oldType(typeCode_);
clear();
typeCode_ = oldType;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
inline Foam::expressions::exprValue::exprValue()
{
clear(); // zero-initialized (type is NONE)
}
inline Foam::expressions::exprValue::exprValue(const exprValue& rhs)
{
deepCopy(rhs);
}
template<class Type>
inline Foam::expressions::exprValue::exprValue(const Type& val)
{
clear(); // type: none, value: zero
set<Type>(val);
if (!good())
{
FatalErrorInFunction
<< "Cannot construct for unsupported type: "
<< typeid(Type).name() << endl
<< abort(FatalError);
}
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
inline bool Foam::expressions::exprValue::good() const noexcept
{
return exprValue::good(typeCode_);
}
inline bool Foam::expressions::exprValue::is_integral() const noexcept
{
return exprValue::is_integral(typeCode_);
}
inline Foam::direction
Foam::expressions::exprValue::nComponents() const noexcept
{
return expressions::Detail::nComponents(typeCode_);
}
inline Foam::word
Foam::expressions::exprValue::valueTypeName() const
{
return Foam::name(typeCode_);
}
inline Foam::word Foam::expressions::exprValue::listCompoundName() const
{
return
(
good()
// List<scalar>, List<vector> ...
? word("List<" + Foam::name(typeCode_) + '>')
: word()
);
}
template<class Type>
inline bool Foam::expressions::exprValue::set(const Type& val)
{
const auto whichCode(exprTypeTraits<Type>::value);
if
(
whichCode == expressions::valueTypeCode::NONE
|| whichCode == expressions::valueTypeCode::INVALID
)
{
clear();
typeCode_ = expressions::valueTypeCode::NONE;
return false;
}
data_.set<Type>(val);
typeCode_ = whichCode;
return true;
}
template<class Type>
inline const Type& Foam::expressions::exprValue::get() const
{
const Type* dataPtr = this->isA<Type>();
if (dataPtr)
{
return *dataPtr;
}
else
{
return pTraits<Type>::zero;
}
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
template<class Type>
inline const Type* Foam::expressions::exprValue::isA() const noexcept
{
// NOTE: typeCode_ is *never* set to INVALID,
// so no false positives for unsupported types
return
(
(typeCode_ == exprTypeTraits<Type>::value)
? data_.get<Type>()
: nullptr
);
}
// ************************************************************************* //