diff --git a/build.cc b/build.cc index 4ea5314..d8b18c0 100644 --- a/build.cc +++ b/build.cc @@ -473,7 +473,7 @@ int main(int argc, char **argv) { try_remove("test_runner.o"); }; - ostd::thread_pool tp; + ostd::thread_pool tp{}; tp.start(); std::queue> future_obj, future_dynobj; diff --git a/examples/concurrency.cc b/examples/concurrency.cc index d6109b0..6b4ff17 100644 --- a/examples/concurrency.cc +++ b/examples/concurrency.cc @@ -13,10 +13,10 @@ using namespace ostd; * task, which may or may not run in parallel with the other one depending * on the scheduler currently in use - several schedulers are shown */ -auto input_array = { 150, 38, 76, 25, 67, 18, -15, 215, 25, -10 }; +static auto input_array = { 150, 38, 76, 25, 67, 18, -15, 215, 25, -10 }; -auto first_half = iter(input_array).slice(0, input_array.size() / 2); -auto second_half = iter(input_array).slice(input_array.size() / 2); +static auto first_half = iter(input_array).slice(0, input_array.size() / 2); +static auto second_half = iter(input_array).slice(input_array.size() / 2); /* this version uses Go-style channels to exchange data; multiple * tasks can put data into channels, the channel itself is a thread diff --git a/examples/listdir.cc b/examples/listdir.cc index 49410d8..91e3ae0 100644 --- a/examples/listdir.cc +++ b/examples/listdir.cc @@ -9,7 +9,7 @@ using namespace ostd; -void list_dirs(filesystem::path const &path, int off = 0) { +inline void list_dirs(filesystem::path const &path, int off = 0) { filesystem::directory_iterator ds{path}; for (auto &v: ds) { auto p = filesystem::path{v}; diff --git a/examples/range.cc b/examples/range.cc index 72aea24..e2fe6b8 100644 --- a/examples/range.cc +++ b/examples/range.cc @@ -33,7 +33,7 @@ int main() { /* prints ABCDEF (ASCII 65, 66, 67, 68, 69, 70) */ writeln("string gen test"); - auto mr = map(range(6), [](int v) -> char { return v + 65; }); + auto mr = map(range(6), [](int v) { return char(v + 65); }); std::string s{mr.iter_begin(), mr.iter_end()}; writeln(s); diff --git a/examples/range_pipe.cc b/examples/range_pipe.cc index 6a8e742..ad0f82f 100644 --- a/examples/range_pipe.cc +++ b/examples/range_pipe.cc @@ -28,7 +28,7 @@ int main() { /* prints ABCDEF (ASCII 65, 66, 67, 68, 69, 70) */ writeln("string gen test"); - auto mr = range(6) | map([](int v) -> char { return v + 65; }); + auto mr = range(6) | map([](int v) { return char(v + 65); }); std::string s{mr.iter_begin(), mr.iter_end()}; writeln(s); @@ -73,7 +73,7 @@ int main() { /* more complex pipe */ writeln("several piped algorithms"); - srand(time(0)); + srand(static_cast(time(0))); std::array arr; generate(iter(arr), []() { return rand() % 128; }); diff --git a/examples/stream1.cc b/examples/stream1.cc index 4369d5b..4e082e3 100644 --- a/examples/stream1.cc +++ b/examples/stream1.cc @@ -9,7 +9,7 @@ using namespace ostd; -void print_result(uint32_t x) { +inline void print_result(uint32_t x) { writefln("got x: 0x%X", x); } diff --git a/gen_unicode.cc b/gen_unicode.cc index 2138fbf..713c837 100644 --- a/gen_unicode.cc +++ b/gen_unicode.cc @@ -135,7 +135,7 @@ struct parse_state { ((i + 1) < codes.size()) && (codes[i + 1] == (codes[i] + off)) ); }; - auto match_range = [&codes, &cases, &match_pair](std::size_t i) { + auto match_range = [&cases, &match_pair](std::size_t i) { return match_pair(i, 1) && ( cases.empty() || (cases[i + 1] == (cases[i] + 1)) ); diff --git a/ostd/algorithm.hh b/ostd/algorithm.hh index 2b1ab51..a440467 100644 --- a/ostd/algorithm.hh +++ b/ostd/algorithm.hh @@ -146,7 +146,7 @@ OSTD_UNIT_TEST { namespace detail { template - static void insort(R range, C &compare) { + inline void insort(R range, C &compare) { range_size_t rlen = range.size(); for (range_size_t i = 1; i < rlen; ++i) { range_size_t j = i; @@ -160,7 +160,7 @@ namespace detail { } template - static void hs_sift_down( + inline void hs_sift_down( R range, range_size_t s, range_size_t e, C &compare ) { range_size_t r = s; @@ -184,7 +184,7 @@ namespace detail { } template - static void heapsort(R range, C &compare) { + inline void heapsort(R range, C &compare) { range_size_t len = range.size(); range_size_t st = (len - 2) / 2; for (;;) { @@ -203,7 +203,7 @@ namespace detail { } template - static void introloop(R range, C &compare, range_size_t depth) { + inline void introloop(R range, C &compare, range_size_t depth) { using std::swap; if (range.size() <= 10) { detail::insort(range, compare); @@ -594,7 +594,7 @@ inline auto none_of(Predicate &&pred) { * Iterates the range and as soon as `range.front()` is equal to `v`, * returns `range`. The `range` is at least ostd::input_range_tag. * - * @sse ostd::find_last(), ostd::find_if(), ostd::find_if_not(), + * @see ostd::find_last(), ostd::find_if(), ostd::find_if_not(), * ostd::find_one_of() */ template @@ -625,7 +625,7 @@ inline auto find(Value &&v) { * the previous result of ostd::find() in case nothing is found next, this * algortihm requires `range` to be at least ostd::forward_range_tag. * - * @sse ostd::find(), ostd::find_if(), ostd::find_if_not(), + * @see ostd::find(), ostd::find_if(), ostd::find_if_not(), * ostd::find_one_of() */ template @@ -661,7 +661,7 @@ inline auto find_last(Value &&v) { * Iterates the range and as soon as `pred(range.front())` is true, * returns `range`. The `range` is at least ostd::input_range_tag. * - * @sse ostd::find(), ostd::find_last(), ostd::find_if_not(), + * @see ostd::find(), ostd::find_last(), ostd::find_if_not(), * ostd::find_one_of() */ template @@ -690,7 +690,7 @@ inline auto find_if(Predicate &&pred) { * Iterates the range and as soon as `!pred(range.front())` is true, * returns `range`. The `range` is at least ostd::input_range_tag. * - * @sse ostd::find(), ostd::find_last(), ostd::find_if(), ostd::find_one_of() + * @see ostd::find(), ostd::find_last(), ostd::find_if(), ostd::find_one_of() */ template inline InputRange find_if_not(InputRange range, Predicate pred) { @@ -729,7 +729,7 @@ inline auto find_if_not(Predicate &&pred) { * Use ostd::find_one_of() if you want to use the `==` operator * instead of calling `compare`. * - * @sse ostd::find(), ostd::find_last(), ostd::find_if(), ostd::find_if_not(), + * @see ostd::find(), ostd::find_last(), ostd::find_if(), ostd::find_if_not(), * ostd::find_one_of() */ template @@ -779,7 +779,7 @@ inline auto find_one_of_cmp(ForwardRange &&values, Compare &&compare) { * Use ostd::find_one_of_cmp() if you want to use a comparison * function instead of the `==` operator. * - * @sse ostd::find(), ostd::find_last(), ostd::find_if(), ostd::find_if_not(), + * @see ostd::find(), ostd::find_last(), ostd::find_if(), ostd::find_if_not(), * ostd::find_one_of_cmp() */ template diff --git a/ostd/argparse.hh b/ostd/argparse.hh index 1bb947b..ab63b67 100644 --- a/ostd/argparse.hh +++ b/ostd/argparse.hh @@ -492,7 +492,7 @@ private: case arg_value::OPTIONAL: case arg_value::ALL: break; - default: + case arg_value::REST: throw arg_error{"invalid argument requirement"}; } } @@ -917,7 +917,6 @@ protected: private: std::string p_name, p_title; - std::vector> p_opts; }; template @@ -959,9 +958,6 @@ inline bool arg_description_container::for_each( return false; } break; - default: - /* should never happen */ - throw arg_error{"invalid argument type"}; } } return true; @@ -1519,8 +1515,8 @@ struct default_help_formatter { /** @brief Formats the options (after usage line). * * Positional arguments not belonging to any group are formatted - * first, with `\nPositional arguments:\n` header. Same goes with - * optional arguments, except with `\nOptional arguments:\n`. + * first, with `\\nPositional arguments:\\n` header. Same goes with + * optional arguments, except with `\\nOptional arguments:\\n`. * * If either positional or optional arguments without group don't * exist, the section is skipped. @@ -1529,7 +1525,7 @@ struct default_help_formatter { * help strings are aligned and offset by 2 spaces from the longest * argument string. * - * Group titles are formatted as `\nTITLE:\n`. + * Group titles are formatted as `\\nTITLE:\\n`. * * Within groups, positional arguments come first and optional * arguments second. Mutually exclusive groups are expanded. @@ -1586,8 +1582,6 @@ struct default_help_formatter { } ); break; - default: - break; } return true; }, false, false); @@ -1636,9 +1630,7 @@ struct default_help_formatter { } auto &garg = static_cast(arg); format(out, "\n%s:\n", garg.title()); - garg.for_each([ - &write_help, &out, &allopt, &allpos - ](auto const &marg) { + garg.for_each([&allopt, &allpos](auto const &marg) { switch (marg.type()) { case arg_type::OPTIONAL: allopt.push_back( @@ -1650,7 +1642,8 @@ struct default_help_formatter { static_cast(&marg) ); break; - default: + case arg_type::GROUP: + case arg_type::MUTUALLY_EXCLUSIVE_GROUP: /* should never happen */ throw arg_error{"invalid argument type"}; } @@ -1700,7 +1693,7 @@ struct default_help_formatter { } format(out, " [%s ...]", mt); break; - default: + case arg_value::REST: break; } names.pop_front(); @@ -1741,7 +1734,8 @@ struct default_help_formatter { case arg_type::POSITIONAL: format_option(out, static_cast(arg)); break; - default: + case arg_type::GROUP: + case arg_type::MUTUALLY_EXCLUSIVE_GROUP: /* should never happen */ throw arg_error{"invalid argument type"}; } @@ -1767,7 +1761,7 @@ inline auto arg_print_help(OutputRange o, arg_parser &p) { p.print_help(o); p.stop_parsing(); }; -}; +} /** @brief Like ostd::arg_print_help() with ostd::cout. */ inline auto arg_print_help(arg_parser &p) { diff --git a/ostd/context_stack.hh b/ostd/context_stack.hh index 6f0aa73..4002180 100644 --- a/ostd/context_stack.hh +++ b/ostd/context_stack.hh @@ -34,6 +34,35 @@ namespace ostd { * @{ */ +struct coroutine_context; + +namespace detail { + /* from boost.fcontext */ + using fcontext_t = void *; + + struct transfer_t { + fcontext_t ctx; + void *data; + }; + + extern "C" OSTD_EXPORT + transfer_t OSTD_CDECL ostd_jump_fcontext( + fcontext_t const to, void *vp + ); + + extern "C" OSTD_EXPORT + fcontext_t OSTD_CDECL ostd_make_fcontext( + void *sp, std::size_t size, void (*fn)(transfer_t) + ); + + extern "C" OSTD_EXPORT + transfer_t OSTD_CDECL ostd_ontop_fcontext( + fcontext_t const to, void *vp, transfer_t (*fn)(transfer_t) + ); + + OSTD_EXPORT extern thread_local coroutine_context *coro_current; +} /* namespace detail */ + /** @brief An allocated stack. * * This represents a stack allocated by a stack allocator. It doesn't diff --git a/ostd/coroutine.hh b/ostd/coroutine.hh index f54e539..22df6a0 100644 --- a/ostd/coroutine.hh +++ b/ostd/coroutine.hh @@ -58,35 +58,6 @@ struct coroutine_error: std::runtime_error { using std::runtime_error::runtime_error; }; -struct coroutine_context; - -namespace detail { - /* from boost.fcontext */ - using fcontext_t = void *; - - struct transfer_t { - fcontext_t ctx; - void *data; - }; - - extern "C" OSTD_EXPORT - transfer_t OSTD_CDECL ostd_jump_fcontext( - fcontext_t const to, void *vp - ); - - extern "C" OSTD_EXPORT - fcontext_t OSTD_CDECL ostd_make_fcontext( - void *sp, std::size_t size, void (*fn)(transfer_t) - ); - - extern "C" OSTD_EXPORT - transfer_t OSTD_CDECL ostd_ontop_fcontext( - fcontext_t const to, void *vp, transfer_t (*fn)(transfer_t) - ); - - OSTD_EXPORT extern thread_local coroutine_context *coro_current; -} /* namespace detail */ - /** @brief An encapsulated context any coroutine-type inherits from. * * Internally, this provides some basic infrastructure for creating and @@ -135,7 +106,7 @@ protected: */ coroutine_context(coroutine_context &&c): p_stack(std::move(c.p_stack)), p_coro(c.p_coro), p_orig(c.p_orig), - p_except(std::move(c.p_except)), p_state(c.p_state), p_free(c.p_free) + p_free(c.p_free), p_except(std::move(c.p_except)), p_state(c.p_state) { c.p_coro = c.p_orig = nullptr; c.p_stack = { nullptr, 0 }; @@ -249,9 +220,9 @@ protected: swap(p_stack, other.p_stack); swap(p_coro, other.p_coro); swap(p_orig, other.p_orig); + swap(p_free, other.p_free); swap(p_except, other.p_except); swap(p_state, other.p_state); - swap(p_free, other.p_free); } /** @brief Allocates a stack and creates a context. @@ -276,10 +247,10 @@ protected: p_stack = sa.allocate(); void *sp = get_stack_ptr(); - std::size_t asize = p_stack.size - ( + auto asize = std::size_t(p_stack.size - std::size_t( static_cast(p_stack.ptr) - static_cast(sp) - ); + )); p_coro = detail::ostd_make_fcontext(sp, asize, &context_call); new (sp) SA(std::move(sa)); @@ -374,9 +345,9 @@ release: stack_context p_stack; detail::fcontext_t p_coro = nullptr; detail::fcontext_t p_orig = nullptr; + void (*p_free)(void *) = nullptr; std::exception_ptr p_except; state p_state = state::HOLD; - void (*p_free)(void *) = nullptr; }; template @@ -653,7 +624,7 @@ namespace detail { * as they were passed, exactly as if they were passed as regular args. * * @tparam R The return template type. - * @tparam A... The argument template types. + * @tparam A The argument template types. */ template struct coroutine: coroutine_context { diff --git a/ostd/format.hh b/ostd/format.hh index 2f842d9..391270b 100644 --- a/ostd/format.hh +++ b/ostd/format.hh @@ -134,7 +134,7 @@ namespace detail { * 7 .. string * 8 .. custom object */ - inline constexpr unsigned char const fmt_specs[] = { + static inline constexpr unsigned char const fmt_specs[] = { /* uppercase spec set */ 1, 3, 8, 8, /* A B C D */ 1, 1, 1, 8, /* E F G H */ @@ -160,12 +160,12 @@ namespace detail { 0, 0, 0, 0, 0 }; - inline constexpr int const fmt_bases[] = { + static inline constexpr int const fmt_bases[] = { 0, 0, 0, 2, 8, 10, 16, 0 }; /* non-printable escapes up to 0x20 (space) */ - inline constexpr char const *fmt_escapes[] = { + static inline constexpr char const *fmt_escapes[] = { "\\0" , "\\x01", "\\x02", "\\x03", "\\x04", "\\x05", "\\x06", "\\a" , "\\b" , "\\t" , "\\n" , "\\v" , "\\f" , "\\r" , "\\x0E", "\\x0F", "\\x10", "\\x11", @@ -215,33 +215,35 @@ namespace detail { * pair, array, possibly other types added later or overridden... */ template - std::true_type tuple_like_test(typename std::tuple_size::type *); + inline std::true_type tuple_like_test(typename std::tuple_size::type *); template - std::false_type tuple_like_test(...); + inline std::false_type tuple_like_test(...); template - inline constexpr bool is_tuple_like = decltype(tuple_like_test(0))::value; + static inline constexpr bool is_tuple_like = + decltype(tuple_like_test(0))::value; /* character type tests */ template - inline constexpr bool is_character = std::is_same_v || - std::is_same_v || - std::is_same_v || - std::is_same_v; + static inline constexpr bool is_character = std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v; /* test if format traits are available for the type */ template - static std::true_type test_tofmt(decltype(format_traits::to_format( + inline std::true_type test_tofmt(decltype(format_traits::to_format( std::declval(), std::declval(), std::declval() )) *); template - static std::false_type test_tofmt(...); + inline std::false_type test_tofmt(...); template - inline constexpr bool fmt_tofmt_test = decltype(test_tofmt(0))::value; + static inline constexpr bool fmt_tofmt_test = + decltype(test_tofmt(0))::value; template inline int ac_to_mb(C c, F const &f, char *buf) { @@ -252,7 +254,7 @@ namespace detail { if (ret != std::codecvt_base::ok) { return -1; } - return ton - &buf[0]; + return int(ton - &buf[0]); } inline int wc_to_mb_loc(wchar_t c, char *buf, std::locale const &loc) { @@ -847,14 +849,16 @@ private: if (p.front() == need) { p_is_tuple = tuple; if (tuple) { - p_nested = begin_inner.slice(0, &p[0] - &begin_inner[0] - 1); + p_nested = begin_inner.slice( + 0, std::size_t(&p[0] - &begin_inner[0] - 1) + ); p_nested_sep = nullptr; } else { p_nested = begin_inner.slice( - 0, &begin_delim[0] - &begin_inner[0] + 0, std::size_t(&begin_delim[0] - &begin_inner[0]) ); p_nested_sep = begin_delim.slice( - 0, &p[0] - &begin_delim[0] - 1 + 0, std::size_t(&p[0] - &begin_delim[0] - 1) ); } p.pop_front(); @@ -864,14 +868,16 @@ private: } /* found actual delimiter start... */ if ((p.front() == '|') && !tuple) { - p_nested = begin_inner.slice(0, &p[0] - &begin_inner[0] - 1); + p_nested = begin_inner.slice( + 0, std::size_t(&p[0] - &begin_inner[0] - 1) + ); p.pop_front(); p_nested_sep = p; for (p = find(p, '%'); !p.empty(); p = find(p, '%')) { p.pop_front(); if (p.front() == ')') { p_nested_sep = p_nested_sep.slice( - 0, &p[0] - &p_nested_sep[0] - 1 + 0, std::size_t(&p[0] - &p_nested_sep[0] - 1) ); p.pop_front(); p_fmt = p; @@ -1083,15 +1089,15 @@ private: throw format_error{"cannot format integers with the given spec"}; } /* 32 for lowercase variants, 0 for uppercase */ - int cmask = ((isp >= 'a') << 5); + char cmask = char((isp >= 'a') << 5); - int base = detail::fmt_bases[specn]; + T base = T(detail::fmt_bases[specn]); if (!val) { ndig = 1; buf[0] = '0'; } else { for (; val; val /= base) { - T vb = val % base; + auto vb = char(val % base); buf[ndig++] = (vb + "70"[vb < 10]) | cmask; } } @@ -1132,7 +1138,7 @@ private: } ++grpp; } - total += nseps * ntsep; + total += nseps * std::size_t(ntsep); } /* here ends the bullshit */ @@ -1244,7 +1250,8 @@ private: fmt_num_put nump; nump.put( fmt_out{&writer, &p_loc}, st, - (p_flags & FMT_FLAG_ZERO) ? L'0' : L' ', val + (p_flags & FMT_FLAG_ZERO) ? L'0' : L' ', + std::conditional_t, T, double>(val) ); } @@ -1291,10 +1298,10 @@ private: throw format_error{"tuples need the '%s' spec"}; } writer.put('{'); - write_range_val(writer, [&writer, escape, this](auto const &rval) { - format_spec sp{'s', p_loc, escape ? FMT_FLAG_AT : 0}; + write_range_val(writer, [&writer, this](auto const &rval, bool esc) { + format_spec sp{'s', p_loc, esc ? FMT_FLAG_AT : 0}; sp.write_arg(writer, 0, rval); - }, ", ", val); + }, ", ", val, escape); writer.put('}'); return; } @@ -1369,13 +1376,11 @@ private: ) const { if constexpr(detail::is_tuple_like) { if (expandval) { - std::apply([&writer, escape, &fmt, this]( - auto const &...args - ) mutable { + std::apply([ + this, &writer, &fmt, flags = escape ? FMT_FLAG_AT : 0 + ](auto const &...args) mutable { format_spec sp{fmt, p_loc}; - if (escape) { - sp.p_gflags |= FMT_FLAG_AT; - } + sp.p_gflags |= flags; sp.write_fmt(writer, args...); }, item); return; @@ -1388,9 +1393,10 @@ private: sp.write_fmt(writer, item); } - template + template void write_range_val( - R &writer, F &&func, [[maybe_unused]] string_range sep, T const &val + R &writer, F &&func, [[maybe_unused]] string_range sep, T const &val, + A const &...args ) const { if constexpr(detail::iterable_test) { auto range = ostd::iter(val); @@ -1398,7 +1404,7 @@ private: return; } for (;;) { - func(range.front()); + func(range.front(), args...); range.pop_front(); if (range.empty()) { break; @@ -1424,13 +1430,12 @@ private: } } else { write_range_val(writer, [ - this, &writer, escape = p_gflags & FMT_FLAG_AT, expandval, - fmt = rest() - ](auto const &rval) { + fmt = rest(), this, &writer + ](auto const &rval, bool expval, bool escape) { this->write_range_item( - writer, escape, expandval, fmt, rval + writer, escape, expval, fmt, rval ); - }, sep, val); + }, sep, val, expandval, p_gflags & FMT_FLAG_AT); } } @@ -1458,7 +1463,7 @@ private: } } else { if constexpr(detail::is_tuple_like) { - std::apply([this, &writer, &val](auto const &...vals) mutable { + std::apply([this, &writer](auto const &...vals) mutable { this->write_fmt(writer, vals...); }, val); } else { diff --git a/ostd/process.hh b/ostd/process.hh index a87fa3a..30921d0 100644 --- a/ostd/process.hh +++ b/ostd/process.hh @@ -110,7 +110,37 @@ struct OSTD_EXPORT subprocess { private: struct nat {}; + std::aligned_storage_t<2 * sizeof(void *), alignof(void *)> p_data; + void *p_current = nullptr; + public: + /** @brief The standard input stream when redirected. + * + * If no redirection is done (see ostd::subprocess::use_in) then + * this stream will not be opened. + * + * @see ostd::subprocess::out, ostd::subprocess::err + */ + file_stream in = file_stream{}; + + /** @brief The standard output stream when redirected. + * + * If no redirection is done (see ostd::subprocess::use_out) then + * this stream will not be opened. + * + * @see ostd::subprocess::in, ostd::subprocess::err + */ + file_stream out = file_stream{}; + + /** @brief The standard error stream when redirected. + * + * If no redirection is done (see ostd::subprocess::use_err) then + * this stream will not be opened. + * + * @see ostd::subprocess::in, ostd::subprocess::out + */ + file_stream err = file_stream{}; + /** @brief The standard input redirection mode. * * The value is one of ostd::subprocess_stream. Set this before opening @@ -154,33 +184,6 @@ public: */ subprocess_stream use_err = subprocess_stream::DEFAULT; - /** @brief The standard input stream when redirected. - * - * If no redirection is done (see ostd::subprocess::use_in) then - * this stream will not be opened. - * - * @see ostd::subprocess::out, ostd::subprocess::err - */ - file_stream in = file_stream{}; - - /** @brief The standard output stream when redirected. - * - * If no redirection is done (see ostd::subprocess::use_out) then - * this stream will not be opened. - * - * @see ostd::subprocess::in, ostd::subprocess::err - */ - file_stream out = file_stream{}; - - /** @brief The standard error stream when redirected. - * - * If no redirection is done (see ostd::subprocess::use_err) then - * this stream will not be opened. - * - * @see ostd::subprocess::in, ostd::subprocess::out - */ - file_stream err = file_stream{}; - /** @brief Initializes the structure with the given stream redirections. */ subprocess( subprocess_stream in_use = subprocess_stream::DEFAULT, @@ -232,8 +235,8 @@ public: /** @brief Moves the subprocess data. */ subprocess(subprocess &&i) noexcept: - use_in(i.use_in), use_out(i.use_out), use_err(i.use_err), - in(std::move(i.in)), out(std::move(i.out)), err(std::move(i.err)) + in(std::move(i.in)), out(std::move(i.out)), err(std::move(i.err)), + use_in(i.use_in), use_out(i.use_out), use_err(i.use_err) { move_data(i); } @@ -460,9 +463,6 @@ private: void reset(); void move_data(subprocess &i); void swap_data(subprocess &i); - - std::aligned_storage_t<2 * sizeof(void *)> p_data; - void *p_current = nullptr; }; /** @} */ diff --git a/ostd/range.hh b/ostd/range.hh index 8a43341..dedffde 100644 --- a/ostd/range.hh +++ b/ostd/range.hh @@ -186,7 +186,8 @@ namespace detail { }; template - inline constexpr bool test_range_category = range_category_test::value; + static inline constexpr bool const test_range_category = + range_category_test::value; template struct range_traits_base { @@ -199,7 +200,7 @@ namespace detail { }; template - inline constexpr bool test_elem_reference = + static inline constexpr bool const test_elem_reference = std::is_convertible_v && std::is_lvalue_reference_v && std::is_same_v< @@ -388,7 +389,7 @@ using range_reference_t = typename range_traits::reference; * */ template -inline constexpr bool is_range_element_swappable_with = +static inline constexpr bool const is_range_element_swappable_with = range_traits::template is_element_swappable_with; /** @brief Checks whether a range can swap elements within itself. @@ -401,7 +402,7 @@ inline constexpr bool is_range_element_swappable_with = * */ template -inline constexpr bool is_range_element_swappable = +static inline constexpr bool const is_range_element_swappable = range_traits::is_element_swappable; /** @brief Checks whether two ranges can nothrow swap elements with each other. @@ -414,7 +415,7 @@ inline constexpr bool is_range_element_swappable = * */ template -inline constexpr bool is_range_element_nothrow_swappable_with = +static inline constexpr bool const is_range_element_nothrow_swappable_with = range_traits::template is_element_nothrow_swappable_with; /** @brief Checks whether a range can nothrow swap elements within itself. @@ -427,21 +428,21 @@ inline constexpr bool is_range_element_nothrow_swappable_with = * */ template -inline constexpr bool is_range_element_nothrow_swappable = +static inline constexpr bool const is_range_element_nothrow_swappable = range_traits::is_element_nothrow_swappable; // is input range namespace detail { template - inline constexpr bool is_input_range_core = + static inline constexpr bool const is_input_range_core = std::is_convertible_v, input_range_tag>; template> - inline constexpr bool is_input_range_base = false; + static inline constexpr bool const is_input_range_base = false; template - inline constexpr bool is_input_range_base = + static inline constexpr bool const is_input_range_base = detail::is_input_range_core; } @@ -453,20 +454,21 @@ namespace detail { * @see ostd::is_forward_range, ostd::is_output_range */ template -inline constexpr bool is_input_range = detail::is_input_range_base; +static inline constexpr bool const is_input_range = + detail::is_input_range_base; // is forward range namespace detail { template - inline constexpr bool is_forward_range_core = + static inline constexpr bool const is_forward_range_core = std::is_convertible_v, forward_range_tag>; template> - inline constexpr bool is_forward_range_base = false; + static inline constexpr bool const is_forward_range_base = false; template - inline constexpr bool is_forward_range_base = + static inline constexpr bool const is_forward_range_base = detail::is_forward_range_core; } @@ -478,20 +480,21 @@ namespace detail { * @see ostd::is_input_range, ostd::is_bidirectional_range */ template -inline constexpr bool is_forward_range = detail::is_forward_range_base; +static inline constexpr bool const is_forward_range = + detail::is_forward_range_base; // is bidirectional range namespace detail { template - inline constexpr bool is_bidirectional_range_core = + static inline constexpr bool const is_bidirectional_range_core = std::is_convertible_v, bidirectional_range_tag>; template> - inline constexpr bool is_bidirectional_range_base = false; + static inline constexpr bool const is_bidirectional_range_base = false; template - inline constexpr bool is_bidirectional_range_base = + static inline constexpr bool const is_bidirectional_range_base = detail::is_bidirectional_range_core; } @@ -503,21 +506,21 @@ namespace detail { * @see ostd::is_forward_range, ostd::is_random_access_range */ template -inline constexpr bool is_bidirectional_range = +static inline constexpr bool const is_bidirectional_range = detail::is_bidirectional_range_base; // is random access range namespace detail { template - inline constexpr bool is_random_access_range_core = + static inline constexpr bool const is_random_access_range_core = std::is_convertible_v, random_access_range_tag>; template> - inline constexpr bool is_random_access_range_base = false; + static inline constexpr bool const is_random_access_range_base = false; template - inline constexpr bool is_random_access_range_base = + static inline constexpr bool const is_random_access_range_base = detail::is_random_access_range_core; } @@ -530,21 +533,21 @@ namespace detail { * @see ostd::is_bidirectional_range, ostd::is_finite_random_access_range */ template -inline constexpr bool is_random_access_range = +static inline constexpr bool const is_random_access_range = detail::is_random_access_range_base; // is finite random access range namespace detail { template - inline constexpr bool is_finite_random_access_range_core = + static inline constexpr bool const is_finite_random_access_range_core = std::is_convertible_v, finite_random_access_range_tag>; template> - inline constexpr bool is_finite_random_access_range_base = false; + static inline constexpr bool const is_finite_random_access_range_base = false; template - inline constexpr bool is_finite_random_access_range_base = + static inline constexpr bool const is_finite_random_access_range_base = detail::is_finite_random_access_range_core; } @@ -557,21 +560,21 @@ namespace detail { * @see ostd::is_random_access_range, ostd::is_contiguous_range */ template -inline constexpr bool is_finite_random_access_range = +static inline constexpr bool const is_finite_random_access_range = detail::is_finite_random_access_range_base; // is contiguous range namespace detail { template - inline constexpr bool is_contiguous_range_core = + static inline constexpr bool const is_contiguous_range_core = std::is_convertible_v, contiguous_range_tag>; template> - inline constexpr bool is_contiguous_range_base = false; + static inline constexpr bool const is_contiguous_range_base = false; template - inline constexpr bool is_contiguous_range_base = + static inline constexpr bool const is_contiguous_range_base = detail::is_contiguous_range_core; } @@ -582,25 +585,26 @@ namespace detail { * @see ostd::is_finite_random_access_range */ template -inline constexpr bool is_contiguous_range = detail::is_contiguous_range_base; +static inline constexpr bool const is_contiguous_range = + detail::is_contiguous_range_base; // is output range namespace detail { template - static std::true_type test_outrange(typename std::is_same< + inline std::true_type test_outrange(typename std::is_same< decltype(std::declval().put(std::declval())), void >::type *); template - static std::false_type test_outrange(...); + inline std::false_type test_outrange(...); template - inline constexpr bool output_range_test = + static inline constexpr bool const output_range_test = decltype(test_outrange())::value; template - inline constexpr bool is_output_range_core = + static inline constexpr bool const is_output_range_core = std::is_convertible_v, output_range_tag> || ( is_input_range && ( output_range_test const &> || @@ -610,10 +614,10 @@ namespace detail { ); template> - inline constexpr bool is_output_range_base = false; + static inline constexpr bool const is_output_range_base = false; template - inline constexpr bool is_output_range_base = + static inline constexpr bool const is_output_range_base = detail::is_output_range_core; } @@ -627,7 +631,8 @@ namespace detail { * @see ostd::is_input_range */ template -inline constexpr bool is_output_range = detail::is_output_range_base; +static inline constexpr bool const is_output_range = + detail::is_output_range_base; namespace detail { // range iterator @@ -1203,28 +1208,28 @@ inline auto zip(R1 &&r1, R &&...rr) { namespace detail { template - static std::true_type test_direct_iter( + inline std::true_type test_direct_iter( decltype(std::declval().iter()) * ); template - static std::false_type test_direct_iter(...); + inline std::false_type test_direct_iter(...); template - inline constexpr bool direct_iter_test = + static inline constexpr bool const direct_iter_test = decltype(test_direct_iter(0))::value; template - static std::true_type test_std_iter( + inline std::true_type test_std_iter( decltype(std::begin(std::declval())) *, decltype(std::end(std::declval())) * ); template - static std::false_type test_std_iter(...); + inline std::false_type test_std_iter(...); template - inline constexpr bool std_iter_test = + static inline constexpr bool const std_iter_test = decltype(test_std_iter(0, 0))::value; /* direct iter and std iter; the case for std iter is @@ -1298,14 +1303,15 @@ inline auto citer(T const &r) -> decltype(ranged_traits::iter(r)) { namespace detail { template - static std::true_type test_iterable( + inline std::true_type test_iterable( decltype(ostd::iter(std::declval())) * ); template - static std::false_type test_iterable(...); + inline std::false_type test_iterable(...); template - inline constexpr bool iterable_test = decltype(test_iterable(0))::value; + static inline constexpr bool const iterable_test = + decltype(test_iterable(0))::value; } namespace detail { @@ -1387,7 +1393,7 @@ OSTD_UNIT_TEST { fail_if(r.empty() || (r.front() != 9)); r.pop_front(); fail_if(!r.empty()); -}; +} #endif namespace detail { @@ -1894,7 +1900,8 @@ struct iterator_range: input_range> { * If the indexes are not within bounds, the behavior is undefined. */ iterator_range slice(size_type start, size_type end) const { - return iterator_range(p_beg + start, p_beg + end); + using DT = typename std::iterator_traits::difference_type; + return iterator_range(p_beg + DT(start), p_beg + DT(end)); } /** @brief Slices the range with size() for the second argument. @@ -1911,7 +1918,10 @@ struct iterator_range: input_range> { * Only valid if the range is at least finite random access. * If the index is not within bounds, the behavior is undefined. */ - reference operator[](size_type i) const { return p_beg[i]; } + reference operator[](size_type i) const { + using DT = typename std::iterator_traits::difference_type; + return p_beg[DT(i)]; + } /** @brief Gets the pointer to the first element. * diff --git a/ostd/stream.hh b/ostd/stream.hh index 7c422e6..ce4fede 100644 --- a/ostd/stream.hh +++ b/ostd/stream.hh @@ -567,6 +567,13 @@ struct stream_range: input_range> { p_stream(r.p_stream), p_item(r.p_item) {} + /** @brief Stream ranges can be copied, the cached value is also copied. */ + stream_range &operator=(stream_range const &r) { + p_stream = r.p_stream; + p_item = r.p_item; + return *this; + } + /** @brief Checks if the range (stream) is empty. * * If there is a value cached in this range, this returns false. diff --git a/ostd/string.hh b/ostd/string.hh index 42db4fa..bb18ffb 100644 --- a/ostd/string.hh +++ b/ostd/string.hh @@ -92,7 +92,7 @@ private: public: /** @brief Constructs an empty slice. */ - basic_char_range() noexcept: p_beg(nullptr), p_end(nullptr) {}; + basic_char_range() noexcept: p_beg(nullptr), p_end(nullptr) {} /** @brief Constructs a slice from two pointers. * @@ -108,6 +108,11 @@ public: p_beg(nullptr), p_end(nullptr) {} + /** @brief Slices are arbitrarily copy constructible. */ + basic_char_range(basic_char_range const &v) noexcept: + p_beg(v.p_beg), p_end(v.p_end) + {} + /** @brief Constructs a slice from a pointer or a static array. * * This constructor handles two cases. The input must be convertible @@ -232,7 +237,7 @@ public: reference back() const noexcept { return *(p_end - 1); } /** @brief Gets the number of value_type in the slice. */ - size_type size() const noexcept { return p_end - p_beg; } + size_type size() const noexcept { return size_type(p_end - p_beg); } /** @brief Gets the number of code points in the slice. * @@ -319,7 +324,7 @@ public: int compare(basic_char_range s) const noexcept { size_type s1 = size(), s2 = s.size(); for (size_type i = 0, ms = std::min(s1, s2); i < ms; ++i) { - int d = p_beg[i] - s[i]; + int d = int(p_beg[i]) - int(s[i]); if (d) { return d; } @@ -859,7 +864,7 @@ namespace utf { p_current = -1; throw utf_error{"UTF-8 decoding failed"}; } else { - p_current = ret; + p_current = std::int32_t(ret); } } diff --git a/ostd/unit_test.hh b/ostd/unit_test.hh index 6437fb9..c9172c0 100644 --- a/ostd/unit_test.hh +++ b/ostd/unit_test.hh @@ -46,9 +46,9 @@ namespace test { #define OSTD_TEST_MODULE_CURRENT OSTD_TEST_MODULE_STR(OSTD_BUILD_TESTS) namespace detail { - static std::vector test_cases; + static inline std::vector test_cases; - static bool add_test(std::string testn, void (*func)()) { + inline bool add_test(std::string testn, void (*func)()) { if (testn == OSTD_TEST_MODULE_CURRENT) { test_cases.push_back(func); } @@ -73,13 +73,13 @@ namespace detail { * after including any headers and undefined at the end of the file. */ #define OSTD_UNIT_TEST \ -static void OSTD_TEST_FUNC_NAME(test_func, OSTD_TEST_MODULE, __LINE__)(); \ -static bool OSTD_TEST_FUNC_NAME(test_case, OSTD_TEST_MODULE, __LINE__) = \ +inline void OSTD_TEST_FUNC_NAME(test_func, OSTD_TEST_MODULE, __LINE__)(); \ +static inline bool OSTD_TEST_FUNC_NAME(test_case, OSTD_TEST_MODULE, __LINE__) = \ ostd::test::detail::add_test( \ OSTD_TEST_MODULE_STR(OSTD_TEST_MODULE), \ &OSTD_TEST_FUNC_NAME(test_func, OSTD_TEST_MODULE, __LINE__) \ ); \ -static void OSTD_TEST_FUNC_NAME(test_func, OSTD_TEST_MODULE, __LINE__)() +inline void OSTD_TEST_FUNC_NAME(test_func, OSTD_TEST_MODULE, __LINE__)() /** @brief Makes the test fail if the given value is true. * @@ -90,7 +90,7 @@ static void OSTD_TEST_FUNC_NAME(test_func, OSTD_TEST_MODULE, __LINE__)() * * @see ostd::test::fail(), ostd::test::fail_if_not() */ -void fail_if(bool b) { +inline void fail_if(bool b) { if (b) { throw detail::test_error{}; } @@ -100,7 +100,7 @@ void fail_if(bool b) { * * The test will fail if the given value is false. */ -void fail_if_not(bool b) { +inline void fail_if_not(bool b) { if (!b) { throw detail::test_error{}; } @@ -112,7 +112,7 @@ void fail_if_not(bool b) { * * @see ostd::test::fail_if() */ -void fail() { +[[noreturn]] inline void fail() { throw detail::test_error{}; } @@ -121,7 +121,7 @@ void fail() { * @returns An std::pair containing the number of tests that succeeded * as the first value and failed as the second value. */ -std::pair run() { +inline std::pair run() { std::size_t succ = 0, fail = 0; for (auto &f: detail::test_cases) { try { diff --git a/src/posix/context_stack.cc b/src/posix/context_stack.cc index 8cd59b2..a300f58 100644 --- a/src/posix/context_stack.cc +++ b/src/posix/context_stack.cc @@ -40,7 +40,7 @@ namespace detail { OSTD_EXPORT void *stack_alloc(std::size_t sz) { if constexpr(CONTEXT_USE_MMAP) { void *p = mmap( - 0, sz, PROT_READ | PROT_WRITE, + nullptr, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | CONTEXT_MAP_ANON, -1, 0 ); if (p == MAP_FAILED) { diff --git a/src/string.cc b/src/string.cc index d6e2764..f55d6f6 100644 --- a/src/string.cc +++ b/src/string.cc @@ -47,7 +47,7 @@ namespace detail { ret = (ret << 6) | bch; } /* number of continuation bytes */ - std::size_t n = sr.data() - r.data() - 1; + auto n = sr.data() - r.data() - 1; /* invalid sequence - too many continuation bits */ if (n > 3) { return false; @@ -71,12 +71,12 @@ namespace detail { std::uint8_t (&ret)[4], std::uint32_t ch ) noexcept { if (ch <= 0x7F) { - ret[0] = ch; + ret[0] = std::uint8_t(ch); return 1; } if (ch <= 0x7FF) { - ret[0] = 0xC0 | (ch >> 6); - ret[1] = 0x80 | (ch & 0x3F); + ret[0] = std::uint8_t(0xC0 | (ch >> 6)); + ret[1] = std::uint8_t(0x80 | (ch & 0x3F)); return 2; } if (ch <= 0xFFFF) { @@ -86,16 +86,16 @@ namespace detail { if ((ch >= 0xD800) && (ch <= 0xDFFF)) { return 0; } - ret[0] = 0xE0 | (ch >> 12); - ret[1] = 0x80 | ((ch >> 6) & 0x3F); - ret[2] = 0x80 | (ch & 0x3F); + ret[0] = std::uint8_t(0xE0 | (ch >> 12)); + ret[1] = std::uint8_t(0x80 | ((ch >> 6) & 0x3F)); + ret[2] = std::uint8_t(0x80 | (ch & 0x3F)); return 3; } if (ch <= MaxCodepoint) { - ret[0] = 0xF0 | (ch >> 18); - ret[1] = 0x80 | ((ch >> 12) | 0x3F); - ret[2] = 0x80 | ((ch >> 6) | 0x3F); - ret[3] = 0x80 | (ch | 0x3F); + ret[0] = std::uint8_t(0xF0 | (ch >> 18)); + ret[1] = std::uint8_t(0x80 | ((ch >> 12) | 0x3F)); + ret[2] = std::uint8_t(0x80 | ((ch >> 6) | 0x3F)); + ret[3] = std::uint8_t(0x80 | (ch | 0x3F)); return 4; } return 0; @@ -176,7 +176,7 @@ bool isxdigit(char32_t c) noexcept { inline int codepoint_cmp1(void const *a, void const *b) noexcept { char32_t c1 = *static_cast(a); char32_t c2 = *static_cast(b); - return (c1 - c2); + return (int(c1) - int(c2)); } inline int codepoint_cmp2(void const *a, void const *b) noexcept { @@ -185,7 +185,7 @@ inline int codepoint_cmp2(void const *a, void const *b) noexcept { if ((c >= p[0]) && (c <= p[1])) { return 0; } - return (c - p[0]); + return (int(c) - int(p[0])); } template< @@ -345,7 +345,12 @@ int case_compare(string_range s1, string_range s2) noexcept { s1 = s1.slice(0, ms); s2 = s2.slice(0, ms); for (;;) { - char32_t ldec = s1.front(), rdec = s2.front(); + /* enforce correct semantics with signed chars; first convert to + * 8-bit unsigned and then to char32_t (which is always unsigned) + * in order to not get large values from 32-bit unsigned underflow + */ + auto ldec = char32_t(std::uint8_t(s1.front())); + auto rdec = char32_t(std::uint8_t(s2.front())); if ((ldec <= 0x7F) || !utf::decode(s1, ldec)) { s1.pop_front(); } @@ -356,8 +361,13 @@ int case_compare(string_range s1, string_range s2) noexcept { if (d) { return d; } + if (s1.empty() || s2.empty()) { + s1l = s1.size(); + s2l = s2.size(); + break; + } } - return (s1l < s2l) ? -1 : ((s1 > s2) ? 1 : 0); + return (s1l < s2l) ? -1 : ((s1l > s2l) ? 1 : 0); } int case_compare(u32string_range s1, u32string_range s2) noexcept {