Merge pull request #4 from claremacrae/add_cpp

Add C++ version
This commit is contained in:
Emily Bache
2019-08-13 14:04:08 +02:00
committed by GitHub
16 changed files with 31458 additions and 0 deletions

2
cpp/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.idea/
cmake-build-*/

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})

17
cpp/tests/invoice.json Normal file
View File

@@ -0,0 +1,17 @@
{
"customer": "BigCo",
"performances": [
{
"playID": "hamlet",
"audience": 55
},
{
"playID": "as-like",
"audience": 35
},
{
"playID": "othello",
"audience": 40
}
]
}

View File

@@ -0,0 +1,13 @@
{
"customer": "BigCoII",
"performances": [
{
"playID": "henry-v",
"audience": 53
},
{
"playID": "as-like",
"audience": 55
}
]
}

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

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

4
cpp/tests/new_plays.json Normal file
View File

@@ -0,0 +1,4 @@
{
"henry-v": {"name": "Henry V", "type": "history"},
"as-like": {"name": "As You Like It", "type": "pastoral"}
}

5
cpp/tests/plays.json Normal file
View File

@@ -0,0 +1,5 @@
{
"hamlet": {"name": "Hamlet", "type": "tragedy"},
"as-like": {"name": "As You Like It", "type": "comedy"},
"othello": {"name": "Othello", "type": "tragedy"}
}

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);
}

View File

@@ -0,0 +1,6 @@
Statement for BigCo
Hamlet: $650.00 (55 seats)
As You Like It: $580.00 (35 seats)
Othello: $500.00 (40 seats)
Amount owed is $1,730.00
You earned 47 credits

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