write_something.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. #include "simple/io/open.h"
  2. #include "simple/io/write.h"
  3. #include "simple/io/read.h"
  4. #include "simple/io/read_iterator.h"
  5. #include "simple/io/resize.h"
  6. #include "simple/io/execute.h"
  7. #include "simple/io/seek.h"
  8. #include <array>
  9. // aaaaaaaaaaaaaaaa
  10. #include "simple/support/bits.hpp"
  11. #include "simple/support/algorithm/traits.hpp"
  12. #include "simple/support/algorithm/utils.hpp"
  13. #include "simple/support/algorithm/mismatch.hpp"
  14. #include <charconv>
  15. using namespace std::literals;
  16. using namespace simple;
  17. using namespace io;
  18. template <typename B, std::enable_if_t<std::is_same_v<B,bool>>* = nullptr>
  19. [[nodiscard]] std::string get_message(const B& b)
  20. {
  21. return "bool: "s + (b ? "true" : "false");
  22. }
  23. namespace can_to_string_helper
  24. {
  25. using std::to_string;
  26. template <typename T, typename Enabled = std::nullptr_t>
  27. struct check : std::false_type {};
  28. template <typename T>
  29. struct check<T, decltype(void(to_string(std::declval<T>())), nullptr)>
  30. : std::true_type {};
  31. } // namespace can_to_string_helper
  32. template <typename T>
  33. struct can_to_string : can_to_string_helper::check<T> {};
  34. template <typename T>
  35. constexpr bool can_to_string_v = can_to_string<T>::value;
  36. template <typename R, std::enable_if_t<support::is_template_instance_v<support::range,R>>* = nullptr>
  37. [[nodiscard]] std::string get_message(const R& r)
  38. {
  39. using value_type = typename R::bounds_type::value_type;
  40. // TODO if pointer and null don't size, cause that UBish
  41. std::string message = "simple::support::range<[typeid]"s + typeid(value_type).name() + "[typeid]>: "s
  42. + (r.valid() ? "valid" : "invalid");
  43. if constexpr (support::is_range_v<R>)
  44. {
  45. // TODO: handle well known iterators
  46. // at least contiguous container iterators can be converted to pointers in C++20 to_address
  47. // naive linked list would have nullptr as end iterator, which could also be useful info
  48. // the node addresses as well could be more or less meaningful dependent on the allocator
  49. if constexpr (std::is_pointer_v<value_type>)
  50. {
  51. // convert to integer so that it's safe to work with
  52. support::range ir{
  53. reinterpret_cast<std::uintptr_t>(r.begin()),
  54. reinterpret_cast<std::uintptr_t>(r.end())
  55. };
  56. // now this is not UB, even if the pointers are bogus
  57. message += " | size " + std::to_string(ir.upper() - ir.lower());
  58. // and to print had to convert to integer anyway
  59. std::string ptr;
  60. auto min_bit_count = [](auto x){ return x == 0 ? 1 : support::bit_count(x) - support::count_leading_zeros(x); };
  61. auto ptrstr = [&](auto x)
  62. {
  63. auto ptr_bit_count = min_bit_count(x);
  64. ptr.resize(ptr_bit_count / 4 + ((ptr_bit_count % 4) != 0));
  65. std::to_chars(ptr.data(), ptr.data() + ptr.size(), x, 16);
  66. };
  67. ptrstr(ir.lower());
  68. message += " | bounds [" + ptr;
  69. ptrstr(ir.upper());
  70. message += ", " + ptr + ")";
  71. }
  72. }
  73. else // not an iterable range, assume arithmetic
  74. {
  75. message += " | size " + std::to_string(r.upper() - r.lower());
  76. using std::to_string;
  77. if constexpr (can_to_string_v<value_type>)
  78. message += " | bounds [" + to_string(r.lower()) + ", " + to_string(r.upper()) + ")";
  79. }
  80. message += " |";
  81. return message;
  82. }
  83. template <typename A, std::enable_if_t<std::is_arithmetic_v<A> && not std::is_same_v<A,bool>>* = nullptr>
  84. [[nodiscard]] std::string get_message(const A& a)
  85. {
  86. return "Builtin arithmetic ([typeid]"s + typeid(a).name() + "[typeid]): " + std::to_string(a);
  87. }
  88. template <typename StdStr, std::enable_if_t<std::is_same_v<StdStr, std::string>>* = nullptr>
  89. [[nodiscard]] std::string get_message(const StdStr& str)
  90. {
  91. // TODO: limit string size, huge strings are not useful as messages
  92. // and yet you do want a reasonable amount of structured text like say JSON
  93. return "std::string: size "s + std::to_string(str.size()) + " | " + str;
  94. }
  95. template <typename... Ts>
  96. [[nodiscard]] std::string get_message(const meta::list<Ts...>&)
  97. {
  98. return "meta::list: size "s + std::to_string(sizeof...(Ts)) + ((" | " + get_message(Ts{})) + ...);
  99. }
  100. template <typename T, typename Enabled = std::nullptr_t>
  101. struct can_get_message : std::false_type {};
  102. template <typename T>
  103. struct can_get_message<T, decltype(void(get_message(std::declval<T>())), nullptr)>
  104. : std::true_type {};
  105. template <typename T>
  106. constexpr bool can_get_message_v = can_get_message<T>::value;
  107. template <typename T, std::enable_if_t<not can_get_message_v<T>>* = nullptr>
  108. [[nodiscard]] std::string get_message(const T& t)
  109. {
  110. // TODO: if trivially copyable show byte representation in hex?
  111. // TODO: typeid is rtti and not super readable, might be better to just print __PRETTY_FUNCTION__ (c++20 function_name)
  112. return std::string("[typeid]") + typeid(t).name() + "[typeid]";
  113. };
  114. template <typename Error, typename F = std::nullptr_t, typename... Args>
  115. class exception_wrapper : public std::runtime_error, public Error
  116. {
  117. public:
  118. std::tuple<Args...> args;
  119. F f = nullptr;
  120. exception_wrapper(Error error, F&& f, Args&&... args) :
  121. std::runtime_error(
  122. "Function: " + get_message(f) + '\n' +
  123. (("Argument: " + get_message(args) + '\n') + ... + ""s) +
  124. "Error: " + get_message(error)),
  125. Error(std::move(error)),
  126. args{std::forward<Args>(args)...},
  127. f(std::forward<F>(f))
  128. {}
  129. exception_wrapper(Error error) :
  130. std::runtime_error("Error: " + get_message(error)),
  131. Error(std::move(error))
  132. {}
  133. };
  134. template <typename Error, typename F, typename... Args>
  135. exception_wrapper(Error error, F&& f, Args&&... args) -> exception_wrapper<Error, F, Args...>;
  136. template <std::size_t index = 0, typename Variant = void,
  137. std::enable_if_t<support::is_template_instance_v<std::variant, Variant>>* = nullptr>
  138. // TODO: [[nodiscard]] unless the required alternative is monostate
  139. decltype(auto) require(Variant&& var)
  140. {
  141. // static_assert(first variant alternative does not repeat);
  142. return std::visit([](auto&& a) -> decltype(std::get<index>(std::forward<Variant>(var)))
  143. {
  144. if constexpr (std::is_same_v<
  145. std::variant_alternative_t<index, Variant>,
  146. std::remove_reference_t<decltype(a)>>
  147. ) return std::forward<decltype(a)>(a);
  148. else throw exception_wrapper(a);
  149. }, std::forward<Variant>(var));
  150. };
  151. template <std::size_t index = 0, typename F = void, typename... Args,
  152. typename Variant = std::invoke_result_t<F, Args...>,
  153. std::enable_if_t<support::is_template_instance_v<std::variant, std::remove_reference_t<Variant>>>* = nullptr
  154. >
  155. // TODO: [[nodiscard]] unless the required alternative is monostate
  156. decltype(auto) require(F&& f, Args&&... args)
  157. {
  158. // static_assert(first variant alternative does not repeat);
  159. return std::visit([fargs = std::forward_as_tuple(std::forward<F>(f), std::forward<Args>(args)...)](auto&& a)
  160. -> std::variant_alternative_t<index, Variant>
  161. {
  162. if constexpr (std::is_same_v<
  163. std::variant_alternative_t<index, Variant>,
  164. std::remove_reference_t<decltype(a)>>
  165. ) return std::forward<decltype(a)>(a);
  166. else throw std::apply([a](auto&&... eargs)
  167. // TODO if earg is move only and passed as lvalue, only send off its type with the exception, leave the value untouched... currently cryptic compiler error instead
  168. { return exception_wrapper(a, std::forward<decltype(eargs)>(eargs)...); },
  169. fargs);
  170. }, std::invoke(std::forward<F>(f), std::forward<Args>(args)...));
  171. };
  172. // TODO: request(F, Args...) noexcept - return first variant arg with index >0 or F(Args...)
  173. // assert that only one arg is error (index>0), otherwise have to tuple and that can get out of hand quick, up to the user to reconcile independent operations
  174. int main() try
  175. {
  176. auto sf = require( open_f(meta::list(mode::write, mode::create), "helotherefile") );
  177. auto data = as_byte_view("uomo");
  178. // we offer up sf but it won't be taken unless error occurs in which case it'll be sent off with the exception
  179. // in general std move should really be called std::offer
  180. assert(( require( write_f, std::move(sf), data ) == data.end() ));
  181. assert(( require( write_f, std::move(sf), data ) == data.end() ));
  182. assert(( require( write_f, std::move(sf), data ) == data.end() ));
  183. std::array<std::byte, 10> buf{};
  184. std::byte* red = require( read_f, standard_input, as_byte_range(buf) );
  185. while(red)
  186. {
  187. assert(( require( write_f, standard_output, byte_view{buf.data(), red} ) == red ));
  188. red = require( read_f, standard_input, as_byte_range(buf));
  189. }
  190. const char* expected = "uomouomouomo";
  191. auto expected_data = as_byte_view(expected);
  192. auto rd = require( open_f, meta::list(mode::read), "helotherefile"s );
  193. assert(( support::mismatch(read_iterator(rd, as_byte_range(buf)), read_iterator(),
  194. expected_data.begin(), expected_data.end()) == std::pair{read_iterator(), expected_data.end()} ));
  195. assert( require( size_f, rd ) == 12 );
  196. require( resize_f, sf, 3 );
  197. assert( require( size_f, rd ) == 3 );
  198. require( seek_f, rd, 0 );
  199. auto resized = as_byte_view("uom");
  200. assert(( support::mismatch(read_iterator(rd, as_byte_range(buf)), read_iterator(),
  201. resized.begin(), resized.end()) == std::pair{read_iterator(), resized.end()} ));
  202. {
  203. auto expected_echo = as_byte_view("uomouomouomo\n");
  204. auto [process, out] = require( execute_f, meta::list{standard_output}, "echo"s, "echo"s, expected );
  205. assert(( support::mismatch(read_iterator(out, as_byte_range(buf)), read_iterator(),
  206. expected_echo.begin(), expected_echo.end()) == std::pair{read_iterator(), expected_echo.end()} ));
  207. }
  208. // TODO: remove them test files
  209. return 0;
  210. }
  211. catch(...)
  212. {
  213. perror("omaaaagooot");
  214. throw;
  215. }