pax_global_header00006660000000000000000000000064143535613170014522gustar00rootroot0000000000000052 comment=4426a73f266adf7dd000ba987a934eb9f367e48d posixsignalmanager-0.3/000077500000000000000000000000001435356131700152575ustar00rootroot00000000000000posixsignalmanager-0.3/COPYING000066400000000000000000000024721435356131700163170ustar00rootroot00000000000000Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. posixsignalmanager-0.3/PosixSignalManager.cpp000066400000000000000000000731171435356131700215270ustar00rootroot00000000000000// SPDX-License-Identifier: BSL-1.0 #include "PosixSignalManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NSIG #if defined(__linux__) || !defined(SIGRTMAX) || defined(__sun) #define NUM_SIGNALS NSIG #else #if (SIGRTMAX + 1) > NSIG #define NUM_SIGNALS (SIGRTMAX + 1) #else #define NUM_SIGNALS NSIG #endif #endif #else #error missing signal number macro #endif #ifdef __cpp_lib_atomic_is_always_lock_free #define STATIC_ASSERT_ALWAYS_LOCKFREE(type) static_assert (type::is_always_lock_free) #else #define STATIC_ASSERT_ALWAYS_LOCKFREE(type) /* not supported */ #endif STATIC_ASSERT_ALWAYS_LOCKFREE(std::atomic); STATIC_ASSERT_ALWAYS_LOCKFREE(std::atomic); // POSIX requires write(2) on a O_NONBLOCK pipe to be atomic with payloads smaller than PIPE_BUF, which we depend // on for siginfo_t. static_assert (sizeof(siginfo_t) < PIPE_BUF, "siginfo_t is bigger than limit for atomic pipe writes"); #define LIBNAME "PosixSignalManager: " inline namespace PosixSignalManager_v0 { class PosixSignalFlagsPrivate { friend class PosixSignalFlags; bool reraise = true; bool stopChain = false; }; namespace { PosixSignalManager *instance = nullptr; // all state must be lockfree accessable from async signal context. enum class NodeType { SyncHandler, SyncTerminationHandler, SyncCrashHandler, NotifyFd }; struct Node { int id; // mainline (locked) access only int signo; // mainline (locked) access only NodeType type; // mainline (locked) access only pid_t pidFilter; // only written to in init, readonly after that }; struct SyncHandlerNode : public Node { PosixSignalManager::SyncHandler *handler = nullptr; void *data = nullptr; // only written to in init, readonly after that std::atomic next; }; struct SyncTerminationHandlerNode : public Node { PosixSignalManager::SyncTerminationHandler *handler = nullptr; void *data = nullptr; // only written to in init, readonly after that std::atomic next; }; struct NotifyFdNode : public Node { int write_fd = -1; // only written to in init, readonly after that int read_fd = -1; // only written to in init, readonly after that std::atomic next; }; enum class InternalChainingMode : int { NeverChain, ChainAlways, ChainIfReraiseSet }; STATIC_ASSERT_ALWAYS_LOCKFREE(std::atomic); struct SignalState { std::atomic syncHandlers; std::atomic notifyFds; std::atomic chainingMode; bool handlerInstalled; // mainline (locked) access only }; std::atomic syncTerminationHandlers; std::atomic syncCrashHandlers; SignalState signalStates[NUM_SIGNALS] = { }; std::atomic asyncSignalHandlerRunning; bool signalHandlerInstalled[NUM_SIGNALS] = { false }; struct sigaction originalSignalActions[NUM_SIGNALS] = { }; // read by signal handler after reading SignalState::InternalChainingMode != Never // stored before writing SignalState::InternalChainingMode to != Never void PosixSignalManager_init() { // mainline (locked) access only asyncSignalHandlerRunning.store(0, std::memory_order_seq_cst); for (int i = 0; i < NUM_SIGNALS; i++) { signalStates[i].syncHandlers.store(nullptr, std::memory_order_seq_cst); signalStates[i].notifyFds.store(nullptr, std::memory_order_seq_cst); signalStates[i].chainingMode.store(InternalChainingMode::NeverChain, std::memory_order_seq_cst); signalStates[i].handlerInstalled = false; } syncTerminationHandlers.store(nullptr, std::memory_order_seq_cst); syncCrashHandlers.store(nullptr, std::memory_order_seq_cst); } void PosixSignalManager_classify_signo(int signo, bool *isTermination, bool *isCrash, bool *specialEffect) { *isTermination = false; *isCrash = false; *specialEffect = false; switch (signo) { case SIGALRM: case SIGHUP: case SIGINT: #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__APPLE__) && !defined(__NetBSD__) // ^^^ various bsds ignore sigio by default case SIGIO: #endif case SIGPIPE: case SIGPROF: #if defined(SIGPWR) && !defined(__NetBSD__) && !defined(__sun) case SIGPWR: #endif case SIGQUIT: #ifdef SIGSTKFLT case SIGSTKFLT: #endif case SIGTERM: case SIGUSR1: case SIGUSR2: case SIGVTALRM: case SIGXCPU: case SIGXFSZ: #ifdef SIGLOST case SIGLOST: #endif *isTermination = true; break; #if defined(SIGEMT) case SIGEMT: #endif case SIGBUS: case SIGILL: case SIGSEGV: case SIGABRT: case SIGFPE: case SIGSYS: case SIGTRAP: *isCrash = true; break; case SIGTSTP: case SIGTTIN: case SIGTTOU: *specialEffect = true; default: break; } #ifdef SIGRTMIN if (signo >= SIGRTMIN && signo <= SIGRTMAX) { *isTermination = true; } #endif } void PosixSignalManager_sigdie(const char *msg, int code) { // If any of the writes fail, we can't do much about it, so don't even bother checking the return. (void)!write(2, msg, strlen(msg)); if (code) { // open coded int to ascii code, because async signal safety requirements. unsigned int tmp; if (code < 0) { (void)!write(2, "-", 1); tmp = -(unsigned int)code; } else { tmp = (unsigned int)code; } int digitValue = 1; while (tmp / digitValue >= 10) { digitValue *= 10; } do { char ch = '0' + (tmp / digitValue); (void)!write(2, &ch, 1); tmp /= 10; digitValue /= 10; } while (digitValue > 0); } (void)!write(2, "\r\n", 2); signal(SIGABRT, SIG_DFL); abort(); } void PosixSignalManager_handler(int signo, siginfo_t *info, void *context) { if (signo >= NUM_SIGNALS) { // avoid buffer overlow in code below, should never happen: // signo < NUM_SIGNALS should always be true because we don't set this handler for anything higher return; } int savedErrno = errno; // At least on linux this assumes a modern libc that does not cache getpid(). pid_t currentPid = getpid(); bool isUser = info->si_code == SI_USER || info->si_code == SI_QUEUE #ifdef SI_FROMUSER || SI_FROMUSER(info) #endif #ifdef SI_TKILL || info->si_code == SI_TKILL #endif #ifdef SI_LWP || info->si_code == SI_LWP #endif ; bool isDynamic = (info->si_code < 0 && !isUser) || info->si_code == SI_TIMER #ifdef SI_MESGQ || info->si_code == SI_MESGQ #endif #ifdef SI_ASYNCIO || info->si_code == SI_ASYNCIO #endif #ifdef SI_SIGIO || info->si_code == SI_SIGIO #endif ; bool isTermination = false; bool isCrash = false; bool specialEffect = false; PosixSignalManager_classify_signo(signo, &isTermination, &isCrash, &specialEffect); asyncSignalHandlerRunning.fetch_add(1, std::memory_order_seq_cst); SignalState* signalState = &signalStates[signo]; // mainline may not delete nodes until asyncSignalHandlerRunning reaches 0 again PosixSignalFlagsPrivate cbPriv; PosixSignalFlags cb(&cbPriv); SyncHandlerNode* syncHandler = signalState->syncHandlers.load(std::memory_order_seq_cst); NotifyFdNode* notifyFd = signalState->notifyFds.load(std::memory_order_seq_cst); if (syncHandler) { while (syncHandler) { if (syncHandler->pidFilter == 0 || syncHandler->pidFilter == currentPid) { syncHandler->handler(syncHandler->data, cb, info, context); if (cb.isStopChainSet()) { break; } } syncHandler = syncHandler->next.load(std::memory_order_seq_cst); } } if (!cb.isStopChainSet()) { while (notifyFd) { if (notifyFd->pidFilter == 0 || notifyFd->pidFilter == currentPid) { // We depend on this write to be atomic. Posix requires writes smaller than PIPE_BUF to be // atomic and the size requirement is checked above in a static_assert. // If the pipe is full the signal is silently dropped. (void)!write(notifyFd->write_fd, info, sizeof(*info)); // error of write explicitly not handled. cb.clearReraise(); } notifyFd = notifyFd->next.load(std::memory_order_seq_cst); } } bool shouldChain = false; if (signalState->chainingMode != InternalChainingMode::NeverChain && originalSignalActions[signo].sa_handler != SIG_IGN && originalSignalActions[signo].sa_handler != SIG_DFL) { if (signalState->chainingMode == InternalChainingMode::ChainAlways || (signalState->chainingMode == InternalChainingMode::ChainIfReraiseSet && cb.isReraiseSet())) { shouldChain = true; // when chaining don't reraise signal. cb.clearReraise(); } } if (cb.isReraiseSet()) { if (isTermination) { SyncTerminationHandlerNode* thn = syncTerminationHandlers.load(std::memory_order_seq_cst); while (thn) { if (thn->pidFilter == 0 || thn->pidFilter == currentPid) { thn->handler(thn->data, info, context); } thn = thn->next.load(std::memory_order_seq_cst); } } else if (isCrash) { SyncTerminationHandlerNode* thn = syncCrashHandlers.load(std::memory_order_seq_cst); while (thn) { if (thn->pidFilter == 0 || thn->pidFilter == currentPid) { thn->handler(thn->data, info, context); } thn = thn->next.load(std::memory_order_seq_cst); } } struct sigaction newAction, prevAction; sigset_t unblock, prevBlocked; if ((signo == SIGSEGV || signo == SIGBUS || signo == SIGILL || signo == SIGFPE) && !isUser && !isDynamic #if defined(__OpenBSD__) || defined(__APPLE__) // It seems that OpenBSD does not reliably set si_code when using raise(2). // Same for MacOS. && false #endif ) { // Fatal, best reraise option is just return from handler with default signal disposition // NOTE this will not work on linux < 4.14 if the signal is not a real SIGSEGV, but an io event // masquerading as SIGSEGV etc. Just don't do that. newAction.sa_handler = SIG_DFL; newAction.sa_flags = 0; if (sigemptyset(&newAction.sa_mask) != 0) { PosixSignalManager_sigdie(LIBNAME "Error in signal handler. Can not create empty signal set in crash reraise: ", errno); } if (sigaction(signo, &newAction, &prevAction) != 0) { PosixSignalManager_sigdie(LIBNAME "Error in signal handler. Can not reset handler to default in crash reraise: ", errno); } } else if (isCrash || isTermination || specialEffect) { // trigger default signal handling. newAction.sa_handler = SIG_DFL; newAction.sa_flags = 0; if (sigaction(signo, &newAction, &prevAction) != 0) { PosixSignalManager_sigdie(LIBNAME "Error in signal handler. Can not reset handler to default in reraise: ", errno); } if (raise(signo) != 0) { PosixSignalManager_sigdie(LIBNAME "Error in signal handler. Can not raise signal in reraise: ", errno); } if (sigemptyset(&unblock) != 0) { PosixSignalManager_sigdie(LIBNAME "Error in signal handler. Can not create empty signal set in reraise: ", errno); } if (sigaddset(&unblock, signo) != 0) { PosixSignalManager_sigdie(LIBNAME "Error in signal handler. Can not add signal to signal set in reraise: ", errno); } if (sigprocmask(SIG_UNBLOCK, &unblock, &prevBlocked) != 0) { PosixSignalManager_sigdie(LIBNAME "Error in signal handler. Can not unblock signal in reraise: ", errno); } // signal triggers here after unblock // For signals like SIGTSTP this code is reachable if (sigprocmask(SIG_SETMASK, &prevBlocked, nullptr) != 0) { PosixSignalManager_sigdie(LIBNAME "Error in signal handler. Can not restore signal mask in reraise: ", errno); } if (sigaction(signo, &prevAction, nullptr) != 0) { PosixSignalManager_sigdie(LIBNAME "Error in signal handler. Can not restore handler in reraise: ", errno); } } } asyncSignalHandlerRunning.fetch_sub(1, std::memory_order_seq_cst); errno = savedErrno; // do this as the very last action if (shouldChain) { if (originalSignalActions[signo].sa_flags & SA_SIGINFO) { originalSignalActions[signo].sa_sigaction(signo, info, context); } else { originalSignalActions[signo].sa_handler(signo); } } } void PosixSignalManager_install_handler(int signo) { // mainline (locked) access only if (!signalHandlerInstalled[signo]) { sigaction(signo, nullptr, &originalSignalActions[signo]); struct sigaction sa; memset(&sa, 0, sizeof(sa)); sigemptyset(&sa.sa_mask); sa.sa_flags = SA_SIGINFO | SA_RESTART; sa.sa_sigaction = &PosixSignalManager_handler; sigaction(signo, &sa, nullptr); // TODO error handling signalHandlerInstalled[signo] = true; } } } PosixSignalFlags::PosixSignalFlags(PosixSignalFlagsPrivate *impl) : _impl(impl) { } PosixSignalFlags::~PosixSignalFlags() { } void PosixSignalFlags::reraise() { _impl->reraise = true; } void PosixSignalFlags::clearReraise() { _impl->reraise = false; } bool PosixSignalFlags::isReraiseSet() { return _impl->reraise; } void PosixSignalFlags::stopChain() { _impl->stopChain = true; } bool PosixSignalFlags::isStopChainSet() { return _impl->stopChain; } class PosixSignalOptionsPrivate { public: enum { ForkDefault, ForkFollow, ForkNoFollow } _forkFilter = ForkDefault; }; class PosixSignalNotifierPrivate { public: PosixSignalNotifierPrivate(int signo) : signo(signo) { } int signo = 0; int registrationId = -1; }; PosixSignalNotifier::PosixSignalNotifier(int signo, QObject *parent) : PosixSignalNotifier(signo, PosixSignalOptions(), parent) { } PosixSignalNotifier::PosixSignalNotifier(int signo, const PosixSignalOptions &options, QObject *parent) : QObject(parent), impl(new PosixSignalNotifierPrivate(signo)) { impl->registrationId = PosixSignalManager::instance()->addSignalNotifier(signo, options, this); } PosixSignalNotifier::~PosixSignalNotifier() { PosixSignalManager::instance()->removeHandler(impl->registrationId); } void PosixSignalNotifier::_readyRead(int socket) { QSharedPointer info = QSharedPointer::create(); // We always write a full siginfo_t and nothing else should be reading from this fd. So we should always // be able to get a full siginfo_t out. int toRead = sizeof(siginfo_t); int filled = 0; do { int len = read(socket, ((char*)info.data()) + filled, toRead); if (len == 0) { qDebug(LIBNAME "Got end of stream while reading from self pipe"); break; } if (len < 0) { if (errno == EINTR) { continue; } else if (errno == EAGAIN || errno == EWOULDBLOCK) { // spurious wakeup of some sort? break; } else { qDebug(LIBNAME "Got error while reading from self pipe"); break; } } filled += len; toRead -= len; } while (toRead > 0); if (filled == sizeof(siginfo_t)) { activated(impl->signo, info); } } class PosixSignalManagerPrivate { public: static QMutex mutex; QMap idMap; int nextId = 1; int generateId() { // assume less than 2**31 active registrations while (true) { int id = nextId; ++nextId; if (nextId == std::numeric_limits::max()) { nextId = 1; } if (!idMap.contains(id)) { return id; } } } }; QMutex PosixSignalManagerPrivate::mutex; PosixSignalManager::PosixSignalManager() : impl(new PosixSignalManagerPrivate()) { } PosixSignalManager *PosixSignalManager::create() { QMutexLocker locker(&PosixSignalManagerPrivate::mutex); if (::instance) { qDebug() << "PosixSignalManager::create: Already created"; throw std::runtime_error("PosixSignalManager::create: Already created"); } PosixSignalManager_init(); ::instance = new PosixSignalManager(); return ::instance; } PosixSignalManager *PosixSignalManager::instance() { QMutexLocker locker(&PosixSignalManagerPrivate::mutex); if (!::instance) { qDebug() << "PosixSignalManager::instance: Called before PosixSignalManager::create"; throw std::runtime_error("PosixSignalManager::instance: Called before PosixSignalManager::create"); } return ::instance; } bool PosixSignalManager::isCreated() { QMutexLocker locker(&PosixSignalManagerPrivate::mutex); return ::instance != nullptr; } namespace { template void addToRoot(T* newNode, std::atomic &root) { // mainline (locked) access only T* node = root.load(std::memory_order_seq_cst); if (!node) { root.store(newNode, std::memory_order_seq_cst); } else { while (true) { T* nextNode = node->next.load(std::memory_order_seq_cst); if (!nextNode) break; node = nextNode; } node->next.store(newNode, std::memory_order_seq_cst); } } void installIfDefault(int signo) { // mainline (locked) access only if (!signalStates[signo].handlerInstalled) { struct sigaction sa; sigaction(signo, nullptr, &sa); if (sa.sa_handler == SIG_DFL) { PosixSignalManager_install_handler(signo); } } } void installIfNeeded(int signo) { // mainline (locked) access only if (!signalStates[signo].handlerInstalled) { PosixSignalManager_install_handler(signo); } } } int PosixSignalManager::addSyncTerminationHandler(PosixSignalManager::SyncTerminationHandler handler, void *data, const PosixSignalOptions &options) { QMutexLocker locker(&PosixSignalManagerPrivate::mutex); PosixSignalManagerPrivate *const d = impl.data(); SyncTerminationHandlerNode* newNode = new SyncTerminationHandlerNode(); // lifetime is complicated. FIXME document more? newNode->handler = handler; newNode->data = data; newNode->signo = 0; newNode->type = NodeType::SyncTerminationHandler; newNode->id = d->generateId(); newNode->pidFilter = (options._impl->_forkFilter == PosixSignalOptionsPrivate::ForkNoFollow) ? getpid() : 0; d->idMap[newNode->id] = newNode; addToRoot(newNode, syncTerminationHandlers); installIfDefault(SIGALRM); installIfDefault(SIGHUP); installIfDefault(SIGINT); #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__APPLE__) && !defined(__NetBSD__) // ^^^ various bsds ignore sigio by default installIfDefault(SIGIO); #endif installIfDefault(SIGPIPE); installIfDefault(SIGPROF); #if defined(SIGPWR) && !defined(__NetBSD__) installIfDefault(SIGPWR); #endif installIfDefault(SIGQUIT); #ifdef SIGSTKFLT installIfDefault(SIGSTKFLT); #endif installIfDefault(SIGTERM); installIfDefault(SIGUSR1); installIfDefault(SIGUSR2); installIfDefault(SIGVTALRM); installIfDefault(SIGXCPU); installIfDefault(SIGXFSZ); #ifdef SIGRTMAX for (int i = SIGRTMIN; i < SIGRTMAX; i++) { installIfDefault(i); } #endif return newNode->id; } int PosixSignalManager::addSyncCrashHandler(PosixSignalManager::SyncTerminationHandler handler, void *data, const PosixSignalOptions &options) { QMutexLocker locker(&PosixSignalManagerPrivate::mutex); PosixSignalManagerPrivate *const d = impl.data(); SyncTerminationHandlerNode* newNode = new SyncTerminationHandlerNode(); // lifetime is complicated. FIXME document more? newNode->handler = handler; newNode->data = data; newNode->signo = 0; newNode->type = NodeType::SyncCrashHandler; newNode->id = d->generateId(); newNode->pidFilter = (options._impl->_forkFilter == PosixSignalOptionsPrivate::ForkNoFollow) ? getpid() : 0; d->idMap[newNode->id] = newNode; addToRoot(newNode, syncCrashHandlers); #if defined(SIGEMT) installIfDefault(SIGEMT); #endif installIfDefault(SIGBUS); installIfDefault(SIGILL); installIfDefault(SIGSEGV); installIfDefault(SIGABRT); installIfDefault(SIGFPE); installIfDefault(SIGSYS); installIfDefault(SIGTRAP); return newNode->id; } int PosixSignalManager::addSyncSignalHandler(int signo, PosixSignalManager::SyncHandler handler, void *data, const PosixSignalOptions &options) { QMutexLocker locker(&PosixSignalManagerPrivate::mutex); PosixSignalManagerPrivate *const d = impl.data(); if (signo >= NUM_SIGNALS || signo < 1) { // error return -1; } SyncHandlerNode* newNode = new SyncHandlerNode(); // lifetime is complicated. FIXME document more? newNode->handler = handler; newNode->data = data; newNode->type = NodeType::SyncHandler; newNode->signo = signo; newNode->id = d->generateId(); newNode->pidFilter = (options._impl->_forkFilter == PosixSignalOptionsPrivate::ForkNoFollow) ? getpid() : 0; d->idMap[newNode->id] = newNode; addToRoot(newNode, signalStates[signo].syncHandlers); installIfNeeded(signo); return newNode->id; } namespace { template void removeAndFreeHandler(Node *n, int id, std::atomic &root) { // mainline (locked) access only T *nodeToRemove = static_cast(n); T *node = root.load(std::memory_order_seq_cst); if (node == nodeToRemove) { root.store(nodeToRemove->next.load(std::memory_order_seq_cst), std::memory_order_seq_cst); } else { while (node) { T* nextNode = node->next.load(std::memory_order_seq_cst); if (nextNode == nodeToRemove) { node->next.store(nodeToRemove->next.load(std::memory_order_seq_cst), std::memory_order_seq_cst); break; } node = nextNode; } if (!node) { qDebug() << "PosixSignalManager::removeHandler: Id " << id << " not properly linked"; throw std::runtime_error("PosixSignalManager::removeHandler: Id not properly linked"); } } while (asyncSignalHandlerRunning.load(std::memory_order_seq_cst) != 0) { // spin wait until no signal handler is running } delete nodeToRemove; } } void PosixSignalManager::removeHandler(int id) { QMutexLocker locker(&PosixSignalManagerPrivate::mutex); PosixSignalManagerPrivate *const d = impl.data(); if (!d->idMap.contains(id)) { qDebug() << "PosixSignalManager::removeHandler: Id " << id << " does not exist"; throw std::runtime_error("PosixSignalManager::removeHandler: Id does not exist"); } Node *n = d->idMap[id]; d->idMap.remove(id); int signo = n->signo; if (n->type == NodeType::SyncHandler) { removeAndFreeHandler(n, id, signalStates[signo].syncHandlers); } else if (n->type == NodeType::SyncTerminationHandler) { removeAndFreeHandler(n, id, syncTerminationHandlers); } else if (n->type == NodeType::SyncCrashHandler) { removeAndFreeHandler(n, id, syncCrashHandlers); } else if (n->type == NodeType::NotifyFd) { int pipe_write = static_cast(n)->write_fd; int pipe_read = static_cast(n)->read_fd; removeAndFreeHandler(n, id, signalStates[signo].notifyFds); close(pipe_write); close(pipe_read); } } bool PosixSignalManager::setupSignalChaining(int signo, PosixSignalManager::ChainingMode mode) { QMutexLocker locker(&PosixSignalManagerPrivate::mutex); if (signo >= NUM_SIGNALS || signo < 1) { // error return false; } if (mode != ChainingMode::ChainAlways && mode != ChainingMode::ChainIfReraiseSet) { return false; } installIfNeeded(signo); if (mode == ChainingMode::ChainAlways) { signalStates[signo].chainingMode.store(InternalChainingMode::ChainAlways, std::memory_order_seq_cst); } if (mode == ChainingMode::ChainIfReraiseSet) { signalStates[signo].chainingMode.store(InternalChainingMode::ChainIfReraiseSet, std::memory_order_seq_cst); } return true; } void PosixSignalManager::barrier() { while (asyncSignalHandlerRunning.load(std::memory_order_seq_cst) != 0) { // spin wait until no signal handler is running } } int PosixSignalManager::classifySignal(int signo) { bool isTermination = false; bool isCrash = false; bool specialEffect = false; PosixSignalManager_classify_signo(signo, &isTermination, &isCrash, &specialEffect); return ((isTermination || isCrash) ? 1 : 0) | (isCrash ? 2 : 0); } int PosixSignalManager::addSignalNotifier(int signo, const PosixSignalOptions &options, PosixSignalNotifier *notifier) { QMutexLocker locker(&PosixSignalManagerPrivate::mutex); PosixSignalManagerPrivate *const d = impl.data(); if (signo >= NUM_SIGNALS || signo < 1) { // error return -1; } bool isTermination = false; bool isCrash = false; bool specialEffect = false; PosixSignalManager_classify_signo(signo, &isTermination, &isCrash, &specialEffect); if (isCrash) { // error return -1; } int pipes[2]; int r; #ifndef NO_PIPE2 std::initializer_list flags = { #ifdef O_NOSIGPIPE #ifdef __linux__ O_CLOEXEC | O_NONBLOCK | O_NOSIGPIPE | O_DIRECT, #else O_CLOEXEC | O_NONBLOCK | O_NOSIGPIPE, #endif #endif #ifdef __linux__ O_CLOEXEC | O_NONBLOCK | O_DIRECT, #endif O_CLOEXEC | O_NONBLOCK }; for (int f : flags) { r = ::pipe2(pipes, f); if (r == 0 || errno != EINVAL) break; } #else r = ::pipe(pipes); #endif if (r != 0) { qDebug() << "PosixSignalNotifier: Can't create internal pipe"; throw std::runtime_error("PosixSignalNotifier: Can't create internal pipe"); } #ifdef NO_PIPE2 fcntl(pipes[1], F_SETFD, FD_CLOEXEC); fcntl(pipes[0], F_SETFD, FD_CLOEXEC); #endif NotifyFdNode* newNode = new NotifyFdNode(); // lifetime is complicated. FIXME document more? newNode->write_fd = pipes[1]; newNode->read_fd = pipes[0]; newNode->type = NodeType::NotifyFd; newNode->signo = signo; newNode->id = d->generateId(); newNode->pidFilter = (options._impl->_forkFilter == PosixSignalOptionsPrivate::ForkFollow) ? 0 : getpid(); d->idMap[newNode->id] = newNode; addToRoot(newNode, signalStates[signo].notifyFds); installIfNeeded(signo); QSocketNotifier* qsn = new QSocketNotifier(pipes[0], QSocketNotifier::Read, notifier); QObject::connect(qsn, &QSocketNotifier::activated, notifier, &PosixSignalNotifier::_readyRead); return newNode->id; } PosixSignalOptions::PosixSignalOptions() : _impl(std::make_unique()) { } PosixSignalOptions::PosixSignalOptions(const PosixSignalOptions &other) : _impl(std::make_unique(*other._impl)) { } PosixSignalOptions::~PosixSignalOptions() { } PosixSignalOptions &PosixSignalOptions::operator=(const PosixSignalOptions &other) { *this->_impl = *other._impl; return *this; } PosixSignalOptions PosixSignalOptions::dontFollowForks() { PosixSignalOptions ret = *this; ret._impl->_forkFilter = PosixSignalOptionsPrivate::ForkNoFollow; return ret; } PosixSignalOptions PosixSignalOptions::followForks() { PosixSignalOptions ret = *this; ret._impl->_forkFilter = PosixSignalOptionsPrivate::ForkFollow; return ret; } } posixsignalmanager-0.3/PosixSignalManager.h000066400000000000000000000056711435356131700211740ustar00rootroot00000000000000// SPDX-License-Identifier: BSL-1.0 #ifndef POSIXSIGNALMANAGER_INCLUDED #define POSIXSIGNALMANAGER_INCLUDED #include #include #include inline namespace PosixSignalManager_v0 { class PosixSignalFlagsPrivate; class PosixSignalFlags { PosixSignalFlags(const PosixSignalFlags&) = delete; PosixSignalFlags &operator=(const PosixSignalFlags&) = delete; public: void reraise(); void clearReraise(); bool isReraiseSet(); void stopChain(); bool isStopChainSet(); public: // internal interface PosixSignalFlags(PosixSignalFlagsPrivate *impl); ~PosixSignalFlags(); private: PosixSignalFlagsPrivate* _impl; }; class PosixSignalOptionsPrivate; class PosixSignalOptions { friend class PosixSignalManager; public: PosixSignalOptions(); PosixSignalOptions(const PosixSignalOptions &other); ~PosixSignalOptions(); PosixSignalOptions &operator=(const PosixSignalOptions &other); public: PosixSignalOptions dontFollowForks(); PosixSignalOptions followForks(); private: std::unique_ptr _impl; }; class PosixSignalNotifierPrivate; class PosixSignalNotifier : public QObject { Q_OBJECT public: PosixSignalNotifier(int signo, QObject *parent = nullptr); PosixSignalNotifier(int signo, const PosixSignalOptions &options, QObject *parent = nullptr); ~PosixSignalNotifier() override; Q_SIGNALS: void activated(int signo, QSharedPointer info); private Q_SLOTS: void _readyRead(int socket); private: QScopedPointer impl; friend class PosixSignalManager; }; class PosixSignalManagerPrivate; class PosixSignalManager { public: using SyncHandler = void(void *data, PosixSignalFlags &flags, const siginfo_t *info, void *context); using SyncTerminationHandler = void(void *data, const siginfo_t *info, void *context); enum class ChainingMode : int { ChainAlways, ChainIfReraiseSet }; public: static PosixSignalManager *create(); static PosixSignalManager *instance(); static bool isCreated(); public: // SIGINT, SIGTERM etc int addSyncTerminationHandler(SyncTerminationHandler handler, void *data, const PosixSignalOptions &options = PosixSignalOptions()); // SIGSEGV etc int addSyncCrashHandler(SyncTerminationHandler handler, void *data, const PosixSignalOptions &options = PosixSignalOptions()); int addSyncSignalHandler(int signo, SyncHandler handler, void *data, const PosixSignalOptions &options = PosixSignalOptions()); void removeHandler(int id); bool setupSignalChaining(int signo, ChainingMode mode); void barrier(); public: static int classifySignal(int signo); public: // internal interface int addSignalNotifier(int signo, const PosixSignalOptions &options, PosixSignalNotifier* notifier); private: PosixSignalManager(); QScopedPointer impl; }; } #endif // POSIXSIGNALMANAGER_INCLUDED posixsignalmanager-0.3/README.md000066400000000000000000000244071435356131700165450ustar00rootroot00000000000000

Posix Signal Manager

Library safe, synchronous and asynchronous handling of posix signals for Qt applications and libraries. ## Why? The interface of libc for posix signals is not library safe, as each signal can only have one handler. This library offers an interface where multiple parts of an application can share access to signals. The easiest way is to use `PosixSignalNotifier` to convert posix signals to a Qt signal. The Qt signal is delivered via the Qt event loop and thus it is safe to use all the usual apis available to the application. It is also possible to register handlers for crashes (SIGSEGV etc) or signal based termination (SIGINT, SIGTERM, etc) using `PosixSignalManager::addSyncCrashHandler` and `PosixSignalManager::addSyncTerminationHandler` to add emergency cleanup. These handlers have to be [async signal safe](https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03), which is a quite restrictive environment. If waiting for a event loop iteration is not acceptable or the signal is of a type that can only be handled directly from a posix signal handler there is also the possiblity to use `PosixSignalManager::addSyncSignalHandler` to register handlers that run directly in the async signal context. Again these handlers have to be [async signal safe](https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03), which is a quite restrictive environment. Synchronous handlers can stop processing the signal or mark it to be reraised. ## Alternatives Often using signals is not the best way to achieve robust code. If possible consider alternatives to signals. * process management: [pidfd(linux)](https://man7.org/linux/man-pages/man2/pidfd_open.2.html), [pdfork(freebsd)](https://www.freebsd.org/cgi/man.cgi?query=pdfork&sektion=2) * timers: [QTimer](https://doc.qt.io/qt-5/qtimer.html) [timerfd](https://man7.org/linux/man-pages/man2/timerfd_create.2.html) * interprocess communication: [dbus](https://doc.qt.io/qt-5/qtdbus-index.html) see also [dbus @ freedesktop](https://www.freedesktop.org/wiki/Software/dbus/) ## Building / Installing $ meson setup -Dprefix=$HOME/opt/signalmanager/ _build $ ninja -C _build $ ninja -C _build install ## Examples ### Reload configuration on SIGHUP This example handles SIGHUP using a Qt signal on a future event loop iteration. ``` cpp if (!PosixSignalManager::isCreated()) PosixSignalManager::create(); auto* notifier = new PosixSignalNotifier(SIGHUP, parent); QObject::connect(notifier, &PosixSignalNotifier::activated, parent, &SomeClass::reloadConfig); ``` ### Quit gracefully on SIGTERM This example handles SIGTERM by terminating the event loop on a future event loop iteration. ``` cpp if (!PosixSignalManager::isCreated()) PosixSignalManager::create(); auto* notifier = new PosixSignalNotifier(SIGTERM, parent); QObject::connect(notifier, &PosixSignalNotifier::activated, QCoreApplication::instance(), [](auto signal, auto info) { QCoreApplication::instance()->quit(); }); ``` If the program is prone to get stuck without servicing the event loop and a further SIGTERM should be usable to do an emergency stop it is possible to combine the Qt signal based clean shutdown with an emergency shutdown from the signal handler ``` cpp if (!PosixSignalManager::isCreated()) PosixSignalManager::create(); auto* notifier = new PosixSignalNotifier(SIGTERM, parent); QObject::connect(notifier, &PosixSignalNotifier::activated, QCoreApplication::instance(), [](auto signal, auto info) { QCoreApplication::instance()->quit(); }); auto* manager = PosixSignalManager::instance(); manager->addSyncSignalHandler(SIGTERM, [](void *data, PosixSignalFlags &flags, const siginfo_t *info, void *context) { static std::atomic count = 0; if (count == 0) { flags.clearReraise(); } else { // emergency shutdown here // maybe remove a pid file or some other externally visible cleanup // request posix signal manager to terminate application as if no signal handler had been set flags.reraise(); // or as alternative // _exit(1); } ++count; }, nullptr); ``` ## Documentation Before most functionality of this library can be used, its main singleton must be created. Applications may only create this singleton once. If a library wants to detect if PosixSignalManager is already in use it can detect this by calling `PosixSignalManager::isCreated()`. If usage of PosixSignalManager is optional the library can then either use PosixSignalManager or can fall back to using sigaction. Otherwise all users should ensure to call `PosixSignalManager::create()` if the singleton was not yet created and proceed with usage. The instance of the singleton can be retrieved using `PosixSignalManager::instance`. After the singleton is created the main interfaces of PosixSignalManager are `PosixSignalNotifier` for message loop based signal processing and `PosixSignalManager` for low level synchronous signal handling. ### PosixSignalNotifier `PosixSignalNotifier` is the easiest way to handle the usual signals used for communication. Just create an instance and pass the signal number to catch as the first constructor argument and connect your handler to the `activated` signal of the instance. The `activated` signal passes the number of the caught signal as the first parameter and the full signal details as a `siginfo_t` structure in the second parameter. Signals handled with this class are processed in the event loop of the thread that called its constructor as soon as that event loop reacts the newly available data in PosixSignalNotifier's internal pipe. If the instance of PosixSignalNotifier is deleted the signal handling reverts back. Although the low level event handler is not removed, PosixSignalManager will emulate the signals default action when no PosixSignalNotifier instances or synchronous handlers remain. This class is not suitable to handle crash signals such as "Segmentation Fault"(SIGSEGV), "Bus Error"(SIGBUS), "Illegal Instruction"(SIGILL), "Floating Point Error"(SIGFPE) and similar signals. ### PosixSignalManager If handling the signal via PosixSignalNotifier is not enough PosixSignalManager offers an interface to hook into the signal's low level handler. All handler functions used with PosixSignalManager must be [async signal safe](https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03) ([linux](https://man7.org/linux/man-pages/man7/signal-safety.7.html)). That means most functions and methods one would usually use are not permitted to be called in signal handling functions. This includes all of Qt, most of the c++ standard library(always lookfree std::atomic should be ok) and even most of the standard c library. Also calling (sig)longjump from synchronous handlers is not supported in this library. All handlers are executed in the order of their registration. One common use case is to register cleanup handlers using `PosixSignalManager::addSyncCrashHandler` and `PosixSignalManager::addSyncTerminationHandler` to do last moment cleanup before a process dies due to an otherwise unhandled signal. The handlers registered using these methods always run after all other handlers and only if the signal is not marked as handled or handled using `PosixSignalNotifier`. The crash handlers are called on signals normally associated with process crashes or failed assertations (like SIGSEGV, SIGBUS, SIGABRT, etc). Crash handlers are also suitable to print or log crash information. The termination handlers are called on signals that are fatal that are not crash signals. Termination handler can not stop or modify signal propagation(except calling abort, _exit or similar). For synchronous handling of signals `PosixSignalManager::addSyncSignalHandler` can be used. This method registers a handler for a specific signal. The signal handler can influence the further processing of the signal by calling methods on the `PosixSignalFlags` object passed as its second parameter. To prevent the default action of the signal (which is often process termination) the handler should call `PosixSignalFlags::clearReraise` if it could handle the signal. Most handlers for communication signals should always call this method, on the other hand handlers that can only handle a subset of possible signal causes (like SIGSEGV/SIGBUS handlers for specific data or code segments) should call clearReraise only if they could handle or fix the cause of the signal. The existance of an instance of `PosixSignalNotifier` implicitly disables reraise of a signal. In addition handlers can suppress calling of later registered handlers for the same signal by calling `PosixSignalFlags::stopChain`. All handler registrations return an `int` registration id that can be used with `PosixSignalManager::removeHandler` to remove the handler when no longer needed. Although the low level event handler is not removed PosixSignalManager will emulate the signals default action when no PosixSignalNotifier instances or synchronous handlers remain. Variables used from the signal or termination handlers need to be threated with special care as these can be accessed from the handlers any time while they are manipulated. Even locally blocking the signal will not prevent this in multithreaded processes. Thus it is safest to always use lock free algorithms that atomically replace old state with new state. To help with freeing previously used memory after swaping to a new state, PosixSignalManager offers `PosixSignalManager::barrier` which guarantees that every handler that was running before the call started has terminated after the call returns. ## Compatiblity and Porting The main library needs little porting to not yet supported posix-like operating systems. The main concern is updating the conditions for default fatal signals and possibly updating what method to use to reraise crash signals. The tests cover many edge cases and are more work to port. This library currently is known to work and pass its tests on: Linux (>= 4.14, various distros, glibc and musl), FreeBSD, OpenBSD, NetBSD, Illumos and Apple. ## License PosixSignalManager is licensed under the [Boost Software License 1.0](COPYING) posixsignalmanager-0.3/meson.build000066400000000000000000000050511435356131700174220ustar00rootroot00000000000000# SPDX-License-Identifier: BSL-1.0 project('posixsignalmanager', ['cpp'], default_options : ['cpp_std=c++14'], version: '0.3') macros = [] cxx = meson.get_compiler('cpp') clock_gettime_test_code = ''' #include struct timespec t; int main (int argc, char ** argv) { return clock_gettime(CLOCK_REALTIME, &t); }''' librt = [] if cxx.links(clock_gettime_test_code, args : '-lrt', name : 'clock_gettime in librt') librt = cxx.find_library('rt') endif if not cxx.has_function('pipe2') macros += ['-DNO_PIPE2'] endif qt5 = import('qt5') qt5_dep = dependency('qt5', modules: 'Core') main_vscript = 'posixsignalmanager.symver' if host_machine.system() == 'linux' # for now, only do this on linux, expand supported platforms as needed main_ld_vscript = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), main_vscript) else main_ld_vscript = [] endif posixsignalmanager_lib = both_libraries('posixsignalmanager', 'PosixSignalManager.cpp', qt5.preprocess(moc_headers: 'PosixSignalManager.h'), dependencies : [qt5_dep, librt], cpp_args: macros, link_args : main_ld_vscript, link_depends : main_vscript, soversion: '0a', darwin_versions: ['1', '1'], install: true) posixsignalmanager_dep = declare_dependency(link_with: posixsignalmanager_lib, include_directories: include_directories('.'), dependencies: [qt5_dep, librt]) install_headers('PosixSignalManager.h') import('pkgconfig').generate( posixsignalmanager_lib, description: 'management of posix signals for environments using the qt event loop', filebase: 'PosixSignalManager', name: 'PosixSignalManager', subdirs: ['.'] ) if not meson.is_subproject() catch2_dep = dependency('catch2', required : get_option('system-catch2')) if not catch2_dep.found() catch2_dep = declare_dependency(compile_args: ['-DBUNDLED_CATCH2']) endif testlib = static_library('testlib', 'tests/catch_main.cpp', dependencies: [catch2_dep]) testdep_socket = [] if host_machine.system() == 'sunos' testdep_socket = cxx.find_library('socket') endif test('tests', executable('tests', 'tests/tests.cpp', link_with: [testlib], dependencies: [posixsignalmanager_dep, qt5_dep, librt, catch2_dep, testdep_socket])) endif posixsignalmanager-0.3/meson_options.txt000066400000000000000000000000761435356131700207170ustar00rootroot00000000000000option('system-catch2', type : 'feature', value : 'disabled') posixsignalmanager-0.3/posixsignalmanager.symver000066400000000000000000000055651435356131700224340ustar00rootroot00000000000000POSIXSIGNALMANAGER_0.3 { global: extern "C++" { "PosixSignalManager_v0::PosixSignalFlags::clearReraise()"; "PosixSignalManager_v0::PosixSignalFlags::isReraiseSet()"; "PosixSignalManager_v0::PosixSignalFlags::isStopChainSet()"; "PosixSignalManager_v0::PosixSignalFlags::reraise()"; "PosixSignalManager_v0::PosixSignalFlags::stopChain()"; "PosixSignalManager_v0::PosixSignalFlags::~PosixSignalFlags()"; "PosixSignalManager_v0::PosixSignalManager::addSyncCrashHandler(void (*)(void*, siginfo_t const*, void*), void*, PosixSignalManager_v0::PosixSignalOptions const&)"; "PosixSignalManager_v0::PosixSignalManager::addSyncSignalHandler(int, void (*)(void*, PosixSignalManager_v0::PosixSignalFlags&, siginfo_t const*, void*), void*, PosixSignalManager_v0::PosixSignalOptions const&)"; "PosixSignalManager_v0::PosixSignalManager::addSyncTerminationHandler(void (*)(void*, siginfo_t const*, void*), void*, PosixSignalManager_v0::PosixSignalOptions const&)"; "PosixSignalManager_v0::PosixSignalManager::barrier()"; "PosixSignalManager_v0::PosixSignalManager::classifySignal(int)"; "PosixSignalManager_v0::PosixSignalManager::create()"; "PosixSignalManager_v0::PosixSignalManager::instance()"; "PosixSignalManager_v0::PosixSignalManager::isCreated()"; "PosixSignalManager_v0::PosixSignalManager::removeHandler(int)"; "PosixSignalManager_v0::PosixSignalManager::setupSignalChaining(int, PosixSignalManager_v0::PosixSignalManager::ChainingMode)"; "PosixSignalManager_v0::PosixSignalNotifier::PosixSignalNotifier(int, PosixSignalManager_v0::PosixSignalOptions const&, QObject*)"; "PosixSignalManager_v0::PosixSignalNotifier::PosixSignalNotifier(int, QObject*)"; "PosixSignalManager_v0::PosixSignalNotifier::activated(int, QSharedPointer)"; "PosixSignalManager_v0::PosixSignalNotifier::metaObject() const"; "PosixSignalManager_v0::PosixSignalNotifier::staticMetaObject"; "PosixSignalManager_v0::PosixSignalNotifier::~PosixSignalNotifier()"; "typeinfo for PosixSignalManager_v0::PosixSignalNotifier"; "typeinfo name for PosixSignalManager_v0::PosixSignalNotifier"; "vtable for PosixSignalManager_v0::PosixSignalNotifier"; "PosixSignalManager_v0::PosixSignalOptions::PosixSignalOptions()"; "PosixSignalManager_v0::PosixSignalOptions::PosixSignalOptions(PosixSignalManager_v0::PosixSignalOptions const&)"; "PosixSignalManager_v0::PosixSignalOptions::dontFollowForks()"; "PosixSignalManager_v0::PosixSignalOptions::followForks()"; "PosixSignalManager_v0::PosixSignalOptions::operator=(PosixSignalManager_v0::PosixSignalOptions const&)"; "PosixSignalManager_v0::PosixSignalOptions::~PosixSignalOptions()"; }; local: extern "C++" { PosixSignalManager_v0::* }; }; posixsignalmanager-0.3/tests/000077500000000000000000000000001435356131700164215ustar00rootroot00000000000000posixsignalmanager-0.3/tests/catch.hpp000066400000000000000000024047431435356131700202320ustar00rootroot00000000000000/* * Catch v2.13.7 * Generated: 2021-07-28 20:29:27.753164 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2021 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED // start catch.hpp #define CATCH_VERSION_MAJOR 2 #define CATCH_VERSION_MINOR 13 #define CATCH_VERSION_PATCH 7 #ifdef __clang__ # pragma clang system_header #elif defined __GNUC__ # pragma GCC system_header #endif // start catch_suppress_warnings.h #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro # pragma warning(push) # pragma warning(disable: 161 1682) # else // __ICC # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" # pragma clang diagnostic ignored "-Wswitch-enum" # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif #elif defined __GNUC__ // Because REQUIREs trigger GCC's -Wparentheses, and because still // supported version of g++ have only buggy support for _Pragmas, // Wparentheses have to be suppressed globally. # pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wunused-variable" # pragma GCC diagnostic ignored "-Wpadded" #endif // end catch_suppress_warnings.h #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) # define CATCH_IMPL # define CATCH_CONFIG_ALL_PARTS #endif // In the impl file, we want to have access to all parts of the headers // Can also be used to sanely support PCHs #if defined(CATCH_CONFIG_ALL_PARTS) # define CATCH_CONFIG_EXTERNAL_INTERFACES # if defined(CATCH_CONFIG_DISABLE_MATCHERS) # undef CATCH_CONFIG_DISABLE_MATCHERS # endif # if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) # define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER # endif #endif #if !defined(CATCH_CONFIG_IMPL_ONLY) // start catch_platform.h // See e.g.: // https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html #ifdef __APPLE__ # include # if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) # define CATCH_PLATFORM_MAC # elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) # define CATCH_PLATFORM_IPHONE # endif #elif defined(linux) || defined(__linux) || defined(__linux__) # define CATCH_PLATFORM_LINUX #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) # define CATCH_PLATFORM_WINDOWS #endif // end catch_platform.h #ifdef CATCH_IMPL # ifndef CLARA_CONFIG_MAIN # define CLARA_CONFIG_MAIN_NOT_DEFINED # define CLARA_CONFIG_MAIN # endif #endif // start catch_user_interfaces.h namespace Catch { unsigned int rngSeed(); } // end catch_user_interfaces.h // start catch_tag_alias_autoregistrar.h // start catch_common.h // start catch_compiler_capabilities.h // Detect a number of compiler features - by compiler // The following features are defined: // // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? // CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? // CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? // CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too // **************** // In general each macro has a _NO_ form // (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. // Many features, at point of detection, define an _INTERNAL_ macro, so they // can be combined, en-mass, with the _NO_ forms later. #ifdef __cplusplus # if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) # define CATCH_CPP14_OR_GREATER # endif # if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) # define CATCH_CPP17_OR_GREATER # endif #endif // Only GCC compiler should be used in this block, so other compilers trying to // mask themselves as GCC should be ignored. #if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) # define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) #endif #if defined(__clang__) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) // As of this writing, IBM XL's implementation of __builtin_constant_p has a bug // which results in calls to destructors being emitted for each temporary, // without a matching initialization. In practice, this can result in something // like `std::string::~string` being called on an uninitialized value. // // For example, this code will likely segfault under IBM XL: // ``` // REQUIRE(std::string("12") + "34" == "1234") // ``` // // Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. # if !defined(__ibmxl__) && !defined(__CUDACC__) # define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ # endif # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) # define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) # define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) #endif // __clang__ //////////////////////////////////////////////////////////////////////////////// // Assume that non-Windows platforms support posix signals by default #if !defined(CATCH_PLATFORM_WINDOWS) #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS #endif //////////////////////////////////////////////////////////////////////////////// // We know some environments not to support full POSIX signals #if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS #endif #ifdef __OS400__ # define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS # define CATCH_CONFIG_COLOUR_NONE #endif //////////////////////////////////////////////////////////////////////////////// // Android somehow still does not support std::to_string #if defined(__ANDROID__) # define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING # define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE #endif //////////////////////////////////////////////////////////////////////////////// // Not all Windows environments support SEH properly #if defined(__MINGW32__) # define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH #endif //////////////////////////////////////////////////////////////////////////////// // PS4 #if defined(__ORBIS__) # define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE #endif //////////////////////////////////////////////////////////////////////////////// // Cygwin #ifdef __CYGWIN__ // Required for some versions of Cygwin to declare gettimeofday // see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin # define _BSD_SOURCE // some versions of cygwin (most) do not support std::to_string. Use the libstd check. // https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 # if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) # define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING # endif #endif // __CYGWIN__ //////////////////////////////////////////////////////////////////////////////// // Visual C++ #if defined(_MSC_VER) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) // Universal Windows platform does not support SEH // Or console colours (or console at all...) # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) # define CATCH_CONFIG_COLOUR_NONE # else # define CATCH_INTERNAL_CONFIG_WINDOWS_SEH # endif // MSVC traditional preprocessor needs some workaround for __VA_ARGS__ // _MSVC_TRADITIONAL == 0 means new conformant preprocessor // _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor # if !defined(__clang__) // Handle Clang masquerading for msvc # if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) # define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR # endif // MSVC_TRADITIONAL # endif // __clang__ #endif // _MSC_VER #if defined(_REENTRANT) || defined(_MSC_VER) // Enable async processing, as -pthread is specified or no additional linking is required # define CATCH_INTERNAL_CONFIG_USE_ASYNC #endif // _MSC_VER //////////////////////////////////////////////////////////////////////////////// // Check if we are compiled with -fno-exceptions or equivalent #if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) # define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED #endif //////////////////////////////////////////////////////////////////////////////// // DJGPP #ifdef __DJGPP__ # define CATCH_INTERNAL_CONFIG_NO_WCHAR #endif // __DJGPP__ //////////////////////////////////////////////////////////////////////////////// // Embarcadero C++Build #if defined(__BORLANDC__) #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN #endif //////////////////////////////////////////////////////////////////////////////// // Use of __COUNTER__ is suppressed during code analysis in // CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly // handled by it. // Otherwise all supported compilers support COUNTER macro, // but user still might want to turn it off #if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) #define CATCH_INTERNAL_CONFIG_COUNTER #endif //////////////////////////////////////////////////////////////////////////////// // RTX is a special version of Windows that is real time. // This means that it is detected as Windows, but does not provide // the same set of capabilities as real Windows does. #if defined(UNDER_RTSS) || defined(RTX64_BUILD) #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH #define CATCH_INTERNAL_CONFIG_NO_ASYNC #define CATCH_CONFIG_COLOUR_NONE #endif #if !defined(_GLIBCXX_USE_C99_MATH_TR1) #define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER #endif // Various stdlib support checks that require __has_include #if defined(__has_include) // Check if string_view is available and usable #if __has_include() && defined(CATCH_CPP17_OR_GREATER) # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW #endif // Check if optional is available and usable # if __has_include() && defined(CATCH_CPP17_OR_GREATER) # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) // Check if byte is available and usable # if __has_include() && defined(CATCH_CPP17_OR_GREATER) # include # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) # define CATCH_INTERNAL_CONFIG_CPP17_BYTE # endif # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) // Check if variant is available and usable # if __has_include() && defined(CATCH_CPP17_OR_GREATER) # if defined(__clang__) && (__clang_major__ < 8) // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 // fix should be in clang 8, workaround in libstdc++ 8.2 # include # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) # define CATCH_CONFIG_NO_CPP17_VARIANT # else # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) # else # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT # endif // defined(__clang__) && (__clang_major__ < 8) # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) #endif // defined(__has_include) #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) # define CATCH_CONFIG_COUNTER #endif #if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) # define CATCH_CONFIG_WINDOWS_SEH #endif // This is set by default, because we assume that unix compilers are posix-signal-compatible by default. #if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) # define CATCH_CONFIG_POSIX_SIGNALS #endif // This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. #if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) # define CATCH_CONFIG_WCHAR #endif #if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) # define CATCH_CONFIG_CPP11_TO_STRING #endif #if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) # define CATCH_CONFIG_CPP17_OPTIONAL #endif #if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) # define CATCH_CONFIG_CPP17_STRING_VIEW #endif #if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) # define CATCH_CONFIG_CPP17_VARIANT #endif #if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) # define CATCH_CONFIG_CPP17_BYTE #endif #if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) # define CATCH_INTERNAL_CONFIG_NEW_CAPTURE #endif #if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) # define CATCH_CONFIG_NEW_CAPTURE #endif #if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) # define CATCH_CONFIG_DISABLE_EXCEPTIONS #endif #if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) # define CATCH_CONFIG_POLYFILL_ISNAN #endif #if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) # define CATCH_CONFIG_USE_ASYNC #endif #if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) # define CATCH_CONFIG_ANDROID_LOGWRITE #endif #if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) # define CATCH_CONFIG_GLOBAL_NEXTAFTER #endif // Even if we do not think the compiler has that warning, we still have // to provide a macro that can be used by the code. #if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION #endif #if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION #endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS #endif // The goal of this macro is to avoid evaluation of the arguments, but // still have the compiler warn on problems inside... #if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) # define CATCH_INTERNAL_IGNORE_BUT_WARN(...) #endif #if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) # undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #elif defined(__clang__) && (__clang_major__ < 5) # undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) #define CATCH_TRY if ((true)) #define CATCH_CATCH_ALL if ((false)) #define CATCH_CATCH_ANON(type) if ((false)) #else #define CATCH_TRY try #define CATCH_CATCH_ALL catch (...) #define CATCH_CATCH_ANON(type) catch (type) #endif #if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) #define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #endif // end catch_compiler_capabilities.h #define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line #define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) #ifdef CATCH_CONFIG_COUNTER # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) #else # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) #endif #include #include #include // We need a dummy global operator<< so we can bring it into Catch namespace later struct Catch_global_namespace_dummy {}; std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); namespace Catch { struct CaseSensitive { enum Choice { Yes, No }; }; class NonCopyable { NonCopyable( NonCopyable const& ) = delete; NonCopyable( NonCopyable && ) = delete; NonCopyable& operator = ( NonCopyable const& ) = delete; NonCopyable& operator = ( NonCopyable && ) = delete; protected: NonCopyable(); virtual ~NonCopyable(); }; struct SourceLineInfo { SourceLineInfo() = delete; SourceLineInfo( char const* _file, std::size_t _line ) noexcept : file( _file ), line( _line ) {} SourceLineInfo( SourceLineInfo const& other ) = default; SourceLineInfo& operator = ( SourceLineInfo const& ) = default; SourceLineInfo( SourceLineInfo&& ) noexcept = default; SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; bool empty() const noexcept { return file[0] == '\0'; } bool operator == ( SourceLineInfo const& other ) const noexcept; bool operator < ( SourceLineInfo const& other ) const noexcept; char const* file; std::size_t line; }; std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); // Bring in operator<< from global namespace into Catch namespace // This is necessary because the overload of operator<< above makes // lookup stop at namespace Catch using ::operator<<; // Use this in variadic streaming macros to allow // >> +StreamEndStop // as well as // >> stuff +StreamEndStop struct StreamEndStop { std::string operator+() const; }; template T const& operator + ( T const& value, StreamEndStop ) { return value; } } #define CATCH_INTERNAL_LINEINFO \ ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) // end catch_common.h namespace Catch { struct RegistrarForTagAliases { RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); }; } // end namespace Catch #define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION // end catch_tag_alias_autoregistrar.h // start catch_test_registry.h // start catch_interfaces_testcase.h #include namespace Catch { class TestSpec; struct ITestInvoker { virtual void invoke () const = 0; virtual ~ITestInvoker(); }; class TestCase; struct IConfig; struct ITestCaseRegistry { virtual ~ITestCaseRegistry(); virtual std::vector const& getAllTests() const = 0; virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; }; bool isThrowSafe( TestCase const& testCase, IConfig const& config ); bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); } // end catch_interfaces_testcase.h // start catch_stringref.h #include #include #include #include namespace Catch { /// A non-owning string class (similar to the forthcoming std::string_view) /// Note that, because a StringRef may be a substring of another string, /// it may not be null terminated. class StringRef { public: using size_type = std::size_t; using const_iterator = const char*; private: static constexpr char const* const s_empty = ""; char const* m_start = s_empty; size_type m_size = 0; public: // construction constexpr StringRef() noexcept = default; StringRef( char const* rawChars ) noexcept; constexpr StringRef( char const* rawChars, size_type size ) noexcept : m_start( rawChars ), m_size( size ) {} StringRef( std::string const& stdString ) noexcept : m_start( stdString.c_str() ), m_size( stdString.size() ) {} explicit operator std::string() const { return std::string(m_start, m_size); } public: // operators auto operator == ( StringRef const& other ) const noexcept -> bool; auto operator != (StringRef const& other) const noexcept -> bool { return !(*this == other); } auto operator[] ( size_type index ) const noexcept -> char { assert(index < m_size); return m_start[index]; } public: // named queries constexpr auto empty() const noexcept -> bool { return m_size == 0; } constexpr auto size() const noexcept -> size_type { return m_size; } // Returns the current start pointer. If the StringRef is not // null-terminated, throws std::domain_exception auto c_str() const -> char const*; public: // substrings and searches // Returns a substring of [start, start + length). // If start + length > size(), then the substring is [start, size()). // If start > size(), then the substring is empty. auto substr( size_type start, size_type length ) const noexcept -> StringRef; // Returns the current start pointer. May not be null-terminated. auto data() const noexcept -> char const*; constexpr auto isNullTerminated() const noexcept -> bool { return m_start[m_size] == '\0'; } public: // iterators constexpr const_iterator begin() const { return m_start; } constexpr const_iterator end() const { return m_start + m_size; } }; auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { return StringRef( rawChars, size ); } } // namespace Catch constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { return Catch::StringRef( rawChars, size ); } // end catch_stringref.h // start catch_preprocessor.hpp #define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ #define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) #define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) #define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) #define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) #define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) #ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ // MSVC needs more evaluations #define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) #define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) #else #define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) #endif #define CATCH_REC_END(...) #define CATCH_REC_OUT #define CATCH_EMPTY() #define CATCH_DEFER(id) id CATCH_EMPTY() #define CATCH_REC_GET_END2() 0, CATCH_REC_END #define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 #define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 #define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT #define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) #define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) #define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) #define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) #define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) #define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) #define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) #define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) // Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, // and passes userdata as the first parameter to each invocation, // e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) #define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) #define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) #define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) #define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ #define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ #define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF #define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ #define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) #else // MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF #define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) #define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ #define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) #endif #define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ #define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) #define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) #define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) #else #define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) #define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) #endif #define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) #define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) #define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) #define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) #define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) #define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) #define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) #define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) #define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) #define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) #define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) #define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) #define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N #define INTERNAL_CATCH_TYPE_GEN\ template struct TypeList {};\ template\ constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ template class...> struct TemplateTypeList{};\ template class...Cs>\ constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ template\ struct append;\ template\ struct rewrap;\ template class, typename...>\ struct create;\ template class, typename>\ struct convert;\ \ template \ struct append { using type = T; };\ template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ template< template class L1, typename...E1, typename...Rest>\ struct append, TypeList, Rest...> { using type = L1; };\ \ template< template class Container, template class List, typename...elems>\ struct rewrap, List> { using type = TypeList>; };\ template< template class Container, template class List, class...Elems, typename...Elements>\ struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ \ template