Skip to content

Commit

Permalink
stdutils: Additions to string.h
Browse files Browse the repository at this point in the history
- Add string::Indent.
- Remove some old code from string.h
    Use C++17's library header <filesystem> for this kind of
    filename manipulations.
- Freshen up: tolower, toupper and capitalize.
  • Loading branch information
pierre-dejoue committed Dec 22, 2023
1 parent dd99fe3 commit 0cd25cb
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 34 deletions.
49 changes: 46 additions & 3 deletions src/stdutils/include/stdutils/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// This code is distributed under the terms of the MIT License
#pragma once

#include <ostream>
#include <string>
#include <string_view>

Expand All @@ -13,13 +14,55 @@ namespace string

std::string tolower(const std::string& in);

std::string toupper(const std::string& in);

std::string capitalize(const std::string& in);

std::string file_extension(std::string_view filepath);
/**
* Indent: Utility class to easily output indentation to a stream
*
* Example usage:
*
* const Indent indent(4); // My indentation is 4 spaces
*
* out << indent; // Output 1 indentation
* out << indent(2); // Output 2 indentations
*/
template <typename CharT>
class BasicIndent : private std::basic_string<CharT>
{
public:
class Multi;

BasicIndent(std::size_t count, CharT ch = ' ') : std::basic_string<CharT>(count, ch) {}

Multi operator()(std::size_t factor) const { return Multi(*this, factor); }

friend std::basic_ostream<CharT>& operator<<(std::basic_ostream<CharT>& out, const BasicIndent<CharT>& indent)
{
return out << static_cast<const std::basic_string<CharT>&>(indent);
}
};

template <typename CharT>
class BasicIndent<CharT>::Multi
{
public:
Multi(const BasicIndent& indent, std::size_t factor) : m_indent(indent), m_factor(factor) {}

std::string filename(std::string_view filepath);
// NB: Read https://isocpp.org/wiki/faq/templates#template-friends regarding templated friend functions
friend std::basic_ostream<CharT>& operator<<(std::basic_ostream<CharT>& out, const Multi& multi_indent)
{
for (auto c = 0u; c < multi_indent.m_factor; c++)
out << multi_indent.m_indent;
return out;
}
private:
const BasicIndent<CharT>& m_indent;
std::size_t m_factor;
};

std::string filename_wo_extension(std::string_view filepath);
using Indent = BasicIndent<char>;

} // namespace string
} // namespace stdutils
41 changes: 10 additions & 31 deletions src/stdutils/src/string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,45 +14,24 @@ namespace string
std::string tolower(const std::string& in)
{
std::string out(in);
std::transform(in.cbegin(), in.cend(), out.begin(), [](const char& c) { return static_cast<char>(std::tolower(c)); });
// Conversion from char to unsigned char is important. See STL doc.
std::transform(in.cbegin(), in.cend(), out.begin(), [](const unsigned char c) { return static_cast<char>(std::tolower(c)); });
return out;
}

std::string capitalize(const std::string& in)
std::string toupper(const std::string& in)
{
std::string out = tolower(in);
if (!out.empty())
out.front() = static_cast<char>(std::toupper(out.front()));
std::string out(in);
// Conversion from char to unsigned char is important. See STL doc.
std::transform(in.cbegin(), in.cend(), out.begin(), [](const unsigned char c) { return static_cast<char>(std::toupper(c)); });
return out;
}

std::string file_extension(std::string_view filepath)
{
std::string result(filepath);
const auto pos = filepath.find_last_of('.');
if (pos != std::string::npos)
return result.substr(pos + 1);
else
return "";
}

std::string filename(std::string_view filepath)
{
std::string_view result(filepath);
const auto pos = filepath.find_last_of("/\\");
if (pos != std::string::npos)
result = filepath.substr(pos + 1);
return std::string(result);
}

std::string filename_wo_extension(std::string_view filepath)
std::string capitalize(const std::string& in)
{
const std::string name = filename(filepath);
const auto pos = name.find_last_of('.');
if (pos != std::string::npos)
return name.substr(0, pos);
else
return name;
std::string out = tolower(in);
if (!out.empty()) { out.front() = static_cast<char>(std::toupper(static_cast<unsigned char>(out.front()))); }
return out;
}

} // namespace string
Expand Down
1 change: 1 addition & 0 deletions src/tests/stdutils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ include(catch2)
set(UTESTS_SOURCES
src/test_io.cpp
src/test_span.cpp
src/test_string.cpp
)

file(GLOB UTESTS_HEADERS src/*.h)
Expand Down
35 changes: 35 additions & 0 deletions src/tests/stdutils/src/test_string.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2023 Pierre DEJOUE
// This code is distributed under the terms of the MIT License
#include <catch_amalgamated.hpp>

#include <stdutils/string.h>

#include <sstream>
#include <string>

TEST_CASE("strings tolower, toupper and capitalize", "[stdutils::string]")
{
CHECK(stdutils::string::tolower("All Those MOMENTS") == "all those moments");
CHECK(stdutils::string::toupper("will be lost in time") == "WILL BE LOST IN TIME");
CHECK(stdutils::string::capitalize("like tears in rain.") == "Like tears in rain.");
}

TEST_CASE("Indentation", "[stdutils::string]")
{
const stdutils::string::Indent indent(4); // My indentation is 4 spaces
{
std::stringstream out;
out << indent; // Output 1 indentation
CHECK(out.str().size() == 4);
}
{
std::stringstream out;
out << indent(2); // Output 2 indentations
CHECK(out.str().size() == 8);
}
{
std::stringstream out;
out << indent(0); // Output zero indentation
CHECK(out.str().empty());
}
}

0 comments on commit 0cd25cb

Please sign in to comment.