libkysdk-coreai-1.1.0.1/0000775000175000017500000000000015207167112012574 5ustar zpzplibkysdk-coreai-1.1.0.1/CMakeLists.txt0000664000175000017500000001155515207167112015343 0ustar zpzpcmake_minimum_required(VERSION 3.5) project(kysdk-coreai-speech LANGUAGES CXX C) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_SKIP_RPATH ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) ####################################################################### # Options ####################################################################### option(ENABLE_TEST "Build Test" OFF) include_directories(include) include_directories(src) find_package(PkgConfig REQUIRED) find_package(OpenSSL REQUIRED) find_package(KylinAiProto REQUIRED) include_directories(${OPENSSL_INCLUDE_DIR}) pkg_check_modules(GIO REQUIRED gio-unix-2.0) pkg_check_modules(AI_COMMON REQUIRED kysdk-ai-common) find_package(jsoncpp REQUIRED) kylin_ai_generate_gdbus_proto_code(SPEECH_COMMON_PROTO_FILES corespeechservice) kylin_ai_generate_gdbus_proto_code(VISION_COMMON_PROTO_FILES corevisionservice) kylin_ai_generate_gdbus_proto_code(EMBEDDING_COMMON_PROTO_FILES coretextembeddingservice coreimageembeddingservice) include_directories(${GIO_INCLUDE_DIRS}) include_directories(${GIO_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIR} ${AI_COMMON_INCLUDE_DIRS}) include_directories(include/kylin-ai/coreai/speech) include_directories(include/kylin-ai/coreai/vision) include_directories(include/kylin-ai/coreai/embedding) include_directories(include/kylin-ai) set(SPEECH_SERVICE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/speech/_speechrecognitionresult.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/speech/_speechrecognitionsession.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/speech/_speechsynthesisresult.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/speech/_speechsynthesizersession.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/speech/_speechmodelconfig.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/speech/audioconfig.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/speech/audiodatastream.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/speech/coreaispeechserviceproxy.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/speech/coreaispeechserver.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/logger.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/speech/recognizer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/speech/synthesizer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/util.cpp ) set(VISION_SERVICE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/vision/_textrecognitionresult.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/vision/_textrecognitionsession.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/vision/textrecognition.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/vision/corevisionserviceproxy.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/vision/_textrecognitionconfig.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/util.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/logger.cpp ) set(EMBEDDING_SERVICE_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/embedding/embedding.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/embedding/imageembeddingprocessorproxy.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/embedding/imageembeddingsession.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/embedding/textembeddingprocessorproxy.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/embedding/textembeddingsession.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/logger.cpp ) add_library(kysdk-coreai-speech SHARED include/kylin-ai/coreai/speech/audioconfig.h include/kylin-ai/coreai/speech/audiodatastream.h include/kylin-ai/coreai/speech/recognizer.h include/kylin-ai/coreai/speech/result.h include/kylin-ai/coreai/speech/synthesizer.h include/kylin-ai/coreai/speech/error.h include/kylin-ai/coreai/speech/config.h ${SPEECH_SERVICE_SOURCES} ${SPEECH_COMMON_PROTO_FILES} ) add_library(kysdk-coreai-vision SHARED include/kylin-ai/coreai/vision/textrecognitionresult.h include/kylin-ai/coreai/vision/textrecognition.h include/kylin-ai/coreai/vision/error.h include/kylin-ai/coreai/vision/config.h ${VISION_SERVICE_SOURCES} ${VISION_COMMON_PROTO_FILES} ) add_library(kysdk-coreai-embedding SHARED ${EMBEDDING_SERVICE_SOURCES} ${EMBEDDING_COMMON_PROTO_FILES} ) if (ENABLE_TEST) add_subdirectory(tests) endif () set_target_properties(kysdk-coreai-speech PROPERTIES VERSION 1.0.0 SOVERSION 1) set_target_properties(kysdk-coreai-vision PROPERTIES VERSION 1.0.0 SOVERSION 1) set_target_properties(kysdk-coreai-embedding PROPERTIES VERSION 1.0.0 SOVERSION 1) target_link_libraries( kysdk-coreai-speech jsoncpp ${GIO_LIBRARIES} ${OPENSSL_LIBRARIES} ) target_link_libraries( kysdk-coreai-vision jsoncpp ${GIO_LIBRARIES} ${OPENSSL_LIBRARIES} ) target_link_libraries( kysdk-coreai-embedding jsoncpp ${GIO_LIBRARIES} ${OPENSSL_LIBRARIES} ) include(CMakePackageConfigHelpers) include(GNUInstallDirs) install(TARGETS kysdk-coreai-speech DESTINATION ${CMAKE_INSTALL_LIBDIR} ) install(TARGETS kysdk-coreai-vision DESTINATION ${CMAKE_INSTALL_LIBDIR} ) install(TARGETS kysdk-coreai-embedding DESTINATION ${CMAKE_INSTALL_LIBDIR} ) install(DIRECTORY include/kylin-ai DESTINATION include) install(DIRECTORY usr/ DESTINATION /usr) libkysdk-coreai-1.1.0.1/.gitignore0000664000175000017500000000151715207167112014570 0ustar zpzp# This file is used to ignore files which are generated # ---------------------------------------------------------------------------- *~ *.autosave *.a *.core *.moc *.o *.obj *.orig *.rej *.so *.so.* *_pch.h.cpp *_resource.rc *.qm .#* *.*# core !core/ tags .DS_Store .directory *.debug Makefile* *.prl *.app moc_*.cpp ui_*.h qrc_*.cpp Thumbs.db *.res *.rc /.qmake.cache /.qmake.stash # qtcreator generated files *.pro.user* CMakeLists.txt.user* # xemacs temporary files *.flc # Vim temporary files .*.swp # Visual Studio generated files *.ib_pdb_index *.idb *.ilk *.pdb *.sln *.suo *.vcproj *vcproj.*.*.user *.ncb *.sdf *.opensdf *.vcxproj *vcxproj.* # MinGW generated files *.Debug *.Release # Python byte code *.pyc # Binaries # -------- *.dll *.exe build .vscode .cache .reuse # third-party third-party/llama.cpp/common/build-info.cpplibkysdk-coreai-1.1.0.1/src/0000775000175000017500000000000015207167112013363 5ustar zpzplibkysdk-coreai-1.1.0.1/src/logger.h0000664000175000017500000003055415207167112015022 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef UTILS_LOGGER_H #define UTILS_LOGGER_H #include #include #include #include #include // #define CLR_CLR "\033[0m" // 恢复颜色 // #define CLR_BLACK "\033[30m" // 黑色字 // #define CLR_RED "\033[31m" // 红色字 // #define CLR_GREEN "\033[32m" // 绿色字 // #define CLR_YELLOW "\033[33m" // 黄色字 // #define CLR_BLUE "\033[34m" // 蓝色字 // #define CLR_PURPLE "\033[35m" // 紫色字 // #define CLR_SKYBLUE "\033[36m" // 天蓝字 // #define CLR_WHITE "\033[37m" // 白色字 // // #define CLR_BLK_WHT "\033[40;37m" // 黑底白字 // #define CLR_RED_WHT "\033[41;37m" // 红底白字 // #define CLR_GREEN_WHT "\033[42;37m" // 绿底白字 // #define CLR_YELLOW_WHT "\033[43;37m" // 黄底白字 // #define CLR_BLUE_WHT "\033[44;37m" // 蓝底白字 // #define CLR_PURPLE_WHT "\033[45;37m" // 紫底白字 // #define CLR_SKYBLUE_WHT "\033[46;37m" // 天蓝底白字 // #define CLR_WHT_BLK "\033[47;30m" // 白底黑字 #ifndef __FILENAME__ #define __FILENAME__ (strrchr("/" __FILE__, '/') + 1) #endif class Logger { public: enum Level { LOG_LEVEL_SILENT = 0, LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_WARN, LOG_LEVEL_ERROR }; static void printLn(); template static void printLn(T t, Args... args) { std::cout << t << ' '; printLn(args...); } static void printErrorLn(); template static void printErrorLn(T t, Args... args) { std::cerr << t << ' '; printErrorLn(args...); } template static void printLnLevel(const Logger& logger, Level level, Args... args) { std::lock_guard locker(logger.mutex_); #ifdef RUN_IN_DEBUG if (logger.level_ == LOG_LEVEL_SILENT) { // do nothing } else if (level == LOG_LEVEL_DEBUG and level >= logger.level_) { printLn(currentTime(), "\033[36mdebug\033[0m", args...); } else if (level == LOG_LEVEL_INFO and level >= logger.level_) { printLn(currentTime(), "\033[32minfo \033[0m", args...); } else if (level == LOG_LEVEL_WARN and level >= logger.level_) { printLn(currentTime(), "\033[33mwarn \033[0m", args...); } else if (level == LOG_LEVEL_ERROR and level >= logger.level_) { printLn(currentTime(), "\033[31merror\033[0m", args...); } #else // release 暂时往stderr输出 if (logger.level_ == LOG_LEVEL_SILENT) { // do nothing } else if (level == LOG_LEVEL_DEBUG and level >= logger.level_) { printErrorLn(currentTime(), "\033[36mdebug\033[0m", args...); } else if (level == LOG_LEVEL_INFO and level >= logger.level_) { printErrorLn(currentTime(), "\033[32minfo \033[0m", args...); } else if (level == LOG_LEVEL_WARN and level >= logger.level_) { printErrorLn(currentTime(), "\033[33mwarn \033[0m", args...); } else if (level == LOG_LEVEL_ERROR and level >= logger.level_) { printErrorLn(currentTime(), "\033[31merror\033[0m", args...); } #endif } static void printfLnLevel(const Logger& logger, Level level, const char* format) { std::lock_guard locker(logger.mutex_); #ifdef RUN_IN_DEBUG FILE* pFile = stdout; #else FILE *pFile = stderr; #endif if (logger.level_ == LOG_LEVEL_SILENT) { // do nothing } else if (level == LOG_LEVEL_DEBUG and level >= logger.level_) { std::fprintf(pFile, "%s %s %s\n", currentTime().c_str(), "\033[36mdebug\033[0m ", format); } else if (level == LOG_LEVEL_INFO and level >= logger.level_) { std::fprintf(pFile, "%s %s %s\n", currentTime().c_str(), "\033[32minfo \033[0m", format); } else if (level == LOG_LEVEL_WARN and level >= logger.level_) { std::fprintf(pFile, "%s %s %s\n", currentTime().c_str(), "\033[33mwarn \033[0m", format); } else if (level == LOG_LEVEL_ERROR and level >= logger.level_) { std::fprintf(pFile, "%s %s %s\n", currentTime().c_str(), "\033[31merror\033[0m", format); } } template static void printfLnLevel(const Logger& logger, Level level, const char* format, Args... args) { std::lock_guard locker(logger.mutex_); #ifdef RUN_IN_DEBUG FILE* pFile = stdout; #else FILE *pFile = stderr; #endif if (logger.level_ == LOG_LEVEL_SILENT) { // do nothing } else if (level == LOG_LEVEL_DEBUG and level >= logger.level_) { std::fprintf(pFile, "%s %s", currentTime().c_str(), "\033[36mdebug\033[0m "); std::fprintf(pFile, format, args...); std::fprintf(pFile, "\n"); } else if (level == LOG_LEVEL_INFO and level >= logger.level_) { std::fprintf(pFile, "%s %s ", currentTime().c_str(), "\033[32minfo \033[0m"); std::fprintf(pFile, format, args...); std::fprintf(pFile, "\n"); } else if (level == LOG_LEVEL_WARN and level >= logger.level_) { std::fprintf(pFile, "%s %s ", currentTime().c_str(), "\033[33mwarn \033[0m"); std::fprintf(pFile, format, args...); std::fprintf(pFile, "\n"); } else if (level == LOG_LEVEL_ERROR and level >= logger.level_) { std::fprintf(pFile, "%s %s ", currentTime().c_str(), "\033[31merror\033[0m"); std::fprintf(pFile, format, args...); std::fprintf(pFile, "\n"); } } static std::string location(const char* fileName, int line, const char* funcName); static std::string currentTime(); public: Logger(); private: mutable std::mutex mutex_; std::atomic level_{LOG_LEVEL_INFO}; }; static Logger g_Logger{}; #ifdef RUN_IN_DEBUG #define LOGD(...) \ do { \ Logger::printLnLevel( \ g_Logger, Logger::LOG_LEVEL_DEBUG, \ Logger::location(__FILENAME__, __LINE__, __FUNCTION__), \ ##__VA_ARGS__); \ } while (0) #define LOGDF(fmt, ...) \ do { \ Logger::printfLnLevel(g_Logger, Logger::LOG_LEVEL_DEBUG, \ "[%s:%d:%s] " fmt, __FILENAME__, __LINE__, \ __FUNCTION__, ##__VA_ARGS__); \ } while (0) #define LOGI(...) \ do { \ Logger::printLnLevel( \ g_Logger, Logger::LOG_LEVEL_INFO, \ Logger::location(__FILENAME__, __LINE__, __FUNCTION__), \ ##__VA_ARGS__); \ } while (0) #define LOGIF(fmt, ...) \ do { \ Logger::printfLnLevel(g_Logger, Logger::LOG_LEVEL_INFO, \ "[%s:%d:%s] " fmt, __FILENAME__, __LINE__, \ __FUNCTION__, ##__VA_ARGS__); \ } while (0) #define LOGW(...) \ do { \ Logger::printLnLevel( \ g_Logger, Logger::LOG_LEVEL_WARN, \ Logger::location(__FILENAME__, __LINE__, __FUNCTION__), \ ##__VA_ARGS__); \ } while (0) #define LOGWF(fmt, ...) \ do { \ Logger::printfLnLevel(g_Logger, Logger::LOG_LEVEL_WARN, \ "[%s:%d:%s] " fmt, __FILENAME__, __LINE__, \ __FUNCTION__, ##__VA_ARGS__); \ } while (0) #define LOGE(...) \ do { \ Logger::printLnLevel( \ g_Logger, Logger::LOG_LEVEL_ERROR, \ Logger::location(__FILENAME__, __LINE__, __FUNCTION__), \ ##__VA_ARGS__); \ } while (0) #define LOGEF(fmt, ...) \ do { \ Logger::printfLnLevel(g_Logger, Logger::LOG_LEVEL_ERROR, \ "[%s:%d:%s] " fmt, __FILENAME__, __LINE__, \ __FUNCTION__, ##__VA_ARGS__); \ } while (0) #else #define LOGD(...) \ do { \ Logger::printLnLevel(g_Logger, Logger::LOG_LEVEL_DEBUG, \ ##__VA_ARGS__); \ } while (0) #define LOGDF(fmt, ...) \ do { \ Logger::printfLnLevel(g_Logger, Logger::LOG_LEVEL_DEBUG, fmt, \ ##__VA_ARGS__); \ } while (0) #define LOGI(...) \ do { \ Logger::printLnLevel(g_Logger, Logger::LOG_LEVEL_INFO, ##__VA_ARGS__); \ } while (0) #define LOGIF(fmt, ...) \ do { \ Logger::printfLnLevel(g_Logger, Logger::LOG_LEVEL_INFO, fmt, \ ##__VA_ARGS__); \ } while (0) #define LOGW(...) \ do { \ Logger::printLnLevel(g_Logger, Logger::LOG_LEVEL_WARN, ##__VA_ARGS__); \ } while (0) #define LOGWF(fmt, ...) \ do { \ Logger::printfLnLevel(g_Logger, Logger::LOG_LEVEL_WARN, fmt, \ ##__VA_ARGS__); \ } while (0) #define LOGE(...) \ do { \ Logger::printLnLevel(g_Logger, Logger::LOG_LEVEL_ERROR, \ ##__VA_ARGS__); \ } while (0) #define LOGEF(fmt, ...) \ do { \ Logger::printfLnLevel(g_Logger, Logger::LOG_LEVEL_ERROR, fmt, \ ## __VA_ARGS__); \ } while (0) #endif #endif // UTILS_LOGGER_H libkysdk-coreai-1.1.0.1/src/vision/0000775000175000017500000000000015207167112014672 5ustar zpzplibkysdk-coreai-1.1.0.1/src/vision/_textrecognitionresult.cpp0000664000175000017500000000452315207167112022225 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "_textrecognitionresult.h" #include "textrecognitionresult.h" // 获取一行中的文本内容 const char *text_line_get_value(TextLine *text_line) { if (text_line == nullptr) { return nullptr; } return text_line->text.c_str(); } // 获取一行文本的角点位置信息(四个角的位置信息),point_number固定输出为4 PixelPoint *text_line_get_corner_points(TextLine *text_line, int *point_number) { if (text_line == nullptr) { return nullptr; } if (text_line->points.empty()) { return nullptr; } if (point_number == nullptr) { return nullptr; } *point_number = 4; return text_line->points.begin().base(); } // 获取识别结果的整体文本信息,不带格式 const char *text_recognition_result_get_value(TextRecognitionResult *result) { if (result == nullptr) { return nullptr; } return result->allText.c_str(); } TextLine **text_recognition_result_get_text_lines(TextRecognitionResult *result, int *line_count) { if (result == nullptr) { return nullptr; } if (line_count == nullptr) { return nullptr; } *line_count = result->lines.size(); return result->lines.data(); } int text_recognition_result_get_error_code(TextRecognitionResult *result) { if (result == nullptr) { return -1; } return result->errorCode; } const char *text_recognition_result_get_error_message( TextRecognitionResult *result) { if (result == nullptr) { return nullptr; } return result->errorMessage.c_str(); } libkysdk-coreai-1.1.0.1/src/vision/corevisionserviceproxy.h0000664000175000017500000000540515207167112021712 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef _CORE_VISION_SERVICE_PROXY_H_ #define _CORE_VISION_SERVICE_PROXY_H_ #include #include #include #include "_textrecognitionconfig.h" #include "corevisionserviceglue.h" #include "error.h" #include "textrecognition.h" namespace kyai { namespace core { namespace vision { using ReconnectedCallback = std::function; class CoreVisionServer { public: static CoreVisionServer &getInstance(); ~CoreVisionServer(); void init(); bool available() const { return connection_ != nullptr; } GDBusConnection *getConnection() const { return connection_; } private: CoreVisionServer() = default; void init(const std::string &unixPath); void destroy(); private: GDBusConnection *connection_{nullptr}; }; class CoreVisionServiceProxy { public: ~CoreVisionServiceProxy(); bool connectToServer(); bool reconnectServer(); void setRecognitionResultCallback(TextRecognitionResultCallback callback, void *userData); void setReconnectedCallback(ReconnectedCallback callback); int initEngine(const char *config, int *sessionId); void destroyTextRecognition(int sessionId); void recognizeTextFromImageFileAsync(int sessionId, const char *imageFile); void recognizeTextFromImageDataAsync(int sessionId, const char *data, unsigned int imageDataLength); private: static void onRecognitionResultCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); void handleErrorOccurred(int errorCode); void handleServerClosed(); void handleServerTimeout(); private: TextRecognitionResultCallback recognitionCallback_{nullptr}; ReconnectedCallback reconnectedCallback_{nullptr}; void *recognitionUserData_{nullptr}; AiRuntimeCoreVisionService *proxy_{nullptr}; int reconnectAttempts_{0}; static constexpr int maxReconnectAttempts{10}; }; } // namespace vision } // namespace core } // namespace kyai #endif libkysdk-coreai-1.1.0.1/src/vision/_textrecognitionsession.cpp0000664000175000017500000000460715207167112022375 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "_textrecognitionsession.h" #include namespace kyai { namespace core { namespace vision { _TextRecognitionSession::_TextRecognitionSession() { serviceProxy_.setReconnectedCallback([this]() { initSession(); }); } int _TextRecognitionSession::init() { if (!initServer()) { return AiSdkCommonErrorCode::AISDK_RUNTIME_ERROR; } return initSession(); } void _TextRecognitionSession::destroyTextRecognitionSession() { serviceProxy_.destroyTextRecognition(sessionId_); } void _TextRecognitionSession::setRecognitionResultCallback( TextRecognitionResultCallback callback, void *userData) { serviceProxy_.setRecognitionResultCallback(callback, userData); } void _TextRecognitionSession::setRecognizeTextModelConfig( const TextRecognitionModelConfig &config) { modelConfig_ = config; } void _TextRecognitionSession::recognizeTextFromImageFileAsync( const char *imageFile) { serviceProxy_.recognizeTextFromImageFileAsync(sessionId_, imageFile); } void _TextRecognitionSession::recognizeTextFromImageDataAsync( const char *imageData, unsigned int imageDataLength) { serviceProxy_.recognizeTextFromImageDataAsync(sessionId_, imageData, imageDataLength); } bool _TextRecognitionSession::initServer() { if (!serviceProxy_.connectToServer()) { return serviceProxy_.reconnectServer(); } return true; } int _TextRecognitionSession::initSession() { const std::string jsonStringConfig = modelConfigToJson(modelConfig_); int errorCode = serviceProxy_.initEngine(jsonStringConfig.c_str(), &sessionId_); return errorCode; } } // namespace vision } // namespace core } // namespace kyailibkysdk-coreai-1.1.0.1/src/vision/corevisionserviceproxy.cpp0000664000175000017500000002470415207167112022250 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "corevisionserviceproxy.h" #include #include #include #include #include #include #include "_textrecognitionconfig.h" #include "_textrecognitionresult.h" #include "logger.h" #include "util.h" namespace kyai { namespace core { namespace vision { const std::string serverUnixPath = "unix:path=/tmp/.kylin-ai-runtime-unix/" + std::to_string(getuid()) + "/core-vision.sock"; const char *objectPath = "/com/kylin/AiRuntime/CoreVision"; void dissectRecognitionResult(GVariant *parameters, TextRecognitionResult *recognitionResult) { if (parameters == nullptr || recognitionResult == nullptr) { return; } GVariantIter iter; g_variant_iter_init(&iter, parameters); GVariant *text = g_variant_iter_next_value(&iter); GVariant *lines = g_variant_iter_next_value(&iter); GVariant *errorCode = g_variant_iter_next_value(&iter); GVariant *errorMessage = g_variant_iter_next_value(&iter); if (!text || !lines || !errorCode || !errorMessage) { LOGE("recognition result error: result is nullptr!"); return; } const gchar *resultText = g_variant_get_string(text, nullptr); recognitionResult->allText = std::string(resultText); g_variant_unref(text); GVariantIter linesIter; g_variant_iter_init(&linesIter, lines); while (GVariant *lineVariant = g_variant_iter_next_value(&linesIter)) { TextLine *line = new TextLine{}; GVariant *pointsValue; double conf; gchar *oneTextLine; g_variant_get(lineVariant, "(s@a(ii)d)", &oneTextLine, &pointsValue, &conf); if (!oneTextLine || !pointsValue) { LOGE("recognition result error: result is nullptr!"); g_variant_unref(lineVariant); g_variant_unref(lines); delete line; return; } GVariantIter pointsIter; g_variant_iter_init(&pointsIter, pointsValue); while (GVariant *pointVariant = g_variant_iter_next_value(&pointsIter)) { PixelPoint point; g_variant_get(pointVariant, "(ii)", &point.x, &point.y); if (point.x < 0 || point.y < 0) { LOGE("recognition result point error: pixel point < 0!"); point.x = 0; point.y = 0; } line->points.push_back(point); g_variant_unref(pointVariant); } g_variant_unref(pointsValue); line->text = oneTextLine; recognitionResult->lines.push_back(line); g_free(oneTextLine); g_variant_unref(lineVariant); } g_variant_unref(lines); int resultErrorCode = g_variant_get_int32(errorCode); recognitionResult->errorCode = resultErrorCode; g_variant_unref(errorCode); const gchar *resultErrorMessage = g_variant_get_string(errorMessage, nullptr); recognitionResult->errorMessage = std::string(resultErrorMessage); g_variant_unref(errorMessage); } CoreVisionServer &CoreVisionServer::getInstance() { static CoreVisionServer instance; return instance; } CoreVisionServer::~CoreVisionServer() { destroy(); } void CoreVisionServer::destroy() { if (connection_ != nullptr) { g_object_unref(connection_); } } void CoreVisionServer::init() { init(serverUnixPath); } void CoreVisionServer::init(const std::string &unixPath) { GError *error = nullptr; connection_ = g_dbus_connection_new_for_address_sync( unixPath.c_str(), G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, nullptr, nullptr, &error); if (connection_ == nullptr) { g_printerr("Error connecting to D-Bus address %s: %s\n", unixPath.c_str(), error->message); g_error_free(error); } } void CoreVisionServiceProxy::setRecognitionResultCallback( TextRecognitionResultCallback callback, void *userData) { recognitionCallback_ = callback; recognitionUserData_ = userData; } void CoreVisionServiceProxy::setReconnectedCallback( ReconnectedCallback callback) { reconnectedCallback_ = std::move(callback); } CoreVisionServiceProxy::~CoreVisionServiceProxy() { if (proxy_) { g_object_unref(proxy_); } } bool CoreVisionServiceProxy::connectToServer() { CoreVisionServer::getInstance().init(); if (!CoreVisionServer::getInstance().available()) { g_printerr( "Error creating core vision server proxy: Server proxy " "connection is unavailable.\n"); return false; } if (proxy_ != nullptr) { g_object_unref(proxy_); proxy_ = nullptr; } GError *error = nullptr; auto *connection = CoreVisionServer::getInstance().getConnection(); proxy_ = ai_runtime_core_vision_service_proxy_new_sync( connection, G_DBUS_PROXY_FLAGS_NONE, nullptr, objectPath, nullptr, &error); if (proxy_ == nullptr) { LOGE("Error creating core vision service proxy: %s\n", error->message); g_error_free(error); return false; } g_dbus_proxy_set_default_timeout(G_DBUS_PROXY(proxy_), 5 * 60 * 1000); return true; } int CoreVisionServiceProxy::initEngine(const char *config, int *sessionId) { int errorCode = AISDK_NO_ERROR; GError *error = nullptr; bool success = ai_runtime_core_vision_service_call_init_sync( proxy_, config, sessionId, &errorCode, nullptr, &error); if (not success) { if (error) { LOGE("Error calling init: {}:{}", error->code, error->message); g_error_free(error); return AISDK_RUNTIME_ERROR; } } return errorCode; } void CoreVisionServiceProxy::destroyTextRecognition(int sessionId) { ai_runtime_core_vision_service_call_destroy_sync(proxy_, sessionId, nullptr, nullptr); } void CoreVisionServiceProxy::recognizeTextFromImageDataAsync( int sessionId, const char *data, unsigned int imageDataLength) { if (proxy_ == nullptr) { LOGE("Proxy not exist!"); return; } std::string imageData(data, imageDataLength); std::string base64ImageData = util::base64Encode(imageData); ai_runtime_core_vision_service_call_recognize_text_from_image_data( proxy_, sessionId, base64ImageData.c_str(), base64ImageData.size(), nullptr, onRecognitionResultCallback, this); } void CoreVisionServiceProxy::recognizeTextFromImageFileAsync( int sessionId, const char *imageFile) { if (proxy_ == nullptr) { LOGE("Proxy not exist!"); return; } ai_runtime_core_vision_service_call_recognize_text_from_image_file( proxy_, sessionId, imageFile, nullptr, onRecognitionResultCallback, this); } void CoreVisionServiceProxy::onRecognitionResultCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData) { GVariant *result = nullptr; GError *error = nullptr; auto *proxy = static_cast(userData); bool ret = ai_runtime_core_vision_service_call_recognize_text_from_image_file_finish( (AiRuntimeCoreVisionService *)sourceObject, &result, res, &error); if (!ret) { if (error) { LOGE("Error calling recognizeTextFromImageFile: {}:{}", error->code, error->message); proxy->handleErrorOccurred(error->code); g_error_free(error); } if (result) { g_variant_unref(result); } return; } if (proxy->recognitionCallback_ != nullptr) { TextRecognitionResult textRecognitionResult = {}; dissectRecognitionResult(result, &textRecognitionResult); g_variant_unref(result); proxy->recognitionCallback_(&textRecognitionResult, proxy->recognitionUserData_); } else { LOGE("Recognizing callback is not set!"); } } void CoreVisionServiceProxy::handleErrorOccurred(int errorCode) { LOGE("Server disconnected."); switch (errorCode) { case G_IO_ERROR_CLOSED: { handleServerClosed(); break; } case G_IO_ERROR_TIMED_OUT: { handleServerTimeout(); break; } } } void CoreVisionServiceProxy::handleServerClosed() { LOGE("Server closed unexpectedly."); if (recognitionCallback_) { TextRecognitionResult result{ .errorCode = AISDK_RUNTIME_ERROR, .errorMessage = "Server closed unexpectedly."}; recognitionCallback_(&result, recognitionUserData_); } if (reconnectServer()) { if (reconnectedCallback_) { std::fprintf(stderr, "Reconnected to server And call back.\n"); reconnectedCallback_(); } } reconnectAttempts_ = 0; } void CoreVisionServiceProxy::handleServerTimeout() { if (recognitionCallback_) { TextRecognitionResult result{.errorCode = AISDK_SERVICE_TIMEOUT, .errorMessage = "Server timeout."}; recognitionCallback_(&result, recognitionUserData_); } } bool CoreVisionServiceProxy::reconnectServer() { if (reconnectAttempts_ >= maxReconnectAttempts) { LOGE("Max reconnect attempts reached"); return false; } reconnectAttempts_++; LOGD("Reconnect attempt {}.", reconnectAttempts_); if (!connectToServer()) { LOGE("Failed to reconnect to server"); std::this_thread::sleep_for(std::chrono::seconds(1)); return reconnectServer(); } LOGI("Successfully reconnected to server."); return true; } } // namespace vision } // namespace core } // namespace kyailibkysdk-coreai-1.1.0.1/src/vision/_textrecognitionsession.h0000664000175000017500000000332515207167112022036 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef _TEXTRECOGNITIONSESSION_ #define _TEXTRECOGNITIONSESSION_ #include "_textrecognitionconfig.h" #include "corevisionserviceproxy.h" #include "textrecognition.h" #include "textrecognitionresult.h" namespace kyai { namespace core { namespace vision { class _TextRecognitionSession { public: _TextRecognitionSession(); int init(); void destroyTextRecognitionSession(); void setRecognitionResultCallback(TextRecognitionResultCallback callback, void *userData); void recognizeTextFromImageFileAsync(const char *imageFile); void recognizeTextFromImageDataAsync(const char *imageData, unsigned int imageDataLength); void setRecognizeTextModelConfig(const TextRecognitionModelConfig &config); private: bool initServer(); int initSession(); private: CoreVisionServiceProxy serviceProxy_; int sessionId_{0}; TextRecognitionModelConfig modelConfig_{}; }; } // namespace vision } // namespace core } // namespace kyai #endiflibkysdk-coreai-1.1.0.1/src/vision/_textrecognitionconfig.cpp0000664000175000017500000000406415207167112022154 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "_textrecognitionconfig.h" #include TextRecognitionModelConfig *text_recognition_model_config_create() { return new _TextRecognitionModelConfig(); } void text_recognition_model_config_destroy( TextRecognitionModelConfig **config) { if (config == nullptr) { return; } delete *config; *config = nullptr; } void text_recognition_model_config_set_name(TextRecognitionModelConfig *config, const char *name) { if (config == nullptr) { return; } config->name = name; } void text_recognition_model_config_set_deploy_type( TextRecognitionModelConfig *config, ModelDeployType type) { if (config == nullptr) { return; } config->type = type; } std::string modelConfigToJson(const _TextRecognitionModelConfig &modelconfig) { Json::Value root; root["name"] = modelconfig.name; switch (modelconfig.type) { case ModelDeployType::OnDevice: root["type"] = "Local"; break; case ModelDeployType::PrivateCloud: root["type"] = "PrivateCloud"; break; case ModelDeployType::PublicCloud: root["type"] = "PublicCloud"; break; default: root["type"] = "Default"; break; } Json::FastWriter writer; return writer.write(root); } libkysdk-coreai-1.1.0.1/src/vision/_textrecognitionresult.h0000664000175000017500000000222715207167112021671 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef _TEXTRCOGNITIONRESULT_H_ #define _TEXTRCOGNITIONRESULT_H_ #include #include #include "textrecognitionresult.h" struct _TextLine { std::string text; std::vector points; }; struct _TextRecognitionResult { std::string allText; std::vector<_TextLine *> lines; int errorCode; std::string errorMessage; ~_TextRecognitionResult() { for (auto line : lines) { delete line; } } }; #endiflibkysdk-coreai-1.1.0.1/src/vision/_textrecognitionconfig.h0000664000175000017500000000202215207167112021611 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef _TEXTRECOGNITIONCONFIG_H #define _TEXTRECOGNITIONCONFIG_H #include #include "coreai/vision/config.h" struct _TextRecognitionModelConfig { std::string name{}; ModelDeployType type{ModelDeployType(-1)}; }; std::string modelConfigToJson(const _TextRecognitionModelConfig &modelconfig); #endif //_TEXTRECOGNITIONCONFIG_H libkysdk-coreai-1.1.0.1/src/vision/textrecognition.cpp0000664000175000017500000000566315207167112020635 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "textrecognition.h" #include "_textrecognitionsession.h" // 创建session TextRecognitionSession *text_recognition_create_session() { auto *session = new kyai::core::vision::_TextRecognitionSession(); return (TextRecognitionSession *)session; } // 销毁session void text_recognition_destroy_session(TextRecognitionSession **session) { if (session == nullptr) { return; } if (*session == nullptr) { return; } ((kyai::core::vision::_TextRecognitionSession *)*session) ->destroyTextRecognitionSession(); delete (kyai::core::vision::_TextRecognitionSession *)*session; *session = nullptr; } // 初始化session int text_recognition_init_session(TextRecognitionSession *session) { if (session == nullptr) { return AISDK_INVALID_SESSION; } return ((kyai::core::vision::_TextRecognitionSession *)session)->init(); } // 设置结果回调 void text_recognition_result_set_callback( TextRecognitionSession *session, TextRecognitionResultCallback callback, void *user_data) { if (session == nullptr) { return; } ((kyai::core::vision::_TextRecognitionSession *)session) ->setRecognitionResultCallback(callback, user_data); } // 配置Ocr模型 void text_recognition_set_model_config(TextRecognitionSession *session, TextRecognitionModelConfig *config) { if (session == nullptr or config == nullptr) { return; } ((kyai::core::vision::_TextRecognitionSession *)session) ->setRecognizeTextModelConfig(*config); } // 从图像文件中识别 void text_recognition_recognize_text_from_image_file_async( TextRecognitionSession *session, const char *image_file) { if (session == nullptr) { return; } ((kyai::core::vision::_TextRecognitionSession *)session) ->recognizeTextFromImageFileAsync(image_file); } // 从图像数据中进行识别 void text_recognition_recognize_text_from_image_data_async( TextRecognitionSession *session, const char *image_data, unsigned int image_data_length) { if (session == nullptr) { return; } ((kyai::core::vision::_TextRecognitionSession *)session) ->recognizeTextFromImageDataAsync(image_data, image_data_length); }libkysdk-coreai-1.1.0.1/src/logger.cpp0000664000175000017500000000322315207167112015346 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "logger.h" #include #include void Logger::printLn() { std::cout << '\n'; } void Logger::printErrorLn() { std::cerr << '\n'; } std::string Logger::location(const char* fileName, int line, const char* funcName) { std::ostringstream oss; oss << '[' << fileName << ':' << line << ':' << funcName << ']'; return oss.str(); } std::string Logger::currentTime() { struct timeval tv {}; struct tm* tm = nullptr; gettimeofday(&tv, nullptr); time_t tt = tv.tv_sec; tm = localtime(&tt); char buffer[32]{}; #ifdef RUN_IN_DEBUG sprintf(buffer, "[%04d-%02d-%02d %02d:%02d:%02d.%03ld]", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec / 1000); #else sprintf(buffer, "[%ld]", tv.tv_sec * 1000 + tv.tv_usec / 1000); #endif return buffer; } Logger::Logger() { #ifdef RUN_IN_DEBUG level_ = LOG_LEVEL_DEBUG; #endif } libkysdk-coreai-1.1.0.1/src/util.h0000664000175000017500000000217515207167112014516 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef UTIL_H #define UTIL_H #include #include #include namespace util { std::string base64Encode(const void *data, std::size_t size); std::string base64Encode(const std::vector &input); std::string base64Encode(const std::string &input); std::vector base64Decode(const std::string &input); bool stringContains(const std::string &str, const std::string &sub); } // namespace util #endif // UTIL_H libkysdk-coreai-1.1.0.1/src/embedding/0000775000175000017500000000000015207167112015301 5ustar zpzplibkysdk-coreai-1.1.0.1/src/embedding/embeddingcommon.h0000664000175000017500000000252015207167112020600 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef EMBEDDINGCOMMON_H #define EMBEDDINGCOMMON_H #include "coreai/embedding/error.h" #include #include typedef struct _EmbeddingResult { std::vector vector_result = {}; int error_code = 0; std::string error_message = ""; _EmbeddingResult() {} _EmbeddingResult(std::vector vector_result) : vector_result(vector_result), error_code(COREAI_EMBEDDING_SUCESS), error_message("Sucess") {} _EmbeddingResult(int error_code, std::string error_message) : vector_result({}), error_code(error_code), error_message(error_message) {} } EmbeddingResult; #endiflibkysdk-coreai-1.1.0.1/src/embedding/textembeddingsession.h0000664000175000017500000000242215207167112021701 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef TEXTEMBEDDINGSESSION_H #define TEXTEMBEDDINGSESSION_H #include #include "embedding.h" #include "embeddingcommon.h" #include "textembeddingprocessorproxy.h" class _TextEmbeddingSession { public: int connect(); int init(); EmbeddingResult* embeddingText(const std::string& text); void embeddingTextAsync(const std::string& text, TextEmbeddingResultCallback callback, void* callback_user_data); std::string getModelInfo(); private: TextEmbeddingProcessorProxy text_embedding_processor_proxy_; }; #endif libkysdk-coreai-1.1.0.1/src/embedding/imageembeddingsession.cpp0000664000175000017500000000464315207167112022341 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "imageembeddingsession.h" int _ImageEmbeddingSession::connect() { // 先init 与runtime 的连接 return image_embedding_processor_proxy_.connect(); } int _ImageEmbeddingSession::init() { return image_embedding_processor_proxy_.initEngine(); } EmbeddingResult* _ImageEmbeddingSession::embeddingText( const std::string& text) { return image_embedding_processor_proxy_.embeddingText(text); } EmbeddingResult* _ImageEmbeddingSession::embeddingImage( const std::string& file_path) { return image_embedding_processor_proxy_.embeddingImage(file_path); } EmbeddingResult* _ImageEmbeddingSession::embeddingBase64Image( const std::string& file_data) { return image_embedding_processor_proxy_.embeddingBase64Image(file_data); } void _ImageEmbeddingSession::embeddingTextAsync( const std::string& text, ImageEmbeddingResultCallback callback, void* callback_user_data) { image_embedding_processor_proxy_.embeddingTextAsync(text, callback, callback_user_data); } void _ImageEmbeddingSession::embeddingImageAsync( const std::string& file_path, ImageEmbeddingResultCallback callback, void* callback_user_data) { image_embedding_processor_proxy_.embeddingImageAsync(file_path, callback, callback_user_data); } void _ImageEmbeddingSession::embeddingBase64ImageAsync( const std::string& file_data, ImageEmbeddingResultCallback callback, void* callback_user_data) { image_embedding_processor_proxy_.embeddingBase64ImageAsync( file_data, callback, callback_user_data); } std::string _ImageEmbeddingSession::getModelInfo() { return image_embedding_processor_proxy_.getModelInfo(); }libkysdk-coreai-1.1.0.1/src/embedding/imageembeddingprocessorproxy.h0000664000175000017500000000573315207167112023445 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef SERVICES_IMAGEEMBEDDINGPROCESSORPROXY_H #define SERVICES_IMAGEEMBEDDINGPROCESSORPROXY_H #include #include #include #include #include #include "coreimageembeddingserviceglue.h" #include "embeddingcommon.h" class ImageEmbeddingProcessorProxy { public: using ImageEmbeddingCallback = std::function; using ImageEmbeddingResultCallback = std::function; struct EmbeddingRequestContext { ImageEmbeddingResultCallback callback; void* callback_user_data; }; public: ~ImageEmbeddingProcessorProxy(); int connect(); int initEngine(); EmbeddingResult* embeddingText(const std::string& text); EmbeddingResult* embeddingImage(const std::string& file_path); EmbeddingResult* embeddingBase64Image(const std::string& file_data); void embeddingTextAsync(const std::string& text, ImageEmbeddingResultCallback callback, void* callback_user_data); void embeddingImageAsync(const std::string& file_path, ImageEmbeddingResultCallback callback, void* callback_user_data); void embeddingBase64ImageAsync(const std::string& file_data, ImageEmbeddingResultCallback callback, void* callback_user_data); std::string getModelInfo(); private: void destroy(); static EmbeddingResult parseResult(const std::string& json_string_result); static void embeddingTextAsyncCallback(GObject* object, GAsyncResult* res, gpointer data); static void embeddingImageAsyncCallback(GObject* object, GAsyncResult* res, gpointer data); static void embeddingBase64ImageAsyncCallback(GObject* object, GAsyncResult* res, gpointer data); private: AiRuntimeCoreImageEmbeddingService* delegate_ = nullptr; GDBusConnection* connection_ = nullptr; int sessionId_ = -1; int initErrorCode_ = -1; // 0表示初始化成功,非0表示初始化失败 }; #endiflibkysdk-coreai-1.1.0.1/src/embedding/imageembeddingsession.h0000664000175000017500000000345115207167112022002 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef IMAGEEMBEDDINGSESSION_H #define IMAGEEMBEDDINGSESSION_H #include #include "embedding.h" #include "embeddingcommon.h" #include "imageembeddingprocessorproxy.h" class _ImageEmbeddingSession { public: int connect(); int init(); EmbeddingResult* embeddingText(const std::string& text); EmbeddingResult* embeddingImage(const std::string& file_path); EmbeddingResult* embeddingBase64Image(const std::string& file_data); void embeddingTextAsync(const std::string& text, ImageEmbeddingResultCallback callback, void* callback_user_data); void embeddingImageAsync(const std::string& file_path, ImageEmbeddingResultCallback callback, void* callback_user_data); void embeddingBase64ImageAsync(const std::string& file_data, ImageEmbeddingResultCallback callback, void* callback_user_data); std::string getModelInfo(); private: ImageEmbeddingProcessorProxy image_embedding_processor_proxy_; }; #endif libkysdk-coreai-1.1.0.1/src/embedding/textembeddingprocessorproxy.h0000664000175000017500000000406215207167112023341 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef SERVICES_TEXTEMBEDDINGPROCESSORPROXY_H #define SERVICES_TEXTEMBEDDINGPROCESSORPROXY_H #include #include #include #include #include #include "coretextembeddingserviceglue.h" #include "embeddingcommon.h" class TextEmbeddingProcessorProxy { public: using TextEmbeddingCallback = std::function; using TextEmbeddingResultCallback = std::function; struct EmbeddingRequestContext { TextEmbeddingResultCallback callback; void* callback_user_data; }; public: ~TextEmbeddingProcessorProxy(); int connect(); int initEngine(); EmbeddingResult* embeddingText(const std::string& text); void embeddingTextAsync(const std::string& text, TextEmbeddingResultCallback callback, void* callback_user_data); std::string getModelInfo(); private: void destroy(); static EmbeddingResult parseResult(const std::string& json_string_result); static void embeddingTextAsyncCallback(GObject* object, GAsyncResult* res, gpointer data); private: AiRuntimeCoreTextEmbeddingService* delegate_ = nullptr; GDBusConnection* connection_ = nullptr; int sessionId_ = -1; int initErrorCode_ = -1; }; #endiflibkysdk-coreai-1.1.0.1/src/embedding/textembeddingprocessorproxy.cpp0000664000175000017500000002065515207167112023702 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "textembeddingprocessorproxy.h" #include "error.h" #include "logger.h" #include "nlohmann/json.hpp" namespace { const char* textEmbeddingProcessorObjectPath = "/com/kylin/AiRuntime/CoreTextEmbeddingService"; const std::string serverUnixPath = "unix:path=/tmp/.kylin-ai-runtime-unix/" + std::to_string(getuid()) + "/core-textembedding.sock"; } // namespace int TextEmbeddingProcessorProxy::initEngine() { nlohmann::json config; config["engineName"] = "Embedding"; GError* error = nullptr; ai_runtime_core_text_embedding_service_call_init_sync( delegate_, config.dump().c_str(), &sessionId_, &initErrorCode_, nullptr, &error); if (error) { g_printerr("Init engine error: %s\n", error->message); g_error_free(error); return COREAI_EMBEDDING_CONNECTION_ERROR; } if (sessionId_ == -1) { LOGEF("Proxy init engine error sessionId:%i.", sessionId_); return COREAI_EMBEDDING_INIT_ERROR; } LOGIF("Proxy init engine sessionId:%i.", sessionId_); return COREAI_EMBEDDING_SUCESS; } int TextEmbeddingProcessorProxy::connect() { // 获得连接 GError* error = nullptr; connection_ = g_dbus_connection_new_for_address_sync( serverUnixPath.c_str(), G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, nullptr, /* GDBusAuthObserver */ nullptr, /* GCancellable */ &error); if (connection_ == nullptr && error != nullptr) { g_printerr("Error connecting to D-Bus address %s: %s\n", serverUnixPath.c_str(), error->message); g_error_free(error); return COREAI_EMBEDDING_CONNECTION_ERROR; } // 获得代理 delegate_ = ai_runtime_core_text_embedding_service_proxy_new_sync( connection_, G_DBUS_PROXY_FLAGS_NONE, nullptr, textEmbeddingProcessorObjectPath, nullptr, &error); if (delegate_ == nullptr && error != nullptr) { g_printerr("Error creating text embedding processor proxy %s: %s\n", textEmbeddingProcessorObjectPath, error->message); g_error_free(error); return COREAI_EMBEDDING_CONNECTION_ERROR; } return COREAI_EMBEDDING_SUCESS; } TextEmbeddingProcessorProxy::~TextEmbeddingProcessorProxy() { destroy(); } EmbeddingResult* TextEmbeddingProcessorProxy::embeddingText( const std::string& text) { GError* error = nullptr; nlohmann::json object; object["text"] = text; object["sessionId"] = sessionId_; char* json_result = nullptr; ai_runtime_core_text_embedding_service_call_embedding_text_sync( delegate_, object.dump().c_str(), &json_result, nullptr, &error); if (error) { g_printerr("[Dbus-close:aisdk_text]: %s\n", error->message); // 由于runtime可能会崩溃,当前的session就不可用了,让用户重新创建session EmbeddingResult* res = new EmbeddingResult( COREAI_EMBEDDING_CONNECTION_ERROR, error->message); g_error_free(error); return res; } if (!json_result) { LOGE("Te-embedding text result error:json_result is nullptr!"); EmbeddingResult* res = new EmbeddingResult( COREAI_EMBEDDING_RUNTIME_ERROR, "Embedding reulst is nullptr!"); return res; } auto result = parseResult(json_result); g_free(json_result); EmbeddingResult* res = new EmbeddingResult(result); return res; } void TextEmbeddingProcessorProxy::embeddingTextAsync( const std::string& text, TextEmbeddingResultCallback callback, void* callback_user_data) { nlohmann::json object; object["text"] = text; object["sessionId"] = sessionId_; EmbeddingRequestContext* context = new EmbeddingRequestContext{callback, callback_user_data}; ai_runtime_core_text_embedding_service_call_embedding_text( delegate_, object.dump().c_str(), nullptr, (GAsyncReadyCallback)embeddingTextAsyncCallback, context); } void TextEmbeddingProcessorProxy::destroy() { // 如果用户想用新的连接重连,这里清理一下connection if (connection_) { g_object_unref(connection_); connection_ = nullptr; } if (delegate_) { g_object_unref(delegate_); delegate_ = nullptr; } } EmbeddingResult TextEmbeddingProcessorProxy::parseResult( const std::string& json_string_result) { EmbeddingResult result; nlohmann::json root = nlohmann::json::parse(json_string_result, nullptr, false); if (root.empty() || !root.contains("vector_result") || !root["vector_result"].is_array() || !root.contains("errorMessage") || !root.contains("errorCode")) { LOGE("Json format error."); return EmbeddingResult(COREAI_EMBEDDING_RUNTIME_ERROR, "Json format error."); } auto vector_result = root["vector_result"].get>(); for (const auto& value : vector_result) { result.vector_result.push_back(static_cast(value)); } result.error_message = root["errorMessage"].get(); result.error_code = root["errorCode"].get(); return result; } void TextEmbeddingProcessorProxy::embeddingTextAsyncCallback(GObject* object, GAsyncResult* res, gpointer data) { auto* context = static_cast(data); GError* error = NULL; char* json_result = nullptr; ai_runtime_core_text_embedding_service_call_embedding_text_finish( (AiRuntimeCoreTextEmbeddingService*)object, &json_result, res, &error); if (error) { g_printerr("[Dbus-close:aisdk_embedding_text_async]: %s\n", error->message); // 由于runtime可能会崩溃,当前的session就不可用了,让用户重新创建session EmbeddingResult result(COREAI_EMBEDDING_CONNECTION_ERROR, error->message); context->callback(&result, context->callback_user_data); g_error_free(error); delete context; context = nullptr; return; } if (!json_result) { LOGE( "Te-embedding text async result error:json_result is " "nullptr!"); EmbeddingResult result(COREAI_EMBEDDING_RUNTIME_ERROR, "Embedding reulst is nullptr!"); context->callback(&result, context->callback_user_data); delete context; context = nullptr; return; } auto result = parseResult(json_result); g_free(json_result); context->callback(&result, context->callback_user_data); delete context; context = nullptr; } std::string TextEmbeddingProcessorProxy::getModelInfo() { GError* error = nullptr; char* model_info = nullptr; ai_runtime_core_text_embedding_service_call_get_model_info_sync( delegate_, sessionId_, &model_info, nullptr, &error); if (error) { g_printerr("[Dbus-close:aisdk_text]: %s\n", error->message); g_error_free(error); return ""; } if (!model_info) { LOGE("Te-embedding modelInfo is empty!"); return ""; } // engine中返回的向量化模型信息包括文本和图像的,在sdk层拆开json字符串 nlohmann::json root = nlohmann::json::parse(model_info, nullptr, false); if (root.empty() || !root.contains("models") || !root["models"].contains("text_model")) { LOGE( "Te-embedding parse modelInfo error! text_model not found in " "json."); g_free(model_info); return ""; } // 提取 text_model 的部分 std::string res = root["models"]["text_model"].dump(); g_free(model_info); return res; }libkysdk-coreai-1.1.0.1/src/embedding/textembeddingsession.cpp0000664000175000017500000000300315207167112022230 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "textembeddingsession.h" int _TextEmbeddingSession::connect() { // 先init 与runtime 的连接 return text_embedding_processor_proxy_.connect(); } int _TextEmbeddingSession::init() { // 在初始化engine return text_embedding_processor_proxy_.initEngine(); } EmbeddingResult* _TextEmbeddingSession::embeddingText(const std::string& text) { return text_embedding_processor_proxy_.embeddingText(text); } void _TextEmbeddingSession::embeddingTextAsync( const std::string& text, TextEmbeddingResultCallback callback, void* callback_user_data) { text_embedding_processor_proxy_.embeddingTextAsync(text, callback, callback_user_data); } std::string _TextEmbeddingSession::getModelInfo() { return text_embedding_processor_proxy_.getModelInfo(); }libkysdk-coreai-1.1.0.1/src/embedding/imageembeddingprocessorproxy.cpp0000664000175000017500000003555715207167112024007 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "imageembeddingprocessorproxy.h" #include #include "error.h" #include "logger.h" #include "nlohmann/json.hpp" namespace { const char* imageEmbeddingProcessorObjectPath = "/com/kylin/AiRuntime/CoreImageEmbeddingService"; const std::string serverUnixPath = "unix:path=/tmp/.kylin-ai-runtime-unix/" + std::to_string(getuid()) + "/core-imageembedding.sock"; } // namespace ImageEmbeddingProcessorProxy::~ImageEmbeddingProcessorProxy() { destroy(); } int ImageEmbeddingProcessorProxy::connect() { GError* error = nullptr; connection_ = g_dbus_connection_new_for_address_sync( serverUnixPath.c_str(), G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, nullptr, /* GDBusAuthObserver */ nullptr, /* GCancellable */ &error); if (connection_ == nullptr && error != nullptr) { g_printerr("Error connecting to D-Bus address %s: %s\n", serverUnixPath.c_str(), error->message); g_error_free(error); return COREAI_EMBEDDING_CONNECTION_ERROR; } delegate_ = ai_runtime_core_image_embedding_service_proxy_new_sync( connection_, G_DBUS_PROXY_FLAGS_NONE, nullptr, imageEmbeddingProcessorObjectPath, nullptr, &error); if (delegate_ == nullptr && error != nullptr) { g_printerr("Error creating image embedding processor proxy %s: %s\n", imageEmbeddingProcessorObjectPath, error->message); g_error_free(error); return COREAI_EMBEDDING_CONNECTION_ERROR; } return COREAI_EMBEDDING_SUCESS; } void ImageEmbeddingProcessorProxy::destroy() { if (connection_) { g_object_unref(connection_); connection_ = nullptr; } if (delegate_) { g_object_unref(delegate_); delegate_ = nullptr; } } int ImageEmbeddingProcessorProxy::initEngine() { nlohmann::json config; config["engineName"] = "Embedding"; GError* error = nullptr; ai_runtime_core_image_embedding_service_call_init_sync( delegate_, config.dump().c_str(), &sessionId_, &initErrorCode_, nullptr, &error); if (error) { g_printerr("Init engine error: %s\n", error->message); g_error_free(error); return COREAI_EMBEDDING_CONNECTION_ERROR; } if (sessionId_ == -1) { LOGEF("Proxy init engine error sessionId:%i.", sessionId_); return COREAI_EMBEDDING_INIT_ERROR; } LOGIF("Proxy init engine sessionId:%i.\n", sessionId_); return COREAI_EMBEDDING_SUCESS; } EmbeddingResult* ImageEmbeddingProcessorProxy::embeddingText( const std::string& text) { GError* error = nullptr; nlohmann::json object; object["text"] = text; object["sessionId"] = sessionId_; char* json_result = nullptr; ai_runtime_core_image_embedding_service_call_embedding_text_sync( delegate_, object.dump().c_str(), &json_result, nullptr, &error); if (error) { g_printerr("[Dbus-close:aisdk_image_embedding_text]: %s\n", error->message); // 由于runtime可能会崩溃,当前的session就不可用了,让用户重新创建session EmbeddingResult* res = new EmbeddingResult( COREAI_EMBEDDING_CONNECTION_ERROR, error->message); g_error_free(error); return res; } if (!json_result) { LOGE("Im-embedding text result error:json_result is nullptr!"); EmbeddingResult* res = new EmbeddingResult( COREAI_EMBEDDING_RUNTIME_ERROR, "Embedding reulst is nullptr!"); return res; } std::string string_json_result(json_result); g_free(json_result); auto result = parseResult(string_json_result); EmbeddingResult* res = new EmbeddingResult(result); return res; } EmbeddingResult* ImageEmbeddingProcessorProxy::embeddingImage( const std::string& file_path) { GError* error = nullptr; nlohmann::json object; object["file_path"] = file_path; object["sessionId"] = sessionId_; char* json_result = nullptr; ai_runtime_core_image_embedding_service_call_embedding_image_file_sync( delegate_, object.dump().c_str(), &json_result, nullptr, &error); if (error) { g_printerr("[Dbus-close:aisdk_image_embedding_image]: %s\n", error->message); // 由于runtime可能会崩溃,当前的session就不可用了,让用户重新创建sessino EmbeddingResult* res = new EmbeddingResult( COREAI_EMBEDDING_CONNECTION_ERROR, error->message); g_error_free(error); return res; } if (!json_result) { LOGE("Im-embedding image result error:json_result is nullptr!"); EmbeddingResult* res = new EmbeddingResult( COREAI_EMBEDDING_RUNTIME_ERROR, "Embedding reulst is nullptr!"); return res; } auto result = parseResult(json_result); g_free(json_result); EmbeddingResult* res = new EmbeddingResult(result); return res; } EmbeddingResult* ImageEmbeddingProcessorProxy::embeddingBase64Image( const std::string& file_data) { GError* error = nullptr; nlohmann::json object; object["base64ImageData"] = file_data; object["sessionId"] = sessionId_; char* json_result = nullptr; ai_runtime_core_image_embedding_service_call_embedding_base64_image_sync( delegate_, object.dump().c_str(), &json_result, nullptr, &error); if (error) { g_printerr("[Dbus-close:aisdk_image_embedding_base64_image]: %s\n", error->message); // 由于runtime可能会崩溃,当前的session就不可用了,让用户重新创建sessino EmbeddingResult* res = new EmbeddingResult( COREAI_EMBEDDING_CONNECTION_ERROR, error->message); g_error_free(error); return res; } if (!json_result) { LOGE("Im-embedding base64 image result error:json_result is nullptr!"); EmbeddingResult* res = new EmbeddingResult( COREAI_EMBEDDING_RUNTIME_ERROR, "Embedding reulst is nullptr!"); return res; } auto result = parseResult(json_result); g_free(json_result); EmbeddingResult* res = new EmbeddingResult(result); return res; } void ImageEmbeddingProcessorProxy::embeddingTextAsync( const std::string& text, ImageEmbeddingResultCallback callback, void* callback_user_data) { nlohmann::json object; object["text"] = text; object["sessionId"] = sessionId_; EmbeddingRequestContext* context = new EmbeddingRequestContext{callback, callback_user_data}; ai_runtime_core_image_embedding_service_call_embedding_text( delegate_, object.dump().c_str(), nullptr, (GAsyncReadyCallback)embeddingTextAsyncCallback, context); } void ImageEmbeddingProcessorProxy::embeddingImageAsync( const std::string& file_path, ImageEmbeddingResultCallback callback, void* callback_user_data) { nlohmann::json object; object["file_path"] = file_path; object["sessionId"] = sessionId_; EmbeddingRequestContext* context = new EmbeddingRequestContext{callback, callback_user_data}; ai_runtime_core_image_embedding_service_call_embedding_image_file( delegate_, object.dump().c_str(), nullptr, (GAsyncReadyCallback)embeddingImageAsyncCallback, context); } void ImageEmbeddingProcessorProxy::embeddingBase64ImageAsync( const std::string& file_data, ImageEmbeddingResultCallback callback, void* callback_user_data) { nlohmann::json object; object["base64ImageData"] = file_data; object["sessionId"] = sessionId_; EmbeddingRequestContext* context = new EmbeddingRequestContext{callback, callback_user_data}; ai_runtime_core_image_embedding_service_call_embedding_base64_image( delegate_, object.dump().c_str(), nullptr, (GAsyncReadyCallback)embeddingBase64ImageAsyncCallback, context); } void ImageEmbeddingProcessorProxy::embeddingTextAsyncCallback(GObject* object, GAsyncResult* res, gpointer data) { auto* context = static_cast(data); GError* error = NULL; char* json_result = nullptr; ai_runtime_core_image_embedding_service_call_embedding_text_finish( (AiRuntimeCoreImageEmbeddingService*)object, &json_result, res, &error); if (error) { g_printerr("[Dbus-close:aisdk_image_embedding_text_async]: %s\n", error->message); // 由于runtime可能会崩溃,当前的session就不可用了,让用户重新创建session EmbeddingResult result(COREAI_EMBEDDING_CONNECTION_ERROR, error->message); context->callback(&result, context->callback_user_data); g_error_free(error); delete context; context = nullptr; return; } if (!json_result) { LOGE("Im-embedding text async result error:json_result is nullptr!"); EmbeddingResult result(COREAI_EMBEDDING_RUNTIME_ERROR, "Embedding reulst is nullptr!"); context->callback(&result, context->callback_user_data); delete context; context = nullptr; return; } auto result = parseResult(json_result); g_free(json_result); context->callback(&result, context->callback_user_data); delete context; context = nullptr; } void ImageEmbeddingProcessorProxy::embeddingImageAsyncCallback( GObject* object, GAsyncResult* res, gpointer data) { auto* context = static_cast(data); GError* error = NULL; char* json_result = nullptr; ai_runtime_core_image_embedding_service_call_embedding_image_file_finish( (AiRuntimeCoreImageEmbeddingService*)object, &json_result, res, &error); if (error) { g_printerr("[Dbus-close:aisdk_image_embedding_image_async]: %s\n", error->message); // 由于runtime可能会崩溃,当前的session就不可用了,让用户重新创建session EmbeddingResult result(COREAI_EMBEDDING_CONNECTION_ERROR, error->message); context->callback(&result, context->callback_user_data); g_error_free(error); delete context; context = nullptr; return; } if (!json_result) { LOGE("Im-embedding image async result error:json_result is nullptr!"); EmbeddingResult result(COREAI_EMBEDDING_RUNTIME_ERROR, "Embedding reulst is nullptr!"); context->callback(&result, context->callback_user_data); delete context; context = nullptr; return; } auto result = parseResult(json_result); g_free(json_result); context->callback(&result, context->callback_user_data); delete context; context = nullptr; } void ImageEmbeddingProcessorProxy::embeddingBase64ImageAsyncCallback( GObject* object, GAsyncResult* res, gpointer data) { auto* context = static_cast(data); GError* error = NULL; char* json_result = nullptr; ai_runtime_core_image_embedding_service_call_embedding_base64_image_finish( (AiRuntimeCoreImageEmbeddingService*)object, &json_result, res, &error); if (error) { g_printerr( "[Dbus-close:aisdk_image_embedding_base64_image_async]: %s\n", error->message); // 由于runtime可能会崩溃,当前的session就不可用了,让用户重新创建sessino EmbeddingResult result(COREAI_EMBEDDING_CONNECTION_ERROR, error->message); context->callback(&result, context->callback_user_data); g_error_free(error); delete context; context = nullptr; return; } if (!json_result) { LOGE( "Im-embedding base64 image async result error:json_result is " "nullptr!"); EmbeddingResult result(COREAI_EMBEDDING_RUNTIME_ERROR, "Embedding reulst is nullptr!"); context->callback(&result, context->callback_user_data); delete context; context = nullptr; return; } auto result = parseResult(json_result); g_free(json_result); context->callback(&result, context->callback_user_data); delete context; context = nullptr; } EmbeddingResult ImageEmbeddingProcessorProxy::parseResult( const std::string& json_string_result) { EmbeddingResult result; nlohmann::json root = nlohmann::json::parse(json_string_result, nullptr, false); if (root.empty() || !root.contains("vector_result") || !root["vector_result"].is_array() || !root.contains("errorMessage") || !root.contains("errorCode")) { LOGE("Json format error."); return EmbeddingResult(COREAI_EMBEDDING_RUNTIME_ERROR, "Json format error."); } auto vector_result = root["vector_result"].get>(); for (const auto& value : vector_result) { result.vector_result.push_back(static_cast(value)); } result.error_message = root["errorMessage"].get(); result.error_code = root["errorCode"].get(); return result; } std::string ImageEmbeddingProcessorProxy::getModelInfo() { GError* error = nullptr; char* model_info = nullptr; ai_runtime_core_image_embedding_service_call_get_model_info_sync( delegate_, sessionId_, &model_info, nullptr, &error); if (error) { g_printerr("[Dbus-close:aisdk_text]: %s\n", error->message); g_error_free(error); return ""; } if (!model_info) { LOGE("Te-embedding modelInfo is empty!"); return ""; } // engine中返回的向量化模型信息包括文本和图像的,在sdk层拆开json字符串 nlohmann::json root = nlohmann::json::parse(model_info, nullptr, false); if (root.empty() || !root.contains("models") || !root["models"].contains("image_model")) { LOGE( "Te-embedding parse modelInfo error! image_model not found in " "json."); g_free(model_info); return ""; } // 提取 image_model 的部分 std::string res = root["models"]["image_model"].dump(); g_free(model_info); return res; }libkysdk-coreai-1.1.0.1/src/embedding/embedding.cpp0000664000175000017500000002064115207167112017726 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "embedding.h" #include #include "embeddingcommon.h" #include "error.h" #include "imageembeddingsession.h" #include "textembeddingsession.h" float *embedding_result_get_vector_data(EmbeddingResult *result) { if (!result) return nullptr; return result->vector_result.data(); } int embedding_result_get_vector_length(EmbeddingResult *result) { if (!result) return 0; return result->vector_result.size(); } int embedding_result_get_error_code(EmbeddingResult *result) { if (!result) return COREAI_EMBEDDING_INPUT_ERROR; return result->error_code; } const char *embedding_result_get_error_message(EmbeddingResult *result) { if (!result) return nullptr; return result->error_message.c_str(); } void embedding_result_destroy(EmbeddingResult **result) { // 双重检查 if (!result) return; if (*result != nullptr) { delete *result; *result = nullptr; } } void embedding_model_info_destroy(char *result) { delete[] result; } _TextEmbeddingSession *text_embedding_create_session() { _TextEmbeddingSession *textEmbeddingSession = new _TextEmbeddingSession(); return textEmbeddingSession; } void text_embedding_destroy_session(_TextEmbeddingSession **session) { // 双重检查 if (!session) return; if (*session != nullptr) { delete *session; *session = nullptr; } } int text_embedding_init_session(_TextEmbeddingSession *session) { if (!session) return COREAI_EMBEDDING_INPUT_ERROR; // 先init 与runtime的连接 if (session->connect() != 0) return COREAI_EMBEDDING_CONNECTION_ERROR; // 模型加载 return session->init(); } bool text_embedding_get_model_info(_TextEmbeddingSession *session, char **model_info) { if (!session) return false; std::string res; res = session->getModelInfo(); if (!res.empty()) { size_t length = res.size() + 1; *model_info = new char[length]; std::strcpy(*model_info, res.c_str()); return true; } return false; } bool text_embedding(TextEmbeddingSession *session, const char *text, EmbeddingResult **result) { if (!session || !text) { *result = new EmbeddingResult(COREAI_EMBEDDING_INPUT_ERROR, "Incorrect input parameter"); return false; } // 这里面会开辟内存 所以需要用户调用destory接口 *result = session->embeddingText(text); if (!result || !(*result)) return false; return true; } void text_embedding_async(_TextEmbeddingSession *session, const char *text, TextEmbeddingResultCallback callback, void *callback_user_data) { if (!session || !text) { EmbeddingResult result(COREAI_EMBEDDING_INPUT_ERROR, "Incorrect input parameter"); callback(&result, callback_user_data); return; } session->embeddingTextAsync(text, callback, callback_user_data); } _ImageEmbeddingSession *image_embedding_create_session() { _ImageEmbeddingSession *imageEmbeddingSession = new _ImageEmbeddingSession(); return imageEmbeddingSession; } void image_embedding_destroy_session(_ImageEmbeddingSession **session) { if (!session) return; if (*session != nullptr) { delete *session; *session = nullptr; } } int image_embedding_init_session(_ImageEmbeddingSession *session) { if (!session) return COREAI_EMBEDDING_INPUT_ERROR; // 先init 与runtime的连接 if (session->connect() != 0) return COREAI_EMBEDDING_CONNECTION_ERROR; // 模型加载 return session->init(); } bool image_embedding_get_model_info(_ImageEmbeddingSession *session, char **model_info) { if (!session) return false; std::string res; res = session->getModelInfo(); if (!res.empty()) { size_t length = res.size() + 1; *model_info = new char[length]; std::strcpy(*model_info, res.c_str()); return true; } return false; } bool text_embedding_by_image_model(_ImageEmbeddingSession *session, const char *text, EmbeddingResult **result) { if (!session || !text) { *result = new EmbeddingResult(COREAI_EMBEDDING_INPUT_ERROR, "Incorrect input parameter"); return false; } *result = session->embeddingText( text); // 这里面会开辟内存 所以需要用户调用destory接口 if (!result || !(*result)) return false; return true; } bool image_embedding_by_image_file(_ImageEmbeddingSession *session, const char *image_file, EmbeddingResult **result) { if (!session || !image_file) { *result = new EmbeddingResult(COREAI_EMBEDDING_INPUT_ERROR, "Incorrect input parameter"); return false; } *result = session->embeddingImage( image_file); // 这里面会开辟内存 所以需要用户调用destory接口 if (!result || !(*result)) return false; return true; } bool image_embedding_by_base64_image_data(_ImageEmbeddingSession *session, const unsigned char *image_data, unsigned int image_data_length, EmbeddingResult **result) { if (!session || !image_data) { *result = new EmbeddingResult(COREAI_EMBEDDING_INPUT_ERROR, "Incorrect input parameter"); return false; } std::string base64_str(reinterpret_cast(image_data), image_data_length); *result = session->embeddingBase64Image( base64_str); // 这里面会开辟内存 所以需要用户调用destory接口 if (!result || !(*result)) return false; return true; } void text_embedding_by_image_model_async(_ImageEmbeddingSession *session, const char *text, ImageEmbeddingResultCallback callback, void *callback_user_data) { if (!session || !text) { EmbeddingResult result(COREAI_EMBEDDING_INPUT_ERROR, "Incorrect input parameter"); callback(&result, callback_user_data); return; } session->embeddingTextAsync(text, callback, callback_user_data); } void image_embedding_from_by_file_async(_ImageEmbeddingSession *session, const char *file_path, ImageEmbeddingResultCallback callback, void *callback_user_data) { if (!session || !file_path) { EmbeddingResult result(COREAI_EMBEDDING_INPUT_ERROR, "Incorrect input parameter"); callback(&result, callback_user_data); return; } session->embeddingImageAsync(file_path, callback, callback_user_data); } void image_embedding_by_base64_image_data_async( _ImageEmbeddingSession *session, const unsigned char *image_data, unsigned int image_data_length, ImageEmbeddingResultCallback callback, void *callback_user_data) { if (!session || !image_data) { EmbeddingResult result(COREAI_EMBEDDING_INPUT_ERROR, "Incorrect input parameter"); callback(&result, callback_user_data); return; } std::string base64_image_str(reinterpret_cast(image_data), image_data_length); session->embeddingBase64ImageAsync(base64_image_str, callback, callback_user_data); }libkysdk-coreai-1.1.0.1/src/speech/0000775000175000017500000000000015207167112014632 5ustar zpzplibkysdk-coreai-1.1.0.1/src/speech/_speechrecognitionsession.cpp0000664000175000017500000000602415207167112022613 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "_speechrecognitionsession.h" #include "logger.h" namespace kyai { namespace core { namespace speech { _SpeechRecognitionSession::_SpeechRecognitionSession() { serviceProxy_.setReconnectedCallback([this] { initSession(); }); } _SpeechRecognitionSession::~_SpeechRecognitionSession() { serviceProxy_.setReconnectedCallback(nullptr); } int _SpeechRecognitionSession::init() { if (!initServer()) { return AiSdkCommonErrorCode::AISDK_RUNTIME_ERROR; } return initSession(); } void _SpeechRecognitionSession::destroyRecognizer() { serviceProxy_.destroyRecognizer(sessionId_); } void _SpeechRecognitionSession::setRecognizerAudioConfig(_AudioConfig *config) { if (config == nullptr) { return; } audioConfig_ = config; serviceProxy_.setRecognizerAudioConfig(sessionId_, config->toJsonString()); } _AudioConfig *_SpeechRecognitionSession::getRecognizerAudioConfig() const { return audioConfig_; } void _SpeechRecognitionSession::setRecognitionResultCallback( SpeechRecognitionResultCallback callback, void *userData) { serviceProxy_.setRecognitionResultCallback(callback, userData); } void _SpeechRecognitionSession::startContinuousRecognition() { serviceProxy_.startContinuousRecognition(sessionId_); } void _SpeechRecognitionSession::stopContinuousRecognition() { serviceProxy_.stopContinuousRecognition(sessionId_); } void _SpeechRecognitionSession::continuousRecognitionWriteAudioData( const uint8_t *data, uint32_t length) { serviceProxy_.continuousRecognitionWriteAudioData(sessionId_, data, length); } void _SpeechRecognitionSession::recognizeOnce() { serviceProxy_.recognizeOnce(sessionId_); } void _SpeechRecognitionSession::setRecognitionModelConfig( const SpeechModelConfig &config) { modelConfig_ = config; } bool _SpeechRecognitionSession::initServer() { if (!serviceProxy_.connectToServer()) { return serviceProxy_.reconnectServer(); } return true; } int _SpeechRecognitionSession::initSession() { const std::string jsonStringConfig = modelConfigToJson(modelConfig_); int errorCode = serviceProxy_.initRecognizer(jsonStringConfig.c_str(), sessionId_); if (errorCode != AISDK_NO_ERROR) { LOGE("Init session error: ", errorCode); } return errorCode; } } // namespace speech } // namespace core } // namespace kyai libkysdk-coreai-1.1.0.1/src/speech/_speechsynthesizersession.h0000664000175000017500000000341715207167112022332 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef _SPEECHSYNTHESIZER_ #define _SPEECHSYNTHESIZER_ #include "_audioconfig.h" #include "_speechmodelconfig.h" #include "coreaispeechserviceproxy.h" #include "result.h" namespace kyai { namespace core { namespace speech { class _SpeechSynthesizerSession { public: _SpeechSynthesizerSession(); ~_SpeechSynthesizerSession(); int init(); void destroySynthesizer(); void setSynthesizerAudioConfig(_AudioConfig *config); void setSynthesizerResultCallback(SpeechSynthesisResultCallback callback, void *userData); void synthesizeTextOnce(const char *text, uint32_t textLength); void synthesizeTextContinuous(const char *text, uint32_t textLength); int stopSpeaking(); void setSynthesizerModelConfig(const SpeechModelConfig &config); private: bool initServer(); int initSession(); private: _AudioConfig *audioConfig_{nullptr}; CoreAiSpeechServiceProxy serviceProxy_; int sessionId_{-1}; SpeechModelConfig modelConfig_{}; }; } // namespace speech } // namespace core } // namespace kyai #endif libkysdk-coreai-1.1.0.1/src/speech/synthesizer.cpp0000664000175000017500000000623215207167112017730 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "synthesizer.h" #include "_speechsynthesizersession.h" SpeechSynthesizerSession *speech_synthesizer_create_session() { auto *session = new kyai::core::speech::_SpeechSynthesizerSession(); return (SpeechSynthesizerSession *)session; } void speech_synthesizer_destroy_session(SpeechSynthesizerSession **session) { if (session == nullptr) { return; } if (*session == nullptr) { return; } ((kyai::core::speech::_SpeechSynthesizerSession *)*session) ->destroySynthesizer(); delete (kyai::core::speech::_SpeechSynthesizerSession *)*session; *session = nullptr; } int speech_synthesizer_init_session(SpeechSynthesizerSession *session) { if (session == nullptr) { return AISDK_INVALID_SESSION; } return ((kyai::core::speech::_SpeechSynthesizerSession *)session)->init(); } void speech_synthesizer_set_audio_config(SpeechSynthesizerSession *session, AudioConfig *audio_config) { if (session == nullptr) { return; } if (audio_config == nullptr) { return; } ((kyai::core::speech::_SpeechSynthesizerSession *)session) ->setSynthesizerAudioConfig((_AudioConfig *)audio_config); } void speech_synthesizer_result_set_callback( SpeechSynthesizerSession *session, SpeechSynthesisResultCallback callback, void *user_data) { if (session == nullptr) { return; } ((kyai::core::speech::_SpeechSynthesizerSession *)session) ->setSynthesizerResultCallback(callback, user_data); } void speech_synthesizer_set_model_config(SpeechSynthesizerSession *session, SpeechModelConfig *config) { if (session == nullptr or config == nullptr) { return; } ((kyai::core::speech::_SpeechSynthesizerSession *)session) ->setSynthesizerModelConfig(*config); } void speech_synthesizer_synthesize_text_async(SpeechSynthesizerSession *session, const char *text, uint32_t text_length) { if (session == nullptr) { return; } ((kyai::core::speech::_SpeechSynthesizerSession *)session) ->synthesizeTextOnce(text, text_length); } int speech_synthesizer_stop_speaking(SpeechSynthesizerSession *session) { if (session == nullptr) { return SPEECH_PARAM_INVALID; } return ((kyai::core::speech::_SpeechSynthesizerSession *)session) ->stopSpeaking(); } libkysdk-coreai-1.1.0.1/src/speech/_speechmodelconfig.cpp0000664000175000017500000000370515207167112021160 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "_speechmodelconfig.h" #include SpeechModelConfig *speech_model_config_create() { return new _SpeechModelConfig(); } void speech_model_config_destroy(SpeechModelConfig **config) { if (config == nullptr) { return; } delete *config; *config = nullptr; } void speech_model_config_set_name(SpeechModelConfig *config, const char *name) { if (config == nullptr) { return; } config->name = name; } void speech_model_config_set_deploy_type(SpeechModelConfig *config, ModelDeployType type) { if (config == nullptr) { return; } config->type = type; } std::string modelConfigToJson(const _SpeechModelConfig &modelconfig) { Json::Value root; root["name"] = modelconfig.name; switch (modelconfig.type) { case ModelDeployType::OnDevice: root["type"] = "Local"; break; case ModelDeployType::PrivateCloud: root["type"] = "PrivateCloud"; break; case ModelDeployType::PublicCloud: root["type"] = "PublicCloud"; break; default: root["type"] = "Default"; break; } Json::FastWriter writer; return writer.write(root); } libkysdk-coreai-1.1.0.1/src/speech/coreaispeechserviceproxy.h0000664000175000017500000001134415207167112022123 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef _CORE_SPEECH_SERVICE_PROXY_H_ #define _CORE_SPEECH_SERVICE_PROXY_H_ #include #include #include #include "_speechmodelconfig.h" #include "corespeechserviceglue.h" #include "error.h" #include "result.h" namespace kyai { namespace core { namespace speech { using ReconnectedCallback = std::function; class CoreAiSpeechServiceProxy { public: ~CoreAiSpeechServiceProxy(); bool connectToServer(); bool reconnectServer(); void setRecognitionResultCallback(SpeechRecognitionResultCallback callback, void *userData); void setSynthesizerCallback(SpeechSynthesisResultCallback callback, void *userData); void setReconnectedCallback(const ReconnectedCallback &callback); int initRecognizer(const char *config, int &sessionId); int initSynthesizer(const char *config, int &sessionId); void destroyRecognizer(int sessionId); void destroySynthesizer(int sessionId); void setRecognizerAudioConfig(int sessionId, const std::string &audioConfig); void startContinuousRecognition(int sessionId); void stopContinuousRecognition(int sessionId); void continuousRecognitionWriteAudioData(int sessionId, const uint8_t *data, uint32_t length); void recognizeOnce(int sessionId); void setSynthesizerAudioConfig(int sessionId, const std::string &audioConfig); void synthesizeTextOnce(int sessionId, const char *text, uint32_t textLength); void synthesizeTextContinuous(int sessionId, const char *text, uint32_t textLength); int stopSpeaking(int sessionId); private: void connectSpeechRecognitionResultSignals(int sessionId); void connectSpeechSynthesisResultSignals(int sessionId); static void onRecognitionResultCallback( GDBusConnection *connection, const gchar *senderName, const gchar *objectPath, const gchar *interfaceName, const gchar *signalName, GVariant *parameters, gpointer userData); static void onSynthesisResult(GDBusConnection *connection, const gchar *senderName, const gchar *objectPath, const gchar *interfaceName, const gchar *signalName, GVariant *parameters, gpointer userData); static void onOnceRecognitionFinishedCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); void handleOnceRecognitionError(int errorCode); void handleOnceRecognitionServerClosed(); void handleOnceRecognitionResultTimeout(); void handleOnceRecognitionGeneralError(); static void onOnceSynthesisFinishedCallback(GObject *sourceObject, GAsyncResult *res, gpointer userData); void handleOnceSynthesisErrorOccurred(int errorCode); void handleOnceSynthesisServerClosed(); void handleOnceSynthesisResultTimeout(); void handleOnceSynthesisGeneralError(); private: SpeechRecognitionResultCallback recognitionCallback_{nullptr}; void *recognitionUserData_{nullptr}; SpeechSynthesisResultCallback synthesizerCallback_{nullptr}; void *synthesizerUserData_{nullptr}; guint recognitionSubscriptionId_{0}; guint synthesizerSubscriptionId_{0}; AiRuntimeCoreSpeechService *proxy_{nullptr}; // 以下变量是为了重连 ReconnectedCallback reconnectedCallback_{nullptr}; int reconnectAttempts_{0}; static constexpr int maxReconnectAttempts = 10; bool recognitionResultSignalConnected_{false}; bool synthesisResultSignalConnected_{false}; bool continuousRecognitionStarted_{false}; }; } // namespace speech } // namespace core } // namespace kyai #endif libkysdk-coreai-1.1.0.1/src/speech/_speechsynthesizersession.cpp0000664000175000017500000000600115207167112022655 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "_speechsynthesizersession.h" #include "logger.h" namespace kyai { namespace core { namespace speech { _SpeechSynthesizerSession::_SpeechSynthesizerSession() { serviceProxy_.setReconnectedCallback([this] { initSession(); }); } _SpeechSynthesizerSession::~_SpeechSynthesizerSession() { serviceProxy_.setReconnectedCallback(nullptr); } int _SpeechSynthesizerSession::init() { if (!initServer()) { return AiSdkCommonErrorCode::AISDK_RUNTIME_ERROR; } return initSession(); } void _SpeechSynthesizerSession::destroySynthesizer() { serviceProxy_.destroySynthesizer(sessionId_); } void _SpeechSynthesizerSession::setSynthesizerAudioConfig( _AudioConfig *config) { if (config == nullptr) { return; } audioConfig_ = config; serviceProxy_.setSynthesizerAudioConfig(sessionId_, config->toJsonString()); } void _SpeechSynthesizerSession::setSynthesizerResultCallback( SpeechSynthesisResultCallback callback, void *userData) { serviceProxy_.setSynthesizerCallback(callback, userData); } void _SpeechSynthesizerSession::synthesizeTextOnce(const char *text, uint32_t textLength) { LOGD(sessionId_, std::string(text, textLength)); serviceProxy_.synthesizeTextOnce(sessionId_, text, textLength); } void _SpeechSynthesizerSession::synthesizeTextContinuous(const char *text, uint32_t textLength) { serviceProxy_.synthesizeTextContinuous(sessionId_, text, textLength); } int _SpeechSynthesizerSession::stopSpeaking() { return serviceProxy_.stopSpeaking(sessionId_); } void _SpeechSynthesizerSession::setSynthesizerModelConfig( const SpeechModelConfig &config) { modelConfig_ = config; } bool _SpeechSynthesizerSession::initServer() { if (!serviceProxy_.connectToServer()) { return serviceProxy_.reconnectServer(); } return true; } int _SpeechSynthesizerSession::initSession() { const std::string jsonStringConfig = modelConfigToJson(modelConfig_); int errorCode = serviceProxy_.initSynthesizer(jsonStringConfig.c_str(), sessionId_); LOGD(sessionId_); if (errorCode != AISDK_NO_ERROR) { LOGE("Init session error: ", errorCode); } return errorCode; } } // namespace speech } // namespace core } // namespace kyai libkysdk-coreai-1.1.0.1/src/speech/coreaispeechserver.cpp0000664000175000017500000000560715207167112021227 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "coreaispeechserver.h" #include namespace kyai::core::speech { const std::string serverUnixPath = "unix:path=/tmp/.kylin-ai-runtime-unix/" + std::to_string(getuid()) + "/core-speech.sock"; CoreAiSpeechServer &CoreAiSpeechServer::getInstance() { static CoreAiSpeechServer instance{}; return instance; } CoreAiSpeechServer::CoreAiSpeechServer() {} CoreAiSpeechServer::~CoreAiSpeechServer() { destroy(); } void CoreAiSpeechServer::init() { init(serverUnixPath); } void CoreAiSpeechServer::init(const std::string &unixPath) { std::lock_guard locker(mutex_); if (connection_ != nullptr && !g_dbus_connection_is_closed(connection_)) { return; } GError *error = nullptr; connection_ = g_dbus_connection_new_for_address_sync( unixPath.c_str(), G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, nullptr, /* GDBusAuthObserver */ nullptr, /* GCancellable */ &error); if (connection_ == nullptr) { g_printerr("Error connecting to D-Bus address %s: %s\n", unixPath.c_str(), error->message); g_error_free(error); return; } handlerId_ = g_signal_connect(connection_, "closed", G_CALLBACK(onConnectionClosed), this); } void CoreAiSpeechServer::destroy() { std::lock_guard locker(mutex_); if (connection_ != nullptr) { // 断开信号连接 if (handlerId_ != 0) { g_signal_handler_disconnect(connection_, handlerId_); } // 关闭并释放连接 g_dbus_connection_close_sync(connection_, nullptr, nullptr); g_object_unref(connection_); } onConnectionClosed_ = nullptr; } void CoreAiSpeechServer::onConnectionClosed(GDBusConnection *connection, gboolean remotePeerVanished, GError *error, gpointer userData) { LOGE("On connection closed."); CoreAiSpeechServer *server = (CoreAiSpeechServer *)(userData); if (server->onConnectionClosed_) { server->onConnectionClosed_(); } } } // namespace kyai::core::speech libkysdk-coreai-1.1.0.1/src/speech/_audioconfig.h0000664000175000017500000000373715207167112017443 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef SRC_AUDIO_CONFIG_H #define SRC_AUDIO_CONFIG_H #include #include #include "audiodatastream.h" #include "util.h" struct InputAudioInfo { enum class InputSource { DEFAULT_MIC, STREAM, PCM_FILE, RAW_DATA }; enum class Format { PCM, MP3 }; // 指的是文件格式 enum class Encode { SAMPLE_S16LE, // Signed 16 Bit PCM, little endian }; // 音频存储格式,也有的库叫format InputSource inputSource{InputSource::DEFAULT_MIC}; Format format{Format::PCM}; Encode encode{Encode::SAMPLE_S16LE}; int rate{16000}; int channel{1}; std::vector audioData; // 原始音频数据 std::filesystem::path inputFilePath{}; AudioDataStream* audioDataStream{nullptr}; }; struct OutputAudioInfo { enum class OutputTarget { PCM_DATA, DEFAULT_SPEAKER, PCM_OUTPUT_FILE }; OutputTarget outputTarget{OutputTarget::DEFAULT_SPEAKER}; int speed{50}; // 语速,可选值:[0-100],默认为50 int volume{50}; // 音量,可选值:[0-100],默认为50 int pitch{50}; // 音高,可选值:[0-100],默认为50 std::filesystem::path outputFilePath{}; }; struct _AudioConfig { std::string toJsonString() const; InputAudioInfo inputAudioInfo{}; OutputAudioInfo outputAudioInfo{}; }; #endif libkysdk-coreai-1.1.0.1/src/speech/audiodatastream.cpp0000664000175000017500000000314715207167112020512 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "audiodatastream.h" #include #include "_audiodatastream.h" AudioDataStream *audio_data_stream_create() { auto *ptr = new _AudioDataStream(); ptr->audio_data_to_stream_write_func = nullptr; ptr->p_user_data = nullptr; return ptr; } void audio_data_stream_destroy(AudioDataStream **stream) { if (stream == nullptr) { return; } delete *stream; *stream = nullptr; } int audio_data_stream_write(AudioDataStream *stream, const uint8_t *data, uint32_t data_length) { if (stream == nullptr) { return -1; } if (data == nullptr) { return -1; } if (data_length == 0) { return -1; } if (stream->audio_data_to_stream_write_func == nullptr) { return -1; } return stream->audio_data_to_stream_write_func(data, data_length, stream->p_user_data); }libkysdk-coreai-1.1.0.1/src/speech/coreaispeechserver.h0000664000175000017500000000350215207167112020664 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef SRC_CORE_AI_SPEECH_SERVER_H #define SRC_CORE_AI_SPEECH_SERVER_H #include #include #include #include namespace kyai::core::speech { using OnConnectionClosed = std::function; class CoreAiSpeechServer { public: static CoreAiSpeechServer &getInstance(); ~CoreAiSpeechServer(); void init(); bool available() const { return connection_ != nullptr; } GDBusConnection *getConnection() const { return connection_; } void setOnConnectionClosed(const OnConnectionClosed &onConnectionClosed) { onConnectionClosed_ = onConnectionClosed; } private: CoreAiSpeechServer(); void init(const std::string &unixPath); void destroy(); static void onConnectionClosed(GDBusConnection *connection, gboolean remotePeerVanished, GError *error, gpointer userData); private: GDBusConnection *connection_{nullptr}; gulong handlerId_{}; OnConnectionClosed onConnectionClosed_; mutable std::mutex mutex_; }; } // namespace kyai::core::speech #endif // SRC_CORE_AI_SPEECH_SERVER_H libkysdk-coreai-1.1.0.1/src/speech/_speechsynthesisresult.cpp0000664000175000017500000000335415207167112022162 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "_speechsynthesisresult.h" #include "result.h" SpeechResultReason speech_synthesis_result_get_reason( SpeechSynthesisResult *result) { return result->reason; } const char *speech_synthesis_result_get_audio_format( SpeechSynthesisResult *result) { return result->audioFormat.c_str(); } int speech_synthesis_result_get_audio_rate(SpeechSynthesisResult *result) { return result->audioRate; } int speech_synthesis_result_get_audio_channel(SpeechSynthesisResult *result) { return result->audioChannel; } const uint8_t *speech_synthesis_result_get_data(SpeechSynthesisResult *result, uint32_t *data_length) { if (result == nullptr) { return nullptr; } *data_length = result->audioData.size(); return result->audioData.data(); } int speech_synthesis_result_get_error_code(SpeechSynthesisResult *result) { return result->errorCode; } const char *speech_synthesis_result_get_error_message( SpeechSynthesisResult *result) { return result->errorMessage.c_str(); } libkysdk-coreai-1.1.0.1/src/speech/_audiodatastream.h0000664000175000017500000000206315207167112020312 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef _AUDIODATASTREAM_H #define _AUDIODATASTREAM_H struct _AudioDataStream { typedef int (*AudioDataToStreamWriteFunc)(const uint8_t *data, uint32_t data_length, void *p_user_data); AudioDataToStreamWriteFunc audio_data_to_stream_write_func; void *p_user_data; }; #endif libkysdk-coreai-1.1.0.1/src/speech/audioconfig.cpp0000664000175000017500000001354015207167112017630 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "audioconfig.h" #include #include #include "_audioconfig.h" #include "logger.h" std::string _AudioConfig::toJsonString() const { Json::Value root; // 设置输入来源类型 switch (inputAudioInfo.inputSource) { case InputAudioInfo::InputSource::DEFAULT_MIC: root["input_source"] = int(InputAudioInfo::InputSource::DEFAULT_MIC); break; case InputAudioInfo::InputSource::STREAM: root["input_source"] = int(InputAudioInfo::InputSource::STREAM); break; case InputAudioInfo::InputSource::PCM_FILE: root["input_source"] = int(InputAudioInfo::InputSource::PCM_FILE); root["pcm_file_path"] = inputAudioInfo.inputFilePath.string(); break; case InputAudioInfo::InputSource::RAW_DATA: root["input_source"] = int(InputAudioInfo::InputSource::RAW_DATA); root["raw_data"] = util::base64Encode(inputAudioInfo.audioData.data(), inputAudioInfo.audioData.size()); break; default: LOGE("未知输入源"); break; } // 设置输出目标类型 switch (outputAudioInfo.outputTarget) { case OutputAudioInfo::OutputTarget::DEFAULT_SPEAKER: root["output_target"] = int(OutputAudioInfo::OutputTarget::DEFAULT_SPEAKER); break; case OutputAudioInfo::OutputTarget::PCM_OUTPUT_FILE: root["output_target"] = int(OutputAudioInfo::OutputTarget::PCM_OUTPUT_FILE); root["output_pcm_file_path"] = outputAudioInfo.outputFilePath.string(); break; case OutputAudioInfo::OutputTarget::PCM_DATA: root["output_target"] = int(OutputAudioInfo::OutputTarget::PCM_DATA); break; default: LOGE("未知输出源"); break; } // 设置采样率 root["sample_rate"] = inputAudioInfo.rate; // 设置输出音频格式 root["speed"] = outputAudioInfo.speed; root["volume"] = outputAudioInfo.volume; root["pitch"] = outputAudioInfo.pitch; // 序列化 JSON 对象 Json::FastWriter writer; return writer.write(root); } AudioConfig * audio_config_create_continuous_audio_input_from_default_microphone() { AudioConfig *config = new AudioConfig{}; config->inputAudioInfo.inputSource = InputAudioInfo::InputSource::DEFAULT_MIC; return config; } AudioConfig *audio_config_create_continuous_audio_input_from_audio_data_stream( AudioDataStream *audio_data_stream) { AudioConfig *config = new AudioConfig{}; config->inputAudioInfo.inputSource = InputAudioInfo::InputSource::STREAM; config->inputAudioInfo.audioDataStream = audio_data_stream; return config; } AudioConfig *audio_config_create_once_audio_input_from_pcm_file( const char *pcm_file) { AudioConfig *config = new AudioConfig{}; config->inputAudioInfo.inputSource = InputAudioInfo::InputSource::PCM_FILE; config->inputAudioInfo.inputFilePath = pcm_file; return config; } AudioConfig *audio_config_create_once_audio_input_from_pcm_data( const uint8_t *audio_data, uint32_t data_length) { AudioConfig *config = new AudioConfig{}; config->inputAudioInfo.inputSource = InputAudioInfo::InputSource::RAW_DATA; config->inputAudioInfo.audioData.assign(audio_data, audio_data + data_length); return config; } AudioConfig *audio_config_create_audio_output_from_pcm_data() { AudioConfig *config = new AudioConfig{}; config->outputAudioInfo.outputTarget = OutputAudioInfo::OutputTarget::PCM_DATA; return config; } // 使用系统默认扬声器作为音频输出 AudioConfig *audio_config_create_audio_output_from_default_speaker() { AudioConfig *config = new AudioConfig{}; config->outputAudioInfo.outputTarget = OutputAudioInfo::OutputTarget::DEFAULT_SPEAKER; return config; } // 使用pcm文件作为音频输出 AudioConfig *audio_config_create_audio_output_from_pcm_file_name( const char *pcm_file) { AudioConfig *config = new AudioConfig{}; config->outputAudioInfo.outputTarget = OutputAudioInfo::OutputTarget::PCM_OUTPUT_FILE; config->outputAudioInfo.outputFilePath = pcm_file; return config; } void audio_config_destroy(AudioConfig **config) { if (config == nullptr) { return; } delete *config; *config = nullptr; } void audio_config_set_input_audio_rate(AudioConfig *config, int rate) { if (config == nullptr) { return; } config->inputAudioInfo.rate = rate; } void audio_config_set_output_audio_speed(AudioConfig *config, int speed) { if (config == nullptr) { return; } config->outputAudioInfo.speed = speed; } void audio_config_set_output_audio_volume(AudioConfig *config, int volume) { if (config == nullptr) { return; } config->outputAudioInfo.volume = volume; } void audio_config_set_output_audio_pitch(AudioConfig *config, int pitch) { if (config == nullptr) { return; } config->outputAudioInfo.pitch = pitch; } libkysdk-coreai-1.1.0.1/src/speech/_speechsynthesisresult.h0000664000175000017500000000203215207167112021617 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef _SPEECHSYNTHESISRESULT_H_ #define _SPEECHSYNTHESISRESULT_H_ #include #include #include "result.h" struct _SpeechSynthesisResult { SpeechResultReason reason; std::string audioFormat; int audioRate; int audioChannel; std::vector audioData; int errorCode; std::string errorMessage; }; #endif libkysdk-coreai-1.1.0.1/src/speech/coreaispeechserviceproxy.cpp0000664000175000017500000005605215207167112022463 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "coreaispeechserviceproxy.h" #include #include #include #include "_speechmodelconfig.h" #include "_speechrecognitionresult.h" #include "_speechsynthesisresult.h" #include "coreaispeechserver.h" #include "logger.h" #include "util.h" namespace kyai { namespace core { namespace speech { const char *objectPath = "/com/kylin/AiRuntime/CoreSpeech"; const char *interface = "com.kylin.AiRuntime.CoreSpeech"; const char *recognitionSpeechResultSignal = "RecognitionSpeechResult"; const char *synthesisResultSignal = "SynthesisResult"; _SpeechRecognitionResult dissectRecognitionResult(GVariant *parameters) { _SpeechRecognitionResult result{}; GVariantIter iter; g_variant_iter_init(&iter, parameters); GVariant *reason = g_variant_iter_next_value(&iter); if (reason != nullptr) { int *pReason = (int *)g_variant_get_data(reason); if (pReason != nullptr) { result.reason = SpeechResultReason(*pReason); } else { LOGE("recognition result error: result is nullptr!"); } g_variant_unref(reason); } else { LOGE("recognition result error: result is nullptr!"); } GVariant *text = g_variant_iter_next_value(&iter); if (text != nullptr) { char *pText = (char *)g_variant_get_data(text); if (pText != nullptr) { result.text = pText; } else { LOGE("recognition result error: result is nullptr!"); } g_variant_unref(text); } else { LOGE("recognition result error: result is nullptr!"); } GVariant *speakerId = g_variant_iter_next_value(&iter); if (speakerId != nullptr) { int *pSpeakerId = (int *)g_variant_get_data(speakerId); if (pSpeakerId != nullptr) { result.speakerId = *pSpeakerId; } else { LOGE("recognition result error: result is nullptr!"); } g_variant_unref(speakerId); } else { LOGE("recognition result error: result is nullptr!"); } GVariant *errorCode = g_variant_iter_next_value(&iter); if (errorCode != nullptr) { int *pErrorCode = (int *)g_variant_get_data(errorCode); if (pErrorCode != nullptr) { result.errorCode = *pErrorCode; } else { LOGE("recognition result error: result is nullptr!"); } g_variant_unref(errorCode); } else { LOGE("recognition result error: result is nullptr!"); } GVariant *errorMessage = g_variant_iter_next_value(&iter); if (errorMessage != nullptr) { char *pErrorMessage = (char *)g_variant_get_data(errorMessage); if (pErrorMessage != nullptr) { result.errorMessage = pErrorMessage; } else { LOGE("recognition result error: result is nullptr!"); } g_variant_unref(errorMessage); } else { LOGE("recognition result error: result is nullptr!"); } return result; } _SpeechSynthesisResult dissectSynthesisResult(GVariant *parameters) { _SpeechSynthesisResult result{}; GVariantIter iter; g_variant_iter_init(&iter, parameters); // 获取字节数组 GVariant *audioData = g_variant_iter_next_value(&iter); if (audioData != nullptr) { gchar *pAudioData = (gchar *)g_variant_get_data(audioData); if (pAudioData != nullptr) { std::string encode = pAudioData; result.audioData = util::base64Decode(encode); } else { LOGE("recognition result error: result is nullptr!"); } g_variant_unref(audioData); } else { LOGE("recognition result error: result is nullptr!"); } GVariant *reason = g_variant_iter_next_value(&iter); if (reason != nullptr) { int *pReason = (int *)g_variant_get_data(reason); if (pReason != nullptr) { result.reason = SpeechResultReason(*pReason); } else { LOGE("recognition result error: result is nullptr!"); } g_variant_unref(reason); } else { LOGE("recognition result error: result is nullptr!"); } // 获取整数 GVariant *audioFormat = g_variant_iter_next_value(&iter); if (audioFormat != nullptr) { char *pAudioFormat = (char *)g_variant_get_data(audioFormat); if (pAudioFormat != nullptr) { result.audioFormat = pAudioFormat; } else { LOGE("recognition result error: result is nullptr!"); } g_variant_unref(audioFormat); } else { LOGE("recognition result error: result is nullptr!"); } GVariant *audioRate = g_variant_iter_next_value(&iter); if (audioRate != nullptr) { int *pAudioRate = (int *)g_variant_get_data(audioRate); if (pAudioRate != nullptr) { result.audioRate = *pAudioRate; } else { LOGE("recognition result error: result is nullptr!"); } g_variant_unref(audioRate); } else { LOGE("recognition result error: result is nullptr!"); } GVariant *audioChannel = g_variant_iter_next_value(&iter); if (audioChannel != nullptr) { int *pAudioChannel = (int *)g_variant_get_data(audioChannel); if (pAudioChannel != nullptr) { result.audioChannel = *pAudioChannel; } else { LOGE("recognition result error: result is nullptr!"); } g_variant_unref(audioChannel); } else { LOGE("recognition result error: result is nullptr!"); } GVariant *errorCode = g_variant_iter_next_value(&iter); if (errorCode != nullptr) { int *pErrorCode = (int *)g_variant_get_data(errorCode); if (pErrorCode != nullptr) { result.errorCode = *pErrorCode; } else { LOGE("recognition result error: result is nullptr!"); } g_variant_unref(errorCode); } else { LOGE("recognition result error: result is nullptr!"); } GVariant *errorMessage = g_variant_iter_next_value(&iter); if (errorMessage != nullptr) { char *pErrorMessage = (char *)g_variant_get_data(errorMessage); if (pErrorMessage != nullptr) { result.errorMessage = pErrorMessage; } else { LOGE("recognition result error: result is nullptr!"); } g_variant_unref(errorMessage); } else { LOGE("recognition result error: result is nullptr!"); } return result; } void CoreAiSpeechServiceProxy::setRecognitionResultCallback( SpeechRecognitionResultCallback callback, void *userData) { recognitionCallback_ = callback; recognitionUserData_ = userData; } void CoreAiSpeechServiceProxy::setSynthesizerCallback( SpeechSynthesisResultCallback callback, void *userData) { synthesizerCallback_ = callback; synthesizerUserData_ = userData; } void CoreAiSpeechServiceProxy::setReconnectedCallback( const ReconnectedCallback &callback) { reconnectedCallback_ = callback; } CoreAiSpeechServiceProxy::~CoreAiSpeechServiceProxy() { if (not CoreAiSpeechServer::getInstance().available()) { g_printerr( "Error creating core speech server proxy: Server proxy " "connection is unavailable.\n"); return; } auto *connection = CoreAiSpeechServer::getInstance().getConnection(); if (recognitionSubscriptionId_ != 0) { // 取消订阅 g_dbus_connection_signal_unsubscribe(connection, recognitionSubscriptionId_); } if (synthesizerSubscriptionId_ != 0) { // 取消订阅 g_dbus_connection_signal_unsubscribe(connection, synthesizerSubscriptionId_); } CoreAiSpeechServer::getInstance().setOnConnectionClosed(nullptr); } bool CoreAiSpeechServiceProxy::connectToServer() { CoreAiSpeechServer::getInstance().init(); if (!CoreAiSpeechServer::getInstance().available()) { g_printerr( "Error creating core speech server proxy: Server proxy " "connection is unavailable.\n"); return false; } if (proxy_ != nullptr) { g_object_unref(proxy_); proxy_ = nullptr; } GError *error = nullptr; auto *connection = CoreAiSpeechServer::getInstance().getConnection(); proxy_ = ai_runtime_core_speech_service_proxy_new_sync( connection, G_DBUS_PROXY_FLAGS_NONE, nullptr, objectPath, nullptr, &error); if (proxy_ == nullptr) { if (error) { g_printerr("Error creating text processor proxy %s: %s\n", objectPath, error->message); g_error_free(error); } return false; } g_dbus_proxy_set_default_timeout(G_DBUS_PROXY(proxy_), 60 * 60 * 1000); CoreAiSpeechServer::getInstance().setOnConnectionClosed([this] { // 流式语音识别报错 if (continuousRecognitionStarted_ && recognitionCallback_) { SpeechRecognitionResult result{}; result.reason = SPEECH_ERROR_OCCURRED; result.text = ""; result.speakerId = -1; result.errorCode = AISDK_RUNTIME_ERROR; result.errorMessage = "Server closed unexpectedly."; recognitionCallback_(&result, recognitionUserData_); } continuousRecognitionStarted_ = false; if (reconnectServer()) { if (reconnectedCallback_) { reconnectedCallback_(); } } }); return true; } bool CoreAiSpeechServiceProxy::reconnectServer() { if (reconnectAttempts_ >= maxReconnectAttempts) { LOGE("Max reconnect attempts reached"); return false; } reconnectAttempts_++; LOGD("Reconnect attempt {}.", reconnectAttempts_); if (!connectToServer()) { LOGE("Failed to reconnect to server"); std::this_thread::sleep_for(std::chrono::seconds(1)); return reconnectServer(); } LOGI("Successfully reconnected to server."); return true; } int CoreAiSpeechServiceProxy::initRecognizer(const char *config, int &sessionId) { int errorCode = AISDK_NO_ERROR; GError *error = nullptr; bool success = ai_runtime_core_speech_service_call_init_recognizer_sync( proxy_, config, &sessionId, &errorCode, nullptr, &error); if (!success) { if (error != nullptr) { LOGE("Error calling init:", error->code, error->message); g_error_free(error); return AISDK_RUNTIME_ERROR; } } if (sessionId != -1) { if (!recognitionResultSignalConnected_) { // 只能调用一次,否则回调函数会被调用两次,关闭连接不行 connectSpeechRecognitionResultSignals(sessionId); } recognitionResultSignalConnected_ = true; reconnectAttempts_ = 0; } return errorCode; } int CoreAiSpeechServiceProxy::initSynthesizer(const char *config, int &sessionId) { int errorCode = AISDK_NO_ERROR; GError *error = nullptr; LOGD(sessionId); bool success = ai_runtime_core_speech_service_call_init_synthesizer_sync( proxy_, config, &sessionId, &errorCode, nullptr, &error); LOGD(sessionId); if (!success) { if (error != nullptr) { LOGE("Error calling init:", error->code, error->message); g_error_free(error); return AISDK_RUNTIME_ERROR; } } if (sessionId != -1) { if (!synthesisResultSignalConnected_) { // 只能调用一次,否则回调函数会被调用两次,关闭连接不行 connectSpeechSynthesisResultSignals(sessionId); } synthesisResultSignalConnected_ = true; reconnectAttempts_ = 0; } return errorCode; } void CoreAiSpeechServiceProxy::destroyRecognizer(int sessionId) { ai_runtime_core_speech_service_call_destroy_recognizer_sync( proxy_, sessionId, nullptr, nullptr); reconnectAttempts_ = 0; } void CoreAiSpeechServiceProxy::destroySynthesizer(int sessionId) { ai_runtime_core_speech_service_call_destroy_synthesizer_sync( proxy_, sessionId, nullptr, nullptr); reconnectAttempts_ = 0; } void CoreAiSpeechServiceProxy::setRecognizerAudioConfig( int sessionId, const std::string &audioConfig) { ai_runtime_core_speech_service_call_set_recognizer_audio_config_sync( proxy_, sessionId, audioConfig.c_str(), nullptr, nullptr); LOGD("setRecognizerAudioConfig:"); } void CoreAiSpeechServiceProxy::startContinuousRecognition(int sessionId) { ai_runtime_core_speech_service_call_start_continuous_recognition( proxy_, sessionId, nullptr, nullptr, nullptr); LOGD("startContinuousRecognition."); continuousRecognitionStarted_ = true; } void CoreAiSpeechServiceProxy::setSynthesizerAudioConfig( int sessionId, const std::string &audioConfig) { bool ret = ai_runtime_core_speech_service_call_set_synthesizer_audio_config_sync( proxy_, sessionId, audioConfig.c_str(), nullptr, nullptr); if (!ret) { std::fprintf( stderr, "CoreSpeechServiceProxy: error to set synthesizer audio config\n"); } } void CoreAiSpeechServiceProxy::synthesizeTextOnce(int sessionId, const char *text, uint32_t textLength) { ai_runtime_core_speech_service_call_synthesize_text_once( proxy_, sessionId, text, textLength, nullptr, onOnceSynthesisFinishedCallback, this); } void CoreAiSpeechServiceProxy::synthesizeTextContinuous(int sessionId, const char *text, uint32_t textLength) { ai_runtime_core_speech_service_call_synthesize_text_continuous( proxy_, sessionId, text, textLength, nullptr, nullptr, nullptr); } int CoreAiSpeechServiceProxy::stopSpeaking(int sessionId) { int errorCode; GError *error = nullptr; bool ret = ai_runtime_core_speech_service_call_stop_speaking_sync( proxy_, sessionId, &errorCode, nullptr, &error); if (!ret) { LOGE("Error to stop speaking.", error->message); return AiSdkCommonErrorCode::AISDK_RUNTIME_ERROR; } return (int)errorCode; } void CoreAiSpeechServiceProxy::stopContinuousRecognition(int sessionId) { ai_runtime_core_speech_service_call_stop_continuous_recognition( proxy_, sessionId, nullptr, nullptr, nullptr); LOGI("stopContinuousRecognition."); continuousRecognitionStarted_ = false; } void CoreAiSpeechServiceProxy::continuousRecognitionWriteAudioData( int sessionId, const uint8_t *data, uint32_t length) { const char *ptr = reinterpret_cast(data); std::string audioData(ptr, length); std::string encode = util::base64Encode(audioData); ai_runtime_core_speech_service_call_continuous_recognition_write_audio_data( proxy_, sessionId, encode.c_str(), nullptr, nullptr, nullptr); } void CoreAiSpeechServiceProxy::recognizeOnce(int sessionId) { ai_runtime_core_speech_service_call_recognize_once( proxy_, sessionId, nullptr, onOnceRecognitionFinishedCallback, this); LOGD("recognizeOnce."); } void CoreAiSpeechServiceProxy::connectSpeechRecognitionResultSignals( int sessionId) { auto *connection = CoreAiSpeechServer::getInstance().getConnection(); std::string interfaceName = interface + std::to_string(sessionId); recognitionSubscriptionId_ = g_dbus_connection_signal_subscribe( connection, nullptr, interfaceName.c_str(), recognitionSpeechResultSignal, objectPath, nullptr, G_DBUS_SIGNAL_FLAGS_NONE, onRecognitionResultCallback, this, nullptr); } void CoreAiSpeechServiceProxy::connectSpeechSynthesisResultSignals( int sessionId) { auto *connection = CoreAiSpeechServer::getInstance().getConnection(); std::string interfaceName = interface + std::to_string(sessionId); synthesizerSubscriptionId_ = g_dbus_connection_signal_subscribe( connection, nullptr, interfaceName.c_str(), synthesisResultSignal, objectPath, nullptr, G_DBUS_SIGNAL_FLAGS_NONE, onSynthesisResult, this, nullptr); } void CoreAiSpeechServiceProxy::onRecognitionResultCallback( GDBusConnection *connection, const gchar *senderName, const gchar *objectPath, const gchar *interfaceName, const gchar *signalName, GVariant *parameters, gpointer userData) { auto *proxy = static_cast(userData); if (proxy->recognitionCallback_ != nullptr) { _SpeechRecognitionResult result = dissectRecognitionResult(parameters); proxy->recognitionCallback_(&result, proxy->recognitionUserData_); } else { LOGE("Recognizing callback is not set!"); } } void CoreAiSpeechServiceProxy::onSynthesisResult( GDBusConnection *connection, const gchar *senderName, const gchar *objectPath, const gchar *interfaceName, const gchar *signalName, GVariant *parameters, gpointer userData) { auto *proxy = static_cast(userData); if (!proxy->synthesizerCallback_) { std::fprintf( stderr, "CoreSpeechServiceProxy: Synthesizer callback is not set!\n"); return; } auto result = dissectSynthesisResult(parameters); proxy->synthesizerCallback_(&result, proxy->synthesizerUserData_); } void CoreAiSpeechServiceProxy::onOnceRecognitionFinishedCallback( GObject *sourceObject, GAsyncResult *res, gpointer userData) { auto *proxy = static_cast(userData); GError *error{nullptr}; bool result = ai_runtime_core_speech_service_call_recognize_once_finish( (AiRuntimeCoreSpeechService *)sourceObject, res, &error); if (!result) { if (error == nullptr) { g_printerr("Error calling once asr: Unknown error\n"); } else { g_printerr("Error calling once asr: %s\n", error->message); proxy->handleOnceRecognitionError(error->code); g_error_free(error); } } } void CoreAiSpeechServiceProxy::handleOnceRecognitionError(int errorCode) { LOGE("Error occurred with code:", errorCode); switch (errorCode) { case G_IO_ERROR_CLOSED: handleOnceRecognitionServerClosed(); break; case G_IO_ERROR_TIMED_OUT: handleOnceRecognitionResultTimeout(); break; default: handleOnceRecognitionGeneralError(); break; } } void CoreAiSpeechServiceProxy::handleOnceRecognitionServerClosed() { LOGE("Server closed unexpectedly."); if (recognitionCallback_) { SpeechRecognitionResult result{}; result.reason = SPEECH_ERROR_OCCURRED; result.text = ""; result.speakerId = -1; result.errorCode = AISDK_RUNTIME_ERROR; result.errorMessage = "Server closed unexpectedly."; recognitionCallback_(&result, recognitionUserData_); } } void CoreAiSpeechServiceProxy::handleOnceRecognitionResultTimeout() { if (recognitionCallback_) { SpeechRecognitionResult result{}; result.reason = SPEECH_ERROR_OCCURRED; result.text = ""; result.speakerId = -1; result.errorCode = AISDK_MODEL_RUN_TIME_OUT; result.errorMessage = "Run model time out."; recognitionCallback_(&result, recognitionUserData_); } } void CoreAiSpeechServiceProxy ::handleOnceRecognitionGeneralError() { if (recognitionCallback_) { SpeechRecognitionResult result{}; result.reason = SPEECH_ERROR_OCCURRED; result.text = ""; result.speakerId = -1; result.errorCode = AISDK_RUNTIME_ERROR; result.errorMessage = "General error occurred."; recognitionCallback_(&result, recognitionUserData_); } } void CoreAiSpeechServiceProxy::onOnceSynthesisFinishedCallback( GObject *sourceObject, GAsyncResult *res, gpointer userData) { auto *proxy = static_cast(userData); GError *error{nullptr}; bool result = ai_runtime_core_speech_service_call_recognize_once_finish( (AiRuntimeCoreSpeechService *)sourceObject, res, &error); if (!result) { if (error == nullptr) { g_printerr("Error calling once tts: Unknown error\n"); } else { g_printerr("Error calling once tts: %s\n", error->message); proxy->handleOnceSynthesisErrorOccurred(error->code); g_error_free(error); } } } void CoreAiSpeechServiceProxy::handleOnceSynthesisErrorOccurred(int errorCode) { LOGE("Error occurred with code:", errorCode); switch (errorCode) { case G_IO_ERROR_CLOSED: handleOnceSynthesisServerClosed(); break; case G_IO_ERROR_TIMED_OUT: handleOnceSynthesisResultTimeout(); break; default: handleOnceSynthesisGeneralError(); break; } } void CoreAiSpeechServiceProxy::handleOnceSynthesisServerClosed() { if (synthesizerCallback_) { SpeechSynthesisResult result{}; result.reason = SPEECH_ERROR_OCCURRED; result.audioChannel = 0; result.audioFormat = ""; result.audioRate = 16000; result.audioChannel = 1; result.audioData = {}; result.errorCode = AISDK_RUNTIME_ERROR; result.errorMessage = "Server closed unexpectedly."; synthesizerCallback_(&result, synthesizerUserData_); } } void CoreAiSpeechServiceProxy::handleOnceSynthesisResultTimeout() { if (synthesizerCallback_) { SpeechSynthesisResult result{}; result.reason = SPEECH_ERROR_OCCURRED; result.audioChannel = 0; result.audioFormat = ""; result.audioRate = 16000; result.audioChannel = 1; result.audioData = {}; result.errorCode = AISDK_MODEL_RUN_TIME_OUT; result.errorMessage = "Run model time out."; synthesizerCallback_(&result, synthesizerUserData_); } } void CoreAiSpeechServiceProxy::handleOnceSynthesisGeneralError() { if (synthesizerCallback_) { SpeechSynthesisResult result{}; result.reason = SPEECH_ERROR_OCCURRED; result.audioChannel = 0; result.audioFormat = ""; result.audioRate = 16000; result.audioChannel = 1; result.audioData = {}; result.errorCode = AISDK_RUNTIME_ERROR; result.errorMessage = "General error occurred."; synthesizerCallback_(&result, synthesizerUserData_); } } } // namespace speech } // namespace core } // namespace kyai libkysdk-coreai-1.1.0.1/src/speech/_speechrecognitionsession.h0000664000175000017500000000362315207167112022262 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef _SPEECHRECOGNIZER_ #define _SPEECHRECOGNIZER_ #include "_audioconfig.h" #include "_speechmodelconfig.h" #include "coreaispeechserviceproxy.h" #include "result.h" namespace kyai { namespace core { namespace speech { class _SpeechRecognitionSession { public: _SpeechRecognitionSession(); ~_SpeechRecognitionSession(); int init(); void destroyRecognizer(); void setRecognizerAudioConfig(_AudioConfig *config); [[nodiscard]] _AudioConfig *getRecognizerAudioConfig() const; void setRecognitionResultCallback(SpeechRecognitionResultCallback callback, void *userData); void startContinuousRecognition(); void stopContinuousRecognition(); void continuousRecognitionWriteAudioData(const uint8_t *data, uint32_t length); void recognizeOnce(); void setRecognitionModelConfig(const SpeechModelConfig &config); private: bool initServer(); int initSession(); private: _AudioConfig *audioConfig_{nullptr}; CoreAiSpeechServiceProxy serviceProxy_; int sessionId_{-1}; SpeechModelConfig modelConfig_{}; }; } // namespace speech } // namespace core } // namespace kyai #endif libkysdk-coreai-1.1.0.1/src/speech/_speechmodelconfig.h0000664000175000017500000000177015207167112020625 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef _COREAISPEECHMODELCONFIG_H #define _COREAISPEECHMODELCONFIG_H #include #include "config.h" struct _SpeechModelConfig { std::string name{}; ModelDeployType type{ModelDeployType(-1)}; }; std::string modelConfigToJson(const _SpeechModelConfig &modelconfig); #endif //_COREAISPEECHMODELCONFIG_H libkysdk-coreai-1.1.0.1/src/speech/_speechrecognitionresult.h0000664000175000017500000000204715207167112022114 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef _SPEECHRCOGNITIONRESULT_H_ #define _SPEECHRCOGNITIONRESULT_H_ #include #include "result.h" struct _SpeechRecognitionResult { SpeechResultReason reason; std::string text; int speakerId; int errorCode; std::string errorMessage; }; const char *speech_recognition_result_get_reason_str( SpeechRecognitionResult *result); #endiflibkysdk-coreai-1.1.0.1/src/speech/recognizer.cpp0000664000175000017500000001122415207167112017505 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "recognizer.h" #include "_audiodatastream.h" #include "_speechrecognitionsession.h" SpeechRecognitionSession *speech_recognizer_create_session() { auto *session = new kyai::core::speech::_SpeechRecognitionSession(); return (SpeechRecognitionSession *)session; } void speech_recognizer_destroy_session(SpeechRecognitionSession **session) { if (session == nullptr) { return; } if (*session == nullptr) { return; } ((kyai::core::speech::_SpeechRecognitionSession *)*session) ->destroyRecognizer(); delete (kyai::core::speech::_SpeechRecognitionSession *)*session; *session = nullptr; } int speech_recognizer_init_session(SpeechRecognitionSession *session) { if (session == nullptr) { return AISDK_INVALID_SESSION; } return ((kyai::core::speech::_SpeechRecognitionSession *)session)->init(); } void speech_recognizer_result_set_callback( SpeechRecognitionSession *session, SpeechRecognitionResultCallback callback, void *user_data) { if (session == nullptr) { return; } ((kyai::core::speech::_SpeechRecognitionSession *)session) ->setRecognitionResultCallback(callback, user_data); } void speech_recognizer_set_model_config(SpeechRecognitionSession *session, SpeechModelConfig *config) { if (session == nullptr or config == nullptr) { return; } ((kyai::core::speech::_SpeechRecognitionSession *)session) ->setRecognitionModelConfig(*config); } static int speech_recognizer_continuous_recognition_write_audio_data( const uint8_t *audio_data, uint32_t audio_length, void *user_data) { if (user_data == nullptr) { return AiSdkCommonErrorCode::AISDK_RUNTIME_ERROR; } // 此函数为private ((kyai::core::speech::_SpeechRecognitionSession *)user_data) ->continuousRecognitionWriteAudioData(audio_data, audio_length); return AiSdkCommonErrorCode::AISDK_NO_ERROR; } void speech_recognizer_set_audio_config(SpeechRecognitionSession *session, AudioConfig *audio_config) { if (session == nullptr) { return; } if (audio_config == nullptr) { return; } ((kyai::core::speech::_SpeechRecognitionSession *)session) ->setRecognizerAudioConfig(audio_config); if (audio_config->inputAudioInfo.inputSource != InputAudioInfo::InputSource::STREAM) { return; } if (audio_config->inputAudioInfo.audioDataStream == nullptr) { return; } audio_config->inputAudioInfo.audioDataStream->p_user_data = session; audio_config->inputAudioInfo.audioDataStream ->audio_data_to_stream_write_func = speech_recognizer_continuous_recognition_write_audio_data; } void speech_recognizer_start_continuous_recognition_async( SpeechRecognitionSession *session) { if (session == nullptr) { return; } ((kyai::core::speech::_SpeechRecognitionSession *)session) ->startContinuousRecognition(); } // 停止连续语音识别 void speech_recognizer_stop_continuous_recognition_async( SpeechRecognitionSession *session) { if (session == nullptr) { return; } ((kyai::core::speech::_SpeechRecognitionSession *)session) ->stopContinuousRecognition(); } // 非流式语音识别 // 支持文件和内存数据 void speech_recognizer_recognize_once_async(SpeechRecognitionSession *session) { if (session == nullptr) { return; } AudioConfig *audioConfig = ((kyai::core::speech::_SpeechRecognitionSession *)session) ->getRecognizerAudioConfig(); if (audioConfig == nullptr) { return; } if (audioConfig->inputAudioInfo.inputSource == InputAudioInfo::InputSource::DEFAULT_MIC) { return; } if (audioConfig->inputAudioInfo.inputSource == InputAudioInfo::InputSource::STREAM) { return; } ((kyai::core::speech::_SpeechRecognitionSession *)session)->recognizeOnce(); } libkysdk-coreai-1.1.0.1/src/speech/_speechrecognitionresult.cpp0000664000175000017500000000421415207167112022445 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "_speechrecognitionresult.h" #include #include "result.h" SpeechResultReason speech_recognition_result_get_reason( SpeechRecognitionResult *result) { return result->reason; } const char *speech_recognition_result_get_reason_str( SpeechRecognitionResult *result) { std::map map{ // {NO_MATCH, "NO_MATCH"}, {SPEECH_ERROR_OCCURRED, "SPEECH_ERROR_OCCURRED"}, {SPEECH_RECOGNITION_STARTED, "SPEECH_RECOGNITION_STARTED"}, {SPEECH_RECOGNIZING, "SPEECH_RECOGNIZING"}, {SPEECH_RECOGNIZED, "SPEECH_RECOGNIZED"}, {SPEECH_RECOGNITION_COMPLETED, "SPEECH_RECOGNITION_COMPLETED"}, {SPEECH_SYNTHESIS_STARTED, "SPEECH_SYNTHESIS_STARTED"}, {SPEECH_SYNTHESIZING, "SPEECH_SYNTHESIZING"}, {SPEECH_SYNTHESIS_COMPLETED, "SPEECH_SYNTHESIS_COMPLETED"}, }; if (map.find(result->reason) != map.end()) { return map[result->reason]; } else { return nullptr; } } const char *speech_recognition_result_get_text( SpeechRecognitionResult *result) { return result->text.c_str(); } int speech_recognition_result_get_speaker_id(SpeechRecognitionResult *result) { return result->speakerId; } int speech_recognition_result_get_error_code(SpeechRecognitionResult *result) { return result->errorCode; } const char *speech_recognition_result_get_error_message( SpeechRecognitionResult *result) { return result->errorMessage.c_str(); } libkysdk-coreai-1.1.0.1/src/util.cpp0000664000175000017500000000406715207167112015053 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "util.h" #include #include #include #include namespace util { std::string base64Encode(const void* data, std::size_t size) { if (data == nullptr) { return ""; } BIO* bio{nullptr}; BIO* b64{nullptr}; BUF_MEM* bufferPtr{nullptr}; b64 = BIO_new(BIO_f_base64()); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); bio = BIO_new(BIO_s_mem()); bio = BIO_push(b64, bio); BIO_write(bio, data, static_cast(size)); BIO_flush(bio); BIO_get_mem_ptr(bio, &bufferPtr); std::string result(bufferPtr->data, bufferPtr->length); BIO_free_all(bio); return result; } std::string base64Encode(const std::vector& input) { return base64Encode(input.data(), input.size()); } std::string base64Encode(const std::string& input) { return base64Encode(input.data(), input.size()); } std::vector base64Decode(const std::string& input) { gsize decodedLength{}; guchar* decodedData = g_base64_decode(input.c_str(), &decodedLength); std::vector result(decodedData, decodedData + decodedLength); g_free(decodedData); return result; } bool stringContains(const std::string& str, const std::string& sub) { if (str.length() < sub.length()) { return false; }; return str.find(sub) != std::string::npos; } } // namespace utillibkysdk-coreai-1.1.0.1/include/0000775000175000017500000000000015207167112014217 5ustar zpzplibkysdk-coreai-1.1.0.1/include/kylin-ai/0000775000175000017500000000000015207167112015734 5ustar zpzplibkysdk-coreai-1.1.0.1/include/kylin-ai/coreai/0000775000175000017500000000000015207167112017176 5ustar zpzplibkysdk-coreai-1.1.0.1/include/kylin-ai/coreai/vision/0000775000175000017500000000000015207167112020505 5ustar zpzplibkysdk-coreai-1.1.0.1/include/kylin-ai/coreai/vision/error.h0000664000175000017500000000172015207167112022007 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef __KYLIN_AI_CORE_AI_VISION_ERROR_H__ #define __KYLIN_AI_CORE_AI_VISION_ERROR_H__ #include #ifdef __cplusplus extern "C" { #endif typedef enum { OCR_IMAGE_ERROR = 100, OCR_PARAM_INVALID, } OcrErrorCode; #ifdef __cplusplus } #endif #endiflibkysdk-coreai-1.1.0.1/include/kylin-ai/coreai/vision/textrecognition.h0000664000175000017500000000423315207167112024105 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef __KYLIN_AI_CORE_AI_VISION_TEXT_RECOGNITION_H__ #define __KYLIN_AI_CORE_AI_VISION_TEXT_RECOGNITION_H__ #include #include "config.h" #include "textrecognitionresult.h" #ifdef __cplusplus extern "C" { #endif typedef struct _TextRecognitionSession TextRecognitionSession; typedef void (*TextRecognitionResultCallback)(TextRecognitionResult *result, void *user_data); // 创建session AISDK_EXTERN TextRecognitionSession *text_recognition_create_session(); // 销毁session AISDK_EXTERN void text_recognition_destroy_session(TextRecognitionSession **session); // 初始化session AISDK_EXTERN int text_recognition_init_session(TextRecognitionSession *session); // 设置结果回调 AISDK_EXTERN void text_recognition_result_set_callback( TextRecognitionSession *session, TextRecognitionResultCallback callback, void *user_data); // 配置Ocr模型 AISDK_EXTERN void text_recognition_set_model_config(TextRecognitionSession *session, TextRecognitionModelConfig *config); // 从图像文件中识别 AISDK_EXTERN void text_recognition_recognize_text_from_image_file_async( TextRecognitionSession *session, const char *image_file); // 从图像数据中进行识别 AISDK_EXTERN void text_recognition_recognize_text_from_image_data_async( TextRecognitionSession *session, const char *image_data, unsigned int image_data_length); #ifdef __cplusplus } #endif #endiflibkysdk-coreai-1.1.0.1/include/kylin-ai/coreai/vision/config.h0000664000175000017500000000274515207167112022133 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef __KYLIN_AI_CORE_AI_VISION_CONFIG_H__ #define __KYLIN_AI_CORE_AI_VISION_CONFIG_H__ #include #include #ifdef __cplusplus extern "C" { #endif typedef struct _TextRecognitionModelConfig TextRecognitionModelConfig; AISDK_EXTERN TextRecognitionModelConfig *text_recognition_model_config_create(); AISDK_EXTERN void text_recognition_model_config_destroy(TextRecognitionModelConfig **config); AISDK_EXTERN void text_recognition_model_config_set_name(TextRecognitionModelConfig *config, const char *name); AISDK_EXTERN void text_recognition_model_config_set_deploy_type( TextRecognitionModelConfig *config, ModelDeployType type); #ifdef __cplusplus } #endif #endif // __KYLIN_AI_CORE_AI_VISION_CONFIG_H__ libkysdk-coreai-1.1.0.1/include/kylin-ai/coreai/vision/textrecognitionresult.h0000664000175000017500000000362615207167112025351 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef __KYLIN_AI_CORE_AI_VISION_TEXT_RECOGNITION_RESULT_H__ #define __KYLIN_AI_CORE_AI_VISION_TEXT_RECOGNITION_RESULT_H__ #include #ifdef __cplusplus extern "C" { #endif typedef struct _TextRecognitionResult TextRecognitionResult; // 像素点的位置 typedef struct { int x; int y; } PixelPoint; // 图像中的一行文本 typedef struct _TextLine TextLine; // 获取一行中的文本内容 AISDK_EXTERN const char *text_line_get_value(TextLine *text_line); // 获取一行文本的角点位置信息(四个角的位置信息),point_number固定输出为4 AISDK_EXTERN PixelPoint *text_line_get_corner_points(TextLine *text_line, int *point_number); // 获取识别结果的整体文本信息,不带格式 AISDK_EXTERN const char *text_recognition_result_get_value(TextRecognitionResult *result); AISDK_EXTERN TextLine **text_recognition_result_get_text_lines(TextRecognitionResult *result, int *line_count); AISDK_EXTERN int text_recognition_result_get_error_code(TextRecognitionResult *result); AISDK_EXTERN const char *text_recognition_result_get_error_message( TextRecognitionResult *result); #ifdef __cplusplus } #endif #endif libkysdk-coreai-1.1.0.1/include/kylin-ai/coreai/embedding/0000775000175000017500000000000015207167112021114 5ustar zpzplibkysdk-coreai-1.1.0.1/include/kylin-ai/coreai/embedding/error.h0000664000175000017500000000207615207167112022423 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef EMBEDDINGERROR_H #define EMBEDDINGERROR_H #ifdef __cplusplus extern "C" { #endif typedef enum : int { COREAI_EMBEDDING_SUCESS = 0, COREAI_EMBEDDING_INPUT_ERROR, COREAI_EMBEDDING_INIT_ERROR, COREAI_EMBEDDING_CONNECTION_ERROR, COREAI_EMBEDDING_RUNTIME_ERROR, COREAI_EMBEDDING_ERROR_UNKNOWN = 99, } CoreAiEmbeddingErrorCode; #ifdef __cplusplus } #endif #endiflibkysdk-coreai-1.1.0.1/include/kylin-ai/coreai/embedding/embedding.h0000664000175000017500000002167515207167112023216 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef EMBEDDING_H #define EMBEDDING_H #ifdef __cplusplus extern "C" { #endif typedef struct _EmbeddingResult EmbeddingResult; /// @brief 获取向量化结果 返回结果与result生命周期保持一致 /// @param result 传入 向量化结果的结构体 /// @return float 数组 存储的向量数据 float *embedding_result_get_vector_data(EmbeddingResult *result); /// @brief 获取向量化结果 /// @param result 传入 向量化结果的结构体 /// @return float 数组 数组长度 int embedding_result_get_vector_length(EmbeddingResult *result); /// @brief 获取向量化错误码 /// @param result 传入 向量化结果的结构体 /// @return 错误码 int embedding_result_get_error_code(EmbeddingResult *result); /// @brief 获取向量化错误信息 返回结果与result生命周期保持一致 /// @param result 传入 向量化结果的结构体 /// @return 错误信息 const char *embedding_result_get_error_message(EmbeddingResult *result); /// @brief 销毁向量化结果 [仅同步接口需要] /// @param result void embedding_result_destroy(EmbeddingResult **result); /// @brief 释放请求模型信息返回的结果 void embedding_model_info_destroy(char *result); typedef struct _TextEmbeddingSession TextEmbeddingSession; /// @param result 生命周期为callback的作用域,资源释放由sdk侧管理 typedef void (*TextEmbeddingResultCallback)(EmbeddingResult *result, void *callback_user_data); TextEmbeddingSession *text_embedding_create_session(); void text_embedding_destroy_session(TextEmbeddingSession **session); int text_embedding_init_session(TextEmbeddingSession *session); /// @brief 获取文本向量化模型信息 同步接口 /// @param session[入参] 传入 text_embedding_create_session() 创建的 session /// @param model_info[出参] 在使用时定义一个char *model_infot=nullptr, /// 然后传入&model_info,需要embedding_model_info_destroy来销毁资源 /// @return 是否成功 ///{ /// "dim" : 768, /// "name" : "ensemble_gte_base_uint8_text", /// "ondevice" : true ///} bool text_embedding_get_model_info(TextEmbeddingSession *session, char **model_info); /// @brief 文本向量化模型 同步接口 向量化文本 /// @param session[入参] 传入 text_embedding_create_session() 创建的 session /// @param text[入参] 传入 待向量化的文本 /// @param result[出参] 在使用时定义一个EmbeddingResult *result=nullptr, /// 然后传入&result,需要embedding_result_destroy来销毁资源 /// @return 返回向量化结果 bool text_embedding(TextEmbeddingSession *session, const char *text, EmbeddingResult **result); /// @brief 文本向量化模型 异步接口 向量化文本 /// @param session 传入 text_embedding_create_session() 创建的 session /// @param text 传入 待向量化的文本 /// @param callback 向量化结果回调函数 传入 TextEmbeddingResultCallback /// 类型的结果回调函数 /// @param callback_user_data 调用者自定义的数据,作为 callback 的参数返回 void text_embedding_async(TextEmbeddingSession *session, const char *text, TextEmbeddingResultCallback callback, void *callback_user_data); typedef struct _ImageEmbeddingSession ImageEmbeddingSession; /// @param callback参数的生命周期仅为callback的作用域,资源释放由sdk侧管理 typedef void (*ImageEmbeddingResultCallback)(EmbeddingResult *result, void *callback_user_data); ImageEmbeddingSession *image_embedding_create_session(); void image_embedding_destroy_session(ImageEmbeddingSession **session); int image_embedding_init_session(ImageEmbeddingSession *session); /// @brief 获取图像向量化模型信息 同步接口 /// @param session[入参] 传入 image_embedding_create_session() 创建的 session /// @param model_info[出参] 在使用时定义一个char *model_infot=nullptr, /// 然后传入&model_info,需要embedding_model_info_destroy来销毁资源 /// @return 是否成功 ///{ /// "dim" : 1024, /// "image_name" : "cn_clip_512_uint8_image", /// "image_text_name" : "ensemble_cn_clip_512_uint8_text", /// "ondevice" : true ///} bool image_embedding_get_model_info(ImageEmbeddingSession *session, char **model_info); /// @brief 图像向量化模型同步接口 向量化文本 /// @param session[入参] 传入 image_embedding_create_session() 创建的 session /// @param text[入参] 传入 待向量化的文本 /// @param result[出参] 在使用时定义一个EmbeddingResult *result=nullptr, /// 然后传入&result,需要embedding_result_destroy来销毁资源 /// @return 返回向量化结果 bool text_embedding_by_image_model(ImageEmbeddingSession *session, const char *text, EmbeddingResult **result); /// @brief 图像向量化模型同步接口 向量化图像 /// @param session[入参] 传入 image_embedding_create_session() 创建的 session /// @param image_file[入参] 传入 待向量化的图像路径 /// @param result[出参] 在使用时定义一个EmbeddingResult *result=nullptr, /// 然后传入&result,需要embedding_result_destroy来销毁资源 /// @return 返回向量化结果 bool image_embedding_by_image_file(ImageEmbeddingSession *session, const char *image_file, EmbeddingResult **result); /// @brief 图像向量化模型同步接口 向量化图像 /// @param session[入参] 传入 image_embedding_create_session() 创建的 session /// @param image_data[入参] 传入 待向量化的base64图像数据 /// @param image_data_length[入参] 传入 base64图像数据的长度 /// @param result[出参] 在使用时定义一个EmbeddingResult *result=nullptr, /// 然后传入&result,需要embedding_result_destroy来销毁资源 /// @return 返回向量化结果 bool image_embedding_by_base64_image_data(ImageEmbeddingSession *session, const unsigned char *image_data, unsigned int image_data_length, EmbeddingResult **result); /// @brief 图像向量化模型 异步不并发接口 向量化文本 /// @param session 传入 image_embedding_create_session() 创建的 session /// @param text 传入 待向量化的文本 /// @param callback 向量化结果回调函数 传入 ImageEmbeddingResultCallback /// 类型的结果回调函数 /// @param callback_user_data 调用者自定义的数据,作为 callback 的参数返回 void text_embedding_by_image_model_async(ImageEmbeddingSession *session, const char *text, ImageEmbeddingResultCallback callback, void *callback_user_data); /// @brief 图像向量化模型 异步不并发接口 向量化图像 /// @param session 传入 image_embedding_create_session() 创建的 session /// @param file_path 传入 待向量化的图像路径 /// @param callback 向量化结果回调函数 传入 ImageEmbeddingResultCallback /// 类型的结果回调函数 /// @param callback_user_data 调用者自定义的数据,作为 callback 的参数返回 void image_embedding_from_by_file_async(ImageEmbeddingSession *session, const char *file_path, ImageEmbeddingResultCallback callback, void *callback_user_data); /// @brief 图像向量化模型 异步不并发接口 向量化图像 /// @param session 传入 image_embedding_create_session() 创建的 session /// @param image_data 传入 待向量化的base64图像数据 /// @param image_data_length 传入 base64图像数据的长度 /// @param callback 向量化结果回调函数 传入 ImageEmbeddingResultCallback /// 类型的结果回调函数 /// @param callback_user_data 调用者自定义的数据,作为 callback 的参数返回 void image_embedding_by_base64_image_data_async( ImageEmbeddingSession *session, const unsigned char *image_data, unsigned int image_data_length, ImageEmbeddingResultCallback callback, void *callback_user_data); #ifdef __cplusplus } #endif #endif libkysdk-coreai-1.1.0.1/include/kylin-ai/coreai/speech/0000775000175000017500000000000015207167112020445 5ustar zpzplibkysdk-coreai-1.1.0.1/include/kylin-ai/coreai/speech/audiodatastream.h0000664000175000017500000000232215207167112023764 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef __KYLIN_AI_AUDIO_DATA_STREAM_H__ #define __KYLIN_AI_AUDIO_DATA_STREAM_H__ #include #include #ifdef __cplusplus extern "C" { #endif // 数据流 typedef struct _AudioDataStream AudioDataStream; AISDK_EXTERN AudioDataStream *audio_data_stream_create(); AISDK_EXTERN void audio_data_stream_destroy(AudioDataStream **stream); AISDK_EXTERN int audio_data_stream_write(AudioDataStream *stream, const uint8_t *data, uint32_t data_length); #ifdef __cplusplus } #endif #endif libkysdk-coreai-1.1.0.1/include/kylin-ai/coreai/speech/recognizer.h0000664000175000017500000000430515207167112022767 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef __SPEECH_RECOGNIZER_H__ #define __SPEECH_RECOGNIZER_H__ #include "audioconfig.h" #include "common/macros.h" #include "config.h" #include "result.h" #ifdef __cplusplus extern "C" { #endif typedef struct _SpeechRecognitionSession SpeechRecognitionSession; AISDK_EXTERN SpeechRecognitionSession *speech_recognizer_create_session(); AISDK_EXTERN void speech_recognizer_destroy_session(SpeechRecognitionSession **session); AISDK_EXTERN int speech_recognizer_init_session(SpeechRecognitionSession *session); // 识别结果回调 AISDK_EXTERN void speech_recognizer_result_set_callback( SpeechRecognitionSession *session, SpeechRecognitionResultCallback callback, void *user_data); // 配置模型 AISDK_EXTERN void speech_recognizer_set_model_config(SpeechRecognitionSession *session, SpeechModelConfig *config); // 设置输入音频的配置 AISDK_EXTERN void speech_recognizer_set_audio_config(SpeechRecognitionSession *session, AudioConfig *audio_config); // 开始连续语音识别 AISDK_EXTERN void speech_recognizer_start_continuous_recognition_async( SpeechRecognitionSession *session); // 停止连续语音识别 AISDK_EXTERN void speech_recognizer_stop_continuous_recognition_async( SpeechRecognitionSession *session); // 1一次性识别,适用于识别一个音频文件或者一段内存数据 AISDK_EXTERN void speech_recognizer_recognize_once_async(SpeechRecognitionSession *session); #ifdef __cplusplus } #endif #endif libkysdk-coreai-1.1.0.1/include/kylin-ai/coreai/speech/synthesizer.h0000664000175000017500000000402015207167112023201 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef __SPEECH_SYNTHESIZER_H__ #define __SPEECH_SYNTHESIZER_H__ #include #include #include "audioconfig.h" #include "config.h" #include "result.h" #ifdef __cplusplus extern "C" { #endif typedef struct _SpeechSynthesizerSession SpeechSynthesizerSession; AISDK_EXTERN SpeechSynthesizerSession *speech_synthesizer_create_session(); AISDK_EXTERN void speech_synthesizer_destroy_session(SpeechSynthesizerSession **session); AISDK_EXTERN int speech_synthesizer_init_session(SpeechSynthesizerSession *session); AISDK_EXTERN void speech_synthesizer_set_audio_config(SpeechSynthesizerSession *session, AudioConfig *audio_config); AISDK_EXTERN void speech_synthesizer_set_model_config(SpeechSynthesizerSession *session, SpeechModelConfig *config); AISDK_EXTERN void speech_synthesizer_result_set_callback( SpeechSynthesizerSession *session, SpeechSynthesisResultCallback callback, void *user_data); AISDK_EXTERN void speech_synthesizer_synthesize_text_async(SpeechSynthesizerSession *session, const char *text, uint32_t text_length); AISDK_EXTERN int speech_synthesizer_stop_speaking(SpeechSynthesizerSession *session); #ifdef __cplusplus } #endif #endif libkysdk-coreai-1.1.0.1/include/kylin-ai/coreai/speech/error.h0000664000175000017500000000215515207167112021752 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef __KYLIN_AI_SPEECH_ERROR_H__ #define __KYLIN_AI_SPEECH_ERROR_H__ #include #ifdef __cplusplus extern "C" { #endif typedef enum { SPEECH_RECOGNITION_AUDIO_DATA_SIZE_INVALID = 100, SPEECH_SYNTHESIS_TEXT_LENGTH_INVALID, SPEECH_PARAM_INVALID, SPEECH_DEFAULT_MICROPHONE_INVALID, SPEECH_UNKNOWN_ERROR, SPEECH_UNSUPPORTED_LANGUAGE } SpeechErrorCode; #ifdef __cplusplus } #endif #endif libkysdk-coreai-1.1.0.1/include/kylin-ai/coreai/speech/result.h0000664000175000017500000000611015207167112022132 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef __KYLIN_AI_SPEECH_RESULT_H__ #define __KYLIN_AI_SPEECH_RESULT_H__ #include #include #ifdef __cplusplus extern "C" { #endif typedef enum { // 出错 SPEECH_ERROR_OCCURRED = 1, // 已启动语音识别 SPEECH_RECOGNITION_STARTED = 2, // 正在进行语音识别,中间结果 SPEECH_RECOGNIZING = 3, // 语音识别的最终结果,经过修正 SPEECH_RECOGNIZED = 4, // 语音识别完成 SPEECH_RECOGNITION_COMPLETED = 5, // 已启动语音合成 SPEECH_SYNTHESIS_STARTED = 6, // 正在进行语音合成,语音合成的结果输出 SPEECH_SYNTHESIZING = 7, // 语音合成已经完成 SPEECH_SYNTHESIS_COMPLETED = 8, } SpeechResultReason; typedef struct _SpeechRecognitionResult SpeechRecognitionResult; AISDK_EXTERN SpeechResultReason speech_recognition_result_get_reason( SpeechRecognitionResult *result); AISDK_EXTERN const char *speech_recognition_result_get_text(SpeechRecognitionResult *result); AISDK_EXTERN int speech_recognition_result_get_speaker_id(SpeechRecognitionResult *result); AISDK_EXTERN int speech_recognition_result_get_error_code(SpeechRecognitionResult *result); AISDK_EXTERN const char *speech_recognition_result_get_error_message( SpeechRecognitionResult *result); typedef struct _SpeechSynthesisResult SpeechSynthesisResult; AISDK_EXTERN SpeechResultReason speech_synthesis_result_get_reason( SpeechSynthesisResult *result); AISDK_EXTERN const char *speech_synthesis_result_get_audio_format( SpeechSynthesisResult *result); AISDK_EXTERN int speech_synthesis_result_get_audio_rate(SpeechSynthesisResult *result); AISDK_EXTERN int speech_synthesis_result_get_audio_channel(SpeechSynthesisResult *result); AISDK_EXTERN const uint8_t *speech_synthesis_result_get_data(SpeechSynthesisResult *result, uint32_t *data_length); AISDK_EXTERN int speech_synthesis_result_get_error_code(SpeechSynthesisResult *result); AISDK_EXTERN const char *speech_synthesis_result_get_error_message( SpeechSynthesisResult *result); typedef void (*SpeechRecognitionResultCallback)(SpeechRecognitionResult *result, void *user_data); typedef void (*SpeechSynthesisResultCallback)(SpeechSynthesisResult *result, void *user_data); #ifdef __cplusplus } #endif #endif libkysdk-coreai-1.1.0.1/include/kylin-ai/coreai/speech/config.h0000664000175000017500000000256515207167112022073 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef _KYLIN_AI_CORE_AI_SPEECH_CONFIG_H #define _KYLIN_AI_CORE_AI_SPEECH_CONFIG_H #include "kylin-ai/common/enums.h" #include "kylin-ai/common/macros.h" #ifdef __cplusplus extern "C" { #endif typedef struct _SpeechModelConfig SpeechModelConfig; AISDK_EXTERN SpeechModelConfig *speech_model_config_create(); AISDK_EXTERN void speech_model_config_destroy(SpeechModelConfig **config); AISDK_EXTERN void speech_model_config_set_name(SpeechModelConfig *config, const char *name); AISDK_EXTERN void speech_model_config_set_deploy_type(SpeechModelConfig *config, ModelDeployType type); #ifdef __cplusplus } #endif #endif //_KYLIN_AI_CORE_AI_SPEECH_CONFIG_H libkysdk-coreai-1.1.0.1/include/kylin-ai/coreai/speech/audioconfig.h0000664000175000017500000000525615207167112023115 0ustar zpzp/* * Copyright 2024 KylinSoft Co., Ltd. * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #ifndef __KYLIN_AI_AUDIO_CONFIG_H__ #define __KYLIN_AI_AUDIO_CONFIG_H__ #include #include #include "audiodatastream.h" #ifdef __cplusplus extern "C" { #endif typedef struct _AudioConfig AudioConfig; /// 流式接口使用的配置 /// // 使用默认麦克风的音频输入 AISDK_EXTERN AudioConfig * audio_config_create_continuous_audio_input_from_default_microphone(); // 使用数据流的数据作为输入 AISDK_EXTERN AudioConfig *audio_config_create_continuous_audio_input_from_audio_data_stream( AudioDataStream *audio_data_stream); ////// /// 非流式接口使用的配置 /// // 使用整块原始数据作为输入 AISDK_EXTERN AudioConfig *audio_config_create_once_audio_input_from_pcm_data( const uint8_t *audio_data, uint32_t data_length); // 使用pcm文件的数据作为输入 AISDK_EXTERN AudioConfig *audio_config_create_once_audio_input_from_pcm_file( const char *pcm_file); // 使用整块原始数据作为输出 AISDK_EXTERN AudioConfig *audio_config_create_audio_output_from_pcm_data(); // 使用系统默认扬声器作为音频输出 AISDK_EXTERN AudioConfig *audio_config_create_audio_output_from_default_speaker(); // 使用pcm文件作为音频输出 AISDK_EXTERN AudioConfig *audio_config_create_audio_output_from_pcm_file_name( const char *pcm_file); AISDK_EXTERN void audio_config_destroy(AudioConfig **config); // 设置音频的格式 AISDK_EXTERN void audio_config_set_input_audio_rate(AudioConfig *config, int rate); // 设置输出音频的格式:语速,可选值:[0-100],默认为50 AISDK_EXTERN void audio_config_set_output_audio_speed(AudioConfig *config, int speed); // 设置输出音频的格式:音量,可选值:[0-100],默认为50 AISDK_EXTERN void audio_config_set_output_audio_volume(AudioConfig *config, int volume); // 设置输出音频的格式:音高,可选值:[0-100],默认为50 AISDK_EXTERN void audio_config_set_output_audio_pitch(AudioConfig *config, int pitch); #ifdef __cplusplus } #endif #endif libkysdk-coreai-1.1.0.1/3rdparty/0000775000175000017500000000000015207167112014344 5ustar zpzplibkysdk-coreai-1.1.0.1/3rdparty/catch2/0000775000175000017500000000000015207167112015510 5ustar zpzplibkysdk-coreai-1.1.0.1/3rdparty/catch2/LICENSE.txt0000664000175000017500000000247215207167112017340 0ustar zpzpBoost 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. libkysdk-coreai-1.1.0.1/3rdparty/catch2/catch_amalgamated.hpp0000664000175000017500000200360215207167112021623 0ustar zpzp // Copyright Catch2 Authors // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at // https://www.boost.org/LICENSE_1_0.txt) // SPDX-License-Identifier: BSL-1.0 // Catch v3.8.1 // Generated: 2025-04-08 12:33:19.851017 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. // ---------------------------------------------------------- #ifndef CATCH_AMALGAMATED_HPP_INCLUDED #define CATCH_AMALGAMATED_HPP_INCLUDED /** \file * This is a convenience header for Catch2. It includes **all** of Catch2 headers. * * Generally the Catch2 users should use specific includes they need, * but this header can be used instead for ease-of-experimentation, or * just plain convenience, at the cost of (significantly) increased * compilation times. * * When a new header is added to either the top level folder, or to the * corresponding internal subfolder, it should be added here. Headers * added to the various subparts (e.g. matchers, generators, etc...), * should go their respective catch-all headers. */ #ifndef CATCH_ALL_HPP_INCLUDED #define CATCH_ALL_HPP_INCLUDED /** \file * This is a convenience header for Catch2's benchmarking. It includes * **all** of Catch2 headers related to benchmarking. * * Generally the Catch2 users should use specific includes they need, * but this header can be used instead for ease-of-experimentation, or * just plain convenience, at the cost of (significantly) increased * compilation times. * * When a new header is added to either the `benchmark` folder, or to * the corresponding internal (detail) subfolder, it should be added here. */ #ifndef CATCH_BENCHMARK_ALL_HPP_INCLUDED #define CATCH_BENCHMARK_ALL_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_BENCHMARK_HPP_INCLUDED #define CATCH_BENCHMARK_HPP_INCLUDED #ifndef CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED #define CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED // Detect a number of compiler features - by compiler // The following features are defined: // // 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. #ifndef CATCH_PLATFORM_HPP_INCLUDED #define CATCH_PLATFORM_HPP_INCLUDED // See e.g.: // https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html #ifdef __APPLE__ # ifndef __has_extension # define __has_extension(x) 0 # endif # 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 # if defined( WINAPI_FAMILY ) && ( WINAPI_FAMILY == WINAPI_FAMILY_APP ) # define CATCH_PLATFORM_WINDOWS_UWP # endif #elif defined(__ORBIS__) || defined(__PROSPERO__) # define CATCH_PLATFORM_PLAYSTATION #endif #endif // CATCH_PLATFORM_HPP_INCLUDED #ifdef __cplusplus # if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) # define CATCH_CPP17_OR_GREATER # endif # if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) # define CATCH_CPP20_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__) && !defined(__NVCOMPILER) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) // This only works on GCC 9+. so we have to also add a global suppression of Wparentheses // for older versions of GCC. # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) # define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ _Pragma( "GCC diagnostic ignored \"-Wunused-result\"" ) # define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ _Pragma( "GCC diagnostic ignored \"-Wunused-variable\"" ) # define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ _Pragma( "GCC diagnostic ignored \"-Wuseless-cast\"" ) # define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ _Pragma( "GCC diagnostic ignored \"-Wshadow\"" ) # define CATCH_INTERNAL_CONFIG_USE_BUILTIN_CONSTANT_P #endif #if defined(__NVCOMPILER) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "diag push" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "diag pop" ) # define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "diag_suppress declared_but_not_referenced" ) #endif #if defined(__CUDACC__) && !defined(__clang__) # ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ // New pragmas introduced in CUDA 11.5+ # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "nv_diagnostic push" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "nv_diagnostic pop" ) # define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "nv_diag_suppress 177" ) # else # define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "diag_suppress 177" ) # endif #endif // clang-cl defines _MSC_VER as well as __clang__, which could cause the // start/stop internal suppression macros to be double defined. #if defined(__clang__) && !defined(_MSC_VER) # define CATCH_INTERNAL_CONFIG_USE_BUILTIN_CONSTANT_P # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) #endif // __clang__ && !_MSC_VER #if defined(__clang__) # 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_VARIABLE_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) # if (__clang_major__ >= 20) # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wvariadic-macro-arguments-omitted\"" ) # elif (__clang_major__ == 19) # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wc++20-extensions\"" ) # else # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) # endif # define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) # define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wcomma\"" ) # define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wshadow\"" ) #endif // __clang__ // 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") // ``` // // Similarly, NVHPC's implementation of `__builtin_constant_p` has a bug which // results in calls to the immediately evaluated lambda expressions to be // reported as unevaluated lambdas. // https://developer.nvidia.com/nvidia_bug/3321845. // // Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. #if defined( __ibmxl__ ) || defined( __CUDACC__ ) || defined( __NVCOMPILER ) # define CATCH_INTERNAL_CONFIG_NO_USE_BUILTIN_CONSTANT_P #endif //////////////////////////////////////////////////////////////////////////////// // We know some environments not to support full POSIX signals #if defined( CATCH_PLATFORM_WINDOWS ) || \ defined( CATCH_PLATFORM_PLAYSTATION ) || \ defined( __CYGWIN__ ) || \ defined( __QNX__ ) || \ defined( __EMSCRIPTEN__ ) || \ defined( __DJGPP__ ) || \ defined( __OS400__ ) # define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS #else # define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS #endif //////////////////////////////////////////////////////////////////////////////// // Assume that some platforms do not support getenv. #if defined( CATCH_PLATFORM_WINDOWS_UWP ) || \ defined( CATCH_PLATFORM_PLAYSTATION ) || \ defined( _GAMING_XBOX ) # define CATCH_INTERNAL_CONFIG_NO_GETENV #else # define CATCH_INTERNAL_CONFIG_GETENV #endif //////////////////////////////////////////////////////////////////////////////// // Android somehow still does not support std::to_string #if defined(__ANDROID__) # define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING #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) // We want to defer to nvcc-specific warning suppression if we are compiled // with nvcc masquerading for MSVC. # if !defined( __CUDACC__ ) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ __pragma( warning( push ) ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ __pragma( warning( pop ) ) # endif // Universal Windows platform does not support SEH // Or console colours (or console at all...) # if defined(CATCH_PLATFORM_WINDOWS_UWP) # define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 # 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 //////////////////////////////////////////////////////////////////////////////// // Embarcadero C++Build #if defined(__BORLANDC__) #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN #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_INTERNAL_CONFIG_NO_COLOUR_WIN32 #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_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 #if defined(CATCH_INTERNAL_CONFIG_GETENV) && !defined(CATCH_INTERNAL_CONFIG_NO_GETENV) && !defined(CATCH_CONFIG_NO_GETENV) && !defined(CATCH_CONFIG_GETENV) # define CATCH_CONFIG_GETENV #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 ) && \ !defined( CATCH_CONFIG_NO_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_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) # define CATCH_CONFIG_GLOBAL_NEXTAFTER #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_CONFIG_USE_BUILTIN_CONSTANT_P ) && \ !defined( CATCH_INTERNAL_CONFIG_NO_USE_BUILTIN_CONSTANT_P ) && !defined(CATCH_CONFIG_USE_BUILTIN_CONSTANT_P) #define CATCH_CONFIG_USE_BUILTIN_CONSTANT_P #endif #if defined( CATCH_CONFIG_USE_BUILTIN_CONSTANT_P ) && \ !defined( CATCH_CONFIG_NO_USE_BUILTIN_CONSTANT_P ) # define CATCH_INTERNAL_IGNORE_BUT_WARN( ... ) \ (void)__builtin_constant_p( __VA_ARGS__ ) /* NOLINT(cppcoreguidelines-pro-type-vararg, \ hicpp-vararg) */ #else # define CATCH_INTERNAL_IGNORE_BUT_WARN( ... ) #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_RESULT) # define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT #endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS #endif #if !defined( CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS ) # define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif #if !defined( CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS ) # define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS #endif #if !defined( CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS ) # define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS #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_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 #if defined( CATCH_PLATFORM_WINDOWS ) && \ !defined( CATCH_CONFIG_COLOUR_WIN32 ) && \ !defined( CATCH_CONFIG_NO_COLOUR_WIN32 ) && \ !defined( CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 ) # define CATCH_CONFIG_COLOUR_WIN32 #endif #if defined( CATCH_CONFIG_SHARED_LIBRARY ) && defined( _MSC_VER ) && \ !defined( CATCH_CONFIG_STATIC ) # ifdef Catch2_EXPORTS # define CATCH_EXPORT //__declspec( dllexport ) // not needed # else # define CATCH_EXPORT __declspec( dllimport ) # endif #else # define CATCH_EXPORT #endif #endif // CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED #ifndef CATCH_CONTEXT_HPP_INCLUDED #define CATCH_CONTEXT_HPP_INCLUDED namespace Catch { class IResultCapture; class IConfig; class Context { IConfig const* m_config = nullptr; IResultCapture* m_resultCapture = nullptr; CATCH_EXPORT static Context* currentContext; friend Context& getCurrentMutableContext(); friend Context const& getCurrentContext(); static void createContext(); friend void cleanUpContext(); public: constexpr IResultCapture* getResultCapture() const { return m_resultCapture; } constexpr IConfig const* getConfig() const { return m_config; } constexpr void setResultCapture( IResultCapture* resultCapture ) { m_resultCapture = resultCapture; } constexpr void setConfig( IConfig const* config ) { m_config = config; } }; Context& getCurrentMutableContext(); inline Context const& getCurrentContext() { // We duplicate the logic from `getCurrentMutableContext` here, // to avoid paying the call overhead in debug mode. if ( !Context::currentContext ) { Context::createContext(); } // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) return *Context::currentContext; } void cleanUpContext(); class SimplePcg32; SimplePcg32& sharedRng(); } #endif // CATCH_CONTEXT_HPP_INCLUDED #ifndef CATCH_MOVE_AND_FORWARD_HPP_INCLUDED #define CATCH_MOVE_AND_FORWARD_HPP_INCLUDED #include //! Replacement for std::move with better compile time performance #define CATCH_MOVE(...) static_cast&&>(__VA_ARGS__) //! Replacement for std::forward with better compile time performance #define CATCH_FORWARD(...) static_cast(__VA_ARGS__) #endif // CATCH_MOVE_AND_FORWARD_HPP_INCLUDED #ifndef CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED #define CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED namespace Catch { //! Used to signal that an assertion macro failed struct TestFailureException{}; //! Used to signal that the remainder of a test should be skipped struct TestSkipException {}; /** * Outlines throwing of `TestFailureException` into a single TU * * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. */ [[noreturn]] void throw_test_failure_exception(); /** * Outlines throwing of `TestSkipException` into a single TU * * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. */ [[noreturn]] void throw_test_skip_exception(); } // namespace Catch #endif // CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED #ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED #define CATCH_UNIQUE_NAME_HPP_INCLUDED /** \file * Wrapper for the CONFIG configuration option * * When generating internal unique names, there are two options. Either * we mix in the current line number, or mix in an incrementing number. * We prefer the latter, using `__COUNTER__`, but users might want to * use the former. */ #ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED #define CATCH_CONFIG_COUNTER_HPP_INCLUDED #if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) #define CATCH_INTERNAL_CONFIG_COUNTER #endif #if defined( CATCH_INTERNAL_CONFIG_COUNTER ) && \ !defined( CATCH_CONFIG_NO_COUNTER ) && \ !defined( CATCH_CONFIG_COUNTER ) # define CATCH_CONFIG_COUNTER #endif #endif // CATCH_CONFIG_COUNTER_HPP_INCLUDED #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 #endif // CATCH_UNIQUE_NAME_HPP_INCLUDED #ifndef CATCH_INTERFACES_CAPTURE_HPP_INCLUDED #define CATCH_INTERFACES_CAPTURE_HPP_INCLUDED #include #ifndef CATCH_STRINGREF_HPP_INCLUDED #define CATCH_STRINGREF_HPP_INCLUDED #include #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*; static constexpr size_type npos{ static_cast( -1 ) }; 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 other ) const noexcept -> bool { return m_size == other.m_size && (std::memcmp( m_start, other.m_start, m_size ) == 0); } auto operator != (StringRef other) const noexcept -> bool { return !(*this == other); } constexpr auto operator[] ( size_type index ) const noexcept -> char { assert(index < m_size); return m_start[index]; } bool operator<(StringRef rhs) const noexcept; public: // named queries constexpr auto empty() const noexcept -> bool { return m_size == 0; } constexpr auto size() const noexcept -> size_type { return m_size; } // 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. constexpr StringRef substr(size_type start, size_type length) const noexcept { if (start < m_size) { const auto shortened_size = m_size - start; return StringRef(m_start + start, (shortened_size < length) ? shortened_size : length); } else { return StringRef(); } } // Returns the current start pointer. May not be null-terminated. constexpr char const* data() const noexcept { return m_start; } constexpr const_iterator begin() const { return m_start; } constexpr const_iterator end() const { return m_start + m_size; } friend std::string& operator += (std::string& lhs, StringRef rhs); friend std::ostream& operator << (std::ostream& os, StringRef str); friend std::string operator+(StringRef lhs, StringRef rhs); /** * Provides a three-way comparison with rhs * * Returns negative number if lhs < rhs, 0 if lhs == rhs, and a positive * number if lhs > rhs */ int compare( StringRef rhs ) const; }; 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 ); } #endif // CATCH_STRINGREF_HPP_INCLUDED #ifndef CATCH_RESULT_TYPE_HPP_INCLUDED #define CATCH_RESULT_TYPE_HPP_INCLUDED namespace Catch { // ResultWas::OfType enum struct ResultWas { enum OfType { Unknown = -1, Ok = 0, Info = 1, Warning = 2, // TODO: Should explicit skip be considered "not OK" (cf. isOk)? I.e., should it have the failure bit? ExplicitSkip = 4, FailureBit = 0x10, ExpressionFailed = FailureBit | 1, ExplicitFailure = FailureBit | 2, Exception = 0x100 | FailureBit, ThrewException = Exception | 1, DidntThrowException = Exception | 2, FatalErrorCondition = 0x200 | FailureBit }; }; constexpr bool isOk( ResultWas::OfType resultType ) { return ( resultType & ResultWas::FailureBit ) == 0; } constexpr bool isJustInfo( int flags ) { return flags == ResultWas::Info; } // ResultDisposition::Flags enum struct ResultDisposition { enum Flags { Normal = 0x01, ContinueOnFailure = 0x02, // Failures fail test, but execution continues FalseTest = 0x04, // Prefix expression with ! SuppressFail = 0x08 // Failures are reported but do not fail the test }; }; constexpr ResultDisposition::Flags operator|( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { return static_cast( static_cast( lhs ) | static_cast( rhs ) ); } constexpr bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } constexpr bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } } // end namespace Catch #endif // CATCH_RESULT_TYPE_HPP_INCLUDED #ifndef CATCH_UNIQUE_PTR_HPP_INCLUDED #define CATCH_UNIQUE_PTR_HPP_INCLUDED #include #include namespace Catch { namespace Detail { /** * A reimplementation of `std::unique_ptr` for improved compilation performance * * Does not support arrays nor custom deleters. */ template class unique_ptr { T* m_ptr; public: constexpr unique_ptr(std::nullptr_t = nullptr): m_ptr{} {} explicit constexpr unique_ptr(T* ptr): m_ptr(ptr) {} template ::value>> unique_ptr(unique_ptr&& from): m_ptr(from.release()) {} template ::value>> unique_ptr& operator=(unique_ptr&& from) { reset(from.release()); return *this; } unique_ptr(unique_ptr const&) = delete; unique_ptr& operator=(unique_ptr const&) = delete; unique_ptr(unique_ptr&& rhs) noexcept: m_ptr(rhs.m_ptr) { rhs.m_ptr = nullptr; } unique_ptr& operator=(unique_ptr&& rhs) noexcept { reset(rhs.release()); return *this; } ~unique_ptr() { delete m_ptr; } T& operator*() { assert(m_ptr); return *m_ptr; } T const& operator*() const { assert(m_ptr); return *m_ptr; } T* operator->() noexcept { assert(m_ptr); return m_ptr; } T const* operator->() const noexcept { assert(m_ptr); return m_ptr; } T* get() { return m_ptr; } T const* get() const { return m_ptr; } void reset(T* ptr = nullptr) { delete m_ptr; m_ptr = ptr; } T* release() { auto temp = m_ptr; m_ptr = nullptr; return temp; } explicit operator bool() const { return m_ptr; } friend void swap(unique_ptr& lhs, unique_ptr& rhs) { auto temp = lhs.m_ptr; lhs.m_ptr = rhs.m_ptr; rhs.m_ptr = temp; } }; //! Specialization to cause compile-time error for arrays template class unique_ptr; template unique_ptr make_unique(Args&&... args) { return unique_ptr(new T(CATCH_FORWARD(args)...)); } } // end namespace Detail } // end namespace Catch #endif // CATCH_UNIQUE_PTR_HPP_INCLUDED #ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED #define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_CLOCK_HPP_INCLUDED #define CATCH_CLOCK_HPP_INCLUDED #include namespace Catch { namespace Benchmark { using IDuration = std::chrono::nanoseconds; using FDuration = std::chrono::duration; template using TimePoint = typename Clock::time_point; using default_clock = std::chrono::steady_clock; } // namespace Benchmark } // namespace Catch #endif // CATCH_CLOCK_HPP_INCLUDED namespace Catch { // We cannot forward declare the type with default template argument // multiple times, so it is split out into a separate header so that // we can prevent multiple declarations in dependees template struct BenchmarkStats; } // end namespace Catch #endif // CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED namespace Catch { class AssertionResult; struct AssertionInfo; struct SectionInfo; struct SectionEndInfo; struct MessageInfo; struct MessageBuilder; struct Counts; struct AssertionReaction; struct SourceLineInfo; class ITransientExpression; class IGeneratorTracker; struct BenchmarkInfo; namespace Generators { class GeneratorUntypedBase; using GeneratorBasePtr = Catch::Detail::unique_ptr; } class IResultCapture { public: virtual ~IResultCapture(); virtual void notifyAssertionStarted( AssertionInfo const& info ) = 0; virtual bool sectionStarted( StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts& assertions ) = 0; virtual void sectionEnded( SectionEndInfo&& endInfo ) = 0; virtual void sectionEndedEarly( SectionEndInfo&& endInfo ) = 0; virtual IGeneratorTracker* acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) = 0; virtual IGeneratorTracker* createGeneratorTracker( StringRef generatorName, SourceLineInfo lineInfo, Generators::GeneratorBasePtr&& generator ) = 0; virtual void benchmarkPreparing( StringRef name ) = 0; virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0; virtual void benchmarkFailed( StringRef error ) = 0; virtual void pushScopedMessage( MessageInfo const& message ) = 0; virtual void popScopedMessage( MessageInfo const& message ) = 0; virtual void emplaceUnscopedMessage( MessageBuilder&& builder ) = 0; virtual void handleFatalErrorCondition( StringRef message ) = 0; virtual void handleExpr ( AssertionInfo const& info, ITransientExpression const& expr, AssertionReaction& reaction ) = 0; virtual void handleMessage ( AssertionInfo const& info, ResultWas::OfType resultType, std::string&& message, AssertionReaction& reaction ) = 0; virtual void handleUnexpectedExceptionNotThrown ( AssertionInfo const& info, AssertionReaction& reaction ) = 0; virtual void handleUnexpectedInflightException ( AssertionInfo const& info, std::string&& message, AssertionReaction& reaction ) = 0; virtual void handleIncomplete ( AssertionInfo const& info ) = 0; virtual void handleNonExpr ( AssertionInfo const &info, ResultWas::OfType resultType, AssertionReaction &reaction ) = 0; virtual bool lastAssertionPassed() = 0; virtual void assertionPassed() = 0; // Deprecated, do not use: virtual std::string getCurrentTestName() const = 0; virtual const AssertionResult* getLastResult() const = 0; virtual void exceptionEarlyReported() = 0; }; IResultCapture& getResultCapture(); } #endif // CATCH_INTERFACES_CAPTURE_HPP_INCLUDED #ifndef CATCH_INTERFACES_CONFIG_HPP_INCLUDED #define CATCH_INTERFACES_CONFIG_HPP_INCLUDED #ifndef CATCH_NONCOPYABLE_HPP_INCLUDED #define CATCH_NONCOPYABLE_HPP_INCLUDED namespace Catch { namespace Detail { //! Deriving classes become noncopyable and nonmovable class NonCopyable { NonCopyable( NonCopyable const& ) = delete; NonCopyable( NonCopyable&& ) = delete; NonCopyable& operator=( NonCopyable const& ) = delete; NonCopyable& operator=( NonCopyable&& ) = delete; protected: NonCopyable() noexcept = default; }; } // namespace Detail } // namespace Catch #endif // CATCH_NONCOPYABLE_HPP_INCLUDED #include #include #include #include namespace Catch { enum class Verbosity { Quiet = 0, Normal, High }; struct WarnAbout { enum What { Nothing = 0x00, //! A test case or leaf section did not run any assertions NoAssertions = 0x01, //! A command line test spec matched no test cases UnmatchedTestSpec = 0x02, }; }; enum class ShowDurations { DefaultForReporter, Always, Never }; enum class TestRunOrder { Declared, LexicographicallySorted, Randomized }; enum class ColourMode : std::uint8_t { //! Let Catch2 pick implementation based on platform detection PlatformDefault, //! Use ANSI colour code escapes ANSI, //! Use Win32 console colour API Win32, //! Don't use any colour None }; struct WaitForKeypress { enum When { Never, BeforeStart = 1, BeforeExit = 2, BeforeStartAndExit = BeforeStart | BeforeExit }; }; class TestSpec; class IStream; class IConfig : public Detail::NonCopyable { public: virtual ~IConfig(); virtual bool allowThrows() const = 0; virtual StringRef name() const = 0; virtual bool includeSuccessfulResults() const = 0; virtual bool shouldDebugBreak() const = 0; virtual bool warnAboutMissingAssertions() const = 0; virtual bool warnAboutUnmatchedTestSpecs() const = 0; virtual bool zeroTestsCountAsSuccess() const = 0; virtual int abortAfter() const = 0; virtual bool showInvisibles() const = 0; virtual ShowDurations showDurations() const = 0; virtual double minDuration() const = 0; virtual TestSpec const& testSpec() const = 0; virtual bool hasTestFilters() const = 0; virtual std::vector const& getTestsOrTags() const = 0; virtual TestRunOrder runOrder() const = 0; virtual uint32_t rngSeed() const = 0; virtual unsigned int shardCount() const = 0; virtual unsigned int shardIndex() const = 0; virtual ColourMode defaultColourMode() const = 0; virtual std::vector const& getSectionsToRun() const = 0; virtual Verbosity verbosity() const = 0; virtual bool skipBenchmarks() const = 0; virtual bool benchmarkNoAnalysis() const = 0; virtual unsigned int benchmarkSamples() const = 0; virtual double benchmarkConfidenceInterval() const = 0; virtual unsigned int benchmarkResamples() const = 0; virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0; }; } #endif // CATCH_INTERFACES_CONFIG_HPP_INCLUDED #ifndef CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED #define CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED #include namespace Catch { class TestCaseHandle; struct TestCaseInfo; class ITestCaseRegistry; class IExceptionTranslatorRegistry; class IExceptionTranslator; class ReporterRegistry; class IReporterFactory; class ITagAliasRegistry; class ITestInvoker; class IMutableEnumValuesRegistry; struct SourceLineInfo; class StartupExceptionRegistry; class EventListenerFactory; using IReporterFactoryPtr = Detail::unique_ptr; class IRegistryHub { public: virtual ~IRegistryHub(); // = default virtual ReporterRegistry const& getReporterRegistry() const = 0; virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; }; class IMutableRegistryHub { public: virtual ~IMutableRegistryHub(); // = default virtual void registerReporter( std::string const& name, IReporterFactoryPtr factory ) = 0; virtual void registerListener( Detail::unique_ptr factory ) = 0; virtual void registerTest(Detail::unique_ptr&& testInfo, Detail::unique_ptr&& invoker) = 0; virtual void registerTranslator( Detail::unique_ptr&& translator ) = 0; virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; virtual void registerStartupException() noexcept = 0; virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0; }; IRegistryHub const& getRegistryHub(); IMutableRegistryHub& getMutableRegistryHub(); void cleanUp(); std::string translateActiveException(); } #endif // CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED #ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED #define CATCH_BENCHMARK_STATS_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_ESTIMATE_HPP_INCLUDED #define CATCH_ESTIMATE_HPP_INCLUDED namespace Catch { namespace Benchmark { template struct Estimate { Type point; Type lower_bound; Type upper_bound; double confidence_interval; }; } // namespace Benchmark } // namespace Catch #endif // CATCH_ESTIMATE_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED #define CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED namespace Catch { namespace Benchmark { struct OutlierClassification { int samples_seen = 0; int low_severe = 0; // more than 3 times IQR below Q1 int low_mild = 0; // 1.5 to 3 times IQR below Q1 int high_mild = 0; // 1.5 to 3 times IQR above Q3 int high_severe = 0; // more than 3 times IQR above Q3 constexpr int total() const { return low_severe + low_mild + high_mild + high_severe; } }; } // namespace Benchmark } // namespace Catch #endif // CATCH_OUTLIERS_CLASSIFICATION_HPP_INCLUDED // The fwd decl & default specialization needs to be seen by VS2017 before // BenchmarkStats itself, or VS2017 will report compilation error. #include #include namespace Catch { struct BenchmarkInfo { std::string name; double estimatedDuration; int iterations; unsigned int samples; unsigned int resamples; double clockResolution; double clockCost; }; // We need to keep template parameter for backwards compatibility, // but we also do not want to use the template paraneter. template struct BenchmarkStats { BenchmarkInfo info; std::vector samples; Benchmark::Estimate mean; Benchmark::Estimate standardDeviation; Benchmark::OutlierClassification outliers; double outlierVariance; }; } // end namespace Catch #endif // CATCH_BENCHMARK_STATS_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_ENVIRONMENT_HPP_INCLUDED #define CATCH_ENVIRONMENT_HPP_INCLUDED namespace Catch { namespace Benchmark { struct EnvironmentEstimate { FDuration mean; OutlierClassification outliers; }; struct Environment { EnvironmentEstimate clock_resolution; EnvironmentEstimate clock_cost; }; } // namespace Benchmark } // namespace Catch #endif // CATCH_ENVIRONMENT_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_EXECUTION_PLAN_HPP_INCLUDED #define CATCH_EXECUTION_PLAN_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED #define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_CHRONOMETER_HPP_INCLUDED #define CATCH_CHRONOMETER_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_OPTIMIZER_HPP_INCLUDED #define CATCH_OPTIMIZER_HPP_INCLUDED #if defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__) # include // atomic_thread_fence #endif #include namespace Catch { namespace Benchmark { #if defined(__GNUC__) || defined(__clang__) template inline void keep_memory(T* p) { asm volatile("" : : "g"(p) : "memory"); } inline void keep_memory() { asm volatile("" : : : "memory"); } namespace Detail { inline void optimizer_barrier() { keep_memory(); } } // namespace Detail #elif defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__) #if defined(_MSVC_VER) #pragma optimize("", off) #elif defined(__IAR_SYSTEMS_ICC__) // For IAR the pragma only affects the following function #pragma optimize=disable #endif template inline void keep_memory(T* p) { // thanks @milleniumbug *reinterpret_cast(p) = *reinterpret_cast(p); } // TODO equivalent keep_memory() #if defined(_MSVC_VER) #pragma optimize("", on) #endif namespace Detail { inline void optimizer_barrier() { std::atomic_thread_fence(std::memory_order_seq_cst); } } // namespace Detail #endif template inline void deoptimize_value(T&& x) { keep_memory(&x); } template inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t::value> { deoptimize_value(CATCH_FORWARD(fn) (CATCH_FORWARD(args)...)); } template inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t::value> { CATCH_FORWARD((fn)) (CATCH_FORWARD(args)...); } } // namespace Benchmark } // namespace Catch #endif // CATCH_OPTIMIZER_HPP_INCLUDED #ifndef CATCH_META_HPP_INCLUDED #define CATCH_META_HPP_INCLUDED #include namespace Catch { template struct true_given : std::true_type {}; struct is_callable_tester { template static true_given()(std::declval()...))> test(int); template static std::false_type test(...); }; template struct is_callable; template struct is_callable : decltype(is_callable_tester::test(0)) {}; #if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703 // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is // replaced with std::invoke_result here. template using FunctionReturnType = std::remove_reference_t>>; #else template using FunctionReturnType = std::remove_reference_t>>; #endif } // namespace Catch namespace mpl_{ struct na; } #endif // CATCH_META_HPP_INCLUDED namespace Catch { namespace Benchmark { namespace Detail { struct ChronometerConcept { virtual void start() = 0; virtual void finish() = 0; virtual ~ChronometerConcept(); // = default; ChronometerConcept() = default; ChronometerConcept(ChronometerConcept const&) = default; ChronometerConcept& operator=(ChronometerConcept const&) = default; }; template struct ChronometerModel final : public ChronometerConcept { void start() override { started = Clock::now(); } void finish() override { finished = Clock::now(); } IDuration elapsed() const { return std::chrono::duration_cast( finished - started ); } TimePoint started; TimePoint finished; }; } // namespace Detail struct Chronometer { public: template void measure(Fun&& fun) { measure(CATCH_FORWARD(fun), is_callable()); } int runs() const { return repeats; } Chronometer(Detail::ChronometerConcept& meter, int repeats_) : impl(&meter) , repeats(repeats_) {} private: template void measure(Fun&& fun, std::false_type) { measure([&fun](int) { return fun(); }, std::true_type()); } template void measure(Fun&& fun, std::true_type) { Detail::optimizer_barrier(); impl->start(); for (int i = 0; i < repeats; ++i) invoke_deoptimized(fun, i); impl->finish(); Detail::optimizer_barrier(); } Detail::ChronometerConcept* impl; int repeats; }; } // namespace Benchmark } // namespace Catch #endif // CATCH_CHRONOMETER_HPP_INCLUDED #include namespace Catch { namespace Benchmark { namespace Detail { template struct is_related : std::is_same, std::decay_t> {}; /// We need to reinvent std::function because every piece of code that might add overhead /// in a measurement context needs to have consistent performance characteristics so that we /// can account for it in the measurement. /// Implementations of std::function with optimizations that aren't always applicable, like /// small buffer optimizations, are not uncommon. /// This is effectively an implementation of std::function without any such optimizations; /// it may be slow, but it is consistently slow. struct BenchmarkFunction { private: struct callable { virtual void call(Chronometer meter) const = 0; virtual ~callable(); // = default; callable() = default; callable(callable&&) = default; callable& operator=(callable&&) = default; }; template struct model : public callable { model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {} model(Fun const& fun_) : fun(fun_) {} void call(Chronometer meter) const override { call(meter, is_callable()); } void call(Chronometer meter, std::true_type) const { fun(meter); } void call(Chronometer meter, std::false_type) const { meter.measure(fun); } Fun fun; }; public: BenchmarkFunction(); template ::value, int> = 0> BenchmarkFunction(Fun&& fun) : f(new model>(CATCH_FORWARD(fun))) {} BenchmarkFunction( BenchmarkFunction&& that ) noexcept: f( CATCH_MOVE( that.f ) ) {} BenchmarkFunction& operator=( BenchmarkFunction&& that ) noexcept { f = CATCH_MOVE( that.f ); return *this; } void operator()(Chronometer meter) const { f->call(meter); } private: Catch::Detail::unique_ptr f; }; } // namespace Detail } // namespace Benchmark } // namespace Catch #endif // CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_REPEAT_HPP_INCLUDED #define CATCH_REPEAT_HPP_INCLUDED #include namespace Catch { namespace Benchmark { namespace Detail { template struct repeater { void operator()(int k) const { for (int i = 0; i < k; ++i) { fun(); } } Fun fun; }; template repeater> repeat(Fun&& fun) { return { CATCH_FORWARD(fun) }; } } // namespace Detail } // namespace Benchmark } // namespace Catch #endif // CATCH_REPEAT_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED #define CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_MEASURE_HPP_INCLUDED #define CATCH_MEASURE_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED #define CATCH_COMPLETE_INVOKE_HPP_INCLUDED namespace Catch { namespace Benchmark { namespace Detail { template struct CompleteType { using type = T; }; template <> struct CompleteType { struct type {}; }; template using CompleteType_t = typename CompleteType::type; template struct CompleteInvoker { template static Result invoke(Fun&& fun, Args&&... args) { return CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); } }; template <> struct CompleteInvoker { template static CompleteType_t invoke(Fun&& fun, Args&&... args) { CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); return {}; } }; // invoke and not return void :( template CompleteType_t> complete_invoke(Fun&& fun, Args&&... args) { return CompleteInvoker>::invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...); } } // namespace Detail template Detail::CompleteType_t> user_code(Fun&& fun) { return Detail::complete_invoke(CATCH_FORWARD(fun)); } } // namespace Benchmark } // namespace Catch #endif // CATCH_COMPLETE_INVOKE_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_TIMING_HPP_INCLUDED #define CATCH_TIMING_HPP_INCLUDED #include namespace Catch { namespace Benchmark { template struct Timing { IDuration elapsed; Result result; int iterations; }; template using TimingOf = Timing>>; } // namespace Benchmark } // namespace Catch #endif // CATCH_TIMING_HPP_INCLUDED namespace Catch { namespace Benchmark { namespace Detail { template TimingOf measure(Fun&& fun, Args&&... args) { auto start = Clock::now(); auto&& r = Detail::complete_invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...); auto end = Clock::now(); auto delta = end - start; return { delta, CATCH_FORWARD(r), 1 }; } } // namespace Detail } // namespace Benchmark } // namespace Catch #endif // CATCH_MEASURE_HPP_INCLUDED #include namespace Catch { namespace Benchmark { namespace Detail { template TimingOf measure_one(Fun&& fun, int iters, std::false_type) { return Detail::measure(fun, iters); } template TimingOf measure_one(Fun&& fun, int iters, std::true_type) { Detail::ChronometerModel meter; auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters)); return { meter.elapsed(), CATCH_MOVE(result), iters }; } template using run_for_at_least_argument_t = std::conditional_t::value, Chronometer, int>; [[noreturn]] void throw_optimized_away_error(); template TimingOf> run_for_at_least(IDuration how_long, const int initial_iterations, Fun&& fun) { auto iters = initial_iterations; while (iters < (1 << 30)) { auto&& Timing = measure_one(fun, iters, is_callable()); if (Timing.elapsed >= how_long) { return { Timing.elapsed, CATCH_MOVE(Timing.result), iters }; } iters *= 2; } throw_optimized_away_error(); } } // namespace Detail } // namespace Benchmark } // namespace Catch #endif // CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED #include namespace Catch { namespace Benchmark { struct ExecutionPlan { int iterations_per_sample; FDuration estimated_duration; Detail::BenchmarkFunction benchmark; FDuration warmup_time; int warmup_iterations; template std::vector run(const IConfig &cfg, Environment env) const { // warmup a bit Detail::run_for_at_least( std::chrono::duration_cast( warmup_time ), warmup_iterations, Detail::repeat( []() { return Clock::now(); } ) ); std::vector times; const auto num_samples = cfg.benchmarkSamples(); times.reserve( num_samples ); for ( size_t i = 0; i < num_samples; ++i ) { Detail::ChronometerModel model; this->benchmark( Chronometer( model, iterations_per_sample ) ); auto sample_time = model.elapsed() - env.clock_cost.mean; if ( sample_time < FDuration::zero() ) { sample_time = FDuration::zero(); } times.push_back(sample_time / iterations_per_sample); } return times; } }; } // namespace Benchmark } // namespace Catch #endif // CATCH_EXECUTION_PLAN_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_ESTIMATE_CLOCK_HPP_INCLUDED #define CATCH_ESTIMATE_CLOCK_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_STATS_HPP_INCLUDED #define CATCH_STATS_HPP_INCLUDED #include namespace Catch { namespace Benchmark { namespace Detail { using sample = std::vector; double weighted_average_quantile( int k, int q, double* first, double* last ); OutlierClassification classify_outliers( double const* first, double const* last ); double mean( double const* first, double const* last ); double normal_cdf( double x ); double erfc_inv(double x); double normal_quantile(double p); Estimate bootstrap( double confidence_level, double* first, double* last, sample const& resample, double ( *estimator )( double const*, double const* ) ); struct bootstrap_analysis { Estimate mean; Estimate standard_deviation; double outlier_variance; }; bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, double* first, double* last); } // namespace Detail } // namespace Benchmark } // namespace Catch #endif // CATCH_STATS_HPP_INCLUDED #include #include #include namespace Catch { namespace Benchmark { namespace Detail { template std::vector resolution(int k) { const size_t points = static_cast( k + 1 ); // To avoid overhead from the branch inside vector::push_back, // we allocate them all and then overwrite. std::vector> times(points); for ( auto& time : times ) { time = Clock::now(); } std::vector deltas; deltas.reserve(static_cast(k)); for ( size_t idx = 1; idx < points; ++idx ) { deltas.push_back( static_cast( ( times[idx] - times[idx - 1] ).count() ) ); } return deltas; } constexpr auto warmup_iterations = 10000; constexpr auto warmup_time = std::chrono::milliseconds(100); constexpr auto minimum_ticks = 1000; constexpr auto warmup_seed = 10000; constexpr auto clock_resolution_estimation_time = std::chrono::milliseconds(500); constexpr auto clock_cost_estimation_time_limit = std::chrono::seconds(1); constexpr auto clock_cost_estimation_tick_limit = 100000; constexpr auto clock_cost_estimation_time = std::chrono::milliseconds(10); constexpr auto clock_cost_estimation_iterations = 10000; template int warmup() { return run_for_at_least(warmup_time, warmup_seed, &resolution) .iterations; } template EnvironmentEstimate estimate_clock_resolution(int iterations) { auto r = run_for_at_least(clock_resolution_estimation_time, iterations, &resolution) .result; return { FDuration(mean(r.data(), r.data() + r.size())), classify_outliers(r.data(), r.data() + r.size()), }; } template EnvironmentEstimate estimate_clock_cost(FDuration resolution) { auto time_limit = (std::min)( resolution * clock_cost_estimation_tick_limit, FDuration(clock_cost_estimation_time_limit)); auto time_clock = [](int k) { return Detail::measure([k] { for (int i = 0; i < k; ++i) { volatile auto ignored = Clock::now(); (void)ignored; } }).elapsed; }; time_clock(1); int iters = clock_cost_estimation_iterations; auto&& r = run_for_at_least(clock_cost_estimation_time, iters, time_clock); std::vector times; int nsamples = static_cast(std::ceil(time_limit / r.elapsed)); times.reserve(static_cast(nsamples)); for ( int s = 0; s < nsamples; ++s ) { times.push_back( static_cast( ( time_clock( r.iterations ) / r.iterations ) .count() ) ); } return { FDuration(mean(times.data(), times.data() + times.size())), classify_outliers(times.data(), times.data() + times.size()), }; } template Environment measure_environment() { #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wexit-time-destructors" #endif static Catch::Detail::unique_ptr env; #if defined(__clang__) # pragma clang diagnostic pop #endif if (env) { return *env; } auto iters = Detail::warmup(); auto resolution = Detail::estimate_clock_resolution(iters); auto cost = Detail::estimate_clock_cost(resolution.mean); env = Catch::Detail::make_unique( Environment{resolution, cost} ); return *env; } } // namespace Detail } // namespace Benchmark } // namespace Catch #endif // CATCH_ESTIMATE_CLOCK_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_ANALYSE_HPP_INCLUDED #define CATCH_ANALYSE_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED #define CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED #include namespace Catch { namespace Benchmark { struct SampleAnalysis { std::vector samples; Estimate mean; Estimate standard_deviation; OutlierClassification outliers; double outlier_variance; }; } // namespace Benchmark } // namespace Catch #endif // CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED namespace Catch { class IConfig; namespace Benchmark { namespace Detail { SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last); } // namespace Detail } // namespace Benchmark } // namespace Catch #endif // CATCH_ANALYSE_HPP_INCLUDED #include #include #include #include #include namespace Catch { namespace Benchmark { struct Benchmark { Benchmark(std::string&& benchmarkName) : name(CATCH_MOVE(benchmarkName)) {} template Benchmark(std::string&& benchmarkName , FUN &&func) : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {} template ExecutionPlan prepare(const IConfig &cfg, Environment env) { auto min_time = env.clock_resolution.mean * Detail::minimum_ticks; auto run_time = std::max(min_time, std::chrono::duration_cast(cfg.benchmarkWarmupTime())); auto&& test = Detail::run_for_at_least(std::chrono::duration_cast(run_time), 1, fun); int new_iters = static_cast(std::ceil(min_time * test.iterations / test.elapsed)); return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), CATCH_MOVE(fun), std::chrono::duration_cast(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; } template void run() { static_assert( Clock::is_steady, "Benchmarking clock should be steady" ); auto const* cfg = getCurrentContext().getConfig(); auto env = Detail::measure_environment(); getResultCapture().benchmarkPreparing(name); CATCH_TRY{ auto plan = user_code([&] { return prepare(*cfg, env); }); BenchmarkInfo info { CATCH_MOVE(name), plan.estimated_duration.count(), plan.iterations_per_sample, cfg->benchmarkSamples(), cfg->benchmarkResamples(), env.clock_resolution.mean.count(), env.clock_cost.mean.count() }; getResultCapture().benchmarkStarting(info); auto samples = user_code([&] { return plan.template run(*cfg, env); }); auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size()); BenchmarkStats<> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; getResultCapture().benchmarkEnded(stats); } CATCH_CATCH_ANON (TestFailureException const&) { getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr); } CATCH_CATCH_ALL{ getResultCapture().benchmarkFailed(translateActiveException()); // We let the exception go further up so that the // test case is marked as failed. std::rethrow_exception(std::current_exception()); } } // sets lambda to be used in fun *and* executes benchmark! template ::value, int> = 0> Benchmark & operator=(Fun func) { auto const* cfg = getCurrentContext().getConfig(); if (!cfg->skipBenchmarks()) { fun = Detail::BenchmarkFunction(func); run(); } return *this; } explicit operator bool() { return true; } private: Detail::BenchmarkFunction fun; std::string name; }; } } // namespace Catch #define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1 #define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2 #define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\ if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ BenchmarkName = [&](int benchmarkIndex) #define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\ if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \ BenchmarkName = [&] #if defined(CATCH_CONFIG_PREFIX_ALL) #define CATCH_BENCHMARK(...) \ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) #define CATCH_BENCHMARK_ADVANCED(name) \ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name) #else #define BENCHMARK(...) \ INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,)) #define BENCHMARK_ADVANCED(name) \ INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name) #endif #endif // CATCH_BENCHMARK_HPP_INCLUDED // Adapted from donated nonius code. #ifndef CATCH_CONSTRUCTOR_HPP_INCLUDED #define CATCH_CONSTRUCTOR_HPP_INCLUDED #include namespace Catch { namespace Benchmark { namespace Detail { template struct ObjectStorage { ObjectStorage() = default; ObjectStorage(const ObjectStorage& other) { new(&data) T(other.stored_object()); } ObjectStorage(ObjectStorage&& other) { new(data) T(CATCH_MOVE(other.stored_object())); } ~ObjectStorage() { destruct_on_exit(); } template void construct(Args&&... args) { new (data) T(CATCH_FORWARD(args)...); } template std::enable_if_t destruct() { stored_object().~T(); } private: // If this is a constructor benchmark, destruct the underlying object template void destruct_on_exit(std::enable_if_t* = nullptr) { destruct(); } // Otherwise, don't template void destruct_on_exit(std::enable_if_t* = nullptr) { } #if defined( __GNUC__ ) && __GNUC__ <= 6 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wstrict-aliasing" #endif T& stored_object() { return *reinterpret_cast( data ); } T const& stored_object() const { return *reinterpret_cast( data ); } #if defined( __GNUC__ ) && __GNUC__ <= 6 # pragma GCC diagnostic pop #endif alignas( T ) unsigned char data[sizeof( T )]{}; }; } // namespace Detail template using storage_for = Detail::ObjectStorage; template using destructable_object = Detail::ObjectStorage; } // namespace Benchmark } // namespace Catch #endif // CATCH_CONSTRUCTOR_HPP_INCLUDED #endif // CATCH_BENCHMARK_ALL_HPP_INCLUDED #ifndef CATCH_APPROX_HPP_INCLUDED #define CATCH_APPROX_HPP_INCLUDED #ifndef CATCH_TOSTRING_HPP_INCLUDED #define CATCH_TOSTRING_HPP_INCLUDED #include #include #include #include /** \file * Wrapper for the WCHAR configuration option * * We want to support platforms that do not provide `wchar_t`, so we * sometimes have to disable providing wchar_t overloads through Catch2, * e.g. the StringMaker specialization for `std::wstring`. */ #ifndef CATCH_CONFIG_WCHAR_HPP_INCLUDED #define CATCH_CONFIG_WCHAR_HPP_INCLUDED // We assume that WCHAR should be enabled by default, and only disabled // for a shortlist (so far only DJGPP) of compilers. #if defined(__DJGPP__) # define CATCH_INTERNAL_CONFIG_NO_WCHAR #endif // __DJGPP__ #if !defined( CATCH_INTERNAL_CONFIG_NO_WCHAR ) && \ !defined( CATCH_CONFIG_NO_WCHAR ) && \ !defined( CATCH_CONFIG_WCHAR ) # define CATCH_CONFIG_WCHAR #endif #endif // CATCH_CONFIG_WCHAR_HPP_INCLUDED #ifndef CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED #define CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED #include #include #include #include namespace Catch { class ReusableStringStream : Detail::NonCopyable { std::size_t m_index; std::ostream* m_oss; public: ReusableStringStream(); ~ReusableStringStream(); //! Returns the serialized state std::string str() const; //! Sets internal state to `str` void str(std::string const& str); #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push // Old versions of GCC do not understand -Wnonnull-compare #pragma GCC diagnostic ignored "-Wpragmas" // Streaming a function pointer triggers Waddress and Wnonnull-compare // on GCC, because it implicitly converts it to bool and then decides // that the check it uses (a? true : false) is tautological and cannot // be null... #pragma GCC diagnostic ignored "-Waddress" #pragma GCC diagnostic ignored "-Wnonnull-compare" #endif template auto operator << ( T const& value ) -> ReusableStringStream& { *m_oss << value; return *this; } #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop #endif auto get() -> std::ostream& { return *m_oss; } }; } #endif // CATCH_REUSABLE_STRING_STREAM_HPP_INCLUDED #ifndef CATCH_VOID_TYPE_HPP_INCLUDED #define CATCH_VOID_TYPE_HPP_INCLUDED namespace Catch { namespace Detail { template struct make_void { using type = void; }; template using void_t = typename make_void::type; } // namespace Detail } // namespace Catch #endif // CATCH_VOID_TYPE_HPP_INCLUDED #ifndef CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED #define CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED #include namespace Catch { namespace Detail { struct EnumInfo { StringRef m_name; std::vector> m_values; ~EnumInfo(); StringRef lookup( int value ) const; }; } // namespace Detail class IMutableEnumValuesRegistry { public: virtual ~IMutableEnumValuesRegistry(); // = default; virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector const& values ) = 0; template Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list values ) { static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int"); std::vector intValues; intValues.reserve( values.size() ); for( auto enumValue : values ) intValues.push_back( static_cast( enumValue ) ); return registerEnum( enumName, allEnums, intValues ); } }; } // Catch #endif // CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED #ifdef CATCH_CONFIG_CPP17_STRING_VIEW #include #endif #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless #endif // 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 { // Bring in global namespace operator<< for ADL lookup in // `IsStreamInsertable` below. using ::operator<<; namespace Detail { inline std::size_t catch_strnlen(const char *str, std::size_t n) { auto ret = std::char_traits::find(str, n, '\0'); if (ret != nullptr) { return static_cast(ret - str); } return n; } constexpr StringRef unprintableString = "{?}"_sr; //! Encases `string in quotes, and optionally escapes invisibles std::string convertIntoString( StringRef string, bool escapeInvisibles ); //! Encases `string` in quotes, and escapes invisibles if user requested //! it via CLI std::string convertIntoString( StringRef string ); std::string rawMemoryToString( const void *object, std::size_t size ); template std::string rawMemoryToString( const T& object ) { return rawMemoryToString( &object, sizeof(object) ); } template class IsStreamInsertable { template static auto test(int) -> decltype(std::declval() << std::declval(), std::true_type()); template static auto test(...)->std::false_type; public: static const bool value = decltype(test(0))::value; }; template std::string convertUnknownEnumToString( E e ); template std::enable_if_t< !std::is_enum::value && !std::is_base_of::value, std::string> convertUnstreamable( T const& ) { return std::string(Detail::unprintableString); } template std::enable_if_t< !std::is_enum::value && std::is_base_of::value, std::string> convertUnstreamable(T const& ex) { return ex.what(); } template std::enable_if_t< std::is_enum::value, std::string> convertUnstreamable( T const& value ) { return convertUnknownEnumToString( value ); } #if defined(_MANAGED) //! Convert a CLR string to a utf8 std::string template std::string clrReferenceToString( T^ ref ) { if (ref == nullptr) return std::string("null"); auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); cli::pin_ptr p = &bytes[0]; return std::string(reinterpret_cast(p), bytes->Length); } #endif } // namespace Detail template struct StringMaker { template static std::enable_if_t<::Catch::Detail::IsStreamInsertable::value, std::string> convert(const Fake& value) { ReusableStringStream rss; // NB: call using the function-like syntax to avoid ambiguity with // user-defined templated operator<< under clang. rss.operator<<(value); return rss.str(); } template static std::enable_if_t::value, std::string> convert( const Fake& value ) { #if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) return Detail::convertUnstreamable(value); #else return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); #endif } }; namespace Detail { // This function dispatches all stringification requests inside of Catch. // Should be preferably called fully qualified, like ::Catch::Detail::stringify template std::string stringify(const T& e) { return ::Catch::StringMaker>>::convert(e); } template std::string convertUnknownEnumToString( E e ) { return ::Catch::Detail::stringify(static_cast>(e)); } #if defined(_MANAGED) template std::string stringify( T^ e ) { return ::Catch::StringMaker::convert(e); } #endif } // namespace Detail // Some predefined specializations template<> struct StringMaker { static std::string convert(const std::string& str); }; #ifdef CATCH_CONFIG_CPP17_STRING_VIEW template<> struct StringMaker { static std::string convert(std::string_view str); }; #endif template<> struct StringMaker { static std::string convert(char const * str); }; template<> struct StringMaker { static std::string convert(char * str); }; #if defined(CATCH_CONFIG_WCHAR) template<> struct StringMaker { static std::string convert(const std::wstring& wstr); }; # ifdef CATCH_CONFIG_CPP17_STRING_VIEW template<> struct StringMaker { static std::string convert(std::wstring_view str); }; # endif template<> struct StringMaker { static std::string convert(wchar_t const * str); }; template<> struct StringMaker { static std::string convert(wchar_t * str); }; #endif // CATCH_CONFIG_WCHAR template struct StringMaker { static std::string convert(char const* str) { return Detail::convertIntoString( StringRef( str, Detail::catch_strnlen( str, SZ ) ) ); } }; template struct StringMaker { static std::string convert(signed char const* str) { auto reinterpreted = reinterpret_cast(str); return Detail::convertIntoString( StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ))); } }; template struct StringMaker { static std::string convert(unsigned char const* str) { auto reinterpreted = reinterpret_cast(str); return Detail::convertIntoString( StringRef(reinterpreted, Detail::catch_strnlen(reinterpreted, SZ))); } }; #if defined(CATCH_CONFIG_CPP17_BYTE) template<> struct StringMaker { static std::string convert(std::byte value); }; #endif // defined(CATCH_CONFIG_CPP17_BYTE) template<> struct StringMaker { static std::string convert(int value); }; template<> struct StringMaker { static std::string convert(long value); }; template<> struct StringMaker { static std::string convert(long long value); }; template<> struct StringMaker { static std::string convert(unsigned int value); }; template<> struct StringMaker { static std::string convert(unsigned long value); }; template<> struct StringMaker { static std::string convert(unsigned long long value); }; template<> struct StringMaker { static std::string convert(bool b) { using namespace std::string_literals; return b ? "true"s : "false"s; } }; template<> struct StringMaker { static std::string convert(char c); }; template<> struct StringMaker { static std::string convert(signed char value); }; template<> struct StringMaker { static std::string convert(unsigned char value); }; template<> struct StringMaker { static std::string convert(std::nullptr_t) { using namespace std::string_literals; return "nullptr"s; } }; template<> struct StringMaker { static std::string convert(float value); CATCH_EXPORT static int precision; }; template<> struct StringMaker { static std::string convert(double value); CATCH_EXPORT static int precision; }; template struct StringMaker { template static std::string convert(U* p) { if (p) { return ::Catch::Detail::rawMemoryToString(p); } else { return "nullptr"; } } }; template struct StringMaker { static std::string convert(R C::* p) { if (p) { return ::Catch::Detail::rawMemoryToString(p); } else { return "nullptr"; } } }; #if defined(_MANAGED) template struct StringMaker { static std::string convert( T^ ref ) { return ::Catch::Detail::clrReferenceToString(ref); } }; #endif namespace Detail { template std::string rangeToString(InputIterator first, Sentinel last) { ReusableStringStream rss; rss << "{ "; if (first != last) { rss << ::Catch::Detail::stringify(*first); for (++first; first != last; ++first) rss << ", " << ::Catch::Detail::stringify(*first); } rss << " }"; return rss.str(); } } } // namespace Catch ////////////////////////////////////////////////////// // Separate std-lib types stringification, so it can be selectively enabled // This means that we do not bring in their headers #if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) # define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER # define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER # define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER # define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER #endif // Separate std::pair specialization #if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) #include namespace Catch { template struct StringMaker > { static std::string convert(const std::pair& pair) { ReusableStringStream rss; rss << "{ " << ::Catch::Detail::stringify(pair.first) << ", " << ::Catch::Detail::stringify(pair.second) << " }"; return rss.str(); } }; } #endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER #if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL) #include namespace Catch { template struct StringMaker > { static std::string convert(const std::optional& optional) { if (optional.has_value()) { return ::Catch::Detail::stringify(*optional); } else { return "{ }"; } } }; template <> struct StringMaker { static std::string convert(const std::nullopt_t&) { return "{ }"; } }; } #endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER // Separate std::tuple specialization #if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) #include namespace Catch { namespace Detail { template< typename Tuple, std::size_t N = 0, bool = (N < std::tuple_size::value) > struct TupleElementPrinter { static void print(const Tuple& tuple, std::ostream& os) { os << (N ? ", " : " ") << ::Catch::Detail::stringify(std::get(tuple)); TupleElementPrinter::print(tuple, os); } }; template< typename Tuple, std::size_t N > struct TupleElementPrinter { static void print(const Tuple&, std::ostream&) {} }; } template struct StringMaker> { static std::string convert(const std::tuple& tuple) { ReusableStringStream rss; rss << '{'; Detail::TupleElementPrinter>::print(tuple, rss.get()); rss << " }"; return rss.str(); } }; } #endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER #if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) #include namespace Catch { template<> struct StringMaker { static std::string convert(const std::monostate&) { return "{ }"; } }; template struct StringMaker> { static std::string convert(const std::variant& variant) { if (variant.valueless_by_exception()) { return "{valueless variant}"; } else { return std::visit( [](const auto& value) { return ::Catch::Detail::stringify(value); }, variant ); } } }; } #endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER namespace Catch { // Import begin/ end from std here using std::begin; using std::end; namespace Detail { template struct is_range_impl : std::false_type {}; template struct is_range_impl()))>> : std::true_type {}; } // namespace Detail template struct is_range : Detail::is_range_impl {}; #if defined(_MANAGED) // Managed types are never ranges template struct is_range { static const bool value = false; }; #endif template std::string rangeToString( Range const& range ) { return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); } // Handle vector specially template std::string rangeToString( std::vector const& v ) { ReusableStringStream rss; rss << "{ "; bool first = true; for( bool b : v ) { if( first ) first = false; else rss << ", "; rss << ::Catch::Detail::stringify( b ); } rss << " }"; return rss.str(); } template struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>> { static std::string convert( R const& range ) { return rangeToString( range ); } }; template struct StringMaker { static std::string convert(T const(&arr)[SZ]) { return rangeToString(arr); } }; } // namespace Catch // Separate std::chrono::duration specialization #include #include #include namespace Catch { template struct ratio_string { static std::string symbol() { Catch::ReusableStringStream rss; rss << '[' << Ratio::num << '/' << Ratio::den << ']'; return rss.str(); } }; template <> struct ratio_string { static char symbol() { return 'a'; } }; template <> struct ratio_string { static char symbol() { return 'f'; } }; template <> struct ratio_string { static char symbol() { return 'p'; } }; template <> struct ratio_string { static char symbol() { return 'n'; } }; template <> struct ratio_string { static char symbol() { return 'u'; } }; template <> struct ratio_string { static char symbol() { return 'm'; } }; //////////// // std::chrono::duration specializations template struct StringMaker> { static std::string convert(std::chrono::duration const& duration) { ReusableStringStream rss; rss << duration.count() << ' ' << ratio_string::symbol() << 's'; return rss.str(); } }; template struct StringMaker>> { static std::string convert(std::chrono::duration> const& duration) { ReusableStringStream rss; rss << duration.count() << " s"; return rss.str(); } }; template struct StringMaker>> { static std::string convert(std::chrono::duration> const& duration) { ReusableStringStream rss; rss << duration.count() << " m"; return rss.str(); } }; template struct StringMaker>> { static std::string convert(std::chrono::duration> const& duration) { ReusableStringStream rss; rss << duration.count() << " h"; return rss.str(); } }; //////////// // std::chrono::time_point specialization // Generic time_point cannot be specialized, only std::chrono::time_point template struct StringMaker> { static std::string convert(std::chrono::time_point const& time_point) { return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; } }; // std::chrono::time_point specialization template struct StringMaker> { static std::string convert(std::chrono::time_point const& time_point) { auto converted = std::chrono::system_clock::to_time_t(time_point); #ifdef _MSC_VER std::tm timeInfo = {}; const auto err = gmtime_s(&timeInfo, &converted); if ( err ) { return "gmtime from provided timepoint has failed. This " "happens e.g. with pre-1970 dates using Microsoft libc"; } #else std::tm* timeInfo = std::gmtime(&converted); #endif auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); char timeStamp[timeStampSize]; const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; #ifdef _MSC_VER std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); #else std::strftime(timeStamp, timeStampSize, fmt, timeInfo); #endif return std::string(timeStamp, timeStampSize - 1); } }; } #define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \ namespace Catch { \ template<> struct StringMaker { \ static std::string convert( enumName value ) { \ static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \ return static_cast(enumInfo.lookup( static_cast( value ) )); \ } \ }; \ } #define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ ) #ifdef _MSC_VER #pragma warning(pop) #endif #endif // CATCH_TOSTRING_HPP_INCLUDED #include namespace Catch { class Approx { private: bool equalityComparisonImpl(double other) const; // Sets and validates the new margin (margin >= 0) void setMargin(double margin); // Sets and validates the new epsilon (0 < epsilon < 1) void setEpsilon(double epsilon); public: explicit Approx ( double value ); static Approx custom(); Approx operator-() const; template ::value>> Approx operator()( T const& value ) const { Approx approx( static_cast(value) ); approx.m_epsilon = m_epsilon; approx.m_margin = m_margin; approx.m_scale = m_scale; return approx; } template ::value>> explicit Approx( T const& value ): Approx(static_cast(value)) {} template ::value>> friend bool operator == ( const T& lhs, Approx const& rhs ) { auto lhs_v = static_cast(lhs); return rhs.equalityComparisonImpl(lhs_v); } template ::value>> friend bool operator == ( Approx const& lhs, const T& rhs ) { return operator==( rhs, lhs ); } template ::value>> friend bool operator != ( T const& lhs, Approx const& rhs ) { return !operator==( lhs, rhs ); } template ::value>> friend bool operator != ( Approx const& lhs, T const& rhs ) { return !operator==( rhs, lhs ); } template ::value>> friend bool operator <= ( T const& lhs, Approx const& rhs ) { return static_cast(lhs) < rhs.m_value || lhs == rhs; } template ::value>> friend bool operator <= ( Approx const& lhs, T const& rhs ) { return lhs.m_value < static_cast(rhs) || lhs == rhs; } template ::value>> friend bool operator >= ( T const& lhs, Approx const& rhs ) { return static_cast(lhs) > rhs.m_value || lhs == rhs; } template ::value>> friend bool operator >= ( Approx const& lhs, T const& rhs ) { return lhs.m_value > static_cast(rhs) || lhs == rhs; } template ::value>> Approx& epsilon( T const& newEpsilon ) { const auto epsilonAsDouble = static_cast(newEpsilon); setEpsilon(epsilonAsDouble); return *this; } template ::value>> Approx& margin( T const& newMargin ) { const auto marginAsDouble = static_cast(newMargin); setMargin(marginAsDouble); return *this; } template ::value>> Approx& scale( T const& newScale ) { m_scale = static_cast(newScale); return *this; } std::string toString() const; private: double m_epsilon; double m_margin; double m_scale; double m_value; }; namespace literals { Approx operator ""_a(long double val); Approx operator ""_a(unsigned long long val); } // end namespace literals template<> struct StringMaker { static std::string convert(Catch::Approx const& value); }; } // end namespace Catch #endif // CATCH_APPROX_HPP_INCLUDED #ifndef CATCH_ASSERTION_INFO_HPP_INCLUDED #define CATCH_ASSERTION_INFO_HPP_INCLUDED #ifndef CATCH_SOURCE_LINE_INFO_HPP_INCLUDED #define CATCH_SOURCE_LINE_INFO_HPP_INCLUDED #include #include namespace Catch { struct SourceLineInfo { SourceLineInfo() = delete; constexpr SourceLineInfo( char const* _file, std::size_t _line ) noexcept: file( _file ), line( _line ) {} bool operator == ( SourceLineInfo const& other ) const noexcept; bool operator < ( SourceLineInfo const& other ) const noexcept; char const* file; std::size_t line; friend std::ostream& operator << (std::ostream& os, SourceLineInfo const& info); }; } #define CATCH_INTERNAL_LINEINFO \ ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) #endif // CATCH_SOURCE_LINE_INFO_HPP_INCLUDED namespace Catch { struct AssertionInfo { // AssertionInfo() = delete; StringRef macroName; SourceLineInfo lineInfo; StringRef capturedExpression; ResultDisposition::Flags resultDisposition; }; } // end namespace Catch #endif // CATCH_ASSERTION_INFO_HPP_INCLUDED #ifndef CATCH_ASSERTION_RESULT_HPP_INCLUDED #define CATCH_ASSERTION_RESULT_HPP_INCLUDED #ifndef CATCH_LAZY_EXPR_HPP_INCLUDED #define CATCH_LAZY_EXPR_HPP_INCLUDED #include namespace Catch { class ITransientExpression; class LazyExpression { friend class AssertionHandler; friend struct AssertionStats; friend class RunContext; ITransientExpression const* m_transientExpression = nullptr; bool m_isNegated; public: constexpr LazyExpression( bool isNegated ): m_isNegated(isNegated) {} constexpr LazyExpression(LazyExpression const& other) = default; LazyExpression& operator = ( LazyExpression const& ) = delete; constexpr explicit operator bool() const { return m_transientExpression != nullptr; } friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; }; } // namespace Catch #endif // CATCH_LAZY_EXPR_HPP_INCLUDED #include namespace Catch { struct AssertionResultData { AssertionResultData() = delete; AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); std::string message; mutable std::string reconstructedExpression; LazyExpression lazyExpression; ResultWas::OfType resultType; std::string reconstructExpression() const; }; class AssertionResult { public: AssertionResult() = delete; AssertionResult( AssertionInfo const& info, AssertionResultData&& data ); bool isOk() const; bool succeeded() const; ResultWas::OfType getResultType() const; bool hasExpression() const; bool hasMessage() const; std::string getExpression() const; std::string getExpressionInMacro() const; bool hasExpandedExpression() const; std::string getExpandedExpression() const; StringRef getMessage() const; SourceLineInfo getSourceInfo() const; StringRef getTestMacroName() const; //protected: AssertionInfo m_info; AssertionResultData m_resultData; }; } // end namespace Catch #endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED #ifndef CATCH_CASE_SENSITIVE_HPP_INCLUDED #define CATCH_CASE_SENSITIVE_HPP_INCLUDED namespace Catch { enum class CaseSensitive { Yes, No }; } // namespace Catch #endif // CATCH_CASE_SENSITIVE_HPP_INCLUDED #ifndef CATCH_CONFIG_HPP_INCLUDED #define CATCH_CONFIG_HPP_INCLUDED #ifndef CATCH_TEST_SPEC_HPP_INCLUDED #define CATCH_TEST_SPEC_HPP_INCLUDED #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wpadded" #endif #ifndef CATCH_WILDCARD_PATTERN_HPP_INCLUDED #define CATCH_WILDCARD_PATTERN_HPP_INCLUDED #include namespace Catch { class WildcardPattern { enum WildcardPosition { NoWildcard = 0, WildcardAtStart = 1, WildcardAtEnd = 2, WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd }; public: WildcardPattern( std::string const& pattern, CaseSensitive caseSensitivity ); bool matches( std::string const& str ) const; private: std::string normaliseString( std::string const& str ) const; CaseSensitive m_caseSensitivity; WildcardPosition m_wildcard = NoWildcard; std::string m_pattern; }; } #endif // CATCH_WILDCARD_PATTERN_HPP_INCLUDED #include #include #include namespace Catch { class IConfig; struct TestCaseInfo; class TestCaseHandle; class TestSpec { class Pattern { public: explicit Pattern( std::string const& name ); virtual ~Pattern(); virtual bool matches( TestCaseInfo const& testCase ) const = 0; std::string const& name() const; private: virtual void serializeTo( std::ostream& out ) const = 0; // Writes string that would be reparsed into the pattern friend std::ostream& operator<<(std::ostream& out, Pattern const& pattern) { pattern.serializeTo( out ); return out; } std::string const m_name; }; class NamePattern : public Pattern { public: explicit NamePattern( std::string const& name, std::string const& filterString ); bool matches( TestCaseInfo const& testCase ) const override; private: void serializeTo( std::ostream& out ) const override; WildcardPattern m_wildcardPattern; }; class TagPattern : public Pattern { public: explicit TagPattern( std::string const& tag, std::string const& filterString ); bool matches( TestCaseInfo const& testCase ) const override; private: void serializeTo( std::ostream& out ) const override; std::string m_tag; }; struct Filter { std::vector> m_required; std::vector> m_forbidden; //! Serializes this filter into a string that would be parsed into //! an equivalent filter void serializeTo( std::ostream& out ) const; friend std::ostream& operator<<(std::ostream& out, Filter const& f) { f.serializeTo( out ); return out; } bool matches( TestCaseInfo const& testCase ) const; }; static std::string extractFilterName( Filter const& filter ); public: struct FilterMatch { std::string name; std::vector tests; }; using Matches = std::vector; using vectorStrings = std::vector; bool hasFilters() const; bool matches( TestCaseInfo const& testCase ) const; Matches matchesByFilter( std::vector const& testCases, IConfig const& config ) const; const vectorStrings & getInvalidSpecs() const; private: std::vector m_filters; std::vector m_invalidSpecs; friend class TestSpecParser; //! Serializes this test spec into a string that would be parsed into //! equivalent test spec void serializeTo( std::ostream& out ) const; friend std::ostream& operator<<(std::ostream& out, TestSpec const& spec) { spec.serializeTo( out ); return out; } }; } #ifdef __clang__ #pragma clang diagnostic pop #endif #endif // CATCH_TEST_SPEC_HPP_INCLUDED #ifndef CATCH_OPTIONAL_HPP_INCLUDED #define CATCH_OPTIONAL_HPP_INCLUDED #include namespace Catch { // An optional type template class Optional { public: Optional(): nullableValue( nullptr ) {} ~Optional() { reset(); } Optional( T const& _value ): nullableValue( new ( storage ) T( _value ) ) {} Optional( T&& _value ): nullableValue( new ( storage ) T( CATCH_MOVE( _value ) ) ) {} Optional& operator=( T const& _value ) { reset(); nullableValue = new ( storage ) T( _value ); return *this; } Optional& operator=( T&& _value ) { reset(); nullableValue = new ( storage ) T( CATCH_MOVE( _value ) ); return *this; } Optional( Optional const& _other ): nullableValue( _other ? new ( storage ) T( *_other ) : nullptr ) {} Optional( Optional&& _other ): nullableValue( _other ? new ( storage ) T( CATCH_MOVE( *_other ) ) : nullptr ) {} Optional& operator=( Optional const& _other ) { if ( &_other != this ) { reset(); if ( _other ) { nullableValue = new ( storage ) T( *_other ); } } return *this; } Optional& operator=( Optional&& _other ) { if ( &_other != this ) { reset(); if ( _other ) { nullableValue = new ( storage ) T( CATCH_MOVE( *_other ) ); } } return *this; } void reset() { if ( nullableValue ) { nullableValue->~T(); } nullableValue = nullptr; } T& operator*() { assert(nullableValue); return *nullableValue; } T const& operator*() const { assert(nullableValue); return *nullableValue; } T* operator->() { assert(nullableValue); return nullableValue; } const T* operator->() const { assert(nullableValue); return nullableValue; } T valueOr( T const& defaultValue ) const { return nullableValue ? *nullableValue : defaultValue; } bool some() const { return nullableValue != nullptr; } bool none() const { return nullableValue == nullptr; } bool operator !() const { return nullableValue == nullptr; } explicit operator bool() const { return some(); } friend bool operator==(Optional const& a, Optional const& b) { if (a.none() && b.none()) { return true; } else if (a.some() && b.some()) { return *a == *b; } else { return false; } } friend bool operator!=(Optional const& a, Optional const& b) { return !( a == b ); } private: T* nullableValue; alignas(alignof(T)) char storage[sizeof(T)]; }; } // end namespace Catch #endif // CATCH_OPTIONAL_HPP_INCLUDED #ifndef CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED #define CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED #include namespace Catch { enum class GenerateFrom { Time, RandomDevice, //! Currently equivalent to RandomDevice, but can change at any point Default }; std::uint32_t generateRandomSeed(GenerateFrom from); } // end namespace Catch #endif // CATCH_RANDOM_SEED_GENERATION_HPP_INCLUDED #ifndef CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED #define CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED #include #include #include namespace Catch { enum class ColourMode : std::uint8_t; namespace Detail { //! Splits the reporter spec into reporter name and kv-pair options std::vector splitReporterSpec( StringRef reporterSpec ); Optional stringToColourMode( StringRef colourMode ); } /** * Structured reporter spec that a reporter can be created from * * Parsing has been validated, but semantics have not. This means e.g. * that the colour mode is known to Catch2, but it might not be * compiled into the binary, and the output filename might not be * openable. */ class ReporterSpec { std::string m_name; Optional m_outputFileName; Optional m_colourMode; std::map m_customOptions; friend bool operator==( ReporterSpec const& lhs, ReporterSpec const& rhs ); friend bool operator!=( ReporterSpec const& lhs, ReporterSpec const& rhs ) { return !( lhs == rhs ); } public: ReporterSpec( std::string name, Optional outputFileName, Optional colourMode, std::map customOptions ); std::string const& name() const { return m_name; } Optional const& outputFile() const { return m_outputFileName; } Optional const& colourMode() const { return m_colourMode; } std::map const& customOptions() const { return m_customOptions; } }; /** * Parses provided reporter spec string into * * Returns empty optional on errors, e.g. * * field that is not first and not a key+value pair * * duplicated keys in kv pair * * unknown catch reporter option * * empty key/value in an custom kv pair * * ... */ Optional parseReporterSpec( StringRef reporterSpec ); } #endif // CATCH_REPORTER_SPEC_PARSER_HPP_INCLUDED #include #include #include #include namespace Catch { class IStream; /** * `ReporterSpec` but with the defaults filled in. * * Like `ReporterSpec`, the semantics are unchecked. */ struct ProcessedReporterSpec { std::string name; std::string outputFilename; ColourMode colourMode; std::map customOptions; friend bool operator==( ProcessedReporterSpec const& lhs, ProcessedReporterSpec const& rhs ); friend bool operator!=( ProcessedReporterSpec const& lhs, ProcessedReporterSpec const& rhs ) { return !( lhs == rhs ); } }; struct ConfigData { bool listTests = false; bool listTags = false; bool listReporters = false; bool listListeners = false; bool showSuccessfulTests = false; bool shouldDebugBreak = false; bool noThrow = false; bool showHelp = false; bool showInvisibles = false; bool filenamesAsTags = false; bool libIdentify = false; bool allowZeroTests = false; int abortAfter = -1; uint32_t rngSeed = generateRandomSeed(GenerateFrom::Default); unsigned int shardCount = 1; unsigned int shardIndex = 0; bool skipBenchmarks = false; bool benchmarkNoAnalysis = false; unsigned int benchmarkSamples = 100; double benchmarkConfidenceInterval = 0.95; unsigned int benchmarkResamples = 100'000; std::chrono::milliseconds::rep benchmarkWarmupTime = 100; Verbosity verbosity = Verbosity::Normal; WarnAbout::What warnings = WarnAbout::Nothing; ShowDurations showDurations = ShowDurations::DefaultForReporter; double minDuration = -1; TestRunOrder runOrder = TestRunOrder::Declared; ColourMode defaultColourMode = ColourMode::PlatformDefault; WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; std::string defaultOutputFilename; std::string name; std::string processName; std::vector reporterSpecifications; std::vector testsOrTags; std::vector sectionsToRun; }; class Config : public IConfig { public: Config() = default; Config( ConfigData const& data ); ~Config() override; // = default in the cpp file bool listTests() const; bool listTags() const; bool listReporters() const; bool listListeners() const; std::vector const& getReporterSpecs() const; std::vector const& getProcessedReporterSpecs() const; std::vector const& getTestsOrTags() const override; std::vector const& getSectionsToRun() const override; TestSpec const& testSpec() const override; bool hasTestFilters() const override; bool showHelp() const; // IConfig interface bool allowThrows() const override; StringRef name() const override; bool includeSuccessfulResults() const override; bool warnAboutMissingAssertions() const override; bool warnAboutUnmatchedTestSpecs() const override; bool zeroTestsCountAsSuccess() const override; ShowDurations showDurations() const override; double minDuration() const override; TestRunOrder runOrder() const override; uint32_t rngSeed() const override; unsigned int shardCount() const override; unsigned int shardIndex() const override; ColourMode defaultColourMode() const override; bool shouldDebugBreak() const override; int abortAfter() const override; bool showInvisibles() const override; Verbosity verbosity() const override; bool skipBenchmarks() const override; bool benchmarkNoAnalysis() const override; unsigned int benchmarkSamples() const override; double benchmarkConfidenceInterval() const override; unsigned int benchmarkResamples() const override; std::chrono::milliseconds benchmarkWarmupTime() const override; private: // Reads Bazel env vars and applies them to the config void readBazelEnvVars(); ConfigData m_data; std::vector m_processedReporterSpecs; TestSpec m_testSpec; bool m_hasTestFilters = false; }; } // end namespace Catch #endif // CATCH_CONFIG_HPP_INCLUDED #ifndef CATCH_GET_RANDOM_SEED_HPP_INCLUDED #define CATCH_GET_RANDOM_SEED_HPP_INCLUDED #include namespace Catch { //! Returns Catch2's current RNG seed. std::uint32_t getSeed(); } #endif // CATCH_GET_RANDOM_SEED_HPP_INCLUDED #ifndef CATCH_MESSAGE_HPP_INCLUDED #define CATCH_MESSAGE_HPP_INCLUDED /** \file * Wrapper for the CATCH_CONFIG_PREFIX_MESSAGES configuration option * * CATCH_CONFIG_PREFIX_ALL can be used to avoid clashes with other macros * by prepending CATCH_. This may not be desirable if the only clashes are with * logger macros such as INFO and WARN. In this cases * CATCH_CONFIG_PREFIX_MESSAGES can be used to only prefix a small subset * of relevant macros. * */ #ifndef CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED #define CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED #if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_PREFIX_MESSAGES) #define CATCH_CONFIG_PREFIX_MESSAGES #endif #endif // CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED #ifndef CATCH_STREAM_END_STOP_HPP_INCLUDED #define CATCH_STREAM_END_STOP_HPP_INCLUDED namespace Catch { // Use this in variadic streaming macros to allow // << +StreamEndStop // as well as // << stuff +StreamEndStop struct StreamEndStop { constexpr StringRef operator+() const { return StringRef(); } template constexpr friend T const& operator+( T const& value, StreamEndStop ) { return value; } }; } // namespace Catch #endif // CATCH_STREAM_END_STOP_HPP_INCLUDED #ifndef CATCH_MESSAGE_INFO_HPP_INCLUDED #define CATCH_MESSAGE_INFO_HPP_INCLUDED #include namespace Catch { struct MessageInfo { MessageInfo( StringRef _macroName, SourceLineInfo const& _lineInfo, ResultWas::OfType _type ); StringRef macroName; std::string message; SourceLineInfo lineInfo; ResultWas::OfType type; unsigned int sequence; bool operator == (MessageInfo const& other) const { return sequence == other.sequence; } bool operator < (MessageInfo const& other) const { return sequence < other.sequence; } private: static unsigned int globalCount; }; } // end namespace Catch #endif // CATCH_MESSAGE_INFO_HPP_INCLUDED #include #include namespace Catch { struct SourceLineInfo; class IResultCapture; struct MessageStream { template MessageStream& operator << ( T const& value ) { m_stream << value; return *this; } ReusableStringStream m_stream; }; struct MessageBuilder : MessageStream { MessageBuilder( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType type ): m_info(macroName, lineInfo, type) {} template MessageBuilder&& operator << ( T const& value ) && { m_stream << value; return CATCH_MOVE(*this); } MessageInfo m_info; }; class ScopedMessage { public: explicit ScopedMessage( MessageBuilder&& builder ); ScopedMessage( ScopedMessage& duplicate ) = delete; ScopedMessage( ScopedMessage&& old ) noexcept; ~ScopedMessage(); MessageInfo m_info; bool m_moved = false; }; class Capturer { std::vector m_messages; IResultCapture& m_resultCapture; size_t m_captured = 0; public: Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); Capturer(Capturer const&) = delete; Capturer& operator=(Capturer const&) = delete; ~Capturer(); void captureValue( size_t index, std::string const& value ); template void captureValues( size_t index, T const& value ) { captureValue( index, Catch::Detail::stringify( value ) ); } template void captureValues( size_t index, T const& value, Ts const&... values ) { captureValue( index, Catch::Detail::stringify(value) ); captureValues( index+1, values... ); } }; } // end namespace Catch /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ do { \ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \ catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ catchAssertionHandler.complete(); \ } while( false ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \ Catch::Capturer varName( macroName##_catch_sr, \ CATCH_INTERNAL_LINEINFO, \ Catch::ResultWas::Info, \ #__VA_ARGS__##_catch_sr ); \ varName.captureValues( 0, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_INFO( macroName, log ) \ const Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \ Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) #if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE) #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) #define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg ) #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) #define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", __VA_ARGS__ ) #elif defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE) #define CATCH_INFO( msg ) (void)(0) #define CATCH_UNSCOPED_INFO( msg ) (void)(0) #define CATCH_WARN( msg ) (void)(0) #define CATCH_CAPTURE( ... ) (void)(0) #elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE) #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) #define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg ) #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) #define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", __VA_ARGS__ ) #elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE) #define INFO( msg ) (void)(0) #define UNSCOPED_INFO( msg ) (void)(0) #define WARN( msg ) (void)(0) #define CAPTURE( ... ) (void)(0) #endif // end of user facing macro declarations #endif // CATCH_MESSAGE_HPP_INCLUDED #ifndef CATCH_SECTION_INFO_HPP_INCLUDED #define CATCH_SECTION_INFO_HPP_INCLUDED #ifndef CATCH_TOTALS_HPP_INCLUDED #define CATCH_TOTALS_HPP_INCLUDED #include namespace Catch { struct Counts { Counts operator - ( Counts const& other ) const; Counts& operator += ( Counts const& other ); std::uint64_t total() const; bool allPassed() const; bool allOk() const; std::uint64_t passed = 0; std::uint64_t failed = 0; std::uint64_t failedButOk = 0; std::uint64_t skipped = 0; }; struct Totals { Totals operator - ( Totals const& other ) const; Totals& operator += ( Totals const& other ); Totals delta( Totals const& prevTotals ) const; Counts assertions; Counts testCases; }; } #endif // CATCH_TOTALS_HPP_INCLUDED #include namespace Catch { struct SectionInfo { // The last argument is ignored, so that people can write // SECTION("ShortName", "Proper description that is long") and // still use the `-c` flag comfortably. SectionInfo( SourceLineInfo const& _lineInfo, std::string _name, const char* const = nullptr ): name(CATCH_MOVE(_name)), lineInfo(_lineInfo) {} std::string name; SourceLineInfo lineInfo; }; struct SectionEndInfo { SectionInfo sectionInfo; Counts prevAssertions; double durationInSeconds; }; } // end namespace Catch #endif // CATCH_SECTION_INFO_HPP_INCLUDED #ifndef CATCH_SESSION_HPP_INCLUDED #define CATCH_SESSION_HPP_INCLUDED #ifndef CATCH_COMMANDLINE_HPP_INCLUDED #define CATCH_COMMANDLINE_HPP_INCLUDED #ifndef CATCH_CLARA_HPP_INCLUDED #define CATCH_CLARA_HPP_INCLUDED #if defined( __clang__ ) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wweak-vtables" # pragma clang diagnostic ignored "-Wshadow" # pragma clang diagnostic ignored "-Wdeprecated" #endif #if defined( __GNUC__ ) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wsign-conversion" #endif #ifndef CLARA_CONFIG_OPTIONAL_TYPE # ifdef __has_include # if __has_include( ) && __cplusplus >= 201703L # include # define CLARA_CONFIG_OPTIONAL_TYPE std::optional # endif # endif #endif #include #include #include #include #include #include #include namespace Catch { namespace Clara { class Args; class Parser; // enum of result types from a parse enum class ParseResultType { Matched, NoMatch, ShortCircuitAll, ShortCircuitSame }; struct accept_many_t {}; constexpr accept_many_t accept_many {}; namespace Detail { struct fake_arg { template operator T(); }; template struct is_unary_function : std::false_type {}; template struct is_unary_function< F, Catch::Detail::void_t()( fake_arg() ) ) > > : std::true_type {}; // Traits for extracting arg and return type of lambdas (for single // argument lambdas) template struct UnaryLambdaTraits : UnaryLambdaTraits {}; template struct UnaryLambdaTraits { static const bool isValid = false; }; template struct UnaryLambdaTraits { static const bool isValid = true; using ArgType = std::remove_const_t>; using ReturnType = ReturnT; }; class TokenStream; // Wraps a token coming from a token stream. These may not directly // correspond to strings as a single string may encode an option + // its argument if the : or = form is used enum class TokenType { Option, Argument }; struct Token { TokenType type; StringRef token; }; // Abstracts iterators into args as a stream of tokens, with option // arguments uniformly handled class TokenStream { using Iterator = std::vector::const_iterator; Iterator it; Iterator itEnd; std::vector m_tokenBuffer; void loadBuffer(); public: explicit TokenStream( Args const& args ); TokenStream( Iterator it, Iterator itEnd ); explicit operator bool() const { return !m_tokenBuffer.empty() || it != itEnd; } size_t count() const { return m_tokenBuffer.size() + ( itEnd - it ); } Token operator*() const { assert( !m_tokenBuffer.empty() ); return m_tokenBuffer.front(); } Token const* operator->() const { assert( !m_tokenBuffer.empty() ); return &m_tokenBuffer.front(); } TokenStream& operator++(); }; //! Denotes type of a parsing result enum class ResultType { Ok, ///< No errors LogicError, ///< Error in user-specified arguments for ///< construction RuntimeError ///< Error in parsing inputs }; class ResultBase { protected: ResultBase( ResultType type ): m_type( type ) {} virtual ~ResultBase(); // = default; ResultBase(ResultBase const&) = default; ResultBase& operator=(ResultBase const&) = default; ResultBase(ResultBase&&) = default; ResultBase& operator=(ResultBase&&) = default; virtual void enforceOk() const = 0; ResultType m_type; }; template class ResultValueBase : public ResultBase { public: T const& value() const& { enforceOk(); return m_value; } T&& value() && { enforceOk(); return CATCH_MOVE( m_value ); } protected: ResultValueBase( ResultType type ): ResultBase( type ) {} ResultValueBase( ResultValueBase const& other ): ResultBase( other ) { if ( m_type == ResultType::Ok ) new ( &m_value ) T( other.m_value ); } ResultValueBase( ResultValueBase&& other ): ResultBase( other ) { if ( m_type == ResultType::Ok ) new ( &m_value ) T( CATCH_MOVE(other.m_value) ); } ResultValueBase( ResultType, T const& value ): ResultBase( ResultType::Ok ) { new ( &m_value ) T( value ); } ResultValueBase( ResultType, T&& value ): ResultBase( ResultType::Ok ) { new ( &m_value ) T( CATCH_MOVE(value) ); } ResultValueBase& operator=( ResultValueBase const& other ) { if ( m_type == ResultType::Ok ) m_value.~T(); ResultBase::operator=( other ); if ( m_type == ResultType::Ok ) new ( &m_value ) T( other.m_value ); return *this; } ResultValueBase& operator=( ResultValueBase&& other ) { if ( m_type == ResultType::Ok ) m_value.~T(); ResultBase::operator=( other ); if ( m_type == ResultType::Ok ) new ( &m_value ) T( CATCH_MOVE(other.m_value) ); return *this; } ~ResultValueBase() override { if ( m_type == ResultType::Ok ) m_value.~T(); } union { T m_value; }; }; template <> class ResultValueBase : public ResultBase { protected: using ResultBase::ResultBase; }; template class BasicResult : public ResultValueBase { public: template explicit BasicResult( BasicResult const& other ): ResultValueBase( other.type() ), m_errorMessage( other.errorMessage() ) { assert( type() != ResultType::Ok ); } template static auto ok( U&& value ) -> BasicResult { return { ResultType::Ok, CATCH_FORWARD(value) }; } static auto ok() -> BasicResult { return { ResultType::Ok }; } static auto logicError( std::string&& message ) -> BasicResult { return { ResultType::LogicError, CATCH_MOVE(message) }; } static auto runtimeError( std::string&& message ) -> BasicResult { return { ResultType::RuntimeError, CATCH_MOVE(message) }; } explicit operator bool() const { return m_type == ResultType::Ok; } auto type() const -> ResultType { return m_type; } auto errorMessage() const -> std::string const& { return m_errorMessage; } protected: void enforceOk() const override { // Errors shouldn't reach this point, but if they do // the actual error message will be in m_errorMessage assert( m_type != ResultType::LogicError ); assert( m_type != ResultType::RuntimeError ); if ( m_type != ResultType::Ok ) std::abort(); } std::string m_errorMessage; // Only populated if resultType is an error BasicResult( ResultType type, std::string&& message ): ResultValueBase( type ), m_errorMessage( CATCH_MOVE(message) ) { assert( m_type != ResultType::Ok ); } using ResultValueBase::ResultValueBase; using ResultBase::m_type; }; class ParseState { public: ParseState( ParseResultType type, TokenStream remainingTokens ); ParseResultType type() const { return m_type; } TokenStream const& remainingTokens() const& { return m_remainingTokens; } TokenStream&& remainingTokens() && { return CATCH_MOVE( m_remainingTokens ); } private: ParseResultType m_type; TokenStream m_remainingTokens; }; using Result = BasicResult; using ParserResult = BasicResult; using InternalParseResult = BasicResult; struct HelpColumns { std::string left; StringRef descriptions; }; template ParserResult convertInto( std::string const& source, T& target ) { std::stringstream ss( source ); ss >> target; if ( ss.fail() ) { return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); } else { return ParserResult::ok( ParseResultType::Matched ); } } ParserResult convertInto( std::string const& source, std::string& target ); ParserResult convertInto( std::string const& source, bool& target ); #ifdef CLARA_CONFIG_OPTIONAL_TYPE template auto convertInto( std::string const& source, CLARA_CONFIG_OPTIONAL_TYPE& target ) -> ParserResult { T temp; auto result = convertInto( source, temp ); if ( result ) target = CATCH_MOVE( temp ); return result; } #endif // CLARA_CONFIG_OPTIONAL_TYPE struct BoundRef : Catch::Detail::NonCopyable { virtual ~BoundRef() = default; virtual bool isContainer() const; virtual bool isFlag() const; }; struct BoundValueRefBase : BoundRef { virtual auto setValue( std::string const& arg ) -> ParserResult = 0; }; struct BoundFlagRefBase : BoundRef { virtual auto setFlag( bool flag ) -> ParserResult = 0; bool isFlag() const override; }; template struct BoundValueRef : BoundValueRefBase { T& m_ref; explicit BoundValueRef( T& ref ): m_ref( ref ) {} ParserResult setValue( std::string const& arg ) override { return convertInto( arg, m_ref ); } }; template struct BoundValueRef> : BoundValueRefBase { std::vector& m_ref; explicit BoundValueRef( std::vector& ref ): m_ref( ref ) {} auto isContainer() const -> bool override { return true; } auto setValue( std::string const& arg ) -> ParserResult override { T temp; auto result = convertInto( arg, temp ); if ( result ) m_ref.push_back( temp ); return result; } }; struct BoundFlagRef : BoundFlagRefBase { bool& m_ref; explicit BoundFlagRef( bool& ref ): m_ref( ref ) {} ParserResult setFlag( bool flag ) override; }; template struct LambdaInvoker { static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); template static auto invoke( L const& lambda, ArgType const& arg ) -> ParserResult { return lambda( arg ); } }; template <> struct LambdaInvoker { template static auto invoke( L const& lambda, ArgType const& arg ) -> ParserResult { lambda( arg ); return ParserResult::ok( ParseResultType::Matched ); } }; template auto invokeLambda( L const& lambda, std::string const& arg ) -> ParserResult { ArgType temp{}; auto result = convertInto( arg, temp ); return !result ? result : LambdaInvoker::ReturnType>::invoke( lambda, temp ); } template struct BoundLambda : BoundValueRefBase { L m_lambda; static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); explicit BoundLambda( L const& lambda ): m_lambda( lambda ) {} auto setValue( std::string const& arg ) -> ParserResult override { return invokeLambda::ArgType>( m_lambda, arg ); } }; template struct BoundManyLambda : BoundLambda { explicit BoundManyLambda( L const& lambda ): BoundLambda( lambda ) {} bool isContainer() const override { return true; } }; template struct BoundFlagLambda : BoundFlagRefBase { L m_lambda; static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); explicit BoundFlagLambda( L const& lambda ): m_lambda( lambda ) {} auto setFlag( bool flag ) -> ParserResult override { return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); } }; enum class Optionality { Optional, Required }; class ParserBase { public: virtual ~ParserBase() = default; virtual auto validate() const -> Result { return Result::ok(); } virtual auto parse( std::string const& exeName, TokenStream tokens ) const -> InternalParseResult = 0; virtual size_t cardinality() const; InternalParseResult parse( Args const& args ) const; }; template class ComposableParserImpl : public ParserBase { public: template auto operator|( T const& other ) const -> Parser; }; // Common code and state for Args and Opts template class ParserRefImpl : public ComposableParserImpl { protected: Optionality m_optionality = Optionality::Optional; std::shared_ptr m_ref; StringRef m_hint; StringRef m_description; explicit ParserRefImpl( std::shared_ptr const& ref ): m_ref( ref ) {} public: template ParserRefImpl( accept_many_t, LambdaT const& ref, StringRef hint ): m_ref( std::make_shared>( ref ) ), m_hint( hint ) {} template ::value>> ParserRefImpl( T& ref, StringRef hint ): m_ref( std::make_shared>( ref ) ), m_hint( hint ) {} template ::value>> ParserRefImpl( LambdaT const& ref, StringRef hint ): m_ref( std::make_shared>( ref ) ), m_hint( hint ) {} DerivedT& operator()( StringRef description ) & { m_description = description; return static_cast( *this ); } DerivedT&& operator()( StringRef description ) && { m_description = description; return static_cast( *this ); } auto optional() -> DerivedT& { m_optionality = Optionality::Optional; return static_cast( *this ); } auto required() -> DerivedT& { m_optionality = Optionality::Required; return static_cast( *this ); } auto isOptional() const -> bool { return m_optionality == Optionality::Optional; } auto cardinality() const -> size_t override { if ( m_ref->isContainer() ) return 0; else return 1; } StringRef hint() const { return m_hint; } }; } // namespace detail // A parser for arguments class Arg : public Detail::ParserRefImpl { public: using ParserRefImpl::ParserRefImpl; using ParserBase::parse; Detail::InternalParseResult parse(std::string const&, Detail::TokenStream tokens) const override; }; // A parser for options class Opt : public Detail::ParserRefImpl { protected: std::vector m_optNames; public: template explicit Opt(LambdaT const& ref) : ParserRefImpl( std::make_shared>(ref)) {} explicit Opt(bool& ref); template ::value>> Opt( LambdaT const& ref, StringRef hint ): ParserRefImpl( ref, hint ) {} template Opt( accept_many_t, LambdaT const& ref, StringRef hint ): ParserRefImpl( accept_many, ref, hint ) {} template ::value>> Opt( T& ref, StringRef hint ): ParserRefImpl( ref, hint ) {} Opt& operator[]( StringRef optName ) & { m_optNames.push_back(optName); return *this; } Opt&& operator[]( StringRef optName ) && { m_optNames.push_back( optName ); return CATCH_MOVE(*this); } Detail::HelpColumns getHelpColumns() const; bool isMatch(StringRef optToken) const; using ParserBase::parse; Detail::InternalParseResult parse(std::string const&, Detail::TokenStream tokens) const override; Detail::Result validate() const override; }; // Specifies the name of the executable class ExeName : public Detail::ComposableParserImpl { std::shared_ptr m_name; std::shared_ptr m_ref; public: ExeName(); explicit ExeName(std::string& ref); template explicit ExeName(LambdaT const& lambda) : ExeName() { m_ref = std::make_shared>(lambda); } // The exe name is not parsed out of the normal tokens, but is // handled specially Detail::InternalParseResult parse(std::string const&, Detail::TokenStream tokens) const override; std::string const& name() const { return *m_name; } Detail::ParserResult set(std::string const& newName); }; // A Combined parser class Parser : Detail::ParserBase { mutable ExeName m_exeName; std::vector m_options; std::vector m_args; public: auto operator|=(ExeName const& exeName) -> Parser& { m_exeName = exeName; return *this; } auto operator|=(Arg const& arg) -> Parser& { m_args.push_back(arg); return *this; } friend Parser& operator|=( Parser& p, Opt const& opt ) { p.m_options.push_back( opt ); return p; } friend Parser& operator|=( Parser& p, Opt&& opt ) { p.m_options.push_back( CATCH_MOVE(opt) ); return p; } Parser& operator|=(Parser const& other); template friend Parser operator|( Parser const& p, T&& rhs ) { Parser temp( p ); temp |= rhs; return temp; } template friend Parser operator|( Parser&& p, T&& rhs ) { p |= CATCH_FORWARD(rhs); return CATCH_MOVE(p); } std::vector getHelpColumns() const; void writeToStream(std::ostream& os) const; friend auto operator<<(std::ostream& os, Parser const& parser) -> std::ostream& { parser.writeToStream(os); return os; } Detail::Result validate() const override; using ParserBase::parse; Detail::InternalParseResult parse(std::string const& exeName, Detail::TokenStream tokens) const override; }; /** * Wrapper over argc + argv, assumes that the inputs outlive it */ class Args { friend Detail::TokenStream; StringRef m_exeName; std::vector m_args; public: Args(int argc, char const* const* argv); // Helper constructor for testing Args(std::initializer_list args); StringRef exeName() const { return m_exeName; } }; // Convenience wrapper for option parser that specifies the help option struct Help : Opt { Help(bool& showHelpFlag); }; // Result type for parser operation using Detail::ParserResult; namespace Detail { template template Parser ComposableParserImpl::operator|(T const& other) const { return Parser() | static_cast(*this) | other; } } } // namespace Clara } // namespace Catch #if defined( __clang__ ) # pragma clang diagnostic pop #endif #if defined( __GNUC__ ) # pragma GCC diagnostic pop #endif #endif // CATCH_CLARA_HPP_INCLUDED namespace Catch { struct ConfigData; Clara::Parser makeCommandLineParser( ConfigData& config ); } // end namespace Catch #endif // CATCH_COMMANDLINE_HPP_INCLUDED namespace Catch { class Session : Detail::NonCopyable { public: Session(); ~Session(); void showHelp() const; void libIdentify(); int applyCommandLine( int argc, char const * const * argv ); #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) int applyCommandLine( int argc, wchar_t const * const * argv ); #endif void useConfigData( ConfigData const& configData ); template int run(int argc, CharT const * const argv[]) { if (m_startupExceptions) return 1; int returnCode = applyCommandLine(argc, argv); if (returnCode == 0) returnCode = run(); return returnCode; } int run(); Clara::Parser const& cli() const; void cli( Clara::Parser const& newParser ); ConfigData& configData(); Config& config(); private: int runInternal(); Clara::Parser m_cli; ConfigData m_configData; Detail::unique_ptr m_config; bool m_startupExceptions = false; }; } // end namespace Catch #endif // CATCH_SESSION_HPP_INCLUDED #ifndef CATCH_TAG_ALIAS_HPP_INCLUDED #define CATCH_TAG_ALIAS_HPP_INCLUDED #include namespace Catch { struct TagAlias { TagAlias(std::string const& _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {} std::string tag; SourceLineInfo lineInfo; }; } // end namespace Catch #endif // CATCH_TAG_ALIAS_HPP_INCLUDED #ifndef CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED #define CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED 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 #endif // CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED #ifndef CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED #define CATCH_TEMPLATE_TEST_MACROS_HPP_INCLUDED // We need this suppression to leak, because it took until GCC 10 // for the front end to handle local suppression via _Pragma properly // inside templates (so `TEMPLATE_TEST_CASE` and co). // **THIS IS DIFFERENT FOR STANDARD TESTS, WHERE GCC 9 IS SUFFICIENT** #if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ < 10 #pragma GCC diagnostic ignored "-Wparentheses" #endif #ifndef CATCH_TEST_MACROS_HPP_INCLUDED #define CATCH_TEST_MACROS_HPP_INCLUDED #ifndef CATCH_TEST_MACRO_IMPL_HPP_INCLUDED #define CATCH_TEST_MACRO_IMPL_HPP_INCLUDED #ifndef CATCH_ASSERTION_HANDLER_HPP_INCLUDED #define CATCH_ASSERTION_HANDLER_HPP_INCLUDED #ifndef CATCH_DECOMPOSER_HPP_INCLUDED #define CATCH_DECOMPOSER_HPP_INCLUDED #ifndef CATCH_COMPARE_TRAITS_HPP_INCLUDED #define CATCH_COMPARE_TRAITS_HPP_INCLUDED #include namespace Catch { namespace Detail { #if defined( __GNUC__ ) && !defined( __clang__ ) # pragma GCC diagnostic push // GCC likes to complain about comparing bool with 0, in the decltype() // that defines the comparable traits below. # pragma GCC diagnostic ignored "-Wbool-compare" // "ordered comparison of pointer with integer zero" same as above, // but it does not have a separate warning flag to suppress # pragma GCC diagnostic ignored "-Wextra" // Did you know that comparing floats with `0` directly // is super-duper dangerous in unevaluated context? # pragma GCC diagnostic ignored "-Wfloat-equal" #endif #if defined( __clang__ ) # pragma clang diagnostic push // Did you know that comparing floats with `0` directly // is super-duper dangerous in unevaluated context? # pragma clang diagnostic ignored "-Wfloat-equal" #endif #define CATCH_DEFINE_COMPARABLE_TRAIT( id, op ) \ template \ struct is_##id##_comparable : std::false_type {}; \ template \ struct is_##id##_comparable< \ T, \ U, \ void_t() op std::declval() )>> \ : std::true_type {}; \ template \ struct is_##id##_0_comparable : std::false_type {}; \ template \ struct is_##id##_0_comparable() op 0 )>> \ : std::true_type {}; // We need all 6 pre-spaceship comparison ops: <, <=, >, >=, ==, != CATCH_DEFINE_COMPARABLE_TRAIT( lt, < ) CATCH_DEFINE_COMPARABLE_TRAIT( le, <= ) CATCH_DEFINE_COMPARABLE_TRAIT( gt, > ) CATCH_DEFINE_COMPARABLE_TRAIT( ge, >= ) CATCH_DEFINE_COMPARABLE_TRAIT( eq, == ) CATCH_DEFINE_COMPARABLE_TRAIT( ne, != ) #undef CATCH_DEFINE_COMPARABLE_TRAIT #if defined( __GNUC__ ) && !defined( __clang__ ) # pragma GCC diagnostic pop #endif #if defined( __clang__ ) # pragma clang diagnostic pop #endif } // namespace Detail } // namespace Catch #endif // CATCH_COMPARE_TRAITS_HPP_INCLUDED #ifndef CATCH_LOGICAL_TRAITS_HPP_INCLUDED #define CATCH_LOGICAL_TRAITS_HPP_INCLUDED #include namespace Catch { namespace Detail { #if defined( __cpp_lib_logical_traits ) && __cpp_lib_logical_traits >= 201510 using std::conjunction; using std::disjunction; using std::negation; #else template struct conjunction : std::true_type {}; template struct conjunction : B1 {}; template struct conjunction : std::conditional_t, B1> {}; template struct disjunction : std::false_type {}; template struct disjunction : B1 {}; template struct disjunction : std::conditional_t> {}; template struct negation : std::integral_constant {}; #endif } // namespace Detail } // namespace Catch #endif // CATCH_LOGICAL_TRAITS_HPP_INCLUDED #include #include /** \file * Why does decomposing look the way it does: * * Conceptually, decomposing is simple. We change `REQUIRE( a == b )` into * `Decomposer{} <= a == b`, so that `Decomposer{} <= a` is evaluated first, * and our custom operator is used for `a == b`, because `a` is transformed * into `ExprLhs` and then into `BinaryExpr`. * * In practice, decomposing ends up a mess, because we have to support * various fun things. * * 1) Types that are only comparable with literal 0, and they do this by * comparing against a magic type with pointer constructor and deleted * other constructors. Example: `REQUIRE((a <=> b) == 0)` in libstdc++ * * 2) Types that are only comparable with literal 0, and they do this by * comparing against a magic type with consteval integer constructor. * Example: `REQUIRE((a <=> b) == 0)` in current MSVC STL. * * 3) Types that have no linkage, and so we cannot form a reference to * them. Example: some implementations of traits. * * 4) Starting with C++20, when the compiler sees `a == b`, it also uses * `b == a` when constructing the overload set. For us this means that * when the compiler handles `ExprLhs == b`, it also tries to resolve * the overload set for `b == ExprLhs`. * * To accomodate these use cases, decomposer ended up rather complex. * * 1) These types are handled by adding SFINAE overloads to our comparison * operators, checking whether `T == U` are comparable with the given * operator, and if not, whether T (or U) are comparable with literal 0. * If yes, the overload compares T (or U) with 0 literal inline in the * definition. * * Note that for extra correctness, we check that the other type is * either an `int` (literal 0 is captured as `int` by templates), or * a `long` (some platforms use 0L for `NULL` and we want to support * that for pointer comparisons). * * 2) For these types, `is_foo_comparable` is true, but letting * them fall into the overload that actually does `T == int` causes * compilation error. Handling them requires that the decomposition * is `constexpr`, so that P2564R3 applies and the `consteval` from * their accompanying magic type is propagated through the `constexpr` * call stack. * * However this is not enough to handle these types automatically, * because our default is to capture types by reference, to avoid * runtime copies. While these references cannot become dangling, * they outlive the constexpr context and thus the default capture * path cannot be actually constexpr. * * The solution is to capture these types by value, by explicitly * specializing `Catch::capture_by_value` for them. Catch2 provides * specialization for `std::foo_ordering`s, but users can specialize * the trait for their own types as well. * * 3) If a type has no linkage, we also cannot capture it by reference. * The solution is once again to capture them by value. We handle * the common cases by using `std::is_arithmetic` as the default * for `Catch::capture_by_value`, but that is only a some-effort * heuristic. But as with 2), users can specialize `capture_by_value` * for their own types as needed. * * 4) To support C++20 and make the SFINAE on our decomposing operators * work, the SFINAE has to happen in return type, rather than in * a template type. This is due to our use of logical type traits * (`conjunction`/`disjunction`/`negation`), that we use to workaround * an issue in older (9-) versions of GCC. I still blame C++20 for * this, because without the comparison order switching, the logical * traits could still be used in template type. * * There are also other side concerns, e.g. supporting both `REQUIRE(a)` * and `REQUIRE(a == b)`, or making `REQUIRE_THAT(a, IsEqual(b))` slot * nicely into the same expression handling logic, but these are rather * straightforward and add only a bit of complexity (e.g. common base * class for decomposed expressions). */ #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4389) // '==' : signed/unsigned mismatch #pragma warning(disable:4018) // more "signed/unsigned mismatch" #pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) #pragma warning(disable:4180) // qualifier applied to function type has no meaning #pragma warning(disable:4800) // Forcing result to true or false #endif #ifdef __clang__ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wsign-compare" # pragma clang diagnostic ignored "-Wnon-virtual-dtor" #elif defined __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wsign-compare" # pragma GCC diagnostic ignored "-Wnon-virtual-dtor" #endif #if defined(CATCH_CPP20_OR_GREATER) && __has_include() # include # if defined( __cpp_lib_three_way_comparison ) && \ __cpp_lib_three_way_comparison >= 201907L # define CATCH_CONFIG_CPP20_COMPARE_OVERLOADS # endif #endif namespace Catch { namespace Detail { // This was added in C++20, but we require only C++14 for now. template using RemoveCVRef_t = std::remove_cv_t>; } // Note: There is nothing that stops us from extending this, // e.g. to `std::is_scalar`, but the more encompassing // traits are usually also more expensive. For now we // keep this as it used to be and it can be changed later. template struct capture_by_value : std::integral_constant{}> {}; #if defined( CATCH_CONFIG_CPP20_COMPARE_OVERLOADS ) template <> struct capture_by_value : std::true_type {}; template <> struct capture_by_value : std::true_type {}; template <> struct capture_by_value : std::true_type {}; #endif template struct always_false : std::false_type {}; class ITransientExpression { bool m_isBinaryExpression; bool m_result; protected: ~ITransientExpression() = default; public: constexpr auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } constexpr auto getResult() const -> bool { return m_result; } //! This function **has** to be overriden by the derived class. virtual void streamReconstructedExpression( std::ostream& os ) const; constexpr ITransientExpression( bool isBinaryExpression, bool result ) : m_isBinaryExpression( isBinaryExpression ), m_result( result ) {} constexpr ITransientExpression( ITransientExpression const& ) = default; constexpr ITransientExpression& operator=( ITransientExpression const& ) = default; friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) { expr.streamReconstructedExpression(out); return out; } }; void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); template class BinaryExpr : public ITransientExpression { LhsT m_lhs; StringRef m_op; RhsT m_rhs; void streamReconstructedExpression( std::ostream &os ) const override { formatReconstructedExpression ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); } public: constexpr BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) : ITransientExpression{ true, comparisonResult }, m_lhs( lhs ), m_op( op ), m_rhs( rhs ) {} template auto operator && ( T ) const -> BinaryExpr const { static_assert(always_false::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template auto operator || ( T ) const -> BinaryExpr const { static_assert(always_false::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template auto operator == ( T ) const -> BinaryExpr const { static_assert(always_false::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template auto operator != ( T ) const -> BinaryExpr const { static_assert(always_false::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template auto operator > ( T ) const -> BinaryExpr const { static_assert(always_false::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template auto operator < ( T ) const -> BinaryExpr const { static_assert(always_false::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template auto operator >= ( T ) const -> BinaryExpr const { static_assert(always_false::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template auto operator <= ( T ) const -> BinaryExpr const { static_assert(always_false::value, "chained comparisons are not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } }; template class UnaryExpr : public ITransientExpression { LhsT m_lhs; void streamReconstructedExpression( std::ostream &os ) const override { os << Catch::Detail::stringify( m_lhs ); } public: explicit constexpr UnaryExpr( LhsT lhs ) : ITransientExpression{ false, static_cast(lhs) }, m_lhs( lhs ) {} }; template class ExprLhs { LhsT m_lhs; public: explicit constexpr ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} #define CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( id, op ) \ template \ constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ -> std::enable_if_t< \ Detail::conjunction, \ Detail::negation>>>::value, \ BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ -> std::enable_if_t< \ Detail::conjunction, \ capture_by_value>::value, \ BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ -> std::enable_if_t< \ Detail::conjunction< \ Detail::negation>, \ Detail::is_eq_0_comparable, \ /* We allow long because we want `ptr op NULL` to be accepted */ \ Detail::disjunction, \ std::is_same>>::value, \ BinaryExpr> { \ if ( rhs != 0 ) { throw_test_failure_exception(); } \ return { \ static_cast( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ -> std::enable_if_t< \ Detail::conjunction< \ Detail::negation>, \ Detail::is_eq_0_comparable, \ /* We allow long because we want `ptr op NULL` to be accepted */ \ Detail::disjunction, \ std::is_same>>::value, \ BinaryExpr> { \ if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \ return { static_cast( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( eq, == ) CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( ne, != ) #undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR #define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \ template \ constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ -> std::enable_if_t< \ Detail::conjunction, \ Detail::negation>>>::value, \ BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ -> std::enable_if_t< \ Detail::conjunction, \ capture_by_value>::value, \ BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ -> std::enable_if_t< \ Detail::conjunction< \ Detail::negation>, \ Detail::is_##id##_0_comparable, \ std::is_same>::value, \ BinaryExpr> { \ if ( rhs != 0 ) { throw_test_failure_exception(); } \ return { \ static_cast( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ -> std::enable_if_t< \ Detail::conjunction< \ Detail::negation>, \ Detail::is_##id##_0_comparable, \ std::is_same>::value, \ BinaryExpr> { \ if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \ return { static_cast( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( lt, < ) CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( le, <= ) CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( gt, > ) CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( ge, >= ) #undef CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR #define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR( op ) \ template \ constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \ -> std::enable_if_t< \ !capture_by_value>::value, \ BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } \ template \ constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \ -> std::enable_if_t::value, \ BinaryExpr> { \ return { \ static_cast( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \ } CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(|) CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(&) CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(^) #undef CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR template friend auto operator && ( ExprLhs &&, RhsT && ) -> BinaryExpr { static_assert(always_false::value, "operator&& is not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } template friend auto operator || ( ExprLhs &&, RhsT && ) -> BinaryExpr { static_assert(always_false::value, "operator|| is not supported inside assertions, " "wrap the expression inside parentheses, or decompose it"); } constexpr auto makeUnaryExpr() const -> UnaryExpr { return UnaryExpr{ m_lhs }; } }; struct Decomposer { template >::value, int> = 0> constexpr friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs { return ExprLhs{ lhs }; } template ::value, int> = 0> constexpr friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs { return ExprLhs{ value }; } }; } // end namespace Catch #ifdef _MSC_VER #pragma warning(pop) #endif #ifdef __clang__ # pragma clang diagnostic pop #elif defined __GNUC__ # pragma GCC diagnostic pop #endif #endif // CATCH_DECOMPOSER_HPP_INCLUDED #include namespace Catch { struct AssertionReaction { bool shouldDebugBreak = false; bool shouldThrow = false; bool shouldSkip = false; }; class AssertionHandler { AssertionInfo m_assertionInfo; AssertionReaction m_reaction; bool m_completed = false; IResultCapture& m_resultCapture; public: AssertionHandler ( StringRef macroName, SourceLineInfo const& lineInfo, StringRef capturedExpression, ResultDisposition::Flags resultDisposition ); ~AssertionHandler() { if ( !m_completed ) { m_resultCapture.handleIncomplete( m_assertionInfo ); } } template constexpr void handleExpr( ExprLhs const& expr ) { handleExpr( expr.makeUnaryExpr() ); } void handleExpr( ITransientExpression const& expr ); void handleMessage(ResultWas::OfType resultType, std::string&& message); void handleExceptionThrownAsExpected(); void handleUnexpectedExceptionNotThrown(); void handleExceptionNotThrownAsExpected(); void handleThrowingCallSkipped(); void handleUnexpectedInflightException(); void complete(); // query auto allowThrows() const -> bool; }; void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str ); } // namespace Catch #endif // CATCH_ASSERTION_HANDLER_HPP_INCLUDED #ifndef CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED #define CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED #if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr #else #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr #endif #endif // CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED // We need this suppression to leak, because it took until GCC 10 // for the front end to handle local suppression via _Pragma properly #if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ <= 9 #pragma GCC diagnostic ignored "-Wparentheses" #endif #if !defined(CATCH_CONFIG_DISABLE) #if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) /////////////////////////////////////////////////////////////////////////////// // Another way to speed-up compilation is to omit local try-catch for REQUIRE* // macros. #define INTERNAL_CATCH_TRY #define INTERNAL_CATCH_CATCH( capturer ) #else // CATCH_CONFIG_FAST_COMPILE #define INTERNAL_CATCH_TRY try #define INTERNAL_CATCH_CATCH( handler ) catch(...) { (handler).handleUnexpectedInflightException(); } #endif /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ do { /* NOLINT(bugprone-infinite-loop) */ \ /* The expression should not be evaluated, but warnings should hopefully be checked */ \ CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ INTERNAL_CATCH_TRY { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); /* NOLINT(bugprone-chained-comparison) */ \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ catchAssertionHandler.complete(); \ } while( (void)0, (false) && static_cast( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ if( Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ if( !Catch::getResultCapture().lastAssertionPassed() ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ do { \ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(__VA_ARGS__); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ catchAssertionHandler.handleExceptionNotThrownAsExpected(); \ } \ catch( ... ) { \ catchAssertionHandler.handleUnexpectedInflightException(); \ } \ catchAssertionHandler.complete(); \ } while( false ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ do { \ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(__VA_ARGS__); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ } \ catch( ... ) { \ catchAssertionHandler.handleExceptionThrownAsExpected(); \ } \ else \ catchAssertionHandler.handleThrowingCallSkipped(); \ catchAssertionHandler.complete(); \ } while( false ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ do { \ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(expr); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ } \ catch( exceptionType const& ) { \ catchAssertionHandler.handleExceptionThrownAsExpected(); \ } \ catch( ... ) { \ catchAssertionHandler.handleUnexpectedInflightException(); \ } \ else \ catchAssertionHandler.handleThrowingCallSkipped(); \ catchAssertionHandler.complete(); \ } while( false ) /////////////////////////////////////////////////////////////////////////////// // Although this is matcher-based, it can be used with just a string #define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ do { \ Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(__VA_ARGS__); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ } \ catch( ... ) { \ Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher ); \ } \ else \ catchAssertionHandler.handleThrowingCallSkipped(); \ catchAssertionHandler.complete(); \ } while( false ) #endif // CATCH_CONFIG_DISABLE #endif // CATCH_TEST_MACRO_IMPL_HPP_INCLUDED #ifndef CATCH_SECTION_HPP_INCLUDED #define CATCH_SECTION_HPP_INCLUDED /** \file * Wrapper for the STATIC_ANALYSIS_SUPPORT configuration option * * Some of Catch2's macros can be defined differently to work better with * static analysis tools, like clang-tidy or coverity. * Currently the main use case is to show that `SECTION`s are executed * exclusively, and not all in one run of a `TEST_CASE`. */ #ifndef CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED #define CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED #if defined(__clang_analyzer__) || defined(__COVERITY__) #define CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT #endif #if defined( CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT ) && \ !defined( CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) && \ !defined( CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) # define CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT #endif #endif // CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED #ifndef CATCH_TIMER_HPP_INCLUDED #define CATCH_TIMER_HPP_INCLUDED #include namespace Catch { class Timer { uint64_t m_nanoseconds = 0; public: void start(); auto getElapsedNanoseconds() const -> uint64_t; auto getElapsedMicroseconds() const -> uint64_t; auto getElapsedMilliseconds() const -> unsigned int; auto getElapsedSeconds() const -> double; }; } // namespace Catch #endif // CATCH_TIMER_HPP_INCLUDED namespace Catch { class Section : Detail::NonCopyable { public: Section( SectionInfo&& info ); Section( SourceLineInfo const& _lineInfo, StringRef _name, const char* const = nullptr ); ~Section(); // This indicates whether the section should be executed or not explicit operator bool() const; private: SectionInfo m_info; Counts m_assertions; bool m_sectionIncluded; Timer m_timer; }; } // end namespace Catch #if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT) # define INTERNAL_CATCH_SECTION( ... ) \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \ catch_internal_Section ) = \ Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION # define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \ catch_internal_Section ) = \ Catch::SectionInfo( \ CATCH_INTERNAL_LINEINFO, \ ( Catch::ReusableStringStream() << __VA_ARGS__ ) \ .str() ) ) \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION #else // These section definitions imply that at most one section at one level // will be intered (because only one section's __LINE__ can be equal to // the dummy `catchInternalSectionHint` variable from `TEST_CASE`). namespace Catch { namespace Detail { // Intentionally without linkage, as it should only be used as a dummy // symbol for static analysis. // The arguments are used as a dummy for checking warnings in the passed // expressions. int GetNewSectionHint( StringRef, const char* const = nullptr ); } // namespace Detail } // namespace Catch # define INTERNAL_CATCH_SECTION( ... ) \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \ catchInternalSectionHint, \ catchInternalSectionHint = \ Catch::Detail::GetNewSectionHint(__VA_ARGS__); \ catchInternalPreviousSectionHint == __LINE__ ) \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION # define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \ catchInternalSectionHint, \ catchInternalSectionHint = Catch::Detail::GetNewSectionHint( \ ( Catch::ReusableStringStream() << __VA_ARGS__ ).str()); \ catchInternalPreviousSectionHint == __LINE__ ) \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION #endif #endif // CATCH_SECTION_HPP_INCLUDED #ifndef CATCH_TEST_REGISTRY_HPP_INCLUDED #define CATCH_TEST_REGISTRY_HPP_INCLUDED #ifndef CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED #define CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED namespace Catch { class ITestInvoker { public: virtual void prepareTestCase(); virtual void tearDownTestCase(); virtual void invoke() const = 0; virtual ~ITestInvoker(); // = default }; } // namespace Catch #endif // CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED #ifndef CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED #define CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED #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_REMOVE_PARENS( ... ) \ INTERNAL_CATCH_EXPAND1( INTERNAL_CATCH_DEF __VA_ARGS__ ) #endif // CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED // GCC 5 and older do not properly handle disabling unused-variable warning // with a _Pragma. This means that we have to leak the suppression to the // user code as well :-( #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 5 #pragma GCC diagnostic ignored "-Wunused-variable" #endif namespace Catch { template class TestInvokerAsMethod : public ITestInvoker { void (C::*m_testAsMethod)(); public: constexpr TestInvokerAsMethod( void ( C::*testAsMethod )() ) noexcept: m_testAsMethod( testAsMethod ) {} void invoke() const override { C obj; (obj.*m_testAsMethod)(); } }; Detail::unique_ptr makeTestInvoker( void(*testAsFunction)() ); template Detail::unique_ptr makeTestInvoker( void (C::*testAsMethod)() ) { return Detail::make_unique>( testAsMethod ); } template class TestInvokerFixture : public ITestInvoker { void ( C::*m_testAsMethod )() const; Detail::unique_ptr m_fixture = nullptr; public: constexpr TestInvokerFixture( void ( C::*testAsMethod )() const ) noexcept: m_testAsMethod( testAsMethod ) {} void prepareTestCase() override { m_fixture = Detail::make_unique(); } void tearDownTestCase() override { m_fixture.reset(); } void invoke() const override { auto* f = m_fixture.get(); ( f->*m_testAsMethod )(); } }; template Detail::unique_ptr makeTestInvokerFixture( void ( C::*testAsMethod )() const ) { return Detail::make_unique>( testAsMethod ); } struct NameAndTags { constexpr NameAndTags( StringRef name_ = StringRef(), StringRef tags_ = StringRef() ) noexcept: name( name_ ), tags( tags_ ) {} StringRef name; StringRef tags; }; struct AutoReg : Detail::NonCopyable { AutoReg( Detail::unique_ptr invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept; }; } // end namespace Catch #if defined(CATCH_CONFIG_DISABLE) #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ static inline void TestName() #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ namespace{ \ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ void test(); \ }; \ } \ void TestName::test() #endif #if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ static void TestName(); \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ namespace{ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ static void TestName() #define INTERNAL_CATCH_TESTCASE( ... ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__ ) #else // ^^ !CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT | vv CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT // Dummy registrator for the dumy test case macros namespace Catch { namespace Detail { struct DummyUse { DummyUse( void ( * )( int ), Catch::NameAndTags const& ); }; } // namespace Detail } // namespace Catch // Note that both the presence of the argument and its exact name are // necessary for the section support. // We provide a shadowed variable so that a `SECTION` inside non-`TEST_CASE` // tests can compile. The redefined `TEST_CASE` shadows this with param. static int catchInternalSectionHint = 0; # define INTERNAL_CATCH_TESTCASE2( fname, ... ) \ static void fname( int ); \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ static const Catch::Detail::DummyUse INTERNAL_CATCH_UNIQUE_NAME( \ dummyUser )( &(fname), Catch::NameAndTags{ __VA_ARGS__ } ); \ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ static void fname( [[maybe_unused]] int catchInternalSectionHint ) \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION # define INTERNAL_CATCH_TESTCASE( ... ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( dummyFunction ), __VA_ARGS__ ) #endif // CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ namespace{ \ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ void test(); \ }; \ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ Catch::makeTestInvoker( &TestName::test ), \ CATCH_INTERNAL_LINEINFO, \ #ClassName##_catch_sr, \ Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ } \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ void TestName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE2( TestName, ClassName, ... ) \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ namespace { \ struct TestName : INTERNAL_CATCH_REMOVE_PARENS( ClassName ) { \ void test() const; \ }; \ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ Catch::makeTestInvokerFixture( &TestName::test ), \ CATCH_INTERNAL_LINEINFO, \ #ClassName##_catch_sr, \ Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ } \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ void TestName::test() const #define INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( ClassName, ... ) \ INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ namespace { \ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ Catch::makeTestInvoker( &QualifiedMethod ), \ CATCH_INTERNAL_LINEINFO, \ "&" #QualifiedMethod##_catch_sr, \ Catch::NameAndTags{ __VA_ARGS__ } ); \ } /* NOLINT */ \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ do { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ } while(false) #endif // CATCH_TEST_REGISTRY_HPP_INCLUDED // All of our user-facing macros support configuration toggle, that // forces them to be defined prefixed with CATCH_. We also like to // support another toggle that can minimize (disable) their implementation. // Given this, we have 4 different configuration options below #if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) #define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) #define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) #define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) #define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) #define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) #define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) #define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) #define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, __VA_ARGS__ ) #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CATCH_SKIP( ... ) INTERNAL_CATCH_MSG( "SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ ) #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) #define CATCH_STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ ) #define CATCH_STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ ) #define CATCH_STATIC_CHECK( ... ) static_assert( __VA_ARGS__ , #__VA_ARGS__ ); CATCH_SUCCEED( #__VA_ARGS__ ) #define CATCH_STATIC_CHECK_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ ) #else #define CATCH_STATIC_REQUIRE( ... ) CATCH_REQUIRE( __VA_ARGS__ ) #define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_FALSE( __VA_ARGS__ ) #define CATCH_STATIC_CHECK( ... ) CATCH_CHECK( __VA_ARGS__ ) #define CATCH_STATIC_CHECK_FALSE( ... ) CATCH_CHECK_FALSE( __VA_ARGS__ ) #endif // "BDD-style" convenience wrappers #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) #define CATCH_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) #define CATCH_AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) #define CATCH_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) #define CATCH_AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) #define CATCH_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) #define CATCH_AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) #elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) // ^^ prefixed, implemented | vv prefixed, disabled #define CATCH_REQUIRE( ... ) (void)(0) #define CATCH_REQUIRE_FALSE( ... ) (void)(0) #define CATCH_REQUIRE_THROWS( ... ) (void)(0) #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) #define CATCH_REQUIRE_NOTHROW( ... ) (void)(0) #define CATCH_CHECK( ... ) (void)(0) #define CATCH_CHECK_FALSE( ... ) (void)(0) #define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__) #define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) #define CATCH_CHECK_NOFAIL( ... ) (void)(0) #define CATCH_CHECK_THROWS( ... ) (void)(0) #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0) #define CATCH_CHECK_NOTHROW( ... ) (void)(0) #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) #define CATCH_METHOD_AS_TEST_CASE( method, ... ) #define CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) #define CATCH_SECTION( ... ) #define CATCH_DYNAMIC_SECTION( ... ) #define CATCH_FAIL( ... ) (void)(0) #define CATCH_FAIL_CHECK( ... ) (void)(0) #define CATCH_SUCCEED( ... ) (void)(0) #define CATCH_SKIP( ... ) (void)(0) #define CATCH_STATIC_REQUIRE( ... ) (void)(0) #define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0) #define CATCH_STATIC_CHECK( ... ) (void)(0) #define CATCH_STATIC_CHECK_FALSE( ... ) (void)(0) // "BDD-style" convenience wrappers #define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className ) #define CATCH_GIVEN( desc ) #define CATCH_AND_GIVEN( desc ) #define CATCH_WHEN( desc ) #define CATCH_AND_WHEN( desc ) #define CATCH_THEN( desc ) #define CATCH_AND_THEN( desc ) #elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) // ^^ prefixed, disabled | vv unprefixed, implemented #define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) #define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) #define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) #define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) #define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) #define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) #define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) #define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) #define TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TEST_CASE_PERSISTENT_FIXTURE( className, __VA_ARGS__ ) #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) #define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ ) #define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) #define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) #define SKIP( ... ) INTERNAL_CATCH_MSG( "SKIP", Catch::ResultWas::ExplicitSkip, Catch::ResultDisposition::Normal, __VA_ARGS__ ) #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) #define STATIC_REQUIRE( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ ) #define STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" ) #define STATIC_CHECK( ... ) static_assert( __VA_ARGS__, #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ ) #define STATIC_CHECK_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" ) #else #define STATIC_REQUIRE( ... ) REQUIRE( __VA_ARGS__ ) #define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ ) #define STATIC_CHECK( ... ) CHECK( __VA_ARGS__ ) #define STATIC_CHECK_FALSE( ... ) CHECK_FALSE( __VA_ARGS__ ) #endif // "BDD-style" convenience wrappers #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) #define GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Given: " << desc ) #define AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc ) #define WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " When: " << desc ) #define AND_WHEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc ) #define THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " Then: " << desc ) #define AND_THEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( " And: " << desc ) #elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) // ^^ unprefixed, implemented | vv unprefixed, disabled #define REQUIRE( ... ) (void)(0) #define REQUIRE_FALSE( ... ) (void)(0) #define REQUIRE_THROWS( ... ) (void)(0) #define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) #define REQUIRE_NOTHROW( ... ) (void)(0) #define CHECK( ... ) (void)(0) #define CHECK_FALSE( ... ) (void)(0) #define CHECKED_IF( ... ) if (__VA_ARGS__) #define CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) #define CHECK_NOFAIL( ... ) (void)(0) #define CHECK_THROWS( ... ) (void)(0) #define CHECK_THROWS_AS( expr, exceptionType ) (void)(0) #define CHECK_NOTHROW( ... ) (void)(0) #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__) #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ )) #define METHOD_AS_TEST_CASE( method, ... ) #define TEST_CASE_PERSISTENT_FIXTURE( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__) #define REGISTER_TEST_CASE( Function, ... ) (void)(0) #define SECTION( ... ) #define DYNAMIC_SECTION( ... ) #define FAIL( ... ) (void)(0) #define FAIL_CHECK( ... ) (void)(0) #define SUCCEED( ... ) (void)(0) #define SKIP( ... ) (void)(0) #define STATIC_REQUIRE( ... ) (void)(0) #define STATIC_REQUIRE_FALSE( ... ) (void)(0) #define STATIC_CHECK( ... ) (void)(0) #define STATIC_CHECK_FALSE( ... ) (void)(0) // "BDD-style" convenience wrappers #define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ) ) #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className ) #define GIVEN( desc ) #define AND_GIVEN( desc ) #define WHEN( desc ) #define AND_WHEN( desc ) #define THEN( desc ) #define AND_THEN( desc ) #endif // ^^ unprefixed, disabled // end of user facing macros #endif // CATCH_TEST_MACROS_HPP_INCLUDED #ifndef CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED #define CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED #ifndef CATCH_PREPROCESSOR_HPP_INCLUDED #define CATCH_PREPROCESSOR_HPP_INCLUDED #if defined(__GNUC__) // We need to silence "empty __VA_ARGS__ warning", and using just _Pragma does not work #pragma GCC system_header #endif #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_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) #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