online compiler and debugger for c/c++

code. compile. run. debug. share.
Source Code   
Language
/* ================================================================= Step 1 ================================================================= */ #include "process_values.h" #if !defined(TEST_AVERAGER)\ && !defined(TEST_MINMAXER) #include "Averager.h" #include <cstdlib> // EXIT_FAILURE (macro) // EXIT_SUCCESS (macro) #include <iostream> // std::cout // std::flush int main(int const ac, char const** av) { switch((ac == 1) ? ' ' : *av[1]) { default: std::cout << "Usage: " << av[0] << " a # (no args)" << std::endl; return EXIT_FAILURE; case ' ': my::process_values<my::Averager<float>>(); // my::process_values<my::MinMaxer<float>>(); break; } return EXIT_SUCCESS; } #else #include "pxt.h" // PX (macro) + overloading magic for TDD) #include <sstream> // std::istringstream // std::ostringstream #ifndef TEST_AVERAGER // ::::::::::::::::::::::::::::::::::::::::: void testing_averager() {} // avoids to change the main program #else #include "Averager.h" auto do_averages = [](auto& _1, auto& _2) { return my::process_values<my::Averager<float>>(_1, _2); }; 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" "1 abc" "\n" // <--- will set, both FAIL and EOF, // for some older C++ Libraries; // see fix in `process_values()` }; 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" "non-numeric input - line ignored" "\n"; } //////////////////////////////////////////////////////////// } void testing_averager() { typical_good_input(); some_empty_inputs(); some_invalid_inputs(); } #endif // TEST_AVERAGER #ifndef TEST_MINMAXER // ::::::::::::::::::::::::::::::::::::::::: void testing_minmaxer() {} // avoids to change the main program #else #include <cassert> // <-- consider to remove when some // tests are actually implemented void testing_minmaxer() { // assert(!"TBD – specific MinMaxer tests need to be added"); // } #endif // TEST_MINMAXER int main() { testing_averager(); testing_minmaxer(); } #endif // (automated tests)
# STEP 1 **IMPORTANT NOTE:**\ The *Interactive Version* of this program wants to read input from the terminal. So please do not be surprised once you compile and run it nothing seems to happen: - **The program probably just waits for some keyboard input.** - So just type some numbers, all in the same line. - The response then should be the average of these. - You can enter as many input lines as you want. - **To terminate enter an empty line.** - To compile and run the *Interactive Version* - at the command line (eg. in VSCode) type the following: ```bash g++ -std=c++17 *.cpp && ./a.out`; ``` - for the **onlineGDB** version just click the *RUN* button. - You may also run builtin tests as suggested when working in *TDD* style (= Test Driven Driven Development). - The command line now should look like this: ```bash gcc -DTEST_AVERAGES -std=c++17 *.cpp && ./a.out; ``` - When using **onlineGDB** find its configuration menu (the cog-wheel top-right) and add the below as *EXTRA* compile option: ```bash -DTEST_AVERAGER ``` When the program indicates it passes all the tests you have two basic options to proceed: - **Option A** is to do a thorough examination of the code **so that can ask informed questions when the trainer later walks you through the code. Be sure to mark **every language construct** (and maybe other aspects) you do not understand and for which you want an explanation later.\ Proceed through the files in the following order, giving the most attention to the first three, between you may need to skip back and forth a bit: - `process_values.h` - `IProcessor.h` - `Averager.h` - `main.cpp` (limit your analysis to line 16 … 19) - `pxt.h` and `pxt.cpp` may be skipped for now as they mostly supply the "magic" of the TDD-part used by the test code in the remaining `main` program. **You shouldn't give it much attention right now, if any at all.** - **Option B** is to analyze only as much as is necessary to understand how the class `Averager` supplies the calculation of the per line averages. Then extend the program so that it can be compiled with a class `MinMaxer` which instead of calculating averages finds and outputs the minimum and maximum af all the values in a line. Here is how you may proceed: - Copy and and rename the files - `Averager.h` -> `Minmaxer.h` - `Averager.cpp` -> `Minmaxer.cpp` - In the *MinMaxer* copies do the obvious renaming (do not forget the include guards in the header). - Replace the single data member `sum_` of `class MinMaxer` with two members `min_` an `max_`. - Optionally change the data member - `int count_{0}` to - `bool first_{true}`. - Adapt the counting logic to properly recognize whether the value is supplied (which must then be assigned to both, `min_` and `max_`) or a subsequent value (which must be assigned to `min_` resp. `max`, if it is smaller or larger that the current one. - The necessary modification for testing already exists in `main.cpp`: - it is suggested you start with the interactive version by just moving the `//`-comment one line up; - then you may proceed by adding TDD-style testing in the in the already prepared section as indicated by\ `assert(!"TBD – specific MinMaxer tests need to be added");` **Note:**\ The easiest way to create a set of tests for the `MinMaxer` class is to simply copy down the tests for the`Averager` and adapt the expectations. OTH, as nothing changed wrt. the logic to recognize valid input, it doesn't seem to make that much sense to keep the tests with bad input. Instead, maybe change the the templated data type and test the `MinMaxer` class eg. with a signed or unsigned integral type. ------------------------------------------------------------------ TODO (optional - meant for further self-study only): - Implement other kinds of *Value Processors* eg. one to calculate a percentage by which the average is placed (at a linear scale) between minimum and maximum, like that: ```txt min_ >|----+---------------------|< max_ --- - - - |----^ offset (= % relative to spread) - - - |<-- spread -------------->| - - - - - ``` - You may choose to write the `Percentager` class from scratch, as its logic straight forward. - Alternatively you may create it by "re-using" the `Averager` and the `Minmaxer`, though that requires a bit of change to both: - either you supply the getters which are necessary so that the `Percentager` can do its work; - alternatively you could increase the visibility of their data members to `protected` and re-use both together as base classes for the `Percentager`. **Note:**\ For the interactive version of `main` you may apply the following modification to control which *Value Processor* to use via a single letter command line argument: ```cpp … // +-- now checking for ONE(!) argument present // | switch((ac == 2) ? ' ' : *av[1]) { default: std::cout << "Usage: " << av[0] << " a # average\n" " -or- " << av[0] << " x # extrema\n" " -or- " << av[0] << " p # percent\n" << std::flush; return EXIT_FAILURE; case 'a': my::process_values<my::Averager<float>>(); break; case 'x': my::process_values<my::MinMaxer<float>>(); break; case 'p': my::process_values<my::Percentager<float>>(); break; } … ```
#ifndef MY_PROCESS_VALUES_H_ #define MY_PROCESS_VALUES_H_ #include "IProcessor.h" #include <iostream> #include <sstream> namespace { template<typename Processor> void process_single_line(std::string const& line, Processor& processor) { std::istringstream iss{line}; typename Processor::value_type value{}; while (iss >> value) { processor += value; } bool const received_some_values_but_not_seen_eol{ processor && not 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 or fail_bit_is_set_but_eof_bit_is_not ) { throw "non-numeric input"; } } } // namespace namespace my { template<typename Processor> void process_values(std::istream& in, std::ostream& out) { std::string line{}; while (std::getline(in, line)) { Processor processor; line.append(1, ' '); // get around EOF bug on // some old C++ libraries try { process_single_line(line, processor); } catch (char const* errmsg) { out << errmsg << " - line ignored" << std::endl; continue; } if (not processor) // empty line (=> end loop) return; out << processor << std::endl; // show result } } template<typename Processor> void process_values() { process_values<Processor>(std::cin, std::cout); } } // namespace #endif // include guard
#include "process_values.h" // for compile test only
#ifndef MY_I_PROCESSOR_H_ #define MY_I_PROCESSOR_H_ #include <iosfwd> #include <type_traits> namespace my { template<typename T> constexpr auto all_required_operations() { T a, b; return noexcept(a = b) && noexcept(a += b) && noexcept(a + b) && noexcept(a < b) && noexcept(a / int{}); } template<typename T> struct IProcessor { static_assert(std::is_arithmetic_v<T>, "designed for arithmetic types only"); using value_type = T; // @throws nothing (but still may crash the whole process if // an implementing class does not adhere to its own `noexcept` // specification) // virtual void operator+=(value_type rhs) /*noexcept(all_required_operations<value_type>())*/ = 0; // @post `true` returned if there has been at least ONE prior // call to `operator++` // virtual explicit operator bool() const =0; // @throws any exception an `std::ostream` may throw for any // reason when the stream insertion operation (`operator<<`) // for something of type `value` throws // // @pre there must have been one prior call to `operator+=` // // @pre the `std::ostream` argument must refer to an ostream // in the "good" state (none of EOF-, FAIL-, or BAD-bit set) // // @post the ostream ostream may not any longer be in the good // state but unless it has been set to notify error condition // via an exception none of its format settings differs from // what it had been when the operation started // virtual void print(std::ostream&) const =0; }; template<typename T> inline decltype(auto) operator<<(std::ostream& lhs, IProcessor<T> const& rhs) { rhs.print(lhs); return lhs; } } // namespace #endif // include guard
#include "IProcessor.h" // for compile test only // NOTE: // to show effectivity of `static_assert` enable the following: // class fails_the_is_arithmetic_test : my::IProcessor<void> {};
#ifndef MY_AVERAGER_H_ #define MY_AVERAGER_H_ #include "IProcessor.h" #include <cassert> #include <iostream> namespace my { template <typename T> class Averager : public IProcessor<T> { public: // normally constructible and destructible Averager() =default; ~Averager() =default; // neither copyable nor movable (as not needed) Averager(Averager const&) =delete; Averager(Averager&&) =delete; Averager& operator=(Averager const&) =delete; Averager& operator=(Averager&&) =delete; using value_type = T; // (mostly for readability) private: value_type sum_{}; int count_{0}; public: void operator+=(value_type rhs) noexcept(noexcept(rhs = rhs) && noexcept(rhs < rhs) && noexcept(rhs + rhs) && noexcept(rhs += rhs)) override { sum_ += rhs; ++count_; } explicit operator bool() const override { return (count_ > 0); } void print(std::ostream& os) const override; }; template<typename T> void Averager<T>::print(std::ostream& os) const { assert(count_ > 0); auto const average = sum_ / count_; os << average; } } // namespace #endif // include guard
#include "Averager.h" // for compile test only
#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_expectations; 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

Compiling Program...

Command line arguments:
Standard Input: Interactive Console Text
×

                

                

Program is not being debugged. Click "Debug" button to start program in debug mode.

#FunctionFile:Line
VariableValue
RegisterValue
ExpressionValue