/*
=================================================================
Step 10 (for Solution see Step 11)
=================================================================
*/
#include "do_averages.h"
#include <sstream>
// std::istringstream
#ifndef TEST
int main() {
do_averages();
}
#else
#include "pxt.h"
void typical_good_input() {
{ ////////////////////////////////////////////////////////////
std::istringstream input{
"1 5 7" "\n"
"1.5 7" "\n"
"15.7" "\n"
};
std::ostringstream output{};
PX(do_averages(input, output), output.str())
>>= "4.33333" "\n"
"4.25" "\n"
"15.7" "\n";
} ////////////////////////////////////////////////////////////
}
void some_empty_inputs() {
{ ////////////////////////////////////////////////////////////
std::istringstream input{"\n"};
std::ostringstream output{};
PX(do_averages(input, output), output.str()) == "";
} ////////////////////////////////////////////////////////////
{ ////////////////////////////////////////////////////////////
std::istringstream input{" \n"};
std::ostringstream output{};
PX(do_averages(input, output), output.str()) == "";
} ////////////////////////////////////////////////////////////
}
void some_invalid_inputs() {
{ ////////////////////////////////////////////////////////////
std::istringstream input{
"1 5 7" "\n"
"1 S 7" "\n"
"15.7" "\n"
};
std::ostringstream output{};
PX(do_averages(input, output), output.str())
>>= "4.33333" "\n"
"non-numeric input - line ignored" "\n"
"15.7" "\n";
} ////////////////////////////////////////////////////////////
{ ////////////////////////////////////////////////////////////
std::istringstream input{
"1 5 7" "\n"
"xxx 7" "\n"
"1 xyz 7" "\n"
"1 xyz" "\n"
};
std::ostringstream output{};
PX(do_averages(input, output), output.str())
>>= "4.33333" "\n"
"non-numeric input - line ignored" "\n"
"non-numeric input - line ignored" "\n"
"non-numeric input - line ignored" "\n";
} ////////////////////////////////////////////////////////////
}
void fails_on_my_mac_only() {
{ ////////////////////////////////////////////////////////////
std::istringstream input{
"1 xyz" "\n"
"1 abc" "\n"
};
std::ostringstream output{};
PX(do_averages(input, output), output.str())
>>= "non-numeric input - line ignored" "\n"
"non-numeric input - line ignored" "\n";
} ////////////////////////////////////////////////////////////
}
int main() {
typical_good_input();
some_empty_inputs();
some_invalid_inputs();
// fails_on_my_mac_only();
}
#endif
# TODO STEP 10
- Turn the `struct sum_count` into a `class Averager`
- by making `operator<<` and `operator+=` friends and
- providing a member function `getCount` to return the `count_`
member in the two places where it is currently
accessed directly.
-----------------------------------------------------------------
TODO (optional - meant for further self-study only):
- Turn `operator+=` for the `Averager` (currently implemented as
free standing function) into a member function of that class.
- To class `Averager` add a helper member function `print` to do
what the overloaded `operator<<` currently does.
- It does not need to return a result, but
- needs an argument of type `std:ostream&`.
- Call that function as implementation of `operator<<` for the
`Averager` class.
- Now that `Averager`-s output operator shouldn't any longer
need to be a `friend` of that class.
- To the class `Averager` add the member function shown below and
use it where currently the `count_` member is accessed via its
getter function.
```cpp
class Averager {
…
public:
…
explicit operator bool() const { return (count_ != 0); }
…
};
```
- Class `Averager`-s member function` is now not used any longer
and may be removed.
#include "do_averages.h"
#include <iostream>
// std::cin
// std::cout
// std::istream
// std::ostream
#include <sstream>
// std::istringstream
// std::ostringstream
#include <string>
// std::getline
// std::string
struct sum_count {
float sum{0.0f};
int count{0};
};
std::ostream& operator<<(std::ostream& lhs, sum_count const& rhs) {
return lhs << rhs.sum/rhs.count;
}
void operator+=(sum_count& lhs, float rhs) {
lhs.sum += rhs;
++lhs.count;
}
void do_single_average(std::string line, sum_count& result) {
std::istringstream iss{line};
float value;
while (iss >> value)
result += value;
bool const received_some_values_but_not_seen_eol{
(result.count > 0) && !iss.eof()
};
bool const fail_bit_is_set_but_eof_bit_is_not{
iss.fail() && !iss.eof()
};
if (received_some_values_but_not_seen_eol
|| fail_bit_is_set_but_eof_bit_is_not)
throw "non-numeric input";
}
void do_averages(std::istream& in, std::ostream& out) {
std::string line;
while (std::getline(in, line)) {
sum_count sc{};
try {
do_single_average(line, sc);
}
catch (char const* errmsg) {
out << errmsg << " - line ignored" << std::endl;
continue;
}
if (sc.count == 0) return;
out << sc << std::endl;
}
}
void do_averages() {
do_averages(std::cin, std::cout);
}
#include <iosfwd>
void do_averages(std::istream&, std::ostream&);
void do_averages();
#ifndef PXT_off
#ifndef PXT_ostream
#define PXT_ostream std::cout
#endif
#define PX(...)\
PXT_namespace::PXT_class{__func__, __FILE__, __LINE__, #__VA_ARGS__, (__VA_ARGS__)}
#if defined(PXT_HAVE_BOOST_TYPEINDEX)\
|| defined(__has_include) && __has_include(<boost/type_index.hpp>)
#include <boost/type_index.hpp>
#define PT(...)\
PXT_namespace::PXT_helper(__func__, __FILE__, __LINE__, #__VA_ARGS__,\
boost::typeindex::type_id_with_cvr<__VA_ARGS__>())
#endif
#if !defined(PX)\
|| !defined(PT)
struct PXT_dummy{
template<typename T> void operator==(T const&) const {}
template<typename T> void operator*=(T const&) const {}
template<typename T> void operator>>=(T const&) const {}
template<typename T> void operator%=(T const&) const {}
};
#if !defined(PX)
#define PX(...) PXT_dummy{}
#endif
#if !defined(PT)
#define PT(...) PXT_dummy{}
#endif
#endif
#include <sstream>
#include <iostream>
namespace PXT_namespace {
class PXT_class {
bool done_{};
char const* func_;
char const* file_;
int const line_{};
char const* texpr_;
std::stringstream streamed_result{};
std::stringstream expected_result{};
std::ostream& source_location() const;
void plain_output();
void two_line_compare();
void multi_line_compare();
void glob_pattern_compare();
struct Result {
unsigned long long total;
unsigned long long failed;
~Result();
};
static Result counter;
public:
static std::ostream& out_stream;
static bool ignore_expecations;
template<typename T>
PXT_class(char const* func, char const* file, int line, char const* texpr, T const& expr)
: func_{func}, file_{file}, line_{line}, texpr_{texpr}
{
streamed_result.copyfmt(out_stream);
streamed_result << expr;
}
~PXT_class() {
if (not done_) plain_output();
}
template<typename T>
void operator==(T const& expr) {
expected_result.copyfmt(streamed_result);
expected_result << expr;
two_line_compare();
}
void operator>>=(std::string lines) {
expected_result << lines;
multi_line_compare();
}
void operator*=(std::string smatch) {
glob_pattern_compare();
}
};
} // namespace
#ifdef PXT_header_only
#include "pxt.cpp"
#endif
#endif
#ifndef PXT_header_only
#include "pxt.h"
#endif
#include <iostream>
namespace PXT_namespace {
std::ostream& PXT_class::out_stream{PXT_ostream};
PXT_class::Result PXT_class::counter{0, 0};
PXT_class::Result::~Result() {
if (failed > 0) {
PXT_class::out_stream << "** " << failed << " of "
<< total << " tests failed **" << std::endl;
}
else if (total > 0) {
PXT_class::out_stream << "** ALL " << total << " TESTS PASSED **" << std::endl;
}
}
std::ostream& PXT_class::source_location() const {
return out_stream << file_ << ' ' << '[' << func_ << ':' << line_ << ']' << ' ' << texpr_;
}
void PXT_class::plain_output() {
source_location() << " == " << streamed_result.str() << std::endl;
}
void PXT_class::two_line_compare() {
done_ = true;
++counter.total;
if (streamed_result.str() != expected_result.str()) {
++counter.failed;
source_location() << '\n'
<< " = received: " << streamed_result.str() << '\n'
<< " ! expected: " << expected_result.str() << std::endl;
}
}
void PXT_class::multi_line_compare() {
std::string streamed_line{};
std::string expected_line{};
std::ostringstream common{};
while (std::getline(streamed_result, streamed_line),
(expected_result >> std::ws),
std::getline(expected_result, expected_line),
!streamed_result.eof()
&& !expected_result.eof()
&& (streamed_line == expected_line)) {
common << " = " << expected_line << '\n';
}
done_ = true;
++counter.total;
if (streamed_result.eof()
&& expected_result.eof()) return; // no difference
++counter.failed;
if (streamed_result.eof()) { // less streamed than expected
source_location() << '\n'
<< common.str()
<< " + expected: " << expected_line << std::endl;
return;
}
if (expected_result.eof()) { // less expected than streamed
source_location() << '\n'
<< common.str()
<< " + received: " << streamed_line << std::endl;
return;
}
source_location() << '\n' // difference in streamed to expected
<< common.str()
<< " ~ received: " << streamed_line << '\n'
<< " ~ expected: " << expected_line << std::endl;
}
} // namespace