diff --git a/Tests/Io/PrintfTest.cpp b/Tests/Io/PrintfTest.cpp new file mode 100644 index 0000000..b7b160d --- /dev/null +++ b/Tests/Io/PrintfTest.cpp @@ -0,0 +1,19 @@ +#include + +#include + +int main() { + std::random_device drng; + std::uniform_int_distribution dist(1, 999); + int num = dist(drng); + + static const char* const days[] = { + "Monday", "Tuesday", "Wednesday", "Thursday", + "Friday", "Saturday", "Sunday", "Hiddenday", "Nullday" + }; + + int di = num % 9; + const char* day = days[di]; + + hsd::xprintf("Hello, User%d!\nYour random day is %s\n", num, day).unwrap(); +} diff --git a/cpp/Printf.cpp b/cpp/Printf.cpp new file mode 100644 index 0000000..7e07421 --- /dev/null +++ b/cpp/Printf.cpp @@ -0,0 +1,49 @@ +#include "Printf.hpp" + +#include + +#include "String.hpp" +#include "Io.hpp" + +using namespace hsd; +Result hsd::xvprintf(const char* fmt, xtuple_iterator tbegin, xtuple_iterator tend) noexcept { + while (*fmt) { + char ch = *fmt++; + if (ch != '%') + std::putchar(ch); + else { + char fch = *fmt++; + if (!fch) { + return runtime_error("\nError: Invalid format string"); + } + if (tbegin == tend) { + return runtime_error("\nError: Passed to few arguments"); + } + switch (fch) { + case 'd': { + int value = tbegin.getnext().unwrap(); + auto s = string::to_string(value); + std::fwrite(s.data(), 1, s.size(), stdout); + break; + } + case 's': { + auto value = tbegin.getnext().unwrap(); + std::fwrite(value, 1, cstring::length(value), stdout); + break; + } + case 'c': { + char value = tbegin.getnext().unwrap(); + std::putchar(value); + break; + } + default: { + return runtime_error("\nError: Invalid format specifier"); + } + } + } + } + if (tbegin != tend) { + return runtime_error("\nError: Not all arguments parsed"); + } + return {}; +} diff --git a/cpp/Printf.hpp b/cpp/Printf.hpp new file mode 100644 index 0000000..78ec161 --- /dev/null +++ b/cpp/Printf.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "Tuparg.hpp" +#include "Result.hpp" + +namespace hsd { + Result xvprintf(const char* fmt, xtuple_iterator tbegin, xtuple_iterator tend) noexcept; + + template + Result xprintf(const char* fmt, Args&&... args) noexcept { + auto targs = make_xtuple(forward(args)...); + const auto& layout = xtuple_layout::get_layout(); + return xvprintf(fmt, layout.begin(targs), layout.end()); + } +} diff --git a/cpp/Result.hpp b/cpp/Result.hpp index 12387b7..8352d6e 100644 --- a/cpp/Result.hpp +++ b/cpp/Result.hpp @@ -69,11 +69,19 @@ namespace hsd const char* _err = nullptr; public: - runtime_error(const char* error) + constexpr runtime_error(const char* error) : _err{error} {} + + constexpr const char* operator()() const { + return _err; + } }; + // Tags for result construction + struct ok_tag_t {}; + struct err_tag_t {}; + template < typename Ok, typename Err > class Result { @@ -90,17 +98,17 @@ namespace hsd HSD_CONSTEXPR Result& operator=(const Result&) = delete; HSD_CONSTEXPR Result& operator=(Result&&) = delete; - HSD_CONSTEXPR Result(const Ok& value) + HSD_CONSTEXPR Result(const Ok& value, ok_tag_t = {}) : _ok_data{value}, _initialized{true} {} - HSD_CONSTEXPR Result(Ok&& value) + HSD_CONSTEXPR Result(Ok&& value, ok_tag_t = {}) : _ok_data{move(value)}, _initialized{true} {} template requires (std::is_convertible_v) - HSD_CONSTEXPR Result(T&& value) + HSD_CONSTEXPR Result(T&& value, err_tag_t = {}) : _err_data{forward(value)}, _initialized{false} {} @@ -391,13 +399,13 @@ namespace hsd constexpr void unwrap_or_default() = delete; constexpr void unwrap_or() = delete; - HSD_CONSTEXPR Result() + HSD_CONSTEXPR Result(ok_tag_t = {}) : _initialized{true} {} template requires (std::is_convertible_v) - HSD_CONSTEXPR Result(T&& value) + HSD_CONSTEXPR Result(T&& value, err_tag_t = {}) : _err_data{forward(value)}, _initialized{false} {} diff --git a/cpp/Tuparg.hpp b/cpp/Tuparg.hpp new file mode 100644 index 0000000..be52167 --- /dev/null +++ b/cpp/Tuparg.hpp @@ -0,0 +1,101 @@ +#pragma once + +#include "Types.hpp" +#include "Result.hpp" +#include + +namespace hsd { + struct xtuple_iterator { + const usize* layout; + void* addr; + + template + X& getnext_unsafe() { + assert(addr); + + X* p = reinterpret_cast(addr); + addr = reinterpret_cast( + reinterpret_cast(addr) + + *layout + ); + if (* ++layout == 0) + addr = nullptr; + return *p; + } + + template + Result, runtime_error> getnext() { + if (!addr) + return runtime_error("Extracting value past end of the container"); + return Result, runtime_error>(getnext_unsafe(), ok_tag_t{}); + } + + bool operator==(const xtuple_iterator& o) const { + return addr == o.addr; + } + + bool operator!=(const xtuple_iterator& o) const { + return addr != o.addr; + } + }; + + template + struct xtuple { + Tfirst _first; + xtuple _rest; + + xtuple(Tfirst f, Trest... r) + : _first(move(f)), _rest(move(r)...) + {} + + consteval static usize get_offset() { + return offsetof(xtuple, _rest); + } + }; + + template + struct xtuple { + Tfirst _first; + + xtuple(Tfirst f) : _first(move(f)) {} + + consteval static usize get_offset() { + return sizeof(xtuple); + } + }; + + template + struct xtuple_layout; + + template + struct xtuple_layout> { + using tuple_type = xtuple; + constexpr static usize count = 1 + sizeof...(Trest); + + usize data[count + 1]; + + consteval xtuple_layout() { + assign_layout(data, 0); + data[count] = 0; + } + + consteval static void assign_layout(usize* p, usize idx) { + p[idx] = tuple_type::get_offset(); + if constexpr (sizeof...(Trest)) + xtuple_layout>::assign_layout(p, idx+1); + } + + static inline xtuple_layout const& get_layout() { + static constinit const xtuple_layout _layout; + return _layout; + } + + auto begin(tuple_type& tup) const { return xtuple_iterator{data, &tup._first}; } + auto end() const { return xtuple_iterator{data + count, nullptr}; } + }; + + template + auto make_xtuple(Args&&... args) { + return xtuple...>(forward(args)...); + } +} diff --git a/cpp/Types.hpp b/cpp/Types.hpp index 7ef9e91..1fb4886 100644 --- a/cpp/Types.hpp +++ b/cpp/Types.hpp @@ -12,7 +12,7 @@ namespace hsd template <> struct remove_unsigned { - using type = char; + using type = signed char; }; template <> @@ -43,6 +43,10 @@ namespace hsd using usize = decltype(sizeof(int)); using isize = detail::remove_unsigned::type; + using uptr = usize; + using iptr = isize; + using ptrdiff = isize; + #ifdef HSD_COMPILER_GCC using u128 = __uint128_t; using i128 = __int128_t; @@ -54,6 +58,7 @@ namespace hsd using u16 = unsigned short; using u8 = unsigned char; + using ilong = long; using i64 = long long; using i32 = int; using i16 = short;