Commit e3d91a11 authored by Mark Olesen's avatar Mark Olesen

ENH: dictionary checking methods with predicates on the input values

- can be used to check the validity of input values.

Example:

    dict.getCheck<label>("nIters", greaterOp1<label>(0));
    dict.getCheck<scalar>("relax", scalarMinMax::zero_one());

- use 'get' prefix for more regular dictionary methods.
  Eg, getOrDefault() as alternative to lookupOrDefault()

- additional ops for convenient construction of predicates

ENH: make dictionary writeOptionalEntries integer

- allow triggering of Fatal if default values are used

ENH: additional scalarRange static methods: ge0, gt0, zero_one

- use GREAT instead of VGREAT for internal placeholders

- additional MinMax static methods: gt, le
parent 74402032
......@@ -2,7 +2,7 @@
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017-2018 OpenCFD Ltd.
\\ / A nd | Copyright (C) 2017-2019 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
......@@ -34,6 +34,8 @@ Description
#include "IOobject.H"
#include "IFstream.H"
#include "dictionary.H"
#include "ops.H"
#include "scalarRange.H"
#include "stringOps.H"
using namespace Foam;
......@@ -108,6 +110,42 @@ scalar try_getScalar(const dictionary& dict, const word& k)
}
// Try with getCheck<scalar>
template<class Predicate>
scalar try_getCheckScalar
(
const dictionary& dict,
const word& k,
const Predicate& pred
)
{
scalar val(-GREAT);
const bool throwingIOError = FatalIOError.throwExceptions();
const bool throwingError = FatalError.throwExceptions();
try
{
val = dict.getCheck<scalar>(k, pred);
Info<< "getCheck<scalar>(" << k << ") = " << val << nl;
}
catch (const Foam::IOerror& err)
{
Info<< "getCheck<scalar>(" << k << ") Caught FatalIOError "
<< err << nl << endl;
}
catch (const Foam::error& err)
{
Info<< "getCheck<scalar>(" << k << ") Caught FatalError "
<< err << nl << endl;
}
FatalError.throwExceptions(throwingError);
FatalIOError.throwExceptions(throwingIOError);
return val;
}
// Try with *entry (from findEntry) and get<scalar>
scalar try_getScalar(const entry* eptr, const word& k)
{
......@@ -311,6 +349,7 @@ int main(int argc, char *argv[])
IStringStream
(
"good 3.14159;\n"
"negative -3.14159;\n"
"empty;\n"
// "bad text;\n" // always fails
// "bad 3.14159 1234;\n" // fails for readScalar
......@@ -338,6 +377,26 @@ int main(int argc, char *argv[])
try_getScalar(dict2, "empty");
}
// With getCheck<scalar>
{
Info<< nl << "Test some input with getCheck<scalar>()" << nl;
try_getCheckScalar(dict2, "good", scalarRange::gt0());
try_getCheckScalar(dict2, "negative", scalarRange::gt0());
try_getCheckScalar(dict2, "good", greaterOp1<scalar>(0));
try_getCheckScalar(dict2, "negative", greaterOp1<scalar>(0));
Info<< nl << "with lambda" << nl;
try_getCheckScalar
(
dict2,
"good",
[](const scalar x) { return x > 0; }
);
}
// With findEntry and get<scalar>
{
Info<< nl
......
......@@ -36,6 +36,8 @@ Description
#include "FlatOutput.H"
#include "Tuple2.H"
#include "StringStream.H"
#include "ops.H"
#include "bitSet.H"
using namespace Foam;
......@@ -44,7 +46,7 @@ void doTest(const scalarList& values, const predicates::scalars& accept)
{
// Also tests that output is suppressed
Info<<"Have: " << accept.size() << " predicates" << accept << endl;
Info<<"values: " << flatOutput(values) << endl;
Info<<"values: " << flatOutput(values) << endl;
for (const scalar& value : values)
{
......@@ -60,6 +62,30 @@ void doTest(const scalarList& values, const predicates::scalars& accept)
}
template<class Predicate>
void testPredicate(const scalarList& values, const Predicate& pred)
{
bitSet matches;
label i=0;
for (const scalar& value : values)
{
if (pred(value))
{
matches.set(i);
}
++i;
}
IndirectList<scalar> matched(values, matches.toc());
Info<< "matched: " << flatOutput(matched.addressing())
<< " = " << flatOutput(matched) << nl;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:
......@@ -149,6 +175,16 @@ int main(int argc, char *argv[])
}
Info<< nl << "Test with ops" << nl;
Info<<"values: " << flatOutput(values) << endl;
{
testPredicate(values, lessOp1<scalar>(10));
testPredicate(values, greaterOp1<scalar>(100));
// Also with dissimilar type
testPredicate(values, lessEqOp1<label>(0));
}
Info<< "\nEnd\n" << endl;
return 0;
......
......@@ -37,6 +37,8 @@ InfoSwitches
writePrecision 6;
writeDictionaries 0;
// Report optional dictionary entries. For value > 1, treat as fatal.
writeOptionalEntries 0;
// Write lagrangian "positions" file in v1706 format (and earlier)
......
......@@ -2,7 +2,7 @@
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2004-2011, 2015-2018 OpenCFD Ltd.
\\ / A nd | Copyright (C) 2004-2011, 2015-2019 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
| Copyright (C) 2011-2017 OpenFOAM Foundation
......@@ -42,13 +42,12 @@ namespace Foam
const Foam::dictionary Foam::dictionary::null;
bool Foam::dictionary::writeOptionalEntries
int Foam::dictionary::writeOptionalEntries
(
Foam::debug::infoSwitch("writeOptionalEntries", 0)
);
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::dictionary::dictionary()
......@@ -311,13 +310,30 @@ void Foam::dictionary::checkITstream
}
void Foam::dictionary::raiseBadInput(const word& keyword) const
{
// Can use FatalIOError instead of SafeFatalIOError
// since predicate checks are not used at the earliest stages
FatalIOError
(
"", // functionName
"", // sourceFileName
0, // sourceFileLineNumber
*this // ios
)
<< "Entry '" << keyword << "' with invalid input in dictionary "
<< name() << nl << nl
<< exit(FatalIOError);
}
bool Foam::dictionary::found
(
const word& keyword,
enum keyType::option matchOpt
) const
{
return csearch(keyword, matchOpt).found();
return csearch(keyword, matchOpt).good();
}
......@@ -359,11 +375,11 @@ const Foam::entry& Foam::dictionary::lookupEntry
{
const const_searcher finder(csearch(keyword, matchOpt));
if (!finder.found())
if (!finder.good())
{
FatalIOErrorInFunction(*this)
<< "Entry '" << keyword << "' not found in dictionary "
<< name()
<< name() << nl
<< exit(FatalIOError);
}
......@@ -395,7 +411,7 @@ bool Foam::dictionary::substituteKeyword(const word& keyword, bool mergeEntry)
const const_searcher finder(csearch(varName, keyType::REGEX_RECURSIVE));
// If defined insert its entries into this dictionary
if (finder.found())
if (finder.good())
{
for (const entry& e : finder.dict())
{
......@@ -427,7 +443,7 @@ bool Foam::dictionary::substituteScopedKeyword
const auto finder(csearchScoped(varName, keyType::REGEX_RECURSIVE));
// If defined insert its entries into this dictionary
if (finder.found())
if (finder.good())
{
for (const entry& e : finder.dict())
{
......@@ -473,11 +489,11 @@ const Foam::dictionary& Foam::dictionary::subDict(const word& keyword) const
// Allow patterns, non-recursive
const const_searcher finder(csearch(keyword, keyType::REGEX));
if (!finder.found())
if (!finder.good())
{
FatalIOErrorInFunction(*this)
<< "Entry '" << keyword << "' not found in dictionary "
<< name()
<< name() << nl
<< exit(FatalIOError);
}
......@@ -490,11 +506,11 @@ Foam::dictionary& Foam::dictionary::subDict(const word& keyword)
// Allow patterns, non-recursive
searcher finder(search(keyword, keyType::REGEX));
if (!finder.found())
if (!finder.good())
{
FatalIOErrorInFunction(*this)
<< "Entry '" << keyword << "' not found in dictionary "
<< name()
<< name() << nl
<< exit(FatalIOError);
}
......@@ -526,7 +542,7 @@ Foam::dictionary Foam::dictionary::subOrEmptyDict
<< exit(FatalIOError);
}
if (finder.found())
if (finder.good())
{
IOWarningInFunction(*this)
<< "Entry '" << keyword
......@@ -552,7 +568,7 @@ const Foam::dictionary& Foam::dictionary::optionalSubDict
return finder.dict();
}
if (finder.found())
if (finder.good())
{
IOWarningInFunction(*this)
<< "Entry '" << keyword
......@@ -611,7 +627,7 @@ Foam::entry* Foam::dictionary::add(entry* entryPtr, bool mergeEntry)
auto iter = hashedEntries_.find(entryPtr->keyword());
if (mergeEntry && iter.found())
if (mergeEntry && iter.good())
{
// Merge dictionary with dictionary
if (iter()->isDict() && entryPtr->isDict())
......@@ -786,7 +802,7 @@ bool Foam::dictionary::merge(const dictionary& dict)
{
auto fnd = hashedEntries_.find(e.keyword());
if (fnd.found())
if (fnd.good())
{
// Recursively merge sub-dictionaries
// TODO: merge without copying
......
This diff is collapsed.
......@@ -2,7 +2,7 @@
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017-2018 OpenCFD Ltd.
\\ / A nd | Copyright (C) 2017-2019 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
......@@ -54,7 +54,7 @@ Foam::dictionary::const_searcher Foam::dictionary::csearchCompat
{
const_searcher finder(csearch(keyword, matchOpt));
if (finder.found())
if (finder.good())
{
return finder;
}
......@@ -63,7 +63,7 @@ Foam::dictionary::const_searcher Foam::dictionary::csearchCompat
{
finder = csearch(word::validate(iter.first), matchOpt);
if (finder.found())
if (finder.good())
{
// Only want a single warning (on master), but guard with a
// parRun check to avoid Pstream::master() when Pstream has not
......@@ -101,7 +101,7 @@ bool Foam::dictionary::foundCompat
enum keyType::option matchOpt
) const
{
return csearchCompat(keyword, compat, matchOpt).found();
return csearchCompat(keyword, compat, matchOpt).good();
}
......@@ -125,7 +125,7 @@ const Foam::entry& Foam::dictionary::lookupEntryCompat
{
const const_searcher finder(csearchCompat(keyword, compat, matchOpt));
if (!finder.found())
if (!finder.good())
{
FatalIOErrorInFunction(*this)
<< "Entry '" << keyword << "' not found in dictionary "
......
......@@ -130,7 +130,7 @@ Foam::dictionary::const_searcher Foam::dictionary::csearchDotScoped
// a.b.c.d it would try
// a.b, a.b.c, a.b.c.d
if (!finder.found())
if (!finder.good())
{
while (!finder.isDict())
{
......@@ -225,7 +225,7 @@ Foam::dictionary::const_searcher Foam::dictionary::csearchSlashScoped
auto finder = dictPtr->csearch(key, matchOpt);
if (finder.found())
if (finder.good())
{
if (remaining)
{
......@@ -269,7 +269,7 @@ Foam::dictionary::const_searcher Foam::dictionary::csearch
auto iter = hashedEntries_.cfind(keyword);
if (iter.found())
if (iter.good())
{
finder.set(iter.val());
return finder;
......@@ -425,7 +425,7 @@ const Foam::dictionary* Foam::dictionary::cfindScopedDict
auto iter = dictPtr->hashedEntries_.cfind(cmpt);
if (iter.found())
if (iter.good())
{
const entry *eptr = iter.val();
......@@ -532,7 +532,7 @@ Foam::dictionary* Foam::dictionary::makeScopedDict(const fileName& dictPath)
auto iter = dictPtr->hashedEntries_.find(cmptName);
if (iter.found())
if (iter.good())
{
entry *eptr = iter.val();
......@@ -581,7 +581,7 @@ bool Foam::dictionary::remove(const word& keyword)
{
auto iter = hashedEntries_.find(keyword);
if (iter.found())
if (iter.good())
{
// Delete from patterns
auto wcLink = patterns_.begin();
......@@ -621,7 +621,7 @@ bool Foam::dictionary::changeKeyword
// Check that oldKeyword exists and can be changed
auto iter = hashedEntries_.find(oldKeyword);
if (!iter.found())
if (!iter.good())
{
return false;
}
......@@ -638,7 +638,7 @@ bool Foam::dictionary::changeKeyword
auto iter2 = hashedEntries_.find(newKeyword);
// newKeyword already exists
if (iter2.found())
if (iter2.good())
{
if (overwrite)
{
......
......@@ -2,7 +2,7 @@
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2004-2010, 2017-2018 OpenCFD Ltd.
\\ / A nd | Copyright (C) 2004-2010, 2017-2019 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
| Copyright (C) 2011-2017 OpenFOAM Foundation
......@@ -64,6 +64,20 @@ T Foam::dictionary::get
}
template<class T, class Predicate>
T Foam::dictionary::getCheck
(
const word& keyword,
const Predicate& pred,
enum keyType::option matchOpt
) const
{
T val;
readCheck<T, Predicate>(keyword, val, pred, matchOpt);
return val;
}
template<class T>
T Foam::dictionary::getCompat
(
......@@ -79,49 +93,111 @@ T Foam::dictionary::getCompat
template<class T>
bool Foam::dictionary::readCompat
T Foam::dictionary::getOrDefault
(
const word& keyword,
std::initializer_list<std::pair<const char*,int>> compat,
T& val,
enum keyType::option matchOpt,
bool mandatory
const T& deflt,
enum keyType::option matchOpt
) const
{
const const_searcher finder(csearchCompat(keyword, compat, matchOpt));
const const_searcher finder(csearch(keyword, matchOpt));
if (finder.found())
if (finder.good())
{
T val;
ITstream& is = finder.ptr()->stream();
is >> val;
checkITstream(is, keyword);
return true;
return val;
}
else if (mandatory)
else if (writeOptionalEntries)
{
FatalIOErrorInFunction(*this)
<< "Entry '" << keyword << "' not found in dictionary "
<< name()
<< exit(FatalIOError);
if (writeOptionalEntries > 1)
{
FatalIOErrorInFunction(*this)
<< "Optional entry '" << keyword
<< "' not found. Default '" << deflt << "' ignored" << nl
<< exit(FatalIOError);
}
else
{
IOInfoInFunction(*this)
<< "Optional entry '" << keyword
<< "' not found. Using default '" << deflt << "'" << nl;
}
}
return false;
return deflt;
}
template<class T>
T Foam::dictionary::lookupOrDefault
T Foam::dictionary::getOrAdd
(
const word& keyword,
const T& deflt,
enum keyType::option matchOpt
)
{
const const_searcher finder(csearch(keyword, matchOpt));
if (finder.good())
{
T val;
ITstream& is = finder.ptr()->stream();
is >> val;
checkITstream(is, keyword);
return val;
}
else if (writeOptionalEntries)
{
if (writeOptionalEntries > 1)
{
FatalIOErrorInFunction(*this)
<< "Optional entry '" << keyword
<< "' not found. Default '" << deflt << "' ignored" << nl
<< exit(FatalIOError);
}
else
{
IOInfoInFunction(*this)
<< "Optional entry '" << keyword
<< "' not found. Adding default '" << deflt << "'" << nl;
}
}
add(new primitiveEntry(keyword, deflt));
return deflt;
}
template<class T, class Predicate>
T Foam::dictionary::getCheckOrDefault
(
const word& keyword,
const T& deflt,
const Predicate& pred,
enum keyType::option matchOpt
) const
{
if (!pred(deflt))
{
// Could be as FULLDEBUG instead?
FatalIOErrorInFunction(*this)
<< "Entry '" << keyword << "' with invalid default in dictionary "
<< name()
<< exit(FatalIOError);
}
const const_searcher finder(csearch(keyword, matchOpt));
if (finder.found())
if (finder.good())
{
T val;
......@@ -130,31 +206,55 @@ T Foam::dictionary::lookupOrDefault
checkITstream(is, keyword);
if (!pred(val))
{
raiseBadInput(keyword);
}
return val;
}
else if (writeOptionalEntries)
{
IOInfoInFunction(*this)
<< "Optional entry '" << keyword
<< "' not found, using default value '" << deflt << "'"
<< nl;
if (writeOptionalEntries > 1)
{
FatalIOErrorInFunction(*this)
<< "Optional entry '" << keyword
<< "' not found. Default '" << deflt << "' ignored" << nl
<< exit(FatalIOError);
}
else
{
IOInfoInFunction(*this)
<< "Optional entry '" << keyword
<< "' not found. Using default '" << deflt << "'" << nl;
}
}
return deflt;
}
template<class T>
T Foam::dictionary::lookupOrAddDefault
template<class T, class Predicate>
T Foam::dictionary::getCheckOrAdd
(
const word& keyword,
const T& deflt,
const Predicate& pred,
enum keyType::option matchOpt
)
{
if (!pred(deflt))
{
// Could be as FULLDEBUG instead?
FatalIOErrorInFunction(*this)
<< "Entry '" << keyword <