diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3544044..4cb1c5f 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,6 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -set(ALL_EXAMPLES todo) +set(ALL_EXAMPLES str_split) message("Examples to be built: ${ALL_EXAMPLES}") foreach(example ${ALL_EXAMPLES}) diff --git a/examples/str_split.cpp b/examples/str_split.cpp new file mode 100644 index 0000000..8a1b312 --- /dev/null +++ b/examples/str_split.cpp @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include + +#include +#include + +using namespace std::literals::string_view_literals; + +using ::beman::str_split::split_by_ascii_whitespace; +using ::beman::str_split::str_split_to; + +int main() { + constexpr std::string_view text = "The quick brown fox jumps over the lazy dog"; + + const std::vector parts = str_split_to(text, split_by_ascii_whitespace()); + for (std::string_view part : parts) { + std::cout << part << std::endl; + } + + return 0; +} diff --git a/examples/todo.cpp b/examples/todo.cpp deleted file mode 100644 index 276064d..0000000 --- a/examples/todo.cpp +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include -#include - -int main() { - // TODO -} diff --git a/include/beman/str_split/CMakeLists.txt b/include/beman/str_split/CMakeLists.txt index a4a50c2..58e6278 100644 --- a/include/beman/str_split/CMakeLists.txt +++ b/include/beman/str_split/CMakeLists.txt @@ -9,7 +9,7 @@ if(BEMAN_STR_SPLIT_USE_MODULES) FILES config.hpp str_split.hpp - todo.hpp + str_split_to.hpp "${PROJECT_BINARY_DIR}/include/beman/str_split/config_generated.hpp" ) else() @@ -20,7 +20,7 @@ else() FILES config.hpp str_split.hpp - todo.hpp + str_split_to.hpp "${PROJECT_BINARY_DIR}/include/beman/str_split/config_generated.hpp" ) endif() diff --git a/include/beman/str_split/str_split.cppm b/include/beman/str_split/str_split.cppm index 1a646cb..14efba0 100644 --- a/include/beman/str_split/str_split.cppm +++ b/include/beman/str_split/str_split.cppm @@ -1,6 +1,12 @@ -export module beman.str_split; +module; + +#include +#include +#include +#include +#include -import std; +export module beman.str_split; #define BEMAN_STR_SPLIT_INCLUDED_FROM_INTERFACE_UNIT export { diff --git a/include/beman/str_split/str_split.hpp b/include/beman/str_split/str_split.hpp index 308ad99..81110b4 100644 --- a/include/beman/str_split/str_split.hpp +++ b/include/beman/str_split/str_split.hpp @@ -11,7 +11,7 @@ import beman.str_split; #else - #include + #include #endif // BEMAN_STR_SPLIT_USE_MODULES() && // !defined(BEMAN_STR_SPLIT_INCLUDED_FROM_INTERFACE_UNIT) diff --git a/include/beman/str_split/str_split_to.hpp b/include/beman/str_split/str_split_to.hpp new file mode 100644 index 0000000..153d83f --- /dev/null +++ b/include/beman/str_split/str_split_to.hpp @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef BEMAN_STR_SPLIT_STR_SPLIT_TO_HPP +#define BEMAN_STR_SPLIT_STR_SPLIT_TO_HPP + +#include + +#if BEMAN_STR_SPLIT_USE_MODULES() && !defined(BEMAN_STR_SPLIT_INCLUDED_FROM_INTERFACE_UNIT) + +import beman.str_split; + +#else + + #if !BEMAN_STR_SPLIT_USE_MODULES() + + #include + #include + #include + + #endif // !BEMAN_STR_SPLIT_USE_MODULES() + +namespace beman::str_split { + +//------------------------------------------------------------------------------ +// Concepts: +//------------------------------------------------------------------------------ + +// TODO(aryann): Should the concepts be placed in a private namespace? + +// A range of chars. +template +concept char_range = std::ranges::input_range && std::same_as, char>; + +// A type that cannot be converted to `std::string_view`. +template +concept not_string_view_convertible = !std::convertible_to; + +template +concept different_from = !std::same_as, Self>; + +//------------------------------------------------------------------------------ +// Patterns: +//------------------------------------------------------------------------------ + +// Splits by a substring. +struct split_by { + public: + // Constructor for anything that can be converted to a `std::string_view`. + constexpr explicit split_by(std::string_view delimiter) : delimiter_(delimiter) {} + + // Constructor for range of characters that are not `std::string_view` convertible. + template + requires( + // Ensures the range's value type is `char`. Notably, this rejects ranges of other values types such as + // `int` and `unsigned char`. This requirement prevents narrowing conversions. + char_range && + + // Ensures this constructor does not compete with the `std::string_view` overload. + not_string_view_convertible && + + // Ensures this constructor does not hijack copy and move construction which would fail to compile with a + // difficult-to-read wall of errors. + different_from) + constexpr explicit split_by(Range&& range) : delimiter_(std::ranges::begin(range), std::ranges::end(range)) {} + + // TODO(aryann): Here and below, implement a find member function that accepts the current "haystack" string and + // returns the position of the first match. We may also need to control the visibility of such function. + + private: + const std::string delimiter_; +}; + +// Splits by the first matching character in a given character sequence. +struct split_by_first_of { + private: + const std::string chars_; +}; + +// Splits by character. +struct split_by_char { + public: + constexpr explicit split_by_char(char delimiter) : delimiter_(delimiter) {} + + private: + const char delimiter_; +}; + +struct split_by_ascii_whitespace {}; + +//------------------------------------------------------------------------------ +// Split functions: +//------------------------------------------------------------------------------ + +// TODO(aryann): Consider an alternative approach where the split type is determined by the function name: +// +// * split(std::string_view): Equivalent to +// str_split(std::string_view, split_by_ascii_whitespace). +// +// * split(std::string_view, Range&&): Equivalent to +// str_split(std::string_view, split_by). +// +// * split_by_first_of(std::string_view, Range&&): Equivalent to +// str_split(std::string_view, split_by_first_of). +// + +template +auto str_split_to(std::basic_string_view text, Delimiter&& delimiter, OutputIt dest) -> OutputIt { + return dest; +} + +template +auto str_split_to(std::basic_string_view text, Delimiter&& delimiter) -> Container { + Container container; + return str_split_to(text, delimiter, container); +} + +template