str/conv: Add basic functions
We need some basic templates for formatting into a buffer or fmtlib to
make other conversions straightforward.
Change-Id: I8c13175394f6b4fd4a55edf8d653e98a1562cc64
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/include/meson.build b/include/meson.build
index 5909aa0..979478e 100644
--- a/include/meson.build
+++ b/include/meson.build
@@ -19,6 +19,7 @@
'stdplus/str/buf.hpp',
'stdplus/str/cat.hpp',
'stdplus/str/cexpr.hpp',
+ 'stdplus/str/conv.hpp',
'stdplus/str/maps.hpp',
'stdplus/util/cexec.hpp',
'stdplus/util/string.hpp',
diff --git a/include/stdplus/str/conv.hpp b/include/stdplus/str/conv.hpp
new file mode 100644
index 0000000..cbf79d3
--- /dev/null
+++ b/include/stdplus/str/conv.hpp
@@ -0,0 +1,157 @@
+#pragma once
+#include <fmt/core.h>
+
+#include <stdplus/str/buf.hpp>
+
+#include <array>
+#include <string>
+#include <string_view>
+#include <type_traits>
+
+namespace stdplus
+{
+
+template <typename T>
+struct ToStr;
+
+template <typename T>
+struct FromStr;
+
+template <typename T>
+constexpr T fromStr(const auto& str)
+{
+ return FromStr<T>{}(
+ std::basic_string_view<std::remove_cvref_t<decltype(*std::begin(str))>>{
+ str});
+}
+
+namespace detail
+{
+
+template <typename T>
+concept ToStrStatic =
+ !std::is_void_v<decltype(std::declval<T>().template operator()<char>(
+ std::declval<typename T::type>()))>;
+
+template <typename T>
+concept ToStrFixed = !std::is_void_v<decltype(std::declval<T>()(
+ std::declval<char*>(), std::declval<typename T::type>()))>;
+
+} // namespace detail
+
+template <typename T>
+struct ToStrAdap : T
+{};
+
+template <detail::ToStrStatic T>
+struct ToStrAdap<T> : T
+{
+ using T::operator();
+
+ template <typename CharT>
+ constexpr CharT* operator()(CharT* base, const T::type& t) const
+ {
+ auto sv = (*this).template operator()<CharT>(t);
+ return std::copy(sv.begin(), sv.end(), base);
+ }
+
+ template <typename CharT>
+ constexpr void operator()(stdplus::BasicStrBuf<CharT>& buf,
+ const T::type& t) const
+ {
+ auto ptr = buf.append(T::buf_size);
+ buf.shrink(T::buf_size - ((*this)(ptr, t) - ptr));
+ }
+};
+
+template <detail::ToStrFixed T>
+struct ToStrAdap<T> : T
+{
+ using T::operator();
+
+ template <typename CharT>
+ constexpr void operator()(stdplus::BasicStrBuf<CharT>& buf,
+ const T::type& t) const
+ {
+ auto ptr = buf.append(T::buf_size);
+ buf.shrink(T::buf_size - ((*this)(ptr, t) - ptr));
+ }
+};
+
+template <typename T, typename CharT = char>
+struct ToStrHandle
+{
+ private:
+ stdplus::BasicStrBuf<CharT> buf;
+
+ public:
+ constexpr std::basic_string_view<CharT> operator()(const T::type& v)
+ {
+ buf.reset();
+ T{}(buf, v);
+ return buf;
+ }
+};
+
+template <detail::ToStrStatic T, typename CharT>
+struct ToStrHandle<T, CharT>
+{
+ static_assert(T::buf_size > 0);
+
+ constexpr std::basic_string_view<CharT>
+ operator()(const T::type& v) noexcept(
+ noexcept(std::declval<T>().template operator()<CharT>(
+ std::declval<typename T::type>())))
+ {
+ return T{}.template operator()<CharT>(v);
+ }
+};
+
+template <detail::ToStrFixed T, typename CharT>
+struct ToStrHandle<T, CharT>
+{
+ private:
+ std::array<CharT, T::buf_size> buf;
+
+ public:
+ constexpr std::basic_string_view<CharT>
+ operator()(const T::type& v) noexcept(noexcept(std::declval<T>()(
+ std::declval<CharT*>(), std::declval<typename T::type>())))
+ {
+ return {buf.data(), T{}(buf.data(), v)};
+ }
+};
+
+template <typename T, typename CharT>
+struct Format
+{
+ private:
+ fmt::formatter<std::basic_string_view<CharT>> formatter;
+
+ public:
+ template <typename ParseContext>
+ constexpr auto parse(ParseContext& ctx)
+ {
+ return ctx.begin();
+ }
+
+ template <typename FormatContext>
+ auto format(auto v, FormatContext& ctx) const
+ {
+ return formatter.format(ToStrHandle<T, CharT>{}(v), ctx);
+ }
+};
+
+template <typename CharT, typename T>
+constexpr auto toBasicStr(const T& t)
+{
+ return std::basic_string<CharT>(ToStrHandle<ToStr<T>, CharT>{}(t));
+}
+
+template <typename T>
+constexpr auto toStr(const T& t)
+{
+ return toBasicStr<char>(t);
+}
+
+} // namespace stdplus
diff --git a/src/meson.build b/src/meson.build
index a064f14..93d68f3 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -60,6 +60,7 @@
'str/buf.cpp',
'str/cat.cpp',
'str/cexpr.cpp',
+ 'str/conv.cpp',
'str/maps.cpp',
'util/cexec.cpp',
'variant.cpp',
diff --git a/src/str/conv.cpp b/src/str/conv.cpp
new file mode 100644
index 0000000..22897a0
--- /dev/null
+++ b/src/str/conv.cpp
@@ -0,0 +1 @@
+#include <stdplus/str/conv.hpp>
diff --git a/test/meson.build b/test/meson.build
index a92e411..1239296 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -16,6 +16,7 @@
'str/buf': [stdplus_dep, gtest_main_dep],
'str/cat': [stdplus_dep, gtest_main_dep],
'str/cexpr': [stdplus_dep, gtest_main_dep],
+ 'str/conv': [stdplus_dep, gmock_dep, gtest_main_dep],
'str/maps': [stdplus_dep, gmock_dep, gtest_main_dep],
'util/cexec': [stdplus_dep, gtest_main_dep],
'variant': [stdplus_dep, gtest_main_dep],
diff --git a/test/str/buf.cpp b/test/str/buf.cpp
index dc94e2c..904a59d 100644
--- a/test/str/buf.cpp
+++ b/test/str/buf.cpp
@@ -18,7 +18,6 @@
}
constexpr auto data = cexprSv<[]() { return makeIncStr(sizeof(StrBuf) + 10); }>;
-// constexpr auto data = cexprSv<[]() { return makeIncStr(32); }>;
TEST(StrBuf, BasicInline)
{
diff --git a/test/str/conv.cpp b/test/str/conv.cpp
new file mode 100644
index 0000000..3a2f41c
--- /dev/null
+++ b/test/str/conv.cpp
@@ -0,0 +1,163 @@
+#include <fmt/format.h>
+
+#include <stdplus/str/conv.hpp>
+
+#include <algorithm>
+#include <string_view>
+
+#include <gtest/gtest.h>
+
+namespace stdplus
+{
+
+struct TestValS
+{
+ static inline constexpr std::string_view value = "test";
+ static inline constexpr std::wstring_view wvalue = L"test";
+};
+
+template <>
+struct ToStr<TestValS>
+{
+ using type = TestValS;
+ static inline constexpr std::size_t buf_size = TestValS::value.size();
+
+ template <typename CharT>
+ constexpr std::basic_string_view<CharT> operator()(TestValS) const noexcept
+ {
+ if constexpr (std::is_same_v<char, CharT>)
+ {
+ return TestValS::value;
+ }
+ else
+ {
+ static_assert(std::is_same_v<wchar_t, CharT>);
+ return TestValS::wvalue;
+ }
+ }
+};
+
+static_assert(!detail::ToStrFixed<ToStr<TestValS>>);
+static_assert(detail::ToStrStatic<ToStr<TestValS>>);
+
+struct TestValF : TestValS
+{};
+
+template <>
+struct ToStr<TestValF>
+{
+ using type = TestValF;
+ static inline constexpr std::size_t buf_size = TestValF::value.size();
+
+ template <typename CharT>
+ constexpr CharT* operator()(CharT* buf, TestValF) const noexcept
+ {
+ return std::copy(TestValF::value.begin(), TestValF::value.end(), buf);
+ }
+};
+
+static_assert(detail::ToStrFixed<ToStr<TestValF>>);
+static_assert(!detail::ToStrStatic<ToStr<TestValF>>);
+
+struct TestValD : TestValS
+{};
+
+template <>
+struct ToStr<TestValD>
+{
+ using type = TestValD;
+
+ template <typename CharT>
+ constexpr void operator()(stdplus::BasicStrBuf<CharT>& buf, TestValD) const
+ {
+ auto ptr = buf.append(TestValD::value.size());
+ std::copy(TestValD::value.begin(), TestValD::value.end(), ptr);
+ }
+};
+
+static_assert(!detail::ToStrFixed<ToStr<TestValD>>);
+static_assert(!detail::ToStrStatic<ToStr<TestValD>>);
+
+} // namespace stdplus
+
+template <typename CharT>
+struct fmt::formatter<stdplus::TestValS, CharT> :
+ stdplus::Format<stdplus::ToStr<stdplus::TestValS>, CharT>
+{};
+
+template <typename CharT>
+struct fmt::formatter<stdplus::TestValF, CharT> :
+ stdplus::Format<stdplus::ToStr<stdplus::TestValF>, CharT>
+{};
+
+template <typename CharT>
+struct fmt::formatter<stdplus::TestValD, CharT> :
+ stdplus::Format<stdplus::ToStr<stdplus::TestValD>, CharT>
+{};
+
+namespace stdplus
+{
+
+TEST(ToStrAdapter, Static)
+{
+ StrBuf buf;
+ ToStrAdap<ToStr<TestValS>>{}(buf, TestValS{});
+ EXPECT_EQ("test", buf);
+ buf.reset();
+ auto ptr = buf.append(4);
+ EXPECT_EQ(4, ToStrAdap<ToStr<TestValS>>{}(ptr, TestValS{}) - ptr);
+ EXPECT_EQ("test", std::string_view(ptr, 4));
+}
+
+TEST(ToStrAdapter, Fixed)
+{
+ StrBuf buf;
+ ToStrAdap<ToStr<TestValF>>{}(buf, TestValF{});
+ EXPECT_EQ("test", buf);
+}
+
+TEST(ToStrAdapter, Dynamic)
+{
+ StrBuf buf;
+ ToStrAdap<ToStr<TestValD>>{}(buf, TestValD{});
+ EXPECT_EQ("test", buf);
+}
+
+TEST(ToStrHandle, Basic)
+{
+ EXPECT_EQ("test", (ToStrHandle<ToStr<TestValS>>{}({})));
+ EXPECT_EQ(L"test", (ToStrHandle<ToStr<TestValS>, wchar_t>{}({})));
+ EXPECT_EQ("test", (ToStrHandle<ToStr<TestValF>>{}({})));
+ EXPECT_EQ(L"test", (ToStrHandle<ToStr<TestValF>, wchar_t>{}({})));
+ EXPECT_EQ("test", (ToStrHandle<ToStr<TestValD>>{}({})));
+ EXPECT_EQ(L"test", (ToStrHandle<ToStr<TestValD>, wchar_t>{}({})));
+}
+
+TEST(Format, Basic)
+{
+ EXPECT_EQ("t test", fmt::format("t {}", TestValS{}));
+ EXPECT_EQ("t test", fmt::format("t {}", TestValF{}));
+ EXPECT_EQ("t test", fmt::format("t {}", TestValD{}));
+}
+
+template <>
+struct FromStr<TestValS>
+{
+ template <typename CharT>
+ constexpr TestValS operator()(std::basic_string_view<CharT> sv) const
+ {
+ if (sv == TestValS::value)
+ {
+ return TestValS{};
+ }
+ throw std::runtime_error("Invalid TestValS");
+ }
+};
+
+TEST(FromStr, Basic)
+{
+ EXPECT_NO_THROW(fromStr<TestValS>("test"));
+ EXPECT_THROW(fromStr<TestValS>("hi"), std::runtime_error);
+}
+
+} // namespace stdplus