Commit 32916fa8 authored by Mark Olesen's avatar Mark Olesen Committed by Andrew Heather
Browse files

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 b043be30
......@@ -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) 2015-2018 OpenCFD Ltd.
\\ / A nd | Copyright (C) 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
......
......@@ -62,8 +62,8 @@ Note
key2 $..key1; // use key1 value from parent
subdict2
{
key2 val2;
key3 $...key1; // use key1 value from grandparent
key2 val2;
key3 $...key1; // use key1 value from grandparent
}
}
......@@ -285,8 +285,9 @@ private:
// Private Data
//- Report optional keywords and values if not present in dictionary
// For value greater than 1: fatal.
// Set/unset via an InfoSwitch
static bool writeOptionalEntries;
static int writeOptionalEntries;
//- The dictionary name
fileName name_;
......@@ -359,6 +360,10 @@ private:
) const;
//- Emit IOError about bad input for the entry
void raiseBadInput(const word& keyword) const;
public:
// Declare name of the class and its debug switch
......@@ -532,7 +537,6 @@ public:
enum keyType::option matchOpt = keyType::REGEX
);
//- Search for an entry (const access) with the given keyword.
//- Find and return a sub-dictionary pointer if present
// (and a sub-dictionary) otherwise return nullptr.
//
......@@ -543,6 +547,7 @@ public:
enum keyType::option matchOpt = keyType::REGEX
) const;
//- Search for an entry (const access) with the given keyword.
//
// \param matchOpt the default search is non-recursive with patterns
//
......@@ -553,22 +558,22 @@ public:
enum keyType::option matchOpt
) const;
//- Find and return a T.
//- Find and return an entry data stream.
//- FatalIOError if not found, or if the number of tokens is incorrect.
//
// \param matchOpt the default search is non-recursive with patterns
template<class T>
T get
ITstream& lookup
(
const word& keyword,
enum keyType::option matchOpt = keyType::REGEX
) const;
//- Find and return an entry data stream.
//- Find and return a T.
//- FatalIOError if not found, or if the number of tokens is incorrect.
//
// \param matchOpt the default search is non-recursive with patterns
ITstream& lookup
template<class T>
T get
(
const word& keyword,
enum keyType::option matchOpt = keyType::REGEX
......@@ -579,7 +584,7 @@ public:
//
// \param matchOpt the default search is non-recursive with patterns
template<class T>
T lookupOrDefault
T getOrDefault
(
const word& keyword,
const T& deflt,
......@@ -592,7 +597,7 @@ public:
//
// \param matchOpt the default search is non-recursive with patterns
template<class T>
T lookupOrAddDefault
T getOrAdd
(
const word& keyword,
const T& deflt,
......@@ -633,6 +638,86 @@ public:
enum keyType::option matchOpt = keyType::REGEX
) const;
//- Find and return a T with additional checking
//- FatalIOError if not found, or if the number of tokens is incorrect.
//
// \param pred the value check predicate
// \param matchOpt the default search is non-recursive with patterns
template<class T, class Predicate>
T getCheck
(
const word& keyword,
const Predicate& pred,
enum keyType::option matchOpt = keyType::REGEX
) const;
//- Find and return a T, or return the given default value.
//- FatalIOError if it is found and the number of tokens is incorrect.
//
// \param pred the value check predicate
// \param matchOpt the default search is non-recursive with patterns
template<class T, class Predicate>
T getCheckOrDefault
(
const word& keyword,
const T& deflt,
const Predicate& pred,
enum keyType::option matchOpt = keyType::REGEX
) const;
//- Find and return a T, or return the given default value
//- and add it to dictionary.
//- FatalIOError if it is found and the number of tokens is incorrect.
//
// \param pred the value check predicate
// \param matchOpt the default search is non-recursive with patterns
template<class T, class Predicate>
T getCheckOrAdd
(
const word& keyword,
const T& deflt,
const Predicate& pred,
enum keyType::option matchOpt = keyType::REGEX
);
//- Find entry and assign to T val.
//- FatalIOError if it is found and the number of tokens is incorrect,
//- or it is mandatory and not found.
//
// \param val the value to read into
// \param pred the value check predicate
// \param matchOpt the default search is non-recursive with patterns
// \param mandatory the keyword is mandatory
//
// \return true if the entry was found.
template<class T, class Predicate>
bool readCheck
(
const word& keyword,
T& val,
const Predicate& pred,
enum keyType::option matchOpt = keyType::REGEX,
bool mandatory = true
) const;
//- Find an entry if present, and assign to T val.
//- FatalIOError if it is found and the number of tokens is incorrect.
// Default search: non-recursive with patterns.
//
// \param val the value to read into
// \param pred the value check predicate
// \param matchOpt the default search is non-recursive with patterns
//
// \return true if the entry was found.
template<class T, class Predicate>
bool readCheckIfPresent
(
const word& keyword,
T& val,
const Predicate& pred,
enum keyType::option matchOpt = keyType::REGEX
) const;
//- Check if entry is found and and is a sub-dictionary.
//
// Search type: non-recursive with patterns.
......@@ -983,32 +1068,32 @@ public:
enum keyType::option
) const;
//- Find and return a T
//- Find and return an entry data stream,
//- using any compatibility names if needed.
//- FatalIOError if not found, or if there are excess tokens.
// Default search: non-recursive with patterns.
//
// \param compat list of old compatibility keywords and the last
// OpenFOAM version for which they were used.
// \param recursive search parent dictionaries
// \param patternMatch use regular expressions
template<class T>
T getCompat
ITstream& lookupCompat
(
const word& keyword,
std::initializer_list<std::pair<const char*,int>> compat,
enum keyType::option = keyType::REGEX
) const;
//- Find and return an entry data stream,
//- Find and return a T
//- using any compatibility names if needed.
//- FatalIOError if not found, or if there are excess tokens.
// Default search: non-recursive with patterns.
//
// \param compat list of old compatibility keywords and the last
// OpenFOAM version for which they were used.
// \param recursive search parent dictionaries
// \param patternMatch use regular expressions
ITstream& lookupCompat
template<class T>
T getCompat
(
const word& keyword,
std::initializer_list<std::pair<const char*,int>> compat,
......@@ -1024,7 +1109,7 @@ public:
// \param recursive search parent dictionaries
// \param patternMatch use regular expressions
template<class T>
T lookupOrDefaultCompat
T getOrDefaultCompat
(
const word& keyword,
std::initializer_list<std::pair<const char*,int>> compat,
......@@ -1105,6 +1190,58 @@ public:
// Housekeeping
//- Find and return a T, or return the given default value.
//- FatalIOError if it is found and the number of tokens is incorrect.
//
// \param matchOpt the default search is non-recursive with patterns
template<class T>
T lookupOrDefault
(
const word& keyword,
const T& deflt,
enum keyType::option matchOpt = keyType::REGEX
) const
{
return getOrDefault<T>(keyword, deflt, matchOpt);
}
//- Find and return a T, or return the given default value
//- and add it to dictionary.
//- FatalIOError if it is found and the number of tokens is incorrect.
//
// \param matchOpt the default search is non-recursive with patterns
template<class T>
T lookupOrAddDefault
(
const word& keyword,
const T& deflt,
enum keyType::option matchOpt = keyType::REGEX
)
{
return getOrAdd<T>(keyword, deflt, matchOpt);
}
//- Find and return a T, or return the given default value
//- using any compatibility names if needed.
// Default search: non-recursive with patterns.
//
// \param compat list of old compatibility keywords and the last
// OpenFOAM version for which they were used.
// \param recursive search parent dictionaries
// \param patternMatch use regular expressions
template<class T>
T lookupOrDefaultCompat
(
const word& keyword,
std::initializer_list<std::pair<const char*,int>> compat,
const T& deflt,
enum keyType::option matchOpt = keyType::REGEX
) const
{
return getOrDefaultCompat<T>(keyword, compat, deflt, matchOpt);
}
//- Deprecated(2018-07) find and return an entry data stream
//
// \deprecated(2018-07) - use lookup() method
......
......@@ -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 (