Add C++ implementation

This commit is contained in:
Clare Macrae
2019-08-13 09:44:16 +01:00
parent 4b9521d849
commit 7c3a7d4220
10 changed files with 31411 additions and 0 deletions

6
cpp/CMakeLists.txt Normal file
View File

@@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.4)
project(theatrical_players_refactoring_kata)
set(CMAKE_CXX_STANDARD 11)
add_subdirectory(tests)

14
cpp/README.md Normal file
View File

@@ -0,0 +1,14 @@
Theatrical Players Refactoring Kata - C++ version
========================================================
See the [top level readme](https://github.com/emilybache/Theatrical-Players-Refactoring-Kata) for general information about this exercise.
## Dependencies
The tests use the following header-only projects, which are in the `third_party` sub-directory:
| Project | Licence |
| --- | --- |
| [doctest test framework](https://github.com/onqtam/doctest) | [MIT](https://github.com/onqtam/doctest/blob/master/LICENSE.txt) |
| [ApprovalTests.cpp](https://github.com/approvals/ApprovalTests.cpp) | [Apache 2.0](https://github.com/approvals/ApprovalTests.cpp/blob/master/LICENSE) |
| [JSON for Modern C++](https://github.com/nlohmann/json) | [MIT](https://github.com/nlohmann/json/blob/develop/LICENSE.MIT) |

95
cpp/statement.cpp Normal file
View File

@@ -0,0 +1,95 @@
#include "statement.h"
#include <iostream>
#include <sstream>
#include <iomanip>
// https://stackoverflow.com/a/7277333/104370
class comma_numpunct : public std::numpunct<char>
{
protected:
virtual char do_thousands_sep() const override
{
return ',';
}
virtual std::string do_grouping() const override
{
return "\03";
}
};
std::string format_as_dollars(float amount)
{
// this creates a new locale based on the current application default
// (which is either the one given on startup, but can be overriden with
// std::locale::global) - then extends it with an extra facet that
// controls numeric output.
const std::locale comma_locale(std::locale(), new comma_numpunct());
std::ostringstream out;
// tell stream to use our new locale.
out.imbue(comma_locale);
out.precision(2);
out << "$" << std::fixed << amount;
return out.str();
}
std::string statement(
const nlohmann::json& invoice,
const nlohmann::json& plays)
{
float total_amount = 0;
int volume_credits = 0;
std::stringstream result;
result << "Statement for " << invoice["customer"].get<std::string>() << '\n';
for( const auto& perf : invoice["performances"])
{
float this_amount = 0;
auto play = plays[perf["playID"].get<std::string>()];
if (play["type"] == "tragedy")
{
this_amount = 40000;
if (perf["audience"] > 30)
{
this_amount += 1000 * (perf["audience"].get<int>() - 30);
}
}
else if (play["type"] == "comedy")
{
this_amount = 30000;
if (perf["audience"] > 20)
{
this_amount += 10000 + 500 * (perf["audience"].get<int>() - 20);
}
this_amount += 300 * perf["audience"].get<int>();
}
else
{
throw std::domain_error("unknown type: " + play["type"].get<std::string>());
}
// add volume credits
volume_credits += std::max(perf["audience"].get<int>() - 30, 0);
// add extra credit for every ten comedy attendees
if ("comedy" == play["type"])
{
volume_credits += perf["audience"].get<int>() / 5;
}
// print line for this order
result << " " << play["name"].get<std::string>() << ": " << format_as_dollars(this_amount/100) <<
" (" << perf["audience"] << " seats)\n";
total_amount += this_amount;
}
result << "Amount owed is " << format_as_dollars(total_amount/100.0f) << "\n";
result << "You earned " << volume_credits << " credits";
return result.str();
}

7
cpp/statement.h Normal file
View File

@@ -0,0 +1,7 @@
// TODO Include Guard
#include "json.hpp"
std::string statement(
const nlohmann::json& invoice,
const nlohmann::json& plays);

15
cpp/tests/CMakeLists.txt Normal file
View File

@@ -0,0 +1,15 @@
project(tests)
add_executable(${PROJECT_NAME}
../statement.cpp
../statement.h
test_statement.cpp
main.cpp)
target_include_directories(
${PROJECT_NAME}
PUBLIC
../third_party ..)
enable_testing()
add_test(NAME ${PROJECT_NAME}_test COMMAND ${PROJECT_NAME})

2
cpp/tests/main.cpp Normal file
View File

@@ -0,0 +1,2 @@
#define APPROVALS_DOCTEST
#include "ApprovalTests.hpp"

View File

@@ -0,0 +1,43 @@
#include "statement.h"
#include "json.hpp"
#include "doctest.h"
#include "ApprovalTests.hpp"
#include <iostream>
namespace
{
std::string get_adjacent_file(const std::string& filename)
{
return ApprovalTestNamer().getDirectory() + filename;
}
nlohmann::json read_json_file(const std::string& filename)
{
std::ifstream invoice_stream(filename);
if ( ! invoice_stream.is_open())
{
std::cout << "Error opening file:" << filename << '\n';
std::cout << "Error: " << strerror(errno) << '\n';
}
nlohmann::json result;
invoice_stream >> result;
return result;
}
}
TEST_CASE("test_example_statement")
{
auto invoice = read_json_file(get_adjacent_file("invoice.json"));
auto plays = read_json_file(get_adjacent_file("plays.json"));
Approvals::verify(statement(invoice, plays));
}
TEST_CASE("test_statement_with_new_play_types")
{
auto invoice = read_json_file(get_adjacent_file("invoice_new_plays.json"));
auto plays = read_json_file(get_adjacent_file("new_plays.json"));
CHECK_THROWS_AS(statement(invoice, plays), std::domain_error);
}

2606
cpp/third_party/ApprovalTests.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

5939
cpp/third_party/doctest.h vendored Normal file

File diff suppressed because it is too large Load Diff

22684
cpp/third_party/json.hpp vendored Normal file

File diff suppressed because it is too large Load Diff