ENH: improve/simplify streaming of exprValue

- ensure that operator<< and operator>> behave symmetrically
This commit is contained in:
Mark Olesen
2023-11-22 14:26:37 +01:00
parent 2ab3490552
commit c5e4b62df7
14 changed files with 739 additions and 42 deletions

View File

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

View File

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

View File

@ -11,7 +11,7 @@ License
This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
Application
Test-exprValue
Test-exprValue1
Description
Test low-level polymorphic value container (exprValue)
@ -59,11 +59,11 @@ expressions::exprValue tryParse(const std::string& str)
Info<< "Direct from string: ";
if (expressions::exprValue::read(str, val2))
{
Info<< "good" << nl;
Info<< "OK" << nl;
}
else
{
Info<< "bad" << nl;
Info<< "NOK" << nl;
}
}
return val;
@ -128,21 +128,22 @@ int main(int argc, char *argv[])
expressions::exprValue oldValue(value);
// Since the IO serialization is not symmetric (in ASCII) there
// is no 'operator>>' defined and thus the regular Pstream::broadcast
// will not compile.
// Even although the data are contiguous and that code branch is never
// used.
// Fails to compile: Pstream::broadcast(value);
// Broadcast manually
UPstream::broadcast
(
value.data_bytes(),
value.size_bytes(),
UPstream::worldComm
);
if (true)
{
// Broadcast with serialization
Pstream::broadcast(value);
}
else
{
// Broadcast manually
UPstream::broadcast
(
value.data_bytes(),
value.size_bytes(),
UPstream::worldComm
);
}
Pout<< "same values: " << (oldValue == value) << nl;

View File

@ -0,0 +1,5 @@
exprValueFieldTag.cxx
Test-exprValue2.cxx
EXE = $(FOAM_USER_APPBIN)/Test-exprValue2

View File

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

View File

@ -0,0 +1,143 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 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/>.
Description
Test exprValue uniformity checks
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "IOobject.H"
#include "IOstreams.H"
#include "labelList.H"
#include "scalarField.H"
#include "vectorField.H"
#include "DynamicList.H"
#include "Random.H"
#include "exprValue.H"
#include "exprValueFieldTag.H"
using namespace Foam;
Ostream& printInfo(const expressions::exprValueFieldTag& tag)
{
tag.print(Pout);
return Pout;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:
int main(int argc, char *argv[])
{
using fieldTag = expressions::exprValueFieldTag;
Random rnd(123456);
argList::noCheckProcessorDirectories();
#include "setRootCase.H"
{
scalarField fld1(20);
scalarField fld2a(20, Zero);
scalarField fld2b(10, 3.10);
scalarField fld3;
forAll(fld1, i)
{
fld1[i] = rnd.position<scalar>(0, 20);
}
fieldTag tag1(fld1.begin(), fld1.end());
fieldTag tag2a(fld2a.begin(), fld2a.end());
fieldTag tag2b(fld2b.begin(), fld2b.end());
fieldTag tag3(fld3.begin(), fld3.end());
printInfo(tag1) << nl;
printInfo(tag2a) << nl;
printInfo(tag2b) << nl;
printInfo(tag3) << nl;
fieldTag result;
result = fieldTag::combineOp{}(tag1, tag2a);
printInfo(result) << nl;
Info<< "combine: ";
printInfo(tag2a) << " and ";
printInfo(tag2b) << nl;
result = fieldTag::combineOp{}(tag2a, tag2b);
printInfo(result) << nl;
}
{
vectorField fld2a(20, Foam::zero{});
vectorField fld2b(10, vector::uniform(3.10));
fieldTag tag2a(fld2a.begin(), fld2a.end());
fieldTag tag2b(fld2b.begin(), fld2b.end());
printInfo(tag2a) << nl;
printInfo(tag2b) << nl;
fieldTag result;
Info<< "combine: ";
printInfo(tag2a) << " and ";
printInfo(tag2b) << nl;
result = fieldTag::combineOp{}(tag2a, tag2b);
printInfo(result) << nl;
}
if (UPstream::parRun())
{
scalarField fld;
if (!UPstream::master())
{
fld.resize(UPstream::myProcNo(), UPstream::myProcNo());
}
fieldTag oldTag(fld.begin(), fld.end());
printInfo(oldTag) << " input: " << fld << " <- before reduce" << nl;
fieldTag newTag = returnReduce(oldTag, fieldTag::combineOp());
printInfo(newTag) << " <- after reduce" << nl;
}
Info<< "\nEnd\n" << nl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,170 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 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::exprValueFieldTag
Description
A polymorphic single-value container for tracking Field content
as uniform etc.
SourceFiles
exprValueFieldTag.cxx
\*---------------------------------------------------------------------------*/
#ifndef Foam_expressions_exprValueFieldTag_H
#define Foam_expressions_exprValueFieldTag_H
#include "exprValue.H"
#include "List.H" // For ListPolicy
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace expressions
{
/*---------------------------------------------------------------------------*\
Class exprValueFieldTag Declaration
\*---------------------------------------------------------------------------*/
class exprValueFieldTag
{
// Private data
//- Uniformity of field
int uniformity_{};
//- Representative (uniform) value for the field
expressions::exprValue value_{};
public:
// Constructors
//- Default construct
exprValueFieldTag() = default;
//- Construct from a range of values
template<class Type>
explicit exprValueFieldTag(const Type* first, const Type* last)
{
set(first, last);
}
// Member Functions
//- True if the uniformity is "empty"
bool empty() const noexcept;
//- True if the uniformity is "uniform"
bool is_uniform() const noexcept;
//- True if the uniformity is "non-uniform"
bool is_nonuniform() const noexcept;
//- Test for equality of the values
bool equal(const exprValueFieldTag& rhs) const;
//- Set value and uniformity from range of data
template<class Type>
void set(const Type* first, const Type* last)
{
uniformity_ =
Foam::Detail::ListPolicy::check_uniformity(first, last);
if (first != last) // or uniformity_ != EMPTY
{
value_.set<Type>(*first);
}
else
{
value_.set<Type>(Foam::zero{});
}
}
//- Set uniform type and value
template<class Type>
void set_uniform(const Type& val)
{
uniformity_ = Foam::Detail::ListPolicy::uniformity::UNIFORM;
value_.set<Type>(val);
}
//- Set as non-uniform
void set_nouniform() noexcept;
// Reduction operations
//- Combine - eg, for global uniformity
void combine(const exprValueFieldTag& b);
//- Binary operator to be used by reduce function for detecting
//- global uniformity
struct combineOp
{
exprValueFieldTag operator()
(
const exprValueFieldTag& a,
const exprValueFieldTag& b
) const
{
exprValueFieldTag result(a);
result.combine(b);
return result;
}
};
// IO Operations
void read(Istream& is);
void write(Ostream& os) const;
//- Print description to Ostream
void print(Ostream& os) const;
};
} // End namespace expressions
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// IO
Istream& operator>>(Istream&, expressions::exprValueFieldTag&);
Ostream& operator<<(Ostream&, const expressions::exprValueFieldTag&);
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,160 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 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 "exprValueFieldTag.H"
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::expressions::exprValueFieldTag::empty() const noexcept
{
return
(
uniformity_ == Foam::Detail::ListPolicy::uniformity::EMPTY
);
}
bool Foam::expressions::exprValueFieldTag::is_uniform() const noexcept
{
return
(
uniformity_ == Foam::Detail::ListPolicy::uniformity::UNIFORM
);
}
bool Foam::expressions::exprValueFieldTag::is_nonuniform() const noexcept
{
return
(
uniformity_ == Foam::Detail::ListPolicy::uniformity::NONUNIFORM
);
}
bool Foam::expressions::exprValueFieldTag::equal
(
const exprValueFieldTag& rhs
) const
{
return (value_ == rhs.value_);
}
void Foam::expressions::exprValueFieldTag::set_nouniform() noexcept
{
uniformity_ = Foam::Detail::ListPolicy::uniformity::NONUNIFORM;
value_ = Foam::zero{};
}
void Foam::expressions::exprValueFieldTag::combine
(
const exprValueFieldTag& b
)
{
if (b.empty())
{
// no-op
return;
}
exprValueFieldTag& a = *this;
if (a.empty())
{
a = b;
}
else if (a.is_nonuniform())
{
// Already non-uniform/mixed
// a.uniformity_ |= b.uniformity_;
a.value_ = Foam::zero{};
}
else if (a.is_uniform() && b.is_uniform())
{
// Both are uniform, but are they the same value?
if (!a.equal(b))
{
a.set_nouniform();
}
}
}
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
void Foam::expressions::exprValueFieldTag::read(Istream& is)
{
label uniformTag;
is >> uniformTag;
uniformity_ = int(uniformTag);
value_.read(is);
}
void Foam::expressions::exprValueFieldTag::write(Ostream& os) const
{
os << label(uniformity_);
value_.write(os, false); // No pruning
}
void Foam::expressions::exprValueFieldTag::print(Ostream& os) const
{
os << "{ uniform:"
<< label(uniformity_)
<< " type:" << label(value_.typeCode())
<< " value: " << value_ << " }";
}
Foam::Istream& Foam::operator>>
(
Istream& is,
expressions::exprValueFieldTag& tag
)
{
tag.read(is);
return is;
}
Foam::Ostream& Foam::operator<<
(
Ostream& os,
const expressions::exprValueFieldTag& tag
)
{
tag.write(os);
return os;
}
// ************************************************************************* //

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.
@ -61,7 +61,37 @@ Foam::expressions::Detail::nComponents
\
case expressions::valueTypeCode::type_##Type : \
{ \
return pTraits<Type>::nComponents; \
return exprTypeTraits<Type>::nComponents; \
}
FOR_ALL_EXPR_TYPE_CODES(doLocalCode);
#undef doLocalCode
}
return 0;
}
Foam::direction
Foam::expressions::Detail::rank
(
const expressions::valueTypeCode typeCode
) noexcept
{
switch (typeCode)
{
case expressions::valueTypeCode::NONE :
case expressions::valueTypeCode::INVALID :
{
break;
}
#undef doLocalCode
#define doLocalCode(Type, UnusedParam) \
\
case expressions::valueTypeCode::type_##Type : \
{ \
return exprTypeTraits<Type>::rank; \
}
FOR_ALL_EXPR_TYPE_CODES(doLocalCode);
@ -112,7 +142,7 @@ Foam::word Foam::name(const expressions::valueTypeCode typeCode)
case expressions::valueTypeCode::INVALID :
{
// returns ""
// returns "", could also return "bad"
break;
}

View File

@ -117,6 +117,9 @@ namespace Detail
//- The number of components associated with given valueTypeCode
::Foam::direction nComponents(const expressions::valueTypeCode) noexcept;
//- The vector-space rank associated with given valueTypeCode
::Foam::direction rank(const expressions::valueTypeCode) noexcept;
} // End namespace Detail

View File

@ -129,6 +129,10 @@ Foam::expressions::exprValue::peekType(const ITstream& is)
switch (endCmpti - firstCmpti)
{
case 0: // Explicitly provided '()' - ie, none
whichCode = expressions::valueTypeCode::NONE;
break;
case 1: // pTraits<sphericalTensor>::nComponents
whichCode = exprTypeTraits<sphericalTensor>::value;
break;
@ -177,6 +181,13 @@ Foam::expressions::exprValue::peekType(const ITstream& is)
{
whichCode = exprTypeTraits<bool>::value;
}
// Treat anything else as 'invalid', which also implicitly
// includes the token "bad"
// else if (tok0.isWord("bad"))
// {
// whichCode = expressions::valueTypeCode::INVALID;
// }
}
return whichCode;
@ -192,7 +203,7 @@ bool Foam::expressions::exprValue::read
ITstream is(str);
// No trailing non-whitespace!
return (val.read(is) && !is.nRemainingTokens());
return (val.readTokens(is) && !is.nRemainingTokens());
}
@ -215,10 +226,33 @@ void Foam::expressions::exprValue::deepCopy(const exprValue& rhs)
}
Foam::tokenList Foam::expressions::exprValue::tokens() const
Foam::tokenList Foam::expressions::exprValue::tokens(bool prune) const
{
// Handling for NONE, INVALID:
// - NONE => pair of ( ) brackets
// - INVALID => "bad" as a word
//
// With prune:
// - no output for either
tokenList toks;
if (!prune)
{
if (typeCode_ == expressions::valueTypeCode::NONE)
{
toks.resize(2);
toks.front() = token::BEGIN_LIST;
toks.back() = token::END_LIST;
return toks;
}
else if (typeCode_ == expressions::valueTypeCode::INVALID)
{
toks.emplace_back(word("bad"));
return toks;
}
}
switch (typeCode_)
{
#undef doLocalCode
@ -245,8 +279,29 @@ Foam::tokenList Foam::expressions::exprValue::tokens() const
}
void Foam::expressions::exprValue::print(Ostream& os) const
void Foam::expressions::exprValue::write(Ostream& os, bool prune) const
{
// Handling for NONE, INVALID:
// - NONE => pair of ( ) brackets
// - INVALID => "bad" as a word
//
// With prune:
// - no output for either
if (!prune)
{
if (typeCode_ == expressions::valueTypeCode::NONE)
{
os << token::BEGIN_LIST << token::END_LIST;
return;
}
else if (typeCode_ == expressions::valueTypeCode::INVALID)
{
os << word("bad");
return;
}
}
switch (typeCode_)
{
#undef doLocalCode
@ -271,12 +326,87 @@ void Foam::expressions::exprValue::print(Ostream& os) const
}
bool Foam::expressions::exprValue::read(ITstream& is)
bool Foam::expressions::exprValue::read(Istream& is)
{
ITstream* stream = dynamic_cast<ITstream*>(&is);
// Reading via tokens - simple for now
// Expect either a single token (scalar, label, word etc)
// or ( ... ) content
ITstream toks;
if (!stream)
{
token tok(is);
is.fatalCheck(FUNCTION_NAME);
if (tok.isPunctuation(token::BEGIN_LIST))
{
// Expecting "( content )" - eg, (x y z), (xx xy ...)
do
{
toks.add_tokens(tok);
is >> tok;
is.fatalCheck(FUNCTION_NAME);
}
while (!tok.isPunctuation(token::END_LIST));
if (tok.isPunctuation(token::END_LIST))
{
toks.add_tokens(tok);
}
}
else if (tok.good())
{
toks.add_tokens(tok);
}
// Truncate to number tokens read
toks.resize(toks.tokenIndex());
toks.seek(0);
stream = &toks;
}
return readTokens(*stream);
}
bool Foam::expressions::exprValue::readTokens(ITstream& is)
{
clear(); // type: none, value: zero
const valueTypeCode whichCode(exprValue::peekType(is));
if (whichCode == expressions::valueTypeCode::NONE)
{
typeCode_ = whichCode;
is.skip(2); // Skip tokens: '( )'
return true;
}
// This one should be rare or even impossible
if (whichCode == expressions::valueTypeCode::INVALID)
{
typeCode_ = whichCode;
if (is.bad())
{
return false;
}
const token& tok0 = is.peek();
if (tok0.isWord("bad"))
{
is.skip(1); // Skip token: "bad"
return true;
}
}
switch (whichCode)
{
#undef doLocalCode
@ -347,13 +477,24 @@ bool Foam::expressions::exprValue::operator<(const exprValue& rhs) const
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
Foam::Istream& Foam::operator>>
(
Istream& is,
expressions::exprValue& val
)
{
val.read(is);
return is;
}
Foam::Ostream& Foam::operator<<
(
Ostream& os,
const expressions::exprValue& val
)
{
val.print(os);
val.write(os, false); // no pruning
return os;
}
@ -367,16 +508,18 @@ Foam::Ostream& Foam::operator<<
{
const auto& val = *iproxy;
if (val.good())
if (val.typeCode() == expressions::valueTypeCode::NONE)
{
os << val.valueTypeName() << ": ";
val.print(os);
os << "none";
}
else if (val.typeCode() == expressions::valueTypeCode::INVALID)
{
os << "bad";
}
else
{
// typeCode_ is *never* set to INVALID,
// so NONE is the only remaining non-good type
os << "none";
os << val.valueTypeName() << ": ";
val.write(os); // pruning is immaterial - !good() already handled
}
return os;

View File

@ -169,7 +169,7 @@ class exprValue
// Private Member Functions
//- Assign zero, preserving the data type
//- Fill the data with zero, preserving the data type
inline void fill_zero();
//- Copy assignment
@ -215,6 +215,12 @@ public:
const expressions::valueTypeCode
) noexcept;
//- The vector-space rank associated with the valueTypeCode
inline static direction rank
(
const expressions::valueTypeCode
) noexcept;
//- True if the specified type is supported
template<class Type>
inline static bool supportedType();
@ -250,6 +256,9 @@ public:
//- The number of components associated with the value type
inline direction nComponents() const noexcept;
//- The vector-space rank associated with the value type
inline direction rank() const noexcept;
//- The name for the value type. Similar to pTraits typeName
inline word valueTypeName() const;
@ -260,9 +269,13 @@ public:
//- Reset to 'none'
void clear();
//- Read read tokens (if possible)
// \return True on success
bool read(Istream& is);
//- Guess type and read tokens (if possible)
// \return True on success
bool read(ITstream& is);
bool readTokens(ITstream& is);
// Typed Access
@ -346,11 +359,19 @@ public:
//- 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;
//- The exprValue as tokens.
// For none : emits pair of brackets.
// For invalid : emits "bad".
//
// \param prune suppress the output for none/invalid
tokenList tokens(bool prune = false) const;
//- Print the (type-specific) content
void print(Ostream& os) const;
//- Write the (type-specific) content.
// For none : emits pair of brackets.
// For invalid : emits "bad".
//
// \param prune suppress the output for none/invalid
void write(Ostream& os, bool prune = false) const;
};
@ -364,7 +385,11 @@ template<> struct is_contiguous<expressions::exprValue> : std::true_type {};
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
//- Write value to output stream in ASCII format. No output for !good().
//- Read/parse value from input stream (uses ASCII format).
Istream& operator>>(Istream& is, expressions::exprValue& val);
//- Write value to output stream (uses ASCII format).
//- Writes 'none' or 'bad' for unknown/unsupported types.
Ostream& operator<<(Ostream& os, const expressions::exprValue& val);
template<>

View File

@ -64,6 +64,15 @@ inline Foam::direction Foam::expressions::exprValue::nComponents
}
inline Foam::direction Foam::expressions::exprValue::rank
(
const expressions::valueTypeCode valTypeCode
) noexcept
{
return expressions::Detail::rank(valTypeCode);
}
template<class Type>
inline bool Foam::expressions::exprValue::supportedType()
{
@ -110,7 +119,7 @@ inline Foam::expressions::exprValue::exprValue(const Type& val)
{
FatalErrorInFunction
<< "Cannot construct for unsupported type: "
<< typeid(Type).name() << endl
<< typeid(Type).name() << nl
<< abort(FatalError);
}
}
@ -137,6 +146,13 @@ Foam::expressions::exprValue::nComponents() const noexcept
}
inline Foam::direction
Foam::expressions::exprValue::rank() const noexcept
{
return expressions::Detail::rank(typeCode_);
}
inline Foam::word
Foam::expressions::exprValue::valueTypeName() const
{