Breaking
- The WFMO backend is removed: the completion port is Windows' only backend (core-cpp#6).
BackendKind::Wfmo,WfmoBackendand the readiness socket transport it drove
(WindowsSocket,WindowsListener) are gone, after the release in which IOCP was the default
and WFMO a fallback.makeDefaultBackend()on Windows no longer falls back: a completion port
the kernel refuses to create is handle exhaustion, and it propagates.listen,listenUnix,
adoptListener,adoptSocketand the dials refuse a loop whose backend lends no completion port
withNetErrorCode::Unsupported, where they used to hand out a readiness socket; a dial refuses
before it creates a socket, so nothing reaches the peer. Two defects of
the removed transport go with it: a closed socket's parked read now answersCancelledon every
platform, whereWindowsSocketansweredBadHandle(core-cpp#46), and the WFMO-only
destruction gap of core-cpp#50 has nothing left to apply to.connectUnix's socket on Windows is
now made uninheritable, as every other one is (core-cpp#28).- Migration: no consumer names any of these; a program that did replaces
makeBackend(BackendKind::Wfmo)withmakeDefaultBackend().BackendKind's enumerators after
Iocpshift down by one, so a value stored or sent as an integer is re-read by name
(toString). A Windows test double that stood in for the loop's backend and expected a
socket from the factories needs a completion port, or drives anISocketof its own.
- Migration: no consumer names any of these; a program that did replaces
core::platform::EnvironmentProviderisProcessEnvironment, acore::Environment; the
working directory has its own seam; Windows reads and writes the environment in UTF-8
(core-cpp#7). Two seams answered "readHOME", with two doubles a mixed test had to keep
agreeing.core::Environmentis now the one read seam, andProcessEnvironment-- what a shell
writes -- derives from it, so code that only reads is handed the same object and
testing::TestProcessEnvironmentis the double for both.set,unset,exportVariableand
setAndExportreturnstd::expected<void, PlatformError>, where they returnedvoidand dropped
the error; a name that is empty or holds=or NUL, or a value that holds NUL, is
PlatformError::InvalidArgument(a new enumerator, last, so no other value moves).
changeDirectoryandcurrentDirectorymove tocore::platform::WorkingDirectory
(nativeWorkingDirectory(),testing::TestWorkingDirectory), andhomeDirectory,userName
andconfigHomeare the free functions of<core/platform/UserPaths.hpp>over a
core::Environment const&(userNameis new there). On Windows,core::LiveEnvironment,
core::setProcessEnvironmentVariableandunsetProcessEnvironmentVariablego through
GetEnvironmentVariableW/SetEnvironmentVariableW, converting to and from UTF-8, where the
code-page API mangled a value such as a user profile path outside the ANSI code page; a name or
value that is not UTF-8 is refused (std::errc::invalid_argument), andProcessEnvironment::keys()
converts UTF-16 names rather than narrowing them a code unit at a time. The read seam's
interface is unchanged.- Migration:
<core/platform/EnvironmentProvider.hpp>is<core/platform/ProcessEnvironment.hpp>;
EnvironmentProviderisProcessEnvironment,nativeEnvironmentProvider()is
nativeProcessEnvironment(), andtesting::TestEnvironmentProvideris
testing::TestProcessEnvironment(<core/platform/testing/TestProcessEnvironment.hpp>), which no
longer takes an initial directory. Handle or discard thestd::expectedfrom everyset,
unset,exportVariableandsetAndExport(std::ignore = env.set(...)where a failure is
acceptable). Replaceenv.changeDirectory(p)andenv.currentDirectory()with a
WorkingDirectory&the object is given --nativeWorkingDirectory()in a composition root,
testing::TestWorkingDirectory(initial)withaddValidPathin a test. Its
currentDirectory()returns astd::filesystem::path, where the old member returned a UTF-8
std::string, so the answer round-trips throughchangeDirectory()on Windows whatever it
spells: a caller that wants the text takescore::platform::normalizePath(cwd.currentDirectory()),
and one that compared with a string literal compares itsgeneric_string(). Replace
env.homeDirectory(),env.userName()andenv.configHome()with
core::platform::homeDirectory(env),userName(env)andconfigHome(env). A function that
only reads can take acore::Environment const&and be handed either double. contour, which
uses onlycore::Environment, changes nothing.
- Migration:
core::cli::parse()returnsstd::expected<FlagStore, ParseError>and throws nothing
(core-cpp#13). It returnedstd::optional<FlagStore>--std::nulloptfor tokens left over --
and threwcore::cli::ParserErrorfor a value of the wrong type, a missing value or an explicit
empty one, andstd::invalid_argumentfor a missing required option. Every one of those is now a
ParseError: aParseErrorKind(NotEnoughArguments,InvalidValue,EmptyValue,
UnexpectedToken,MissingRequiredOption), the index of the token at fault and a message.
ParserErroris removed. Numbers are read whole, withstd::from_chars(floating point with
std::strtod):12abcis refused rather than read as 12,-1is no longer accepted -- and
wrapped -- as an unsigned, a leading+or leading whitespace (+5,5), whichstd::stoi
andstd::stoulaccepted, is refused, and a value out of the type's range is refused rather
than truncated.parse()is[[nodiscard]]: a call that discards the result no longer compiles
under-Werror.App::run()andApp::reparseParameters()print the error's message; their signatures are
unchanged.- Migration: a call site that tested
has_value()or used*parsedandparsed->compiles
as it is when its variable isauto; one that names the type spells
std::expected<core::cli::FlagStore, core::cli::ParseError>, orauto. Replace atry/catch
aroundparse()with a test of the result, and reportparsed.error().message. tuidu's
parseCommandLine()(src/tuidu/Cli.cpp) is the one consumer call site: it declares
std::optional<core::cli::FlagStore> parsedand catchesstd::exceptionaround the call.
contour and endo usecore::cli::Apponly, and need no change.
- Migration: a call site that tested
Added
core::async::TaskKindandRunTask::kind(): an around-task hook can tell a coroutine
resumption from a posted callable (core-cpp#53).TaskKind::Callableis whatpostand
tryPostwere given;TaskKind::Resumptionis a coroutine arriving throughsubmitor
trySubmit, as a handle or asParkedWork, aResumeOnhop, or aKeyedStrandsreroute
through a retired key strand. A hook --StrandOptions::aroundTaskor aKeyedAroundTask--
can now scope per-resumption context to coroutine resumptions only, rather than installing it
around every callable posted to the same strand or key too. Read from what the task already
holds, so it adds nothing to a task and costs one load where a hook asks. Additive; no signature
changes.core::net::closeLingeringandLingerBounds(<core/net/LingeringClose.hpp>, from
fastcached at0708dd54; core-cpp#35): half-close, discard what the peer is still sending until
it closes or a bound runs out -- the whole drain's time, the bytes discarded, the reads made --
then close, so a reply written over a request left unread is followed by a FIN rather than
destroyed by the reset a bare close sends.HttpLimits::lingerbounds it forserve, by
default 250 ms, 64 KiB and four reads -- smaller than fastcached's two seconds, becauseserve
handles one connection at a time and a refused request holds its accept loop for up to that
long. Additive.core-cpp.open-work: every## Open workentry leads with a core-cpp issue, and that issue is
open (core-cpp#12).scripts/check-open-work.pyreads every such section under.agent/and
docs/and the top-level documents, and refuses an entry that does not lead with a core-cpp issue
link, a link whose text and URL disagree, and a heading with no entries. The ctest runs that
offline; CI'sstylejob also runs it--online, which refuses an entry whose issue has closed,
and one whose issue does not exist (404 or 410); it exits 77 (skipped, a warning in CI) rather
than failing when it could not ask GitHub. It has a self-test.- A nightly job answers whether a provenance row's upstream has moved (core-cpp#33).
core-cpp.upstream-driftneeds the upstream checkouts beside core-cpp, so it skips on every CI
runner and its coverage there was zero. Theupstream-driftjob ofdownstream.ymlchecks out
contour, endo and fastcached with full history as siblings and runs the checker: drift is listed
in the job summary, and a row the checkouts prove malformed, or an upstream the checker could not
read, fails the job. core-cpp.iterator-debug-canaryproves the MSVC Debug runtime's iterator checks are live
(core-cpp#11). In every MSVC-driver Debug build (cl-debug,clangcl-debug) it indexes a
std::vectorout of range and passes only on the runtime's ownvector subscript out of range;
a build where_ITERATOR_DEBUG_LEVELfell below 2 reads the element instead, and fails.- A
windows (clang-tidy)CI job analyses the Windows sources (core-cpp#38, core-cpp#44). The
clang-tidyjob's preset is Unix-only, so everywindows/source and every_WIN32arm of a
shared header went unanalysed while it was green. The new job runs the pinned clang-tidy over the
clang-cl tree's compile database throughscripts/tidy-database.py, which refuses a result that
analysed fewerwindows/sources than git tracks, an analyser other than the pin, and a canary it
did not report. (CXX_CLANG_TIDYis not used there: over clang-cl it hands clang-tidy a command it
reads with exceptions disabled.) It is one ofci-ok's needs.
Changed
core::netandcore::tuiask a promise for its stop token through
core::async::HasStopToken(core-cpp#29), andctest -L hygienerefuses the hand-spelled
requires { awaiting.promise().stopToken(); }anywhere in the tree, so the concept is the one
place that states the contract. No behaviour changes: every site assigned the token to a
StopToken, which is what the concept requires it to convert to.- The hygiene scan's
namespace-directoryrule skips a leading forward-declaration block
(core-cpp#23). A header undersrc/core/<dir>/that opened with
namespace core::platform { class Wakeup; }was refused, because that was its first named
namespace, so it had to include the other module's header instead. A block whose body is only
class/struct/union/enumdeclarations ending in;defines nothing and is now skipped,
and the rule applies to the first namespace after it.core/tui/TerminalInput.hppforward-declares
core::platform::Wakeupaccordingly and no longer includescore/platform/Wakeup.hpp; a file
that usedWakeupthrough that include includes it itself (none of the consumers does).
Fixed
serve's refusal of a request it did not read to its end reaches the client (core-cpp#35).
A 413 or 400 was written over request bytes still unread, and the connection's bare close then
sent a reset rather than a FIN: the client read the refusal followed by a connection reset, and
on Windows could lose the refusal itself. The refusal now closes throughcloseLingering. A
request read in full and answered closes as before.NativeFileSystemreports a failure on a path the ANSI code page cannot spell (core-cpp#26).
Its error messages spelled the path withpath::string(), which on Windows narrows through the
code page: such a name was mangled, and MSVC's conversion throws there, so the error path itself
threw out ofreadFile,rename,listDirectoryand the rest. The messages now spell the path
in UTF-8, as the paths core-cpp hands back already are. POSIX was unaffected.testing::InMemoryFileSystemanswers as the native backend does in two more places
(core-cpp#27).isExecutableFile,permissionsandsetPermissionsfollow a symlink to its
target, so a dangling link is not executable and has no permissions to set, where the fake used
to judge the link by bits recorded on the link itself.createDirectoryrefuses a path that is
already there, directory or file, with "File exists", where it used to succeed. What the fake
still does not model is tabled indocs/modules/platform.md.- A Windows
SystemPipe's sockets are no longer inherited by child processes (core-cpp#28).
::socket()and::accept()hand back inheritable handles there, so a consumer that spawned a
process -- contour's and endo's shells -- handed the child the loop's wakeup channel, and a child
that kept it open could hold it alive after the parent closed its end. The sockets are made with
WSA_FLAG_NO_HANDLE_INHERITand the accepted one has its inheritance cleared, as POSIX already
setsFD_CLOEXEC. - Alt+Backspace reaches its key binding (core-cpp#21).
VtParserreadESC DEL-- how xterm,
VTE, iTerm and Alacritty send Alt+Backspace -- andESC BS(Alt+Ctrl+H) as a bare Escape followed
by a plain Backspace, so a modal took the Escape as cancel andDeleteBigWordBackwardcould fire
only under the Kitty keyboard protocol. Both now decode to oneKeyCode::Backspacewith
Modifier::Alt. No signature changes. - A character outside the BMP typed or pasted into a Windows console arrives as valid UTF-8
(core-cpp#20). The console delivers U+1F600 as two key events, one per surrogate, and each was
encoded on its own (CESU-8), whichVtParserthen decoded to two lone surrogates. The Windows
input now pairs surrogates across reads (detail::Utf16ToUtf8, which replaces the private
windows/Win32Utf.hpp), and an unpaired one becomes U+FFFD. In Win32 input mode, where the
console reports each surrogate as its ownCSI ... _key,VtParserpairs the two keys the same
way, across reads, and drops a half it cannot pair.VtParserdrops what no UTF-8
decoder may produce, on every platform: an encoded surrogate, an overlong encoding and a value
above U+10FFFF. No public signature changes. - The vendoring tool refuses a
DESTthat is a symbolic link (core-cpp#25). CMake'sEXISTS
andIS_DIRECTORYresolve through a link, so a link to an empty directory passed every guard, and
the replacement then renamed the link aside and put a real directory in its place: the consumer's
link was gone, its target untouched, and the sync reported success.MODE=syncnow refuses such a
DESTby name, before anything is read or written, and says which directory to name instead. - A
clang-tidybuild re-analyses what a changed.clang-tidyor a replaced analyser governs
(core-cpp#36). Neither was an input of any compile, so editing a rule, or installing another
clang-tidy at the same path, re-analysed nothing whose object was current:no work to domeant
"clean under the rules in force when each object was built". WithCORE_CPP_CLANG_TIDYon, every
analysed compile now depends on the analyser binary and on every.clang-tidyfrom its source's
directory up to the tree's root, andcore-cpp.tidy-inputs(registered only in a clang-tidy Ninja
build, and run by CI'sclang-tidyjob) refuses a build whose statements do not.
Full Changelog: v0.4.3...v0.5.0