1 #include "gtest/gtest.h"
16 #include <string_view>
18 #include <type_traits>
42 const size_t start = path.find_first_not_of(
" ");
43 const size_t end = path.find_last_not_of(
" ");
44 const size_t diff = end - start;
45 return diff > 0uz ? path.substr(start, diff + 1) :
"";
63 const auto func = [](
const char c) {
return std::isprint(c); };
66 auto printable = str | std::views::filter(func);
68 return {printable.begin(), printable.end()};
80 "8=FIX.4.49=14835=D");
84 constexpr std::string
transform_view(
const std::string str,
const auto func) {
85 const auto transformed = str | std::views::transform(func);
86 return {transformed.begin(), transformed.end()};
90 constexpr
char toggle_case(
const char c,
const bool to_upper) {
92 constexpr std::string_view the_lower_case =
"abcdefghijklmnopqrstuvwxyz";
93 constexpr std::string_view the_upper_case =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
94 assert(std::size(the_lower_case) == std::size(the_upper_case));
96 for (
auto i = 0uz; i < std::size(the_upper_case); ++i) {
98 if (c == the_lower_case[i])
99 return the_upper_case[i];
101 if (c == the_upper_case[i])
102 return the_lower_case[i];
115 TEST(cpp11, case_tests_common) {
116 constexpr
auto do_nothing = [](
const char c) {
return c; };
119 static_assert(
transform_view(std::string{
"hello"}, do_nothing) ==
"hello");
121 static_assert(
tolower(
'a') ==
'a');
122 static_assert(
tolower(
'C') ==
'c');
123 static_assert(
tolower(
'Z') ==
'z');
124 static_assert(
tolower(
'0') ==
'0');
126 static_assert(
toupper(
'a') ==
'A');
127 static_assert(
toupper(
'C') ==
'C');
128 static_assert(
toupper(
'z') ==
'Z');
129 static_assert(
toupper(
'0') ==
'0');
154 constexpr
auto func = [](
const char c) {
return c ==
' ' ?
'_' : c; };
162 static_assert(
to_lowercase(
" HeLlO0\tTHERE") ==
" hello0\tthere");
163 static_assert(
to_lowercase(std::string{
" HeLlO0\tTHERE"}) ==
169 static_assert(
to_uppercase(
" HeLlO0\tTHERE") ==
" HELLO0\tTHERE");
170 static_assert(
to_uppercase(std::string{
" HeLlO0\tTHERE"}) ==
177 static_assert(
to_snakecase(
"hello there") ==
"hello_there");
178 static_assert(
to_snakecase(std::string{
"hello there"}) ==
"hello_there");
183 return std::filesystem::path{path}.filename();
189 return std::filesystem::path{path}.extension();
192 std::string
get_stem(
const std::string_view path) {
193 return std::filesystem::path{path}.stem();
196 TEST(cpp17, filename_operations) {
204 EXPECT_EQ(
get_file_name(std::string{
"blah/file.jpg"}),
"file.jpg");
205 EXPECT_EQ(
get_file_name(std::string_view{
"blah/file.jpg"}),
"file.jpg");
219 EXPECT_EQ(
get_stem(
"blah/file.jpg"),
"file");
220 EXPECT_EQ(
get_stem(
"/etc/hello/file.SVG"),
"file");
221 EXPECT_EQ(
get_stem(
"yeah.jpg/"),
"");
222 EXPECT_EQ(
get_stem(
"blah.123"),
"blah");
223 EXPECT_EQ(
get_stem(std::string{
"blah.123"}),
"blah");
224 EXPECT_EQ(
get_stem(std::string_view{
"blah.123"}),
"blah");
230 constexpr std::string_view delim{
" "};
231 std::vector<std::string> words;
233 std::ranges::transform(std::views::split(sentence, delim),
234 std::back_inserter(words), [](
const auto word) {
235 return std::string{word.begin(), word.end()};
242 constexpr std::string_view sentence{
243 "A SYSTEM IS NO BETTER THAN ITS SENSORY ORGANS"};
245 EXPECT_EQ(std::size(words), 9uz);
248 EXPECT_FALSE(
split_string(std::string_view{
"1 2"}).empty());
251 TEST(cpp20, check_substrings) {
254 constexpr std::string_view quotation{
255 R
"(It's a little like wrestling a gorilla:
256 you don't quit when you're tired, you quit when the gorilla is tired
260 EXPECT_TRUE(quotation.starts_with(
"It's"));
261 EXPECT_TRUE(quotation.ends_with(
"Strauss"));
264 EXPECT_TRUE(quotation.contains(
"gorilla"));
265 EXPECT_FALSE(quotation.contains(
"mandrill"));
268 TEST(cpp11, range_based_for_loops) {
278 std::list v1{1, 2, 3, 4, 5};
281 for (std::list<int>::iterator i = v1.begin(); i != v1.end(); ++i)
284 EXPECT_EQ(v1.front(), 2);
287 for (
auto i = v1.begin(); i != v1.end(); ++i)
290 EXPECT_EQ(v1.front(), 3);
296 EXPECT_EQ(v1.front(), 4);
299 const auto increment = [](
auto &i) { ++i; };
300 std::for_each(v1.begin(), v1.end(), increment);
301 EXPECT_EQ(v1.front(), 5);
304 std::ranges::for_each(v1, increment);
305 EXPECT_EQ(v1.front(), 6);
313 struct count_positive_elements {
314 void operator()(
const int i) {
323 const auto function_object =
324 std::for_each(cbegin(v1), cend(v1), count_positive_elements{});
327 const auto count = function_object.count_;
328 EXPECT_EQ(count, 5uz);
343 virtual void func3() = 0;
355 base &operator=(const
base &) = delete;
356 base &operator=(
base &&) noexcept = delete;
359 virtual ~
base() = default;
371 void func3() override final{};
375 void func4() { parent::func3(); }
382 TEST(cpp11, classes_and_type_traits) {
400 EXPECT_TRUE(std::is_const_v<const int>);
401 EXPECT_FALSE(std::is_const_v<int>);
402 EXPECT_TRUE(std::is_const<decltype(s1)>::value);
403 EXPECT_FALSE(std::is_const<decltype(s2)>::value);
412 EXPECT_TRUE(std::has_virtual_destructor<base>::value);
413 EXPECT_FALSE(std::has_virtual_destructor<base2>::value);
416 TEST(cpp11, auto_type) {
450 static_assert(std::is_same_v<decltype(x2),
int>);
455 static_assert(std::is_same_v<decltype(x4),
int>);
458 decltype(
auto) x6 = x3;
459 static_assert(std::is_same_v<decltype(x6),
const int>);
462 const std::vector moon{
"Don't",
"look",
"at",
"the",
"finger"};
463 const auto finger = moon.front();
465 EXPECT_EQ(finger,
"Don't");
469 [[maybe_unused]]
const auto d = 3uz;
472 TEST(cpp11, lambda_expressions) {
481 constexpr
auto sentence = [] {
return "I am a first-class citizen"; };
482 std::string_view s1 = sentence();
483 EXPECT_EQ(s1.front(),
'I');
486 constexpr std::string_view s2 = [] {
487 return "I am a second-class citizen";
489 EXPECT_EQ(s2.back(),
'n');
492 std::vector d{0.0, 0.1, 0.2};
493 std::ranges::for_each(d, [](
auto &i) { ++i; });
495 EXPECT_EQ(d.front(), 1.0);
498 TEST(cpp11, exceptions) {
503 const auto throw_it = []() {
throw "cya!"; };
505 EXPECT_ANY_THROW(throw_it());
508 TEST(cpp11, brace_initialisers) {
513 using container_t = std::vector<std::pair<int, int>>;
520 c.front() = std::make_pair(1.3, 2);
522 c.push_back({1.1, 1.2});
523 c.emplace_back(1.1, 1.2);
524 c.emplace_back(std::make_pair(1.1, 1.3));
526 EXPECT_EQ(c.size(), 5);
538 } s1 = {1, {2, 3, {4, 5, 6}}};
541 EXPECT_EQ(s1.b.a.at(0), 4);
546 const std::vector v1{1, 2, 3, 4, 5};
547 EXPECT_TRUE(not v1.empty());
549 const std::vector v2{
'a',
'b',
'c',
'd',
'e'};
550 EXPECT_TRUE(not v2.empty());
553 TEST(cpp11, narrowing) {
572 TEST(cpp11, learn_the_standard_library) {
579 const std::vector vec{1, 2, 3, 4, 5, 6};
580 const auto count = std::count_if(cbegin(vec), cend(vec),
581 [](
const auto &a) {
return a < 3; });
586 TEST(cpp11, threading) {
609 const auto background_task = []() ->
int {
return 1; };
610 auto f = std::async(std::launch::async, background_task);
615 const auto f1 = f.get();
628 std::vector<std::thread> threads;
629 for ([[maybe_unused]]
const auto _ : {0, 1, 2, 3, 4}) {
630 threads.emplace_back(
631 std::thread([&]() { std::call_once(flag, [&i]() { ++i; }); }));
635 for (
auto &t : threads)
642 TEST(cpp14, return_value_optimisation) {
654 bird b = bird(bird(bird(bird(bird()))));
663 TEST(cpp14, digit_separators) {
670 const auto reg1 = 0x5692a5b6;
671 const auto reg2 = 0x5692'a5b6;
672 EXPECT_EQ(reg1, reg2);
674 const auto reg3 = 1'000.000'01;
675 EXPECT_EQ(reg3, 1000.00001);
678 const uint32_t netmask{0b11111111'11111111'11111111'00000000};
679 EXPECT_EQ(netmask, 0xffffff00);
682 TEST(cpp17, optional_types) {
690 std::deque<std::optional<int>> options{0, 1, 2, 3, 4};
697 const auto c = std::count_if(cbegin(options), cend(options),
698 [](
const auto &o) {
return o; });
702 TEST(cpp17, heterogeneous_types) {
710 const auto t = std::make_tuple(
"one", 2.0, 3);
712 EXPECT_EQ(std::get<0>(t),
"one");
717 std::vector<std::any> martini(10);
720 martini.at(6) =
"time";
721 martini.at(7) =
"place";
722 martini.at(8) =
"where";
724 EXPECT_EQ(martini.size(), 10);
727 TEST(cpp17, filesystem) {
733 std::vector<std::string> files;
734 const std::filesystem::path p{
"."};
735 std::ranges::for_each(
736 std::filesystem::directory_iterator{p},
737 [&files](
const auto &file) { files.push_back(file.path()); });
739 EXPECT_TRUE(not files.empty());
742 TEST(cpp17, parallel_execution_policy) {
748 std::vector vec{1, 23, 4, 5, 6, 7, 8};
750 std::reduce(std::execution::seq, vec.cbegin(), vec.cend(),
int{});
754 std::for_each(std::execution::unseq, vec.begin(), vec.end(),
755 [](
auto &x) { ++x; });
757 EXPECT_EQ(vec.front(), 2);
760 TEST(cpp17, clamp_values) {
770 const decltype(a) b = std::clamp(a, 0uz, 16uz);
771 const auto c = std::clamp(0, 4, 10);
777 TEST(cpp17, compiler_attributes) {
809 TEST(cpp17, maybe_unused_attribute) {
814 [[maybe_unused]] std::string str;
816 #ifdef THIS_IS_NOT_DEFINED_IN_THIS_BUILD_BUT_I_DO_NOT_WANT_WARNING
821 EXPECT_TRUE(str.empty());
824 TEST(cpp20, ranges_and_views) {
830 const std::vector vec1{1, 2, 3, 4, 5, 6, 7, 8};
831 std::vector<int> vec2(vec1.size());
833 std::ranges::copy(vec1, begin(vec2));
835 EXPECT_EQ(vec1, vec2);
838 TEST(cpp20, reduce_and_accumulate) {
840 const std::vector vec{1, 2, 3, 4, 5, 66};
846 const auto sum1 = std::accumulate(vec.cbegin(), vec.cend(), 0);
851 const auto sum2 = std::reduce(cbegin(vec), cend(vec));
853 EXPECT_EQ(sum1, sum2);
856 TEST(cpp20, deprecated_with_comment) {
862 std::string_view
member_{
"cya"};
865 [[nodiscard(
"I have no side-effects")]]
auto func1() {
return member_; }
868 [[deprecated(
"This is neither efficient nor thread-safe")]]
auto
874 [[maybe_unused]]
const auto b = a.func1();
877 EXPECT_FALSE(b.empty());
879 [[maybe_unused]]
const auto c = a.func2();
882 TEST(cpp20, contains_for_associative_containers) {
888 const std::map<std::string_view, int> m{
894 EXPECT_TRUE(m.contains(
"one"));
895 EXPECT_FALSE(m.contains(
"four"));
base(base &&) noexcept=delete
constexpr std::string to_lowercase(const std::string str)
Use the lower case.
std::string get_file_name(const std::string_view path)
You can aslo break down a path using C++17's std::filesystem.
std::string_view trim_whitespace(const std::string_view path)
Remove leading and trailing whitespace.
constexpr std::string to_snakecase(const std::string str)
Use the Python case.
constexpr char toggle_case(const char c, const bool to_upper)
Toggle case.
constexpr char tolower(const char c)
constexpr version of std::tolower
std::string get_file_extension(const std::string_view path)
constexpr char toupper(const char c)
constexpr version of std::toupper
constexpr std::string transform_view(const std::string str, const auto func)
Helper routine to transform a string with a function.
std::string get_stem(const std::string_view path)
std::vector< std::string > split_string(std::string_view sentence)
TEST(cpp11, trim_whitespace)
constexpr std::string to_uppercase(const std::string str)
Use the upper case.
std::string remove_control_characters(const std::string_view str)
Remove non-printable characters.