#include <iostream>
#include <vector>
#include "counter.h"
#include "tests.h"
int main()
{
std::vector<Counter> counters_1;
std::vector<Counter> counters_2;
unsigned int loop_value = 0;
for (loop_value = 0; loop_value < 5; loop_value++) {
counters_1.emplace_back();
}
for (loop_value = 0; loop_value < 3; loop_value++) {
counters_2.emplace_back(10);
}
std::cout << "\nVector 1:\n";
for (loop_value = 0; loop_value < 5; loop_value++) {
std::cout << counters_1[loop_value].GetValue() << "\n";
}
std::cout << "\nVector 2:\n";
for (loop_value = 0; loop_value < 3; loop_value++) {
std::cout << counters_2[loop_value].GetValue() << "\n";
}
int outer_loop_value = 0;
for (outer_loop_value = 0; outer_loop_value < 3; outer_loop_value++) {
for (loop_value = 0; loop_value < 5; loop_value++) {
counters_1[loop_value].Count(1);
}
for (loop_value = 0; loop_value < 3; loop_value++) {
counters_2[loop_value].Count(-1);
}
}
std::cout << "\nVector 1:\n";
for (loop_value = 0; loop_value < 5; loop_value++) {
std::cout << counters_1[loop_value].GetValue() << "\n";
}
std::cout << "\nVector 2:\n";
for (loop_value = 0; loop_value < 3; loop_value++) {
std::cout << counters_2[loop_value].GetValue() << "\n";
}
std::cout << "\n------------------------------------\n\n";
RunTests();
return 0;
}
# Refactoring Exercise
Do it "incrementally" with re-compiling (and -testing) after each
step.
## Step 0
Verify that in the current state all tests are passed.
=> COMPILE and TEST (if all tests pass continue with next step)
## Step 1
Verify that the explicitly defined copy-constructor does exactly
the same as a compiler-supplied default version would do.
* Replace the prototype definition in `counter.h` with a request
to the compiler to supply the default version.
* Remove the implementation in `counter.cpp`
=> COMPILE and TEST (if all tests pass continue with next step)
## Step 2
Verify that the explicitly defined destructor constructor does
exactly the same as a compiler-supplied default version would do.
* Replace the prototype definition in `counter.h` with a request
to the compiler to supply the default version.
* Remove the implementation in `counter.cpp`
=> COMPILE and TEST (if all tests pass continue with next step)
## Step 3
Verify that the explicitly copy assignment (`operator=`) does
exactly the same as a compiler-supplied default version would do.
* Replace the prototype definition in `counter.h` with a request
to the compiler to supply the default version.
* Remove the implementation in `counter.cpp`
=> COMPILE and TEST (if all tests pass continue with next step)
## Step 4
Simplify the implementation of `operator==`. (Hint: it can be
reduced to a single return statement.)
=> COMPILE and TEST (if all tests pass continue with next step)
## Step 5
Simplify the implementation of `operator<`. (Hint: it can be
reduced to a single return statement.)
=> COMPILE and TEST (if all tests pass continue with next step)
## Step 6a (optional, requires C++20)
Implement the "spaceship" `operator<=>` (= 3-way comparison - see
https://en.cppreference.com/w/cpp/language/default_comparisons)
=> COMPILE (if no errors continue with next step)
## Step 6b (optional, requires C++20)
Implement additional tests for the 3-way comparison.
=> COMPILE and TEST (if all tests pass continue with next step)
## Step 6c (optional, requires C++20)
Replace the explicitly defined and implemented comparisons
(`operator==` and `operator<`) with a request to the compiler to
automatically supply the versions based on the "spaceship"
`operator<=>`
Implement additional tests for the 3-way comparison.
=> COMPILE and TEST (if all tests pass continue with next step)
## Step 7 (optional, requires C++23)
Remove the "spaceship" `operator<=>` to demonstrate the default
versions of `operator==` and `operator<` may be requested from
the compiler even without having `operator<=>` defined.
=> COMPILE and TEST (if all tests pass continue with next step)
#ifndef COUNTER_H_
#define COUNTER_H_
class Counter {
private:
int value_;
public:
explicit Counter(int value = 0);
// see README.md Step 1
Counter(const Counter& counter);
// see README.md Step 2
~Counter();
void SetValue(int value);
int GetValue() const;
void Count(int step);
// see README.md Step 3
Counter& operator=(const Counter& counter);
// see README.md optional Step 6 and Step 7
bool operator<(const Counter& counter) const;
bool operator==(const Counter& counter) const;
};
#endif // COUNTER_H_
#include "counter.h"
Counter::Counter(const int value) : value_(value) {}
// see README.md Step 1
Counter::Counter(const Counter& counter) : value_(counter.value_) {}
// see README.md Step 2
Counter::~Counter()
{
// nothing to do
}
void Counter::SetValue(const int value) { value_ = value; }
int Counter::GetValue() const { return value_; }
void Counter::Count(const int step) { value_ = value_ + step; }
Counter& Counter::operator=(const Counter& counter)
{
value_ = counter.value_;
return *this;
}
// see README.md Step 4
bool Counter::operator<(const Counter& counter) const
{
bool result = false;
if (value_ < counter.value_) {
result = true;
}
else {
result = false;
}
return result;
}
// see README.md Step 5
bool Counter::operator==(const Counter& counter) const
{
bool result = false;
if (value_ == counter.value_) {
result = true;
}
else {
result = false;
}
return result;
}
#include "tests.h"
#include "test_functions.h"
#include "counter.h"
void CountHelper(Counter* counter, const int loops, const int step)
{
for (auto i = 0; i < loops; i++) {
counter->Count(step);
}
}
TEST(TestCounterInit1)
{
const auto expected = 0;
Counter counter;
ASSERT(counter.GetValue() == expected, "Counter initialization failed");
}
TEST(TestCounterInit2)
{
const auto expected = 42;
Counter counter(expected);
ASSERT(counter.GetValue() == expected, "Counter initialization failed");
}
TEST(TestCounterInit3)
{
const auto expected = 42;
const auto step = 1;
const Counter counter_base(expected);
ASSERT_INT(counter_base.GetValue(), expected);
auto counter(counter_base);
ASSERT(counter.GetValue() == expected, "Counter initialization failed");
counter.Count(step);
ASSERT(counter.GetValue() == expected + step,
"Counter initialization failed");
}
TEST(TestCounterCount1)
{
const auto expected = 4;
const auto step = 1;
Counter counter;
CountHelper(&counter, expected, step);
ASSERT_INT(counter.GetValue(), expected);
}
TEST(TestCounterCount2)
{
const auto init_value = 10;
const auto expected = 4;
const auto step = -1;
Counter counter(init_value);
CountHelper(&counter, init_value - expected, step);
ASSERT_INT(counter.GetValue(), expected);
}
TEST(TestCounterSetValue)
{
const auto expected = 4;
Counter counter;
counter.SetValue(expected);
ASSERT_INT(counter.GetValue(), expected);
}
TEST(TestCounterCopy)
{
const auto init_value = 10;
const auto expected = 42;
const Counter counter_base(expected);
Counter counter(init_value);
ASSERT_INT(counter.GetValue(), init_value);
counter = counter_base;
ASSERT(counter.GetValue() == expected, "Counter copy failed");
}
TEST(TestCounterCompare1)
{
const Counter counter_left(2);
const Counter counter_right(5);
ASSERT(counter_left < counter_right, "Counter comparison failed");
}
TEST(TestCounterCompare2)
{
const Counter counter_left(5);
const Counter counter_right(5);
ASSERT(!(counter_left < counter_right), "Counter comparison failed");
}
TEST(TestCounterCompare3)
{
const Counter counter_left(23);
const Counter counter_right(5);
ASSERT(!(counter_left < counter_right), "Counter comparison failed");
}
TEST(TestCounterCompare4)
{
const Counter counter_left(2);
const Counter counter_right(5);
ASSERT(!(counter_left == counter_right), "Counter comparison failed");
}
TEST(TestCounterCompare5)
{
const Counter counter_left(5);
const Counter counter_right(5);
ASSERT(counter_left == counter_right, "Counter comparison failed");
}
TEST(TestCounterCompare6)
{
const Counter counter_left(23);
const Counter counter_right(5);
ASSERT(!(counter_left == counter_right), "Counter comparison failed");
}
TEST_SUITE(TestCounter)
{
RUN_TEST(TestCounterInit1);
RUN_TEST(TestCounterInit2);
RUN_TEST(TestCounterInit3);
RUN_TEST(TestCounterCount1);
RUN_TEST(TestCounterCount2);
RUN_TEST(TestCounterSetValue);
RUN_TEST(TestCounterCopy);
RUN_TEST(TestCounterCompare1);
RUN_TEST(TestCounterCompare2);
RUN_TEST(TestCounterCompare3);
RUN_TEST(TestCounterCompare4);
RUN_TEST(TestCounterCompare5);
RUN_TEST(TestCounterCompare6);
}
void RunTests() { RUN_TEST_SUITE(TestCounter); }
// Copyright 2018 MicroConsult GmbH
#ifndef TEST_FUNCTIONS_H_
#define TEST_FUNCTIONS_H_
#include <stdio.h>
static const int test_message_buffer_size = 1024;
static char test_message_buffer[test_message_buffer_size];
#define TEST(fx) static void fx(const char** message)
#define TEST_SUITE(fx) void fx(int* tests_total, int* tests_successful)
#define RUN_TEST(test) \
do { \
const char* message = nullptr; \
++*tests_total; \
test(&message); \
if (message) { \
printf("--- ERROR (%s): %s \n", #test, message); \
} \
else { \
++*tests_successful; \
printf("+++ passed: %s\n", #test); \
} \
} while (0)
#define RUN_TEST_SUITE(test_suite) \
do { \
int tests_total = 0; \
int tests_successful = 0; \
test_suite(&tests_total, &tests_successful); \
printf("-----------------------------------------------\n"); \
if (tests_successful == tests_total) { \
printf("+ Test Suite '%s': all %d tests passed\n\n", #test_suite, \
tests_total); \
} \
else { \
printf("- Test Suite '%s': %d of %d tests passed\n\n", #test_suite, \
tests_successful, tests_total); \
} \
} while (false)
#define ASSERT(test, error_message) \
do { \
if (test) { \
*message = nullptr; \
} \
else { \
snprintf(test_message_buffer, test_message_buffer_size - 1, \
error_message); \
*message = test_message_buffer; \
return; \
} \
} while (false)
#define ASSERT_INT(value, expected) \
do { \
if (value == expected) { \
*message = nullptr; \
} \
else { \
snprintf(test_message_buffer, test_message_buffer_size - 1, \
"Wrong value - expected: %d, current value: %d", expected, \
value); \
*message = test_message_buffer; \
return; \
} \
} while (false)
#endif // TEST_FUNCTIONS_H_
#ifndef TESTS_H_
#define TESTS_H_
void RunTests();
#endif // TESTS_H_