5.9 KiB
5.9 KiB
Helpers
Requirements Helper
Requirements helper was introduced to replace plain checks for making them output informative messages (Debug and Verbose mode) and also replace macros REQUIRE_TRUE.
- it will lazily evaluate values and messages if the type wrapped and has
getValueandgetMsgmethods - it is implicit bool. this makes it usable with logical operators and also inside if conditions. Besides it will benefit from shortcircuit nature of those operators.
- it has the following check methods
Requirements& expect(const T& expVar,const T1& reqVar, Op comparison, const char *first_half="")
Requirements& expectEq(const T& exp,const T1& req)
Requirements& expectNotEq(const T& exp,const T1& req)
Requirements& expectLess(const T& exp,const T1& req)
Requirements& expectLessEq(const T& exp,const T1& req)
Requirements& expectGreater(T exp, T1 req)
Requirements& expectGreaterEq(const T& exp,const T1& req
Requirements& expectTrue(const T& expVar, const char *msg=)
Requirements& expectFalse(const T& expVar, const char *msg=)
- you can either log the success case or throw error on the failure
- it can use plain types for checks.
- if value has stream operator it will be used to output its value. for custom types you may need add that by yourself
ostream& operator<<(ostream& os, const CustomUserType& dt) - there is generic template
InfoVariablewrapper for types to make it informative. you can use lambda operators with them as well to make it lazily evaluated - we added custom
ShapeInfoVariablewrapper for the NDArray and vector<> shapes to make them informative - one can use
expectto add its own proper comparison. simple lambda for that will be like this:
[](const decltype(expType)& l, const decltype(reqType)& r){
//compare and return
return ....;
}
Examples:
firstly, we should enable logging
sd::Environment::getInstance().setDebug(true);
sd::Environment::getInstance().setVerbose(true);
- simple case
Requirements req1("Requirement Helper Example#1");
int x = 20;
req1.expectLess(x, 22);
req1.expectEq(x, 21); //should fail
Output:
Requirement Helper Example#1: {20} expected to be equal to 21
- using InfoVariable wrapper
int age = 15;
Requirements req2("Requirement Helper Example#2");
req2.expectGreaterEq(makeInfoVariable(age, "the user's age"), 18);
Output:
Requirement Helper Example#2: the user's age {15} expected to be greater than or equal 18
- helper behavior while using many checks in one block
int getAge(){
std::cout<<"getAge() was called"<<std::endl;
return 15;
}
....
Requirements req3("Requirement Helper Example#3");
int z = 20;
req3.expectEq(z, 21);
req3.expectGreaterEq(makeInfoVariable(getAge(), "the user's age"), 18);
Output:
Requirement Helper Example#3: {20} expected to be equal to 21
getAge() was called
As it is seen the second check did not happen as the previous failed. But still getAge() method was called as its function argument.
- using shortcircuit to avoid Requirement call at all if the previous one was failed
Requirements req4("Requirement Helper Example#4");
int zz = 20;
req4.expectEq(zz, 21) && //shortcircuit And
req4.expectGreaterEq(makeInfoVariable(getAge(), "the user's age"), 18);
Output:
Requirement Helper Example#4: {20} expected to be equal to 21
- using lambdas with InfoVariable. it will make it lazily evaluated
Requirements req5("Requirement Helper Example#5");
req5.expectEq( 21,
makeInfoVariable(21, []{
std::cout<<"lambda call#1"<<std::endl;
return "twenty one";
}));
req5.expectEq(makeInfoVariable([]{ return 20;}, []{return "twenty";}),
makeInfoVariable(21, []{
std::cout<<"lambda call#2"<<std::endl;
return "twenty one";
}));
req5.expectGreaterEq(makeInfoVariable([]{
std::cout<<"lambda call#3" <<std::endl;
return 15;
},
[]{ return "the user's age";}),
makeInfoVariable([]{return 18;}, []{return "the allowed age";})
);
Output:
lambda call#2
Requirement Helper Example#5: twenty {20} expected to be equal to twenty one 21
- use bool nature and also log the success case
Requirements req6("Requirement Helper Example#6");
NDArray * arr= nullptr;
arr !=nullptr && req6.expectEq(arr->rankOf(), 3) ;
req6.logTheSuccess();
Output:
Requirement Helper Example#6: meets the requirements
- custom comparision lambda and also another usage of the custom wrapper written by us
ShapeInfoVariable. Note: we will usestd::vector<int>. this wrapper can be used withNDArrayas well.
Requirements req7("Requirement Helper Example#7");
req7.expect(makeShapeInfoVariable(std::vector<sd::LongType>{2,3,4,5}, SHAPE_MSG_INPUT0), makeShapeInfoVariable(std::vector<sd::LongType>{2,3,4,7}, SHAPE_MSG_INPUT1),
[](const std::vector<sd::LongType>& l, const std::vector<sd::LongType>& r){
return l == r;
}
, EXPECTED_EQ_MSG);
}
Output:
Requirement Helper Example#7: the Shape of the Input NDArray#0 {[2, 3, 4, 5]} expected to be equal to the Shape of the Input NDArray#1 [2, 3, 4, 7]
- throw error when there is failure
Requirements req8("Requirement Helper Example#8");
req8.expectEq(6,6) &&
req8.expectIn(6, {1,2,3,7,8,9});
req8.throws();
Output:
terminate called after throwing an instance of 'std::invalid_argument'
what(): Op validation failed
...
Requirement Helper Example#8: {6} expected to be one of these {[1, 2, 3, 7, 8, 9, ]}
Here is live example:
Note: some classes were mocked there and do not represent the exact implementations in libnd4j. https://godbolt.org/z/sq98vchs5