From 7bf29870233394c1ecb24c390a1b084b1c68d9af Mon Sep 17 00:00:00 2001 From: yl <2201441955@qq.com> Date: Sun, 20 Oct 2024 21:43:54 +0800 Subject: [PATCH] Fix the code to work --- moxt/cclient.hpp | 1 - moxt/common.hpp | 35 +- moxt/httpx/clientpool.cpp | 144 ----- moxt/httpx/clientpool.hpp | 26 - moxt/httpx/websocket.cpp | 38 +- moxt/httpx/websocket.hpp | 36 +- moxt/liblog.cpp | 30 +- moxt/libmisc.cpp | 3 +- moxt/libnet.cpp | 77 ++- moxt/libnet.hpp | 1 + moxt/libsonic.cpp | 8 + moxt/std23/__functional_base.h | 227 ++++++++ .../std23/__functional_base.h:Zone.Identifier | 0 moxt/std23/function.h | 395 +++++++++++++ moxt/std23/function.h:Zone.Identifier | 0 moxt/std23/function_ref.h | 216 ++++++++ moxt/std23/function_ref.h:Zone.Identifier | 0 moxt/std23/move_only_function.h | 517 ++++++++++++++++++ .../move_only_function.h:Zone.Identifier | 0 moxt/tscns.h | 153 ++++++ moxt/utils/spscqueue_safe.cpp | 5 - moxt/utils/spscqueue_safe.hpp | 46 -- moxt/xmake.lua | 7 - myrepo/packages/f/fmtlog-local/xmake.lua | 8 +- xmake.lua | 16 +- 25 files changed, 1703 insertions(+), 286 deletions(-) delete mode 100644 moxt/httpx/clientpool.cpp delete mode 100644 moxt/httpx/clientpool.hpp create mode 100644 moxt/std23/__functional_base.h create mode 100644 moxt/std23/__functional_base.h:Zone.Identifier create mode 100644 moxt/std23/function.h create mode 100644 moxt/std23/function.h:Zone.Identifier create mode 100644 moxt/std23/function_ref.h create mode 100644 moxt/std23/function_ref.h:Zone.Identifier create mode 100644 moxt/std23/move_only_function.h create mode 100644 moxt/std23/move_only_function.h:Zone.Identifier create mode 100644 moxt/tscns.h delete mode 100644 moxt/utils/spscqueue_safe.cpp delete mode 100644 moxt/utils/spscqueue_safe.hpp diff --git a/moxt/cclient.hpp b/moxt/cclient.hpp index e2f3a6b..5fb179e 100644 --- a/moxt/cclient.hpp +++ b/moxt/cclient.hpp @@ -2,7 +2,6 @@ #define MOXT_CCLIENT_HPP #include "common.hpp" -#include "moxt/httpx/clientpool.hpp" #include "moxt/httpx/httpbase.hpp" #include "moxt/httpx/httpclient.hpp" #include diff --git a/moxt/common.hpp b/moxt/common.hpp index 1b25b21..c336491 100644 --- a/moxt/common.hpp +++ b/moxt/common.hpp @@ -25,28 +25,29 @@ #undef min #endif -// #define USE_FMTLOG 1 -#define USE_SPDLOG 1 +#define USE_FMTLOG 1 +// #define USE_SPDLOG 1 #if defined(USE_FMTLOG) +#define FMTLOG_ACTIVE_LEVEL FMTLOG_LEVEL_DBG #include #elif defined(USE_SPDLOG) -#define SPDLOG_LEVEL_NAMES \ - { "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "CRITICAL", "" } - -#include -#define logd spdlog::debug -#define logi spdlog::info -#define logw spdlog::warn -#define loge spdlog::error -#else -#include -#define logd(fmt, ...) QUILL_LOG_DEBUG(quill::get_logger(), fmt, ##__VA_ARGS__) -#define logi(fmt, ...) QUILL_LOG_INFO(quill::get_logger(), fmt, ##__VA_ARGS__) -#define logw(fmt, ...) \ - QUILL_LOG_WARNING(quill::get_logger(), fmt, ##__VA_ARGS__) -#define loge(fmt, ...) QUILL_LOG_ERROR(quill::get_logger(), fmt, ##__VA_ARGS__) +// #define SPDLOG_LEVEL_NAMES \ +// { "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "CRITICAL", "" } + +// #include +// #define logd spdlog::debug +// #define logi spdlog::info +// #define logw spdlog::warn +// #define loge spdlog::error +// #else +// #include +// #define logd(fmt, ...) QUILL_LOG_DEBUG(quill::get_logger(), fmt, ##__VA_ARGS__) +// #define logi(fmt, ...) QUILL_LOG_INFO(quill::get_logger(), fmt, ##__VA_ARGS__) +// #define logw(fmt, ...) \ +// QUILL_LOG_WARNING(quill::get_logger(), fmt, ##__VA_ARGS__) +// #define loge(fmt, ...) QUILL_LOG_ERROR(quill::get_logger(), fmt, ##__VA_ARGS__) #endif // #define logd spdlog::debug diff --git a/moxt/httpx/clientpool.cpp b/moxt/httpx/clientpool.cpp deleted file mode 100644 index 5b63752..0000000 --- a/moxt/httpx/clientpool.cpp +++ /dev/null @@ -1,144 +0,0 @@ -// -// Created by ken on 2023/7/13. -// - -#include "clientpool.hpp" -#include "libthread.hpp" -#include "moxt/utils/spscqueue_safe.hpp" -#include -#include -#include -#include -#include -#include - -using HttpClient = httpx::HttpClient; - -class HttpClientPoolImpl : public HttpClientPool { - public: - using HttpClient = httpx::HttpClient; - - HttpClientPoolImpl(std::string host, std::string port, - ssl::context::method method); - - ~HttpClientPoolImpl() override; - - HttpClient *acquire(); - - void release(HttpClient *client); - - private: - SPSCQueueSafe pool_; - SPSCQueueSafe closedPool_; - - int maxSize = 3; - std::atomic trackingCount{0}; - - // 是否正在运行 - std::atomic running = false; - - HttpClient *createHttpClient(); - - std::string host_; - std::string port_; - ssl::context::method method_ = ssl::context::tlsv13_client; - - photon::thread *collector = nullptr; - // photon::Timer timer; - void collect(); -}; - -HttpClientPool *HttpClientPool::make(std::string host, std::string port, - ssl::context::method method) { - return new HttpClientPoolImpl(host, port, method); -} - -// 初始化连接池 -HttpClientPoolImpl::HttpClientPoolImpl(std::string host, std::string port, - ssl::context::method method) - : host_(host), port_(port), pool_(12), closedPool_(12), method_(method) { - logd("HttpClientPoolImpl::HttpClientPoolImpl"); - collector = photon::thread_create11(&HttpClientPoolImpl::collect, this); - seq_photon_thread_migrate_to_work_pool(collector); -} - -HttpClientPoolImpl::~HttpClientPoolImpl() { - logi("HttpClientPoolImpl::~HttpClientPoolImpl"); - if (collector != nullptr) { - auto th = collector; - collector = nullptr; - photon::thread_interrupt((photon::thread *)th); - photon::thread_join((photon::join_handle *)th); - } - logi("HttpClientPoolImpl::~HttpClientPoolImpl stop"); - // 清理连接池 - HttpClient *client = nullptr; - while (pool_.pop(client)) { - delete client; - } - while (closedPool_.pop(client)) { - delete client; - } - logi("HttpClientPoolImpl::~HttpClientPoolImpl end"); -} - -void HttpClientPoolImpl::collect() { - while (collector) { - photon::thread_sleep(3); - - HttpClient *client = nullptr; - if (closedPool_.pop(client)) { - client->disconnect(); - delete client; - client = nullptr; - } - } -} - -// 获取连接 -HttpClient *HttpClientPoolImpl::acquire() { - // 从连接池中获取连接 - logd("HttpClientPoolImpl::acquire"); - HttpClient *client0 = nullptr; - if (pool_.pop(client0)) { - logd("HttpClientPoolImpl::acquire success (from pool)"); - trackingCount++; - return client0; - } - // printf("HttpClientPoolImpl::acquire create...\n"); - auto client = createHttpClient(); - logd("HttpClientPoolImpl::acquire create httpclient success"); - if (client->connect()) { - logd("Connect success"); - return client; - } - logw("HttpClientPoolImpl::acquire connect fail"); - return nullptr; -} - -// 释放连接 -void HttpClientPoolImpl::release(HttpClient *client) { - if (client == nullptr) { - return; - } - if (client->isConnected()) { - pool_.push(client); - } else { - closedPool_.push(client); - } - trackingCount--; -} - -HttpClient *HttpClientPoolImpl::createHttpClient() { - logd("HttpClientPoolImpl::createHttpClient host={} port={}", host_, port_); - - // ssl::context ctx(ssl::context::tlsv12_client); - // ssl::context ctx(ssl::context::tlsv13_client); - - ssl::context ctx(method_); - - ctx.set_default_verify_paths(); - - auto client = new httpx::HttpClient(ctx, host_, port_); - return client; -} \ No newline at end of file diff --git a/moxt/httpx/clientpool.hpp b/moxt/httpx/clientpool.hpp deleted file mode 100644 index 39d69ba..0000000 --- a/moxt/httpx/clientpool.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// -// Created by ken on 2023/7/13. -// - -#ifndef MOXT_CONNECTION_POOL_HPP -#define MOXT_CONNECTION_POOL_HPP - -#include "moxt/httpx/httpclient.hpp" -#include "moxt/common.hpp" - -class HttpClientPool { - public: - using HttpClient = httpx::HttpClient; - - virtual ~HttpClientPool() = default; - - virtual HttpClient *acquire() = 0; - - virtual void release(HttpClient *client) = 0; - - static HttpClientPool * - make(std::string host, std::string port, - ssl::context::method method = ssl::context::tlsv13_client); -}; - -#endif // MOXT_CONNECTION_POOL_HPP diff --git a/moxt/httpx/websocket.cpp b/moxt/httpx/websocket.cpp index 4943149..987a47e 100644 --- a/moxt/httpx/websocket.cpp +++ b/moxt/httpx/websocket.cpp @@ -1,6 +1,7 @@ #include "websocket.hpp" -void on_connect_null(WebSocket *ws) {} +void on_connected_null(WebSocket *ws) {} +void on_BeforeReconnectNull(WebSocket *ws) {} void on_heartbeat_null(WebSocket *ws) {} void on_message_null(WebSocket *ws, const char *data, size_t len) { logd("on_message_null: {}", std::string_view(data, len)); @@ -12,7 +13,8 @@ WebSocket::WebSocket(std::string host, std::string port, std::string path, _ioContext(ioContext), _resolver(asio::make_strand(_ioContext)), _sslContext(asio::ssl::context::tlsv12_client), _reconnectTimer(_ioContext), m_HeartbeatTimer(_ioContext), - m_HeartbeatInterval(), on_connect_callback_(on_connect_null), + m_HeartbeatInterval(), on_connected_callback_(on_connected_null), + beforeReconnectCallback_(on_BeforeReconnectNull), on_heartbeat_callback_(on_heartbeat_null), on_message_callback_(on_message_null) { // logd("WebSocket::WebSocket"); @@ -49,8 +51,8 @@ void WebSocket::OnResolve(beast::error_code ec, logd("WebSocket::OnResolve"); if (ec) { - loge("Can't resolve gateway URL '{}': {} ({})", host, - ec.message(), ec.value()); + loge("Can't resolve gateway URL '{}': {} ({})", host, ec.message(), + ec.value()); Disconnect(true); return; } @@ -129,10 +131,11 @@ void WebSocket::OnSslHandshake(beast::error_code ec) { // set a decorator to change the User-Agent of the handshake _websocket->set_option(beast::websocket::stream_base::decorator( - [](beast::websocket::request_type &req) { - req.set(beast::http::field::user_agent, - std::string(BOOST_BEAST_VERSION_STRING) + - " xt-connector"); + [this](beast::websocket::request_type &req) { + // req.set(beast::http::field::user_agent, + // std::string(BOOST_BEAST_VERSION_STRING) + + // " xt-connector"); + this->SetHeaders(req); })); _websocket->async_handshake( @@ -158,14 +161,22 @@ void WebSocket::OnHandshake(beast::error_code ec) { Read(); // 触发连接回调 - // if (on_connect_callback_ != nullptr) { - on_connect_callback_(this); - // } + on_connected_callback_(this); m_HeartbeatInterval = std::chrono::seconds(10); DoHeartbeat({}); } +void WebSocket::SetHeaders(beast::websocket::request_type &req) { + req.set(beast::http::field::user_agent, + std::string(BOOST_BEAST_VERSION_STRING) + "echo"); + // 添加Headers + for (const auto &header : currentHeaders_.headers) { + printf("header: %s %s\n", header.first.c_str(), header.second.c_str()); + req.set(header.first, header.second); + } +} + void WebSocket::Disconnect(bool reconnect /*= false*/) { logd("WebSocket::Disconnect"); @@ -215,6 +226,11 @@ void WebSocket::OnReconnect(beast::error_code ec) { } ++_reconnectCount; + + // 在重连之前调用回调 + // printf("beforeReconnectCallback_ %p\n", this); + beforeReconnectCallback_(this); + Connect(); } diff --git a/moxt/httpx/websocket.hpp b/moxt/httpx/websocket.hpp index e157bcc..75b3c0c 100644 --- a/moxt/httpx/websocket.hpp +++ b/moxt/httpx/websocket.hpp @@ -2,8 +2,8 @@ #include "moxt/common.hpp" #include "moxt/httpx/httpbase.hpp" +#include "moxt/std23/function_ref.h" #include -#include // 参考以下代码实现: namespace asio = boost::asio; @@ -21,7 +21,16 @@ class WebSocket; class WebSocket; -typedef std23::function_ref OnConnectCallback; +struct WebSocketHeaders { + std::map headers; + + void set(const std::string &key, const std::string &value) { + headers[key] = value; + } +}; + +typedef std23::function_ref OnConnectedCallback; +typedef std23::function_ref BeforeReconnectCallback; typedef std23::function_ref OnHeartbeatCallback; typedef std23::function_ref OnMessageCallback; @@ -54,7 +63,9 @@ class WebSocket { asio::steady_timer m_HeartbeatTimer; std::chrono::steady_clock::duration m_HeartbeatInterval; - OnConnectCallback on_connect_callback_; + WebSocketHeaders currentHeaders_; // 当前的 headers + BeforeReconnectCallback beforeReconnectCallback_; // 重连前的回调 + OnConnectedCallback on_connected_callback_; OnMessageCallback on_message_callback_; OnHeartbeatCallback on_heartbeat_callback_; @@ -65,6 +76,7 @@ class WebSocket { asio::ip::tcp::resolver::results_type::endpoint_type ep); void OnSslHandshake(beast::error_code ec); void OnHandshake(beast::error_code ec); + void SetHeaders(beast::websocket::request_type &req); void OnClose(beast::error_code ec); void OnReconnect(beast::error_code ec); @@ -82,18 +94,28 @@ class WebSocket { void Connect(); void Disconnect(bool reconnect = false); void Write(std::string const &data); + + // 设置Headers + void SetHeaders(const std::map &headers) { + currentHeaders_.headers = headers; + } + // 设置连接回调函数 - void set_on_connect(OnConnectCallback callback) { - on_connect_callback_ = callback; + void SetOnConnected(OnConnectedCallback callback) { + on_connected_callback_ = callback; + } + + void SetBeforeReconnectCallback(BeforeReconnectCallback callback) { + beforeReconnectCallback_ = callback; } // 设置心跳回调函数 - void set_on_heartbeat(OnHeartbeatCallback callback) { + void SetOnHeartbeat(OnHeartbeatCallback callback) { on_heartbeat_callback_ = callback; } // 设置消息回调函数 - void set_on_message(OnMessageCallback callback) { + void SetOnMessage(OnMessageCallback callback) { on_message_callback_ = callback; } }; \ No newline at end of file diff --git a/moxt/liblog.cpp b/moxt/liblog.cpp index 501a59e..f95b5ff 100644 --- a/moxt/liblog.cpp +++ b/moxt/liblog.cpp @@ -1,10 +1,13 @@ #include "liblog.hpp" #include "libthread.hpp" +#include +#if defined(USE_SPDLOG) #include "spdlog/async.h" #include "spdlog/common.h" #include "spdlog/sinks/basic_file_sink.h" #include "spdlog/sinks/rotating_file_sink.h" #include "spdlog/sinks/stdout_color_sinks.h" +#endif #include #include #include @@ -65,6 +68,7 @@ SEQ_FUNC void seq_init_log(uint8_t level, const char *filename, init_log(level, filename_); } +#if defined(USE_SPDLOG) void set_log_level(uint8_t &level, std::shared_ptr &logger) { switch (level) { case 0: @@ -84,18 +88,38 @@ void set_log_level(uint8_t &level, std::shared_ptr &logger) { break; } } +#endif void init_log(uint8_t level, const std::string &filename) { #if defined(USE_FMTLOG) auto logLevel = static_cast(level); fmtlog::setLogLevel(logLevel); + std::string logLevelStr; + switch (level) { + case fmtlog::LogLevel::DBG: + logLevelStr = "DBG"; + break; + case fmtlog::LogLevel::INF: + logLevelStr = "INF"; + break; + case fmtlog::LogLevel::WRN: + logLevelStr = "WRN"; + break; + case fmtlog::LogLevel::ERR: + logLevelStr = "ERR"; + break; + } + + // fmt::print("Set log level to {}\n", logLevelStr); + if (!filename.empty()) { fmtlog::setLogFile(filename.c_str(), false); } // fmtlog::setLogCB(logcb, logLevel); - auto work_pool = seq_photon_work_pool(); - work_pool->thread_migrate(photon::thread_create(coro_log_run, nullptr), - -1UL); + // auto work_pool = seq_photon_work_pool(); + // work_pool->thread_migrate(photon::thread_create(coro_log_run, nullptr), + // -1UL); + fmtlog::startPollingThread(1); #elif defined(USE_SPDLOG) // spdlog::init_thread_pool(8192, 1); // std::string pattern = "%Y-%m-%d %H:%M:%S.%e [%l] [%t]: %v"; diff --git a/moxt/libmisc.cpp b/moxt/libmisc.cpp index 1bb9e25..5c55fcb 100644 --- a/moxt/libmisc.cpp +++ b/moxt/libmisc.cpp @@ -1,7 +1,7 @@ #include "libmisc.hpp" #include "common.hpp" -#include "libc.hpp" #include "moxt/utils/floatutil.hpp" +#include "libc.hpp" #include "photon/common/callback.h" #include "simdjson.h" #include @@ -34,6 +34,7 @@ #include #include #include +#include using phmap::flat_hash_map; diff --git a/moxt/libnet.cpp b/moxt/libnet.cpp index 344ffed..65a9d07 100644 --- a/moxt/libnet.cpp +++ b/moxt/libnet.cpp @@ -23,6 +23,8 @@ using namespace photon::net::http; SEQ_FUNC void seq_asio_run() { AsioIOC::default_pool().run(); } +SEQ_FUNC std::thread *seq_asio_run_ex(AsioIOC *ioc, int bindCpu, bool poll); + SEQ_FUNC void seq_asio_poll() { AsioIOC::default_pool().poll(); } SEQ_FUNC void seq_asio_poll_one() { AsioIOC::default_pool().poll_one(); } @@ -43,6 +45,12 @@ SEQ_FUNC void seq_websocket_delete(WebSocket *p) { SEQ_FUNC void seq_websocket_connect(WebSocket *p) { p->Connect(); } +SEQ_FUNC void +seq_websocket_set_headers(WebSocket *p, + std::map *headers) { + p->SetHeaders(*headers); +} + SEQ_FUNC void seq_websocket_disconnect(WebSocket *p) { p->Disconnect(); } SEQ_FUNC void seq_websocket_send(WebSocket *p, const char *text, size_t len) { @@ -50,25 +58,81 @@ SEQ_FUNC void seq_websocket_send(WebSocket *p, const char *text, size_t len) { p->Write(data); } -SEQ_FUNC void seq_websocket_set_on_connect(WebSocket *p, - OnConnectCallback_t callback) { - p->set_on_connect(callback); +SEQ_FUNC void seq_websocket_set_on_connected(WebSocket *p, + OnConnectCallback_t callback) { + p->SetOnConnected(callback); +} + +SEQ_FUNC void +seq_websocket_set_on_before_reconnect(WebSocket *p, + OnBeforeReconnectCallback_t callback) { + p->SetBeforeReconnectCallback(callback); } SEQ_FUNC void seq_websocket_set_on_heartbeat(WebSocket *p, OnHeartbeatCallback_t callback) { - p->set_on_heartbeat(callback); + p->SetOnHeartbeat(callback); } SEQ_FUNC void seq_websocket_set_on_message(WebSocket *p, OnMessageCallback_t callback) { - p->set_on_message(callback); + p->SetOnMessage(callback); } SEQ_FUNC AsioIOC *seq_asio_ioc() { return &AsioIOC::default_pool(); } SEQ_FUNC void seq_asio_ioc_poll(AsioIOC *ioc) { ioc->poll(); } +std::thread *createBackgroundThread(void (*func)(AsioIOC *), AsioIOC *ioc, + int bindCpu = -1) { + // ߳ + std::thread *t = new std::thread(func, ioc); + + // ָ˰󶨵 CPU + if (bindCpu >= 0) { + // ȡ̵߳ľ + pthread_t nativeHandle = t->native_handle(); + + // CPU ׺ + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(bindCpu, &cpuset); + + // ̵ָ߳ CPU + if (pthread_setaffinity_np(nativeHandle, sizeof(cpu_set_t), &cpuset) != + 0) { + std::cerr << "Error setting thread affinity" << std::endl; + } + } + + // ߳Ϊ̨߳ + t->detach(); // ̷߳룬ʹΪ̨߳ + + return t; // ̶߳Ȼʱ߳ѷ룬޷ join +} + +void _asio_run(AsioIOC *ioc) { + logi("_asio_run"); + ioc->run(); + logi("_asio_run stopped"); +} + +void _asio_run_poll(AsioIOC *ioc) { + logi("_asio_run"); + while (true) { + ioc->poll(); + } + logi("_asio_run stopped"); +} + +SEQ_FUNC std::thread *seq_asio_run_ex(AsioIOC *ioc, int bindCpu, bool poll) { + if (poll) { + return createBackgroundThread(_asio_run_poll, ioc, bindCpu); + } else { + return createBackgroundThread(_asio_run, ioc, bindCpu); + } +} + void *coro_net_run(void *arg) { auto &pool = AsioIOC::default_pool(); for (;;) { @@ -79,7 +143,8 @@ void *coro_net_run(void *arg) { } void thread_net_run() { - int ret = photon::init(photon::INIT_EVENT_DEFAULT, photon::INIT_IO_DEFAULT); + // һ׼̣߳cpu + logi("thread_net_run"); AsioIOC::default_pool().run(); logi("thread_net_run stopped"); diff --git a/moxt/libnet.hpp b/moxt/libnet.hpp index d26cec4..3ba2402 100644 --- a/moxt/libnet.hpp +++ b/moxt/libnet.hpp @@ -8,6 +8,7 @@ enum NetDriverType { STD_THREAD = 0, PHOTON = 1 }; class WebSocket; typedef void (*OnConnectCallback_t)(WebSocket *ws); +typedef void (*OnBeforeReconnectCallback_t)(WebSocket *ws); typedef void (*OnHeartbeatCallback_t)(WebSocket *ws); typedef void (*OnMessageCallback_t)(WebSocket *ws, const char *data, size_t len); diff --git a/moxt/libsonic.cpp b/moxt/libsonic.cpp index 9a5dda0..bdcf1c9 100644 --- a/moxt/libsonic.cpp +++ b/moxt/libsonic.cpp @@ -217,6 +217,14 @@ seq_sonic_json_node_add_bool(NodeType *node, node->AddMember(key_, NodeType(value), *alloc); } +SEQ_FUNC void +seq_sonic_json_node_add_node(NodeType *node, + sonic_json::Document::Allocator *alloc, + const char *key, size_t keyLen, NodeType *valueNode) { + auto key_ = std::string_view(key, keyLen); + node->AddMember(key_, *valueNode, *alloc); +} + SEQ_FUNC size_t seq_sonic_json_document_to_string(sonic_json::Document *doc, char *result) { auto s = sonic_json_to_string(doc); diff --git a/moxt/std23/__functional_base.h b/moxt/std23/__functional_base.h new file mode 100644 index 0000000..8edb81e --- /dev/null +++ b/moxt/std23/__functional_base.h @@ -0,0 +1,227 @@ +#ifndef INCLUDE_STD23____FUNCTIONAL__BASE +#define INCLUDE_STD23____FUNCTIONAL__BASE + +#include +#include + +namespace std23 +{ + +template struct nontype_t // freestanding +{ + explicit nontype_t() = default; +}; + +template inline constexpr nontype_t nontype{}; // freestanding + +using std::in_place_type; +using std::in_place_type_t; +using std::initializer_list; +using std::nullptr_t; + +template +requires std::is_invocable_r_v +constexpr R invoke_r(F &&f, Args &&...args) // freestanding + noexcept(std::is_nothrow_invocable_r_v) +{ + if constexpr (std::is_void_v) + std::invoke(std::forward(f), std::forward(args)...); + else + return std::invoke(std::forward(f), std::forward(args)...); +} + +// See also: https://www.agner.org/optimize/calling_conventions.pdf +template +inline constexpr auto _select_param_type = [] +{ + if constexpr (std::is_trivially_copyable_v) + return std::type_identity(); + else + return std::add_rvalue_reference(); +}; + +template +using _param_t = std::invoke_result_t)>::type; + +template +inline constexpr bool _is_not_self = + not std::is_same_v, Self>; + +template class> +inline constexpr bool _looks_nullable_to_impl = std::is_member_pointer_v; + +template class Self> +inline constexpr bool _looks_nullable_to_impl = + std::is_function_v; + +template class Self> +inline constexpr bool _looks_nullable_to_impl, Self> = true; + +template class Self> +inline constexpr bool _looks_nullable_to = + _looks_nullable_to_impl, Self>; + +template inline constexpr bool _is_not_nontype_t = true; +template inline constexpr bool _is_not_nontype_t> = false; + +template struct _adapt_signature; + +template requires std::is_function_v +struct _adapt_signature +{ + using type = F; +}; + +template using _adapt_signature_t = _adapt_signature::type; + +template struct _not_qualifying_this +{}; + +template struct _not_qualifying_this +{ + using type = R(Args...); +}; + +template +struct _not_qualifying_this +{ + using type = R(Args...) noexcept; +}; + +template +struct _not_qualifying_this : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template +struct _not_qualifying_this + : _not_qualifying_this +{}; + +template struct _drop_first_arg_to_invoke; + +template +struct _drop_first_arg_to_invoke +{ + using type = R(Args...); +}; + +template +struct _drop_first_arg_to_invoke +{ + using type = R(Args...) noexcept; +}; + +template requires std::is_object_v +struct _drop_first_arg_to_invoke +{ + using type = std::invoke_result_t(); +}; + +template requires std::is_function_v +struct _drop_first_arg_to_invoke : _not_qualifying_this +{}; + +template +using _drop_first_arg_to_invoke_t = _drop_first_arg_to_invoke::type; + +} // namespace std23 + +#endif diff --git a/moxt/std23/__functional_base.h:Zone.Identifier b/moxt/std23/__functional_base.h:Zone.Identifier new file mode 100644 index 0000000..e69de29 diff --git a/moxt/std23/function.h b/moxt/std23/function.h new file mode 100644 index 0000000..b155482 --- /dev/null +++ b/moxt/std23/function.h @@ -0,0 +1,395 @@ +#ifndef INCLUDE_STD23_FUNCTION +#define INCLUDE_STD23_FUNCTION + +#include "__functional_base.h" + +#include +#include +#include + +namespace std23 +{ + +template struct _opt_fn_sig; + +template struct _opt_fn_sig +{ + using function_type = R(Args...); + static constexpr bool is_variadic = false; + + template + static constexpr bool is_invocable_using = + std::is_invocable_r_v; +}; + +template struct _opt_fn_sig +{ + using function_type = R(Args...); + static constexpr bool is_variadic = true; + + template + static constexpr bool is_invocable_using = + std::is_invocable_r_v; +}; + +template struct _copyable_function +{ + struct lvalue_callable + { + virtual R operator()(Args...) const = 0; + virtual constexpr ~lvalue_callable() = default; + + void copy_into(std::byte *storage) const { copy_into_(storage); } + void move_into(std::byte *storage) noexcept { move_into_(storage); } + + protected: + virtual void copy_into_(void *) const = 0; + virtual void move_into_(void *) noexcept = 0; + }; + + template struct empty_object : lvalue_callable + { + void copy_into_(void *location) const override + { + ::new (location) Self; + } + + void move_into_(void *location) noexcept override + { + ::new (location) Self; + } + }; + + struct constructible_lvalue : lvalue_callable + { + [[noreturn]] R operator()(Args...) const override + { +#if defined(_MSC_VER) + __assume(0); +#else + __builtin_unreachable(); +#endif + } + }; + + template class stored_object : constructible_lvalue + { + std::conditional_t, T, std::unique_ptr> p_; + + public: + template + explicit stored_object(F &&f) + requires(_is_not_self and + not std::is_pointer_v) + : p_(std::make_unique(std::forward(f))) + {} + + explicit stored_object(T p) noexcept requires std::is_pointer_v + : p_(p) + {} + + protected: + decltype(auto) get() const + { + if constexpr (std::is_pointer_v) + return p_; + else + return *p_; + } + + void copy_into_(void *location) const override + { + ::new (location) Self(get()); + } + + void move_into_(void *location) noexcept override + { + ::new (location) Self(std::move(*this)); + } + }; + + template + class stored_object : constructible_lvalue + { + T &target_; + + public: + explicit stored_object(T &target) noexcept : target_(target) {} + + protected: + decltype(auto) get() const { return target_; } + + void copy_into_(void *location) const override + { + ::new (location) Self(*this); + } + + void move_into_(void *location) noexcept override + { + ::new (location) Self(*this); + } + }; + + struct empty_target_object final : empty_object + { + [[noreturn]] R operator()(Args...) const override + { + throw std::bad_function_call{}; + } + }; + + template + struct unbound_target_object final : empty_object> + { + R operator()(Args... args) const override + { + return std23::invoke_r(f, static_cast(args)...); + } + }; + + template + class target_object final : stored_object> + { + using base = stored_object>; + + public: + template + explicit target_object(F &&f) noexcept( + std::is_nothrow_constructible_v) + requires _is_not_self + : base(std::forward(f)) + {} + + R operator()(Args... args) const override + { + return std23::invoke_r(this->get(), static_cast(args)...); + } + }; + + template + class bound_target_object final + : stored_object> + { + using base = stored_object>; + + public: + template + explicit bound_target_object(U &&obj) noexcept( + std::is_nothrow_constructible_v) + requires _is_not_self + : base(std::forward(obj)) + {} + + R operator()(Args... args) const override + { + return std23::invoke_r(f, this->get(), + static_cast(args)...); + } + }; +}; + +template::function_type> +class function; + +template class function +{ + using signature = _opt_fn_sig; + + template + static constexpr bool is_invocable_using = + signature::template is_invocable_using; + + template using lvalue = std::decay_t &; + + using copyable_function = + std::conditional_t..., va_list &>, + _copyable_function...>>; + + using lvalue_callable = copyable_function::lvalue_callable; + using empty_target_object = copyable_function::empty_target_object; + + struct typical_target_object : lvalue_callable + { + union + { + void (*fp)() = nullptr; + void *p; + }; + }; + + template + using target_object_for = + copyable_function::template target_object>; + + template + using unbound_target_object = + copyable_function::template unbound_target_object; + + template + using bound_target_object_for = + copyable_function::template bound_target_object< + f, std::unwrap_ref_decay_t>; + + template> + static bool constexpr is_viable_initializer = + std::is_copy_constructible_v and std::is_constructible_v; + + alignas(typical_target_object) + std::byte storage_[sizeof(typical_target_object)]; + + auto storage_location() noexcept -> void * { return &storage_; } + + auto target() noexcept + { + return std::launder(reinterpret_cast(&storage_)); + } + + auto target() const noexcept + { + return std::launder( + reinterpret_cast(&storage_)); + } + + public: + using result_type = R; + + function() noexcept { ::new (storage_location()) empty_target_object; } + function(nullptr_t) noexcept : function() {} + + template + function(F &&f) noexcept( + std::is_nothrow_constructible_v, F>) + requires _is_not_self and is_invocable_using> and + is_viable_initializer + { + using T = target_object_for; + static_assert(sizeof(T) <= sizeof(storage_)); + + if constexpr (_looks_nullable_to) + { + if (f == nullptr) + { + std::construct_at(this); + return; + } + } + + ::new (storage_location()) T(std::forward(f)); + } + + template + function(nontype_t) noexcept requires is_invocable_using + { + ::new (storage_location()) unbound_target_object; + } + + template + function(nontype_t, U &&obj) noexcept( + std::is_nothrow_constructible_v, U>) + requires is_invocable_using> and + is_viable_initializer + { + using T = bound_target_object_for; + static_assert(sizeof(T) <= sizeof(storage_)); + + ::new (storage_location()) T(std::forward(obj)); + } + + function(function const &other) { other.target()->copy_into(storage_); } + function(function &&other) noexcept { other.target()->move_into(storage_); } + + function &operator=(function const &other) + { + if (&other != this) + { + auto tmp = other; + swap(tmp); + } + + return *this; + } + + function &operator=(function &&other) noexcept + { + if (&other != this) + { + std::destroy_at(this); + return *std::construct_at(this, std::move(other)); + } + else + return *this; + } + + void swap(function &other) noexcept { std::swap(*this, other); } + friend void swap(function &lhs, function &rhs) noexcept { lhs.swap(rhs); } + + ~function() { std::destroy_at(target()); } + + explicit operator bool() const noexcept + { + constexpr empty_target_object null; + return __builtin_memcmp(storage_, &null, sizeof(void *)) != 0; + } + + friend bool operator==(function const &f, nullptr_t) noexcept { return !f; } + + R operator()(Args... args) const requires(!signature::is_variadic) + { + return (*target())(std::forward(args)...); + } + +#if defined(__GNUC__) && (!defined(__clang__) || defined(__INTELLISENSE__)) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-value" +#endif + + R operator()(Args... args...) const + requires(signature::is_variadic and sizeof...(Args) != 0) + { + struct raii + { + va_list data; + ~raii() { va_end(data); } + } va; + va_start(va.data, (args, ...)); + return (*target())(std::forward(args)..., va.data); + } + +#if defined(__GNUC__) && (!defined(__clang__) || defined(__INTELLISENSE__)) +#pragma GCC diagnostic pop +#endif +}; + +template struct _strip_noexcept; + +template struct _strip_noexcept +{ + using type = R(Args...); +}; + +template struct _strip_noexcept +{ + using type = R(Args...); +}; + +template using _strip_noexcept_t = _strip_noexcept::type; + +template requires std::is_function_v +function(F *) -> function<_strip_noexcept_t>; + +template +function(T) -> function<_strip_noexcept_t< + _drop_first_arg_to_invoke_t>>; + +template +function(nontype_t) + -> function<_strip_noexcept_t<_adapt_signature_t>>; + +template +function(nontype_t, T) + -> function<_strip_noexcept_t<_drop_first_arg_to_invoke_t>>; + +} // namespace std23 + +#endif diff --git a/moxt/std23/function.h:Zone.Identifier b/moxt/std23/function.h:Zone.Identifier new file mode 100644 index 0000000..e69de29 diff --git a/moxt/std23/function_ref.h b/moxt/std23/function_ref.h new file mode 100644 index 0000000..4ee2b65 --- /dev/null +++ b/moxt/std23/function_ref.h @@ -0,0 +1,216 @@ +#ifndef INCLUDE_STD23_FUNCTION__REF +#define INCLUDE_STD23_FUNCTION__REF + +#include "__functional_base.h" + +#include + +namespace std23 +{ + +template struct _qual_fn_sig; + +template struct _qual_fn_sig +{ + using function = R(Args...); + static constexpr bool is_noexcept = false; + + template + static constexpr bool is_invocable_using = + std::is_invocable_r_v; + + template using cv = T; +}; + +template struct _qual_fn_sig +{ + using function = R(Args...); + static constexpr bool is_noexcept = true; + + template + static constexpr bool is_invocable_using = + std::is_nothrow_invocable_r_v; + + template using cv = T; +}; + +template +struct _qual_fn_sig : _qual_fn_sig +{ + template using cv = T const; +}; + +template +struct _qual_fn_sig + : _qual_fn_sig +{ + template using cv = T const; +}; + +struct _function_ref_base +{ + union storage + { + void *p_ = nullptr; + void const *cp_; + void (*fp_)(); + + constexpr storage() noexcept = default; + + template requires std::is_object_v + constexpr explicit storage(T *p) noexcept : p_(p) + {} + + template requires std::is_object_v + constexpr explicit storage(T const *p) noexcept : cp_(p) + {} + + template requires std::is_function_v + constexpr explicit storage(T *p) noexcept + : fp_(reinterpret_cast(p)) + {} + }; + + template constexpr static auto get(storage obj) + { + if constexpr (std::is_const_v) + return static_cast(obj.cp_); + else if constexpr (std::is_object_v) + return static_cast(obj.p_); + else + return reinterpret_cast(obj.fp_); + } +}; + +template::function> +class function_ref; // freestanding + +template +class function_ref // freestanding + : _function_ref_base +{ + using signature = _qual_fn_sig; + + template using cv = signature::template cv; + template using cvref = cv &; + static constexpr bool noex = signature::is_noexcept; + + template + static constexpr bool is_invocable_using = + signature::template is_invocable_using; + + typedef R fwd_t(storage, _param_t...) noexcept(noex); + fwd_t *fptr_ = nullptr; + storage obj_; + + public: + template + function_ref(F *f) noexcept + requires std::is_function_v and is_invocable_using + : fptr_( + [](storage fn_, _param_t... args) noexcept(noex) -> R + { + if constexpr (std::is_void_v) + get(fn_)(static_cast(args)...); + else + return get(fn_)(static_cast(args)...); + }), + obj_(f) + { + assert(f != nullptr && "must reference a function"); + } + + template> + constexpr function_ref(F &&f) noexcept + requires(_is_not_self and + not std::is_member_pointer_v and + is_invocable_using>) + : fptr_( + [](storage fn_, _param_t... args) noexcept(noex) -> R + { + cvref obj = *get(fn_); + if constexpr (std::is_void_v) + obj(static_cast(args)...); + else + return obj(static_cast(args)...); + }), + obj_(std::addressof(f)) + {} + + template + function_ref &operator=(T) + requires(_is_not_self and not std::is_pointer_v and + _is_not_nontype_t) + = delete; + + template + constexpr function_ref(nontype_t) noexcept + requires is_invocable_using + : fptr_( + [](storage, _param_t... args) noexcept(noex) -> R { + return std23::invoke_r( + f, static_cast(args)...); + }) + { + using F = decltype(f); + if constexpr (std::is_pointer_v or std::is_member_pointer_v) + static_assert(f != nullptr, "NTTP callable must be usable"); + } + + template> + constexpr function_ref(nontype_t, U &&obj) noexcept + requires(not std::is_rvalue_reference_v and + is_invocable_using>) + : fptr_( + [](storage this_, _param_t... args) noexcept(noex) -> R + { + cvref obj = *get(this_); + return std23::invoke_r( + f, obj, static_cast(args)...); + }), + obj_(std::addressof(obj)) + { + using F = decltype(f); + if constexpr (std::is_pointer_v or std::is_member_pointer_v) + static_assert(f != nullptr, "NTTP callable must be usable"); + } + + template + constexpr function_ref(nontype_t, cv *obj) noexcept + requires is_invocable_using + : fptr_( + [](storage this_, _param_t... args) noexcept(noex) -> R + { + return std23::invoke_r( + f, get>(this_), + static_cast(args)...); + }), + obj_(obj) + { + using F = decltype(f); + if constexpr (std::is_pointer_v or std::is_member_pointer_v) + static_assert(f != nullptr, "NTTP callable must be usable"); + + if constexpr (std::is_member_pointer_v) + assert(obj != nullptr && "must reference an object"); + } + + constexpr R operator()(Args... args) const noexcept(noex) + { + return fptr_(obj_, std::forward(args)...); + } +}; + +template requires std::is_function_v +function_ref(F *) -> function_ref; + +template +function_ref(nontype_t) -> function_ref<_adapt_signature_t>; + +template +function_ref(nontype_t, T &&) + -> function_ref<_drop_first_arg_to_invoke_t>; + +} // namespace std23 + +#endif diff --git a/moxt/std23/function_ref.h:Zone.Identifier b/moxt/std23/function_ref.h:Zone.Identifier new file mode 100644 index 0000000..e69de29 diff --git a/moxt/std23/move_only_function.h b/moxt/std23/move_only_function.h new file mode 100644 index 0000000..bb5df0f --- /dev/null +++ b/moxt/std23/move_only_function.h @@ -0,0 +1,517 @@ +#ifndef INCLUDE_STD23_MOVE__ONLY__FUNCTION +#define INCLUDE_STD23_MOVE__ONLY__FUNCTION + +#include "__functional_base.h" + +#include +#include +#include + +namespace std23 +{ + +template struct _cv_fn_sig +{}; + +template struct _cv_fn_sig +{ + using function = R(Args...); + template using cv = T; +}; + +template struct _cv_fn_sig +{ + using function = R(Args...); + template using cv = T const; +}; + +template struct _ref_quals_fn_sig : _cv_fn_sig +{ + template using ref = T; +}; + +template +struct _ref_quals_fn_sig : _cv_fn_sig +{ + template using ref = T &; +}; + +template +struct _ref_quals_fn_sig : _cv_fn_sig +{ + template using ref = T &; +}; + +template +struct _ref_quals_fn_sig : _cv_fn_sig +{ + template using ref = T &&; +}; + +template +struct _ref_quals_fn_sig : _cv_fn_sig +{ + template using ref = T &&; +}; + +template struct _noex_traits +{ + static constexpr bool is_noexcept = V; +}; + +template +struct _full_fn_sig : _ref_quals_fn_sig, _noex_traits +{}; + +template +struct _full_fn_sig : _ref_quals_fn_sig, + _noex_traits +{}; + +template +struct _full_fn_sig : _ref_quals_fn_sig, + _noex_traits +{}; + +template +struct _full_fn_sig : _ref_quals_fn_sig, + _noex_traits +{}; + +template +struct _full_fn_sig + : _ref_quals_fn_sig, _noex_traits +{}; + +template +struct _full_fn_sig + : _ref_quals_fn_sig, _noex_traits +{}; + +template +struct _full_fn_sig + : _ref_quals_fn_sig, _noex_traits +{}; + +constexpr inline struct +{ + constexpr auto operator()(auto &&rhs) const + { + return new auto(decltype(rhs)(rhs)); + } + + constexpr auto operator()(auto *rhs) const noexcept { return rhs; } + + template + constexpr auto operator()(std::reference_wrapper rhs) const noexcept + { + return std::addressof(rhs.get()); + } + +} _take_reference; + +template +constexpr auto _build_reference = [](auto &&...args) +{ return new T(decltype(args)(args)...); }; + +template +constexpr auto _build_reference = [](auto &&...args) noexcept -> T * +{ return {decltype(args)(args)...}; }; + +template +constexpr auto _build_reference> = + [](auto &rhs) noexcept { return std::addressof(rhs); }; + +struct _move_only_pointer +{ + union value_type + { + void *p_ = nullptr; + void const *cp_; + void (*fp_)(); + } val; + + _move_only_pointer() = default; + _move_only_pointer(_move_only_pointer const &) = delete; + _move_only_pointer &operator=(_move_only_pointer const &) = delete; + + constexpr _move_only_pointer(_move_only_pointer &&other) noexcept + : val(std::exchange(other.val, {})) + {} + + template requires std::is_object_v + constexpr explicit _move_only_pointer(T *p) noexcept : val{.p_ = p} + {} + + template requires std::is_object_v + constexpr explicit _move_only_pointer(T const *p) noexcept : val{.cp_ = p} + {} + + template requires std::is_function_v + constexpr explicit _move_only_pointer(T *p) noexcept + : val{.fp_ = reinterpret_cast(p)} + {} + + template requires std::is_object_v + constexpr _move_only_pointer &operator=(T *p) noexcept + { + val.p_ = p; + return *this; + } + + template requires std::is_object_v + constexpr _move_only_pointer &operator=(T const *p) noexcept + { + val.cp_ = p; + return *this; + } + + template requires std::is_function_v + constexpr _move_only_pointer &operator=(T *p) noexcept + { + val.fp_ = reinterpret_cast(p); + return *this; + } + + constexpr _move_only_pointer &operator=(_move_only_pointer &&other) noexcept + { + val = std::exchange(other.val, {}); + return *this; + } +}; + +template struct _callable_trait +{ + using handle = _move_only_pointer::value_type; + + typedef auto call_t(handle, Args...) noexcept(noex) -> R; + typedef void destroy_t(handle) noexcept; + + struct vtable + { + call_t *call = 0; + destroy_t *destroy = [](handle) noexcept {}; + }; + + static inline constinit vtable const abstract_base; + + template constexpr static auto get(handle val) + { + if constexpr (std::is_const_v) + return static_cast(val.cp_); + else if constexpr (std::is_object_v) + return static_cast(val.p_); + else + return reinterpret_cast(val.fp_); + } + + // See also: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71954 + template class quals> + static inline constinit vtable const callable_target{ + .call = [](handle this_, Args... args) noexcept(noex) -> R + { + if constexpr (std::is_lvalue_reference_v or std::is_pointer_v) + { + using Tp = std::remove_reference_t>; + return std23::invoke_r(*get(this_), + static_cast(args)...); + } + else + { + using Fp = quals::type; + return std23::invoke_r(static_cast(*get(this_)), + static_cast(args)...); + } + }, + .destroy = + [](handle this_) noexcept + { + if constexpr (not std::is_lvalue_reference_v and + not std::is_pointer_v) + delete get(this_); + }, + }; + + template + static inline constinit vtable const unbound_callable_target{ + .call = [](handle, Args... args) noexcept(noex) -> R + { return std23::invoke_r(f, static_cast(args)...); }, + }; + + template class quals> + static inline constinit vtable const bound_callable_target{ + .call = [](handle this_, Args... args) noexcept(noex) -> R + { + if constexpr (std::is_pointer_v) + { + using Tp = std::remove_pointer_t; + return std23::invoke_r(f, get(this_), + static_cast(args)...); + } + else if constexpr (std::is_lvalue_reference_v) + { + using Tp = std::remove_reference_t; + return std23::invoke_r(f, *get(this_), + static_cast(args)...); + } + else + { + using Fp = quals::type; + return std23::invoke_r(f, static_cast(*get(this_)), + static_cast(args)...); + } + }, + .destroy = + [](handle this_) noexcept + { + if constexpr (not std::is_lvalue_reference_v and + not std::is_pointer_v) + delete get(this_); + }, + }; + + template + static inline constinit vtable const boxed_callable_target{ + .call = [](handle this_, Args... args) noexcept(noex) -> R { + return std23::invoke_r(f, get(this_), + static_cast(args)...); + }, + .destroy = + [](handle this_) noexcept + { + using D = std::unique_ptr::deleter_type; + static_assert(std::is_trivially_default_constructible_v); + if (auto p = get(this_)) + D()(p); + }, + }; +}; + +template class Primary> +inline constexpr bool _is_specialization_of = false; + +template class Primary, class... Args> +inline constexpr bool _is_specialization_of, Primary> = true; + +template class Primary> +inline constexpr bool _does_not_specialize = + not _is_specialization_of, Primary>; + +template::function> +class move_only_function; + +template +class move_only_function +{ + using signature = _full_fn_sig; + + template using cv = signature::template cv; + template using ref = signature::template ref; + + static constexpr bool noex = signature::is_noexcept; + static constexpr bool is_const = std::is_same_v, void const>; + static constexpr bool is_lvalue_only = std::is_same_v, int &>; + static constexpr bool is_rvalue_only = std::is_same_v, int &&>; + + template using cvref = ref>; + template + struct inv_quals_f + : std::conditional, cv &> + {}; + template using inv_quals = inv_quals_f::type; + + template + static constexpr bool is_invocable_using = + std::conditional_t, + std::is_invocable_r>::value; + + template + static constexpr bool is_callable_from = + is_invocable_using> and is_invocable_using>; + + template + static constexpr bool is_callable_as_if_from = + is_invocable_using>; + + using trait = _callable_trait...>; + using vtable = trait::vtable; + + std::reference_wrapper vtbl_ = trait::abstract_base; + _move_only_pointer obj_; + + public: + using result_type = R; + + move_only_function() = default; + move_only_function(nullptr_t) noexcept : move_only_function() {} + + template> + move_only_function(F &&f) noexcept( + std::is_nothrow_invocable_v) + requires _is_not_self and + _does_not_specialize and + is_callable_from and std::is_constructible_v + { + if constexpr (_looks_nullable_to) + { + if (f == nullptr) + return; + } + + vtbl_ = trait::template callable_target, + inv_quals_f>; + obj_ = _take_reference(std::forward(f)); + } + + template + move_only_function(nontype_t) noexcept + requires is_invocable_using + : vtbl_(trait::template unbound_callable_target) + {} + + template> + move_only_function(nontype_t, T &&x) noexcept( + std::is_nothrow_invocable_v) + requires is_callable_as_if_from and + std::is_constructible_v + : vtbl_(trait::template bound_callable_target< + f, std::unwrap_ref_decay_t, inv_quals_f>), + obj_(_take_reference(std::forward(x))) + {} + + template + move_only_function(nontype_t, std::unique_ptr &&x) noexcept + requires std::is_base_of_v and is_callable_as_if_from + : vtbl_(trait::template boxed_callable_target), obj_(x.release()) + {} + + template + explicit move_only_function(in_place_type_t, Inits &&...inits) noexcept( + std::is_nothrow_invocable_v), Inits...>) + requires is_callable_from and std::is_constructible_v + : vtbl_(trait::template callable_target, + inv_quals_f>), + obj_(_build_reference(std::forward(inits)...)) + { + static_assert(std::is_same_v, T>); + } + + template + explicit move_only_function(in_place_type_t, initializer_list ilist, + Inits &&...inits) noexcept( // + std::is_nothrow_invocable_v), + decltype((ilist)), Inits...>) + requires is_callable_from and + std::is_constructible_v + : vtbl_(trait::template callable_target, + inv_quals_f>), + obj_(_build_reference(ilist, std::forward(inits)...)) + { + static_assert(std::is_same_v, T>); + } + + template + explicit move_only_function(nontype_t, in_place_type_t, + Inits &&...inits) noexcept( // + std::is_nothrow_invocable_v), Inits...>) + requires is_callable_as_if_from and + std::is_constructible_v + : vtbl_(trait::template bound_callable_target< + f, std::unwrap_reference_t, inv_quals_f>), + obj_(_build_reference(std::forward(inits)...)) + { + static_assert(std::is_same_v, T>); + } + + template + explicit move_only_function(nontype_t t, + in_place_type_t>, + Inits &&...inits) noexcept( // + std::is_nothrow_constructible_v, Inits...>) + requires std::is_base_of_v and is_callable_as_if_from and + std::is_constructible_v, + Inits...> + : move_only_function(t, + std::unique_ptr(std::forward(inits)...)) + {} + + template + explicit move_only_function(nontype_t, in_place_type_t, + initializer_list ilist, + Inits &&...inits) noexcept( // + std::is_nothrow_invocable_v), + decltype((ilist)), Inits...>) + requires is_callable_as_if_from and + std::is_constructible_v + : vtbl_(trait::template bound_callable_target< + f, std::unwrap_reference_t, inv_quals_f>), + obj_(_build_reference(ilist, std::forward(inits)...)) + { + static_assert(std::is_same_v, T>); + } + + move_only_function(move_only_function &&) = default; + move_only_function &operator=(move_only_function &&) = default; + + void swap(move_only_function &other) noexcept + { + std::swap(*this, other); + } + + friend void swap(move_only_function &lhs, move_only_function &rhs) noexcept + { + lhs.swap(rhs); + } + + ~move_only_function() { vtbl_.get().destroy(obj_.val); } + + explicit operator bool() const noexcept + { + return &vtbl_.get() != &trait::abstract_base; + } + + friend bool operator==(move_only_function const &f, nullptr_t) noexcept + { + return !f; + } + + R operator()(Args... args) noexcept(noex) + requires(!is_const and !is_lvalue_only and !is_rvalue_only) + { + return vtbl_.get().call(obj_.val, std::forward(args)...); + } + + R operator()(Args... args) const noexcept(noex) + requires(is_const and !is_lvalue_only and !is_rvalue_only) + { + return vtbl_.get().call(obj_.val, std::forward(args)...); + } + + R operator()(Args... args) &noexcept(noex) + requires(!is_const and is_lvalue_only and !is_rvalue_only) + { + return vtbl_.get().call(obj_.val, std::forward(args)...); + } + + R operator()(Args... args) const &noexcept(noex) + requires(is_const and is_lvalue_only and !is_rvalue_only) + { + return vtbl_.get().call(obj_.val, std::forward(args)...); + } + + R operator()(Args... args) &&noexcept(noex) + requires(!is_const and !is_lvalue_only and is_rvalue_only) + { + return vtbl_.get().call(obj_.val, std::forward(args)...); + } + + R operator()(Args... args) const &&noexcept(noex) + requires(is_const and !is_lvalue_only and is_rvalue_only) + { + return vtbl_.get().call(obj_.val, std::forward(args)...); + } +}; + +} // namespace std23 + +#endif diff --git a/moxt/std23/move_only_function.h:Zone.Identifier b/moxt/std23/move_only_function.h:Zone.Identifier new file mode 100644 index 0000000..e69de29 diff --git a/moxt/tscns.h b/moxt/tscns.h new file mode 100644 index 0000000..7efedb5 --- /dev/null +++ b/moxt/tscns.h @@ -0,0 +1,153 @@ +/* +MIT License + +Copyright (c) 2022 Meng Rao + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#pragma once +#include +#include +#include + +#ifdef _MSC_VER +#include +#endif + +class TSCNS +{ +public: + static const int64_t NsPerSec = 1000000000; + + void init(int64_t init_calibrate_ns = 20000000, int64_t calibrate_interval_ns = 3 * NsPerSec) { + calibate_interval_ns_ = calibrate_interval_ns; + int64_t base_tsc, base_ns; + syncTime(base_tsc, base_ns); + int64_t expire_ns = base_ns + init_calibrate_ns; + while (rdsysns() < expire_ns) std::this_thread::yield(); + int64_t delayed_tsc, delayed_ns; + syncTime(delayed_tsc, delayed_ns); + double init_ns_per_tsc = (double)(delayed_ns - base_ns) / (delayed_tsc - base_tsc); + saveParam(base_tsc, base_ns, base_ns, init_ns_per_tsc); + } + + void calibrate() { + if (rdtsc() < next_calibrate_tsc_) return; + int64_t tsc, ns; + syncTime(tsc, ns); + int64_t calulated_ns = tsc2ns(tsc); + int64_t ns_err = calulated_ns - ns; + int64_t expected_err_at_next_calibration = + ns_err + (ns_err - base_ns_err_) * calibate_interval_ns_ / (ns - base_ns_ + base_ns_err_); + double new_ns_per_tsc = + ns_per_tsc_ * (1.0 - (double)expected_err_at_next_calibration / calibate_interval_ns_); + saveParam(tsc, calulated_ns, ns, new_ns_per_tsc); + } + + static inline int64_t rdtsc() { +#ifdef _MSC_VER + return __rdtsc(); +#elif defined(__i386__) || defined(__x86_64__) || defined(__amd64__) + return __builtin_ia32_rdtsc(); +#else + return rdsysns(); +#endif + } + + inline int64_t tsc2ns(int64_t tsc) const { + while (true) { + uint32_t before_seq = param_seq_.load(std::memory_order_acquire) & ~1; + std::atomic_signal_fence(std::memory_order_acq_rel); + int64_t ns = base_ns_ + (int64_t)((tsc - base_tsc_) * ns_per_tsc_); + std::atomic_signal_fence(std::memory_order_acq_rel); + uint32_t after_seq = param_seq_.load(std::memory_order_acquire); + if (before_seq == after_seq) return ns; + } + } + + inline int64_t rdns() const { return tsc2ns(rdtsc()); } + + static inline int64_t rdsysns() { + using namespace std::chrono; + return duration_cast(system_clock::now().time_since_epoch()).count(); + } + + double getTscGhz() const { return 1.0 / ns_per_tsc_; } + + // Linux kernel sync time by finding the first trial with tsc diff < 50000 + // We try several times and return the one with the mininum tsc diff. + // Note that MSVC has a 100ns resolution clock, so we need to combine those ns with the same + // value, and drop the first and the last value as they may not scan a full 100ns range + static void syncTime(int64_t& tsc_out, int64_t& ns_out) { +#ifdef _MSC_VER + const int N = 15; +#else + const int N = 3; +#endif + int64_t tsc[N + 1]; + int64_t ns[N + 1]; + + tsc[0] = rdtsc(); + for (int i = 1; i <= N; i++) { + ns[i] = rdsysns(); + tsc[i] = rdtsc(); + } + +#ifdef _MSC_VER + int j = 1; + for (int i = 2; i <= N; i++) { + if (ns[i] == ns[i - 1]) continue; + tsc[j - 1] = tsc[i - 1]; + ns[j++] = ns[i]; + } + j--; +#else + int j = N + 1; +#endif + + int best = 1; + for (int i = 2; i < j; i++) { + if (tsc[i] - tsc[i - 1] < tsc[best] - tsc[best - 1]) best = i; + } + tsc_out = (tsc[best] + tsc[best - 1]) >> 1; + ns_out = ns[best]; + } + + void saveParam(int64_t base_tsc, int64_t base_ns, int64_t sys_ns, double new_ns_per_tsc) { + base_ns_err_ = base_ns - sys_ns; + next_calibrate_tsc_ = base_tsc + (int64_t)((calibate_interval_ns_ - 1000) / new_ns_per_tsc); + uint32_t seq = param_seq_.load(std::memory_order_relaxed); + param_seq_.store(++seq, std::memory_order_release); + std::atomic_signal_fence(std::memory_order_acq_rel); + base_tsc_ = base_tsc; + base_ns_ = base_ns; + ns_per_tsc_ = new_ns_per_tsc; + std::atomic_signal_fence(std::memory_order_acq_rel); + param_seq_.store(++seq, std::memory_order_release); + } + + alignas(64) std::atomic param_seq_ = 0; + double ns_per_tsc_; + int64_t base_tsc_; + int64_t base_ns_; + int64_t calibate_interval_ns_; + int64_t base_ns_err_; + int64_t next_calibrate_tsc_; +}; \ No newline at end of file diff --git a/moxt/utils/spscqueue_safe.cpp b/moxt/utils/spscqueue_safe.cpp deleted file mode 100644 index 5dee518..0000000 --- a/moxt/utils/spscqueue_safe.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// -// Created by ken on 2023/7/17. -// - -#include "spscqueue_safe.hpp" diff --git a/moxt/utils/spscqueue_safe.hpp b/moxt/utils/spscqueue_safe.hpp deleted file mode 100644 index 6878747..0000000 --- a/moxt/utils/spscqueue_safe.hpp +++ /dev/null @@ -1,46 +0,0 @@ -// -// Created by ken on 2023/7/17. -// - -#ifndef MOXT_SPSCQUEUE_SAFE_HPP -#define MOXT_SPSCQUEUE_SAFE_HPP - -#include "spinlock.hpp" -#include - -template > -class SPSCQueueSafe : private rigtorp::SPSCQueue { - public: - SPSCQueueSafe(const size_t capacity, - const Allocator &allocator = Allocator()) - : rigtorp::SPSCQueue(capacity, allocator) {} - - void push(const T &v) { - lock_.lock(); - rigtorp::SPSCQueue::push(v); - lock_.unlock(); - } - - bool pop(T &v) { - bool ok = false; - lock_.lock(); - auto ptr = rigtorp::SPSCQueue::front(); - if (ptr) { - v = *ptr; - rigtorp::SPSCQueue::pop(); - ok = true; - } - lock_.unlock(); - return ok; - } - - size_t size() const { - auto size = rigtorp::SPSCQueue::size(); - return size; - } - - private: - CSpinLock lock_; -}; - -#endif // MOXT_SPSCQUEUE_SAFE_HPP diff --git a/moxt/xmake.lua b/moxt/xmake.lua index a3ef099..73182ca 100644 --- a/moxt/xmake.lua +++ b/moxt/xmake.lua @@ -36,7 +36,6 @@ target("moxt") add_packages("fpm") add_packages("boost") - add_packages("tscns") add_packages("fmt") add_packages("fmtlog") @@ -54,8 +53,6 @@ target("moxt") add_packages("libcurl") add_packages("libaio") add_packages("concurrentqueue") - add_packages("spscqueue") - add_packages("nontype_functional") add_packages("parallel-hashmap") add_packages("yyjson") add_packages("nanobench") @@ -76,7 +73,6 @@ target("moxt") tulipindicators_lib_dir = path.join(tulipindicators_root, "build/linux/x86_64/release") end add_packages("tulipindicators") - -- add_linkdirs("~/cpp/moxt-cpp-pro/build/linux/x86_64/release") add_linkdirs(tulipindicators_lib_dir) add_links("tulipindicators") @@ -118,7 +114,6 @@ target("moxt_sole") end add_packages("fpm") add_packages("boost") - add_packages("tscns") add_packages("fmt") add_packages("fmtlog") @@ -136,8 +131,6 @@ target("moxt_sole") add_packages("libcurl") add_packages("libaio") add_packages("concurrentqueue") - add_packages("spscqueue") - add_packages("nontype_functional") add_packages("parallel-hashmap") add_packages("yyjson") add_packages("nanobench") diff --git a/myrepo/packages/f/fmtlog-local/xmake.lua b/myrepo/packages/f/fmtlog-local/xmake.lua index 7f091a2..104b0d9 100644 --- a/myrepo/packages/f/fmtlog-local/xmake.lua +++ b/myrepo/packages/f/fmtlog-local/xmake.lua @@ -3,10 +3,10 @@ package("fmtlog-local") set_description("fmtlog is a performant fmtlib-style logging library with latency in nanoseconds.") set_license("MIT") - -- add_urls("https://github.com/MengRao/fmtlog/archive/refs/tags/$(version).tar.gz", - -- "https://github.com/MengRao/fmtlog.git") - -- add_versions("v2.1.2", "d286184e04c3c3286417873dd2feac524c53babc6cd60f10179aa5b10416ead7") - add_urls("https://github.com/numenresearch/fmtlog.git") + add_urls("https://github.com/MengRao/fmtlog/archive/refs/tags/$(version).tar.gz", + "https://github.com/MengRao/fmtlog.git") + add_versions("v2.1.2", "d286184e04c3c3286417873dd2feac524c53babc6cd60f10179aa5b10416ead7") + -- add_urls("https://github.com/numenresearch/fmtlog.git") -- add_deps("cmake", "fmt") add_deps("fmt", {configs = {header_only = true}}) diff --git a/xmake.lua b/xmake.lua index 423f5da..addf619 100644 --- a/xmake.lua +++ b/xmake.lua @@ -1,6 +1,6 @@ set_project("moxt") set_version("1.0.0") -set_xmakever("2.8.9") +set_xmakever("2.9.4") set_encodings("utf-8") set_languages("c++20") -- c++20/c++23 @@ -19,14 +19,13 @@ end add_requires("fast_float_local", {alias = 'fast_float'}) add_requires("fmt", {configs = {header_only = true}}) add_requires("fmtlog-local", {alias = 'fmtlog'}) -add_requires("spdlog") +add_requires("spdlog v1.14.1") add_requires("quill-local", {alias = 'quill'}) add_requires("nanobench") add_requires("concurrentqueue") add_requires("nanoid-cpp-local", {alias = 'nanoid-cpp'}) add_requires("zlib", {system=false}) add_requires("openssl", {system=false}) -add_requires("tscns") add_requires("boost", {configs = {json = true, thread = true, context = true, program_options = true, regex = true, system = true, filesystem = true, date_time = true, url = true, coroutine = true}}) @@ -50,15 +49,16 @@ end add_requires("asio") -- add_requires("libuv-local", {alias = 'libuv'}) add_requires("libuv") -add_requires("snmalloc-local", {alias = 'snmalloc'}) +-- add_requires("snmalloc-local", {alias = 'snmalloc'}) +add_requires("snmalloc") add_requires("parallel-hashmap-local", {alias = 'parallel-hashmap'}) -add_requires("spscqueue") -add_requires("mpmcqueue") -add_requires("nontype_functional") +-- add_requires("spscqueue") +-- add_requires("mpmcqueue") +-- add_requires("nontype_functional") add_requires("yyjson-local", {alias = 'yyjson'}) add_requires("sonic-cpp-local", {alias = 'sonic-cpp'}) add_requires("toml++") -add_requires("uwebsockets") +-- add_requires("uwebsockets") add_requires("redis-plus-plus") target("tulipindicators")