diff --git a/exercises/operators/README.md b/exercises/operators/README.md index a1a644bd..17ae3552 100644 --- a/exercises/operators/README.md +++ b/exercises/operators/README.md @@ -1,26 +1,23 @@ ## Instructions -STEP 1 -- Add a free operator<<, reusing str(), and simplify main() first lines. -- Replace equal() with operator==(), and upgrade tests. -- Add operator!=(), reusing operator==(), and upgrade tests. -- Replace compare() with operator<=>(), reusing <=> between doubles, - and upgrade tests. -- Replace multiply() with operator*(), and upgrade tests. +### Main Tasks +- Write an ostream operator with the following signature: `operator<<(ostream &, Fraction const &)`. + The `str()` function of Fraction will help you to implement it. Use this operator to make the `cout`s in the first lines of `main()` look a bit more natural. + - **Note**: If you do this exercise as part of the advanced course, implement the operators as hidden friends. +- Replace the function `equal()` with `operator==()`, and upgrade tests. + Note that equality isn't the same as equivalence. The compare function returns 0 + for 1/1, 2/2, etc, but these are not equal. +- Add `operator!=()`, reusing `operator==()`, and upgrade tests. +- Replace `multiply()` with `operator*()`, and upgrade tests. -STEP 2 -- Replace TestResultPrinter::process() with operator()(), and upgrade CHECK(). - -OPTIONAL STEP 3 -- Add an inplace multiplication operator*=(), and add tests. -- Review operator*() so to reuse operator*=(). -- Ensure calls to operator*=() can be chained, the same as operator<<(). +### Optional if you have time +- Add an inplace multiplication `operator*=()`, and add tests. +- Review `operator*()` so to reuse `operator*=()`. +- Ensure calls to `operator*=()` can be chained, the same as `operator<<()`. ## Take away +- Operators can make certain expressions much more readable. - Do not confuse equality and equivalence. - We can very often implement an arithmetic operator@ in terms of operator@=. -- When implementing <=>, you get <, >, <=, >= for free. -- Object-functions are very used with standard algorithms, - yet tend to be often replaced by lambdas in modern C++. diff --git a/exercises/operators/operators.cpp b/exercises/operators/operators.cpp index fa69514f..f39218a9 100644 --- a/exercises/operators/operators.cpp +++ b/exercises/operators/operators.cpp @@ -19,6 +19,10 @@ class Fraction { return (lhs.m_num==rhs.m_num) && (lhs.m_denom==rhs.m_denom); } + // This function compares two fractions, and returns + // -1 if lhs < rhs + // 0 if they denote the same value (equivalence) + // 1 if lhs > rhs friend int compare( Fraction const & lhs, Fraction const & rhs ) { int v1 = lhs.m_num * rhs.m_denom; int v2 = rhs.m_num * lhs.m_denom; @@ -59,10 +63,10 @@ class TestResultPrinter { }; // This is using the cpp, the C preprocessor to expand a bit of code -// (the what argument) to a pair containing a string representation +// (the arguments in '...') to a pair containing a string representation // of it and the code itself. That way, print is given a string and a // value where the string is the code that lead to the value -#define CHECK(printer, ...) printer.process(#__VA_ARGS__, (__VA_ARGS__)) +#define CHECK(...) TestResultPrinter{50}.process(#__VA_ARGS__, (__VA_ARGS__)) int main() { @@ -74,32 +78,31 @@ int main() { // equality std::cout<0); - CHECK(p2,compare(third,Fraction{2,4})<0); + CHECK(!equal(third,Fraction{2,6})); + CHECK(compare(third,Fraction{2,6})==0); + CHECK(compare(third,Fraction{1,4})>0); + CHECK(compare(third,Fraction{2,4})<0); // multiply std::cout< #include #include -#include #include class Fraction { @@ -24,8 +23,16 @@ class Fraction { return !(lhs==rhs); } - friend auto operator<=>( Fraction const & lhs, Fraction const & rhs ) { - return ((lhs.m_num*rhs.m_denom)<=>(rhs.m_num*lhs.m_denom)); + // This function compares two fractions, and returns + // -1 if lhs < rhs + // 0 if they denote the same value (equivalence) + // 1 if lhs > rhs + friend int compare( Fraction const & lhs, Fraction const & rhs ) { + int v1 = lhs.m_num * rhs.m_denom; + int v2 = rhs.m_num * lhs.m_denom; + if (v1 < v2) return -1; + else if (v1 > v2) return 1; + else return 0; } Fraction & operator*=(Fraction const & other) { @@ -59,7 +66,7 @@ class TestResultPrinter { TestResultPrinter( unsigned int a_width ) : m_width(a_width) {} - void operator()(std::string const & what, bool passed) { + void process(std::string const & what, bool passed) { std::cout << std::left << std::setw(m_width) << what << ": " << (passed ? "PASS" : "** FAIL **") << '\n'; } @@ -70,10 +77,10 @@ class TestResultPrinter { }; // This is using the cpp, the C preprocessor to expand a bit of code -// (the what argument) to a pair containing a string representation +// (the arguments in '...') to a pair containing a string representation // of it and the code itself. That way, print is given a string and a // value where the string is the code that lead to the value -#define CHECK(printer,what) printer(#what, what) +#define CHECK(...) TestResultPrinter{50}.process(#__VA_ARGS__, (__VA_ARGS__)) int main() { @@ -85,53 +92,39 @@ int main() { // equality std::cout<Fraction{2,6})); - CHECK(p2,std::is_gt(third<=>Fraction{1,4})); - CHECK(p2,std::is_lt(third<=>Fraction{2,4})); - CHECK(p2,(third>Fraction{1,4})); - CHECK(p2,(third=Fraction{2,4})); - CHECK(p2,(third>=Fraction{1,4})); - CHECK(p2,(third<=Fraction{2,4})); - CHECK(p2,(third>=Fraction{1,3})); - CHECK(p2,(third<=Fraction{2,3})); - CHECK(p2,!(thirdFraction{2,4})); - CHECK(p2,!(thirdFraction{2,3})); + CHECK(third!=Fraction{2,6}); + CHECK(compare(third,Fraction{2,6})==0); + CHECK(compare(third,Fraction{1,4})>0); + CHECK(compare(third,Fraction{2,4})<0); // multiply std::cout<Fraction{1,1})); - CHECK(p3,std::is_eq((3*third)<=>Fraction{1,1})); - CHECK(p3,((3*third).normalized()==1)); + CHECK((third*2)==Fraction{2,3}); + CHECK((2*third)==Fraction{2,3}); + CHECK(compare(three*third, Fraction{1,1}) == 0); + CHECK(compare(3*third, Fraction{1,1}) == 0); + CHECK((3*third).normalized()==1); // multiply in place std::cout<1)); - CHECK(p4,one.normalized()==1); - CHECK(p4,one!=1); + CHECK(compare(one, 1)==0); + CHECK(one.normalized()==1); + CHECK(one!=1); + // end std::cout<