diff --git a/CMakeLists.txt b/CMakeLists.txt index e95116c2..fb33d647 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,8 +125,8 @@ if((NOT CMAKE_SYSTEM MATCHES "Windows*") AND CMAKE_BUILD_TYPE STREQUAL "coverage #force a single configuration type on Windows builds set(CMAKE_CONFIGURATION_TYPES "debug") set(CONFIGURATION_TYPE "debug") - add_compile_options("SHELL: -coverage -fprofile-arcs -ftest-coverage") - add_link_options("SHELL: -fprofile-arcs -ftest-coverage") + add_compile_options("SHELL: --coverage") + add_link_options("SHELL: --coverage") elseif(CMAKE_SYSTEM MATCHES "Windows*" AND CMAKE_BUILD_TYPE STREQUAL "coverage") message(FATAL_ERROR "\r\n === Coverage not yet supported for Windows systems ===\r\n") endif() @@ -382,9 +382,9 @@ if(NOT ON_CROSS) find_program( MEMORYCHECK_COMMAND valgrind ) set( MEMORYCHECK_COMMAND_OPTIONS "--trace-children=yes --leak-check=full" ) set( MEMORYCHECK_SUPPRESSIONS_FILE "${PROJECT_SOURCE_DIR}/cmake/valgrind_suppress.txt" ) - + set(SOLID_TEST_ALL TRUE CACHE BOOL "Enable/Disable all tests") - + enable_testing() include(CTest) else() diff --git a/solid/frame/aio/src/aioreactor.cpp b/solid/frame/aio/src/aioreactor.cpp index 852aa28e..d74d6e16 100644 --- a/solid/frame/aio/src/aioreactor.cpp +++ b/solid/frame/aio/src/aioreactor.cpp @@ -567,10 +567,10 @@ inline ReactorEventE systemEventsToReactorEvents(const uint32_t _events) inline constexpr ReactorEventE systemEventsToReactorEvents(const unsigned short _flags, const short _filter) { - ReactorEventE retval = ReactorEventE::None; - if (_flags & (EV_EOF | EV_ERROR)) { + if (_flags & (EV_ERROR)) { return ReactorEventE::Hangup; } + if (_filter == EVFILT_READ) { return ReactorEventE::Recv; } else if (_filter == EVFILT_WRITE) { @@ -579,7 +579,7 @@ inline constexpr ReactorEventE systemEventsToReactorEvents(const unsigned short return ReactorEventE::Recv; } - return retval; + return ReactorEventE::None; } #elif defined(SOLID_USE_WSAPOLL) inline ReactorEventE systemEventsToReactorEvents(const uint32_t _events, decltype(WSAPOLLFD::events)& _revs) diff --git a/solid/frame/manager.hpp b/solid/frame/manager.hpp index 9e7d7bb2..d13a550f 100644 --- a/solid/frame/manager.hpp +++ b/solid/frame/manager.hpp @@ -101,14 +101,6 @@ class Manager final : NonCopyable { template bool visit(ActorIdT const& _ruid, F _f); -#if false // TODO:vapa:delete - template - bool visitDynamicCast(ActorIdT const& _ruid, F _f); - - template - bool visitExplicitCast(ActorIdT const& _ruid, F _f); -#endif - ActorIdT id(const ActorBase& _ractor) const; Service& service(const ActorBase& _ractor) const; @@ -189,37 +181,6 @@ inline bool Manager::visit(ActorIdT const& _ruid, F _f) { return doVisit(_ruid, ActorVisitFunctionT{_f}); } -#if false -template -inline bool Manager::visitDynamicCast(ActorIdT const& _ruid, F _f) -{ - auto lambda = [&_f](VisitContext& _rctx) { - T* pt = dynamic_cast(&_rctx.actor()); - if (pt) { - return _f(_rctx, *pt); - } - return false; - }; - ActorVisitFunctionT fct(std::ref(lambda)); - return doVisit(_ruid, fct); -} - -template -inline bool Manager::visitExplicitCast(ActorIdT const& _ruid, F _f) -{ - auto lambda = [&_f](VisitContext& _rctx) { - const std::type_info& req_type = typeid(T); - const std::type_info& val_type = doGetTypeId(*(&_rctx.actor())); - - if (std::type_index(req_type) == std::type_index(val_type)) { - return _f(_rctx, static_cast(_rctx.actor())); - } - return false; - }; - - return doVisit(_ruid, ActorVisitFunctionT{lambda}); -} -#endif template inline size_t Manager::forEachServiceActor(const Service& _rservice, F _f) diff --git a/solid/frame/mprpc/mprpcservice.hpp b/solid/frame/mprpc/mprpcservice.hpp index cfe72777..4b2e8155 100644 --- a/solid/frame/mprpc/mprpcservice.hpp +++ b/solid/frame/mprpc/mprpcservice.hpp @@ -348,13 +348,6 @@ class Service : public frame::Service { const MessageFlagsT& _flags = 0); // send message using connection uid ------------------------------------- -#if 0 - template - ErrorConditionT sendMessage( - RecipientId const& _rrecipient_id, - MessagePointerT const& _rmsgptr, - const MessageFlagsT& _flags = 0); -#endif template ErrorConditionT sendMessage( const RecipientUrl& _recipient_url, @@ -401,23 +394,6 @@ class Service : public frame::Service { MessagePointerT const& _rmsgptr, MessageId& _rmsg_id, const MessageFlagsT& _flags = 0); - // send request using connection uid -------------------------------------- -#if 0 - template - ErrorConditionT sendRequest( - RecipientId const& _rrecipient_id, - MessagePointerT const& _rmsgptr, - Fnc _complete_fnc, - const MessageFlagsT& _flags = 0); - - template - ErrorConditionT sendRequest( - RecipientId const& _rrecipient_id, - MessagePointerT const& _rmsgptr, - Fnc _complete_fnc, - MessageId& _rmsguid, - const MessageFlagsT& _flags = 0); -#endif // send message with complete using recipient name ------------------------ template @@ -444,23 +420,6 @@ class Service : public frame::Service { MessageId& _rmsguid, const MessageFlagsT& _flags); - // send message with complete using connection uid ------------------------ -#if 0 - template - ErrorConditionT sendMessage( - RecipientId const& _rrecipient_id, - MessagePointerT const& _rmsgptr, - Fnc _complete_fnc, - const MessageFlagsT& _flags); - - template - ErrorConditionT sendMessage( - RecipientId const& _rrecipient_id, - MessagePointerT const& _rmsgptr, - Fnc _complete_fnc, - MessageId& _rmsguid, - const MessageFlagsT& _flags); -#endif // send message using ConnectionContext ---------------------------------- template @@ -468,21 +427,6 @@ class Service : public frame::Service { ConnectionContext& _rctx, MessagePointerT const& _rmsgptr, const MessageFlagsT& _flags = 0); -#if 0 - template - ErrorConditionT sendMessage( - ConnectionContext& _rctx, - MessagePointerT const& _rmsgptr, - const MessageFlagsT& _flags = 0); - - template - ErrorConditionT sendMessage( - ConnectionContext& _rctx, - MessagePointerT const& _rmsgptr, - Fnc _complete_fnc, - const MessageFlagsT& _flags = 0); - -#endif //------------------------------------------------------------------------- ErrorConditionT sendRelay(const ActorIdT& _rconid, RelayData&& _urelmsg); ErrorConditionT sendRelayCancel(RelayData&& _urelmsg); @@ -741,19 +685,6 @@ ErrorConditionT Service::sendMessage( MessageCompleteFunctionT complete_handler; return doSendMessage(_recipient_url, msgptr, complete_handler, &_rrecipient_id, &_rmsg_id, _flags); } -// send message using connection uid ------------------------------------- -#if 0 -template -ErrorConditionT Service::sendMessage( - RecipientId const& _rrecipient_id, - MessagePointerT const& _rmsgptr, - const MessageFlagsT& _flags) -{ - auto msgptr(solid::static_pointer_cast(_rmsgptr)); - MessageCompleteFunctionT complete_handler; - return doSendMessage(nullptr, _rrecipient_id, msgptr, complete_handler, nullptr, nullptr, _flags); -} -#endif //------------------------------------------------------------------------- template ErrorConditionT Service::sendMessage( @@ -825,46 +756,6 @@ ErrorConditionT Service::sendRequest( return doSendMessage(_recipient_url, msgptr, complete_handler, &_rrecipient_id, &_rmsguid, _flags | MessageFlagsE::AwaitResponse); } //------------------------------------------------------------------------- -#if 0 -// send request using connection uid -------------------------------------- -template -ErrorConditionT Service::sendRequest( - RecipientId const& _rrecipient_id, - MessagePointerT const& _rmsgptr, - Fnc _complete_fnc, - const MessageFlagsT& _flags) -{ - using CompleteHandlerT = CompleteHandler::send_type, - typename message_complete_traits::recv_type>; - - auto msgptr(solid::static_pointer_cast(_rmsgptr)); - CompleteHandlerT fnc(std::forward(_complete_fnc)); - MessageCompleteFunctionT complete_handler(std::move(fnc)); - - return doSendMessage(nullptr, _rrecipient_id, msgptr, complete_handler, nullptr, nullptr, _flags | MessageFlagsE::AwaitResponse); -} -//------------------------------------------------------------------------- -template -ErrorConditionT Service::sendRequest( - RecipientId const& _rrecipient_id, - MessagePointerT const& _rmsgptr, - Fnc _complete_fnc, - MessageId& _rmsguid, - const MessageFlagsT& _flags) -{ - using CompleteHandlerT = CompleteHandler::send_type, - typename message_complete_traits::recv_type>; - - auto msgptr(solid::static_pointer_cast(_rmsgptr)); - CompleteHandlerT fnc(std::forward(_complete_fnc)); - MessageCompleteFunctionT complete_handler(std::move(fnc)); - - return doSendMessage(nullptr, _rrecipient_id, msgptr, complete_handler, nullptr, &_rmsguid, _flags | MessageFlagsE::AwaitResponse); -} -#endif -//------------------------------------------------------------------------- // send response using recipient id --------------------------------------- template @@ -951,46 +842,6 @@ ErrorConditionT Service::sendMessage( return doSendMessage(_recipient_url, msgptr, complete_handler, &_rrecipient_id, &_rmsguid, _flags); } //------------------------------------------------------------------------- -// send message with complete using connection uid ------------------------ -#if 0 -template -ErrorConditionT Service::sendMessage( - RecipientId const& _rrecipient_id, - MessagePointerT const& _rmsgptr, - Fnc _complete_fnc, - const MessageFlagsT& _flags) -{ - using CompleteHandlerT = CompleteHandler::send_type, - typename message_complete_traits::recv_type>; - - auto msgptr(solid::static_pointer_cast(_rmsgptr)); - CompleteHandlerT fnc(std::forward(_complete_fnc)); - MessageCompleteFunctionT complete_handler(std::move(fnc)); - - return doSendMessage(nullptr, _rrecipient_id, msgptr, complete_handler, nullptr, nullptr, _flags); -} -//------------------------------------------------------------------------- -template -ErrorConditionT Service::sendMessage( - RecipientId const& _rrecipient_id, - MessagePointerT const& _rmsgptr, - Fnc _complete_fnc, - MessageId& _rmsguid, - const MessageFlagsT& _flags) -{ - using CompleteHandlerT = CompleteHandler::send_type, - typename message_complete_traits::recv_type>; - - auto msgptr(solid::static_pointer_cast(_rmsgptr)); - CompleteHandlerT fnc(std::forward(_complete_fnc)); - MessageCompleteFunctionT complete_handler(std::move(fnc)); - - return doSendMessage(nullptr, _rrecipient_id, msgptr, complete_handler, nullptr, &_rmsguid, _flags); -} -#endif -//------------------------------------------------------------------------- // send message using ConnectionContext ---------------------------------- template @@ -1003,36 +854,6 @@ ErrorConditionT Service::sendResponse( MessageCompleteFunctionT complete_handler; return doSendMessage(_rctx, msgptr, complete_handler, nullptr, nullptr, _flags | MessageFlagsE::Response); } -#if 0 -template -ErrorConditionT Service::sendMessage( - ConnectionContext& _rctx, - MessagePointerT const& _rmsgptr, - const MessageFlagsT& _flags) -{ - auto msgptr(solid::static_pointer_cast(_rmsgptr)); - MessageCompleteFunctionT complete_handler; - return doSendMessage(_rctx, msgptr, complete_handler, nullptr, nullptr, _flags); -} - -template -ErrorConditionT Service::sendMessage( - ConnectionContext& _rctx, - MessagePointerT const& _rmsgptr, - Fnc _complete_fnc, - const MessageFlagsT& _flags) -{ - using CompleteHandlerT = CompleteHandler::send_type, - typename message_complete_traits::recv_type>; - - auto msgptr(solid::static_pointer_cast(_rmsgptr)); - CompleteHandlerT fnc(std::forward(_complete_fnc)); - MessageCompleteFunctionT complete_handler(std::move(fnc)); - - return doSendMessage(_rctx, msgptr, complete_handler, nullptr, nullptr, _flags); -} -#endif //------------------------------------------------------------------------- template diff --git a/solid/frame/mprpc/src/mprpcconfiguration.cpp b/solid/frame/mprpc/src/mprpcconfiguration.cpp index bff022ef..a3a30d38 100644 --- a/solid/frame/mprpc/src/mprpcconfiguration.cpp +++ b/solid/frame/mprpc/src/mprpcconfiguration.cpp @@ -39,36 +39,6 @@ std::ostream& operator<<(std::ostream& _ros, const RelayDataFlagsT& _flags) return _ros; } -#if false -/*virtual*/ BufferBase::~BufferBase() -{ -} - -RecvBufferPointerT make_recv_buffer(const size_t _cp) -{ - switch (_cp) { - case 512: - return std::dynamic_pointer_cast(std::make_shared>()); - case 1 * 1024: - return std::dynamic_pointer_cast(std::make_shared>()); - case 2 * 1024: - return std::dynamic_pointer_cast(std::make_shared>()); - case 4 * 1024: - return std::dynamic_pointer_cast(std::make_shared>()); - case 8 * 1024: - return std::dynamic_pointer_cast(std::make_shared>()); - case 16 * 1024: - return std::dynamic_pointer_cast(std::make_shared>()); - case 32 * 1024: - return std::dynamic_pointer_cast(std::make_shared>()); - case 64 * 1024: - return std::dynamic_pointer_cast(std::make_shared>()); - default: - return std::make_shared>(_cp); - } -} -#endif - namespace { SharedBuffer default_allocate_recv_buffer(const uint32_t _cp) { diff --git a/solid/frame/mprpc/src/mprpcconnection.hpp b/solid/frame/mprpc/src/mprpcconnection.hpp index adacab0c..27bbb59a 100644 --- a/solid/frame/mprpc/src/mprpcconnection.hpp +++ b/solid/frame/mprpc/src/mprpcconnection.hpp @@ -380,7 +380,7 @@ class Connection : public frame::aio::Actor { bool poll_pool_more_ = true; bool send_posted_ = false; Any<> any_data_; - char socket_emplace_buf_[static_cast(ConnectionValues::SocketEmplacementSize)]; + char socket_emplace_buf_[static_cast(ConnectionValues::SocketEmplacementSize)] = {}; SocketStubPtrT sock_ptr_; NanoTime timeout_recv_ = NanoTime::max(); // client and server NanoTime timeout_send_soft_ = NanoTime::max(); // client and server diff --git a/solid/frame/mprpc/test/test_clientserver_idempotent.cpp b/solid/frame/mprpc/test/test_clientserver_idempotent.cpp index 1f17c871..b8762b17 100644 --- a/solid/frame/mprpc/test/test_clientserver_idempotent.cpp +++ b/solid/frame/mprpc/test/test_clientserver_idempotent.cpp @@ -389,7 +389,7 @@ int test_clientserver_idempotent(int argc, char* argv[]) cfg.pool_max_active_connection_count = max_per_pool_connection_count; - cfg.client.name_resolve_fnc = frame::mprpc::InternetResolverF(resolver, server_port.c_str(), {"localhost"}, SocketInfo::Inet4); + cfg.client.name_resolve_fnc = frame::mprpc::InternetResolverF(resolver, server_port.c_str(), "localhost", SocketInfo::Inet4); if (secure) { solid_dbg(generic_logger, Info, "Configure SSL client ------------------------------------"); diff --git a/solid/reflection/v1/dispatch.hpp b/solid/reflection/v1/dispatch.hpp index adde8129..162d9311 100644 --- a/solid/reflection/v1/dispatch.hpp +++ b/solid/reflection/v1/dispatch.hpp @@ -43,20 +43,19 @@ constexpr TypeGroupE type_group() { if constexpr (solid::is_shared_ptr_v) { return TypeGroupE::SharedPtr; - } - if constexpr (solid::is_intrusive_ptr_v) { + } else if constexpr (solid::is_intrusive_ptr_v) { return TypeGroupE::IntrusivePtr; - } - if constexpr (solid::is_unique_ptr_v) { + } else if constexpr (solid::is_unique_ptr_v) { return TypeGroupE::UniquePtr; } else { static_assert(!std::is_pointer_v, "Naked pointer are not supported - use std::shared_ptr or std::unique_ptr"); } + if constexpr (std::is_same_v) return TypeGroupE::Basic; - if constexpr (std::is_enum_v) + else if constexpr (std::is_enum_v) return TypeGroupE::Enum; - if constexpr (is_bitset_v) + else if constexpr (is_bitset_v) return TypeGroupE::Bitset; else if constexpr (is_std_vector_bool_v) return TypeGroupE::VectorBool; diff --git a/solid/serialization/v3/binarybase.hpp b/solid/serialization/v3/binarybase.hpp index 7638bbb4..11a7c030 100644 --- a/solid/serialization/v3/binarybase.hpp +++ b/solid/serialization/v3/binarybase.hpp @@ -22,7 +22,7 @@ namespace solid { namespace serialization { -namespace v3 { +inline namespace v3 { extern const LoggerT logger; diff --git a/solid/serialization/v3/binarybasic.hpp b/solid/serialization/v3/binarybasic.hpp index 369efa59..6f365f75 100644 --- a/solid/serialization/v3/binarybasic.hpp +++ b/solid/serialization/v3/binarybasic.hpp @@ -18,7 +18,7 @@ namespace solid { namespace serialization { -namespace v3 { +inline namespace v3 { namespace binary { namespace impl { diff --git a/solid/serialization/v3/binarydeserializer.hpp b/solid/serialization/v3/binarydeserializer.hpp index 01e7e4ab..7edd9b8d 100644 --- a/solid/serialization/v3/binarydeserializer.hpp +++ b/solid/serialization/v3/binarydeserializer.hpp @@ -23,7 +23,7 @@ namespace solid { namespace serialization { -namespace v3 { +inline namespace v3 { namespace binary { class DeserializerBase : public Base { @@ -1088,15 +1088,13 @@ class Deserializer : public DeserializerBase { } } - template + template >*/> +#if 1 + requires( + !std::is_base_of_v && !std::is_floating_point_v || (std::is_pointer_v && (is_shared_ptr_v || is_unique_ptr_v || is_intrusive_ptr_v))) +#endif void addDispatch(const Meta& _meta, T& _rt, ContextT& _rctx, const size_t _id, const char* const _name) { - static_assert(!std::is_base_of_v, "Cannot use std::istream with Deserializer"); - if constexpr (!is_shared_ptr_v && !is_unique_ptr_v && !is_intrusive_ptr_v) { - static_assert(!std::is_pointer_v, "Naked pointer are not supported - use std::shared_ptr or std::unique_ptr"); - } - static_assert(!std::is_floating_point_v, "Floating point values not supported"); - if constexpr (std::is_base_of_v) { addStream(const_cast(_rt), _meta.max_size_, _meta.progress_function_, _rctx, _id, _name); } else if constexpr (std::is_integral_v) { diff --git a/solid/serialization/v3/binaryserializer.hpp b/solid/serialization/v3/binaryserializer.hpp index 82a4e20b..0148a362 100644 --- a/solid/serialization/v3/binaryserializer.hpp +++ b/solid/serialization/v3/binaryserializer.hpp @@ -29,7 +29,7 @@ namespace solid { namespace serialization { -namespace v3 { +inline namespace v3 { namespace binary { class SerializerBase : public Base { @@ -912,14 +912,10 @@ class Serializer : public SerializerBase { } template + requires( + !std::is_base_of_v && !std::is_floating_point_v || (std::is_pointer_v && (is_shared_ptr_v || is_unique_ptr_v || is_intrusive_ptr_v))) void addDispatch(const Meta& _meta, const T& _rt, ContextT& _rctx, const size_t _id, const char* const _name) { - static_assert(!std::is_base_of_v, "Cannot use std::ostream with Serializer"); - if constexpr (!is_shared_ptr_v && !is_unique_ptr_v && !is_intrusive_ptr_v) { - static_assert(!std::is_pointer_v, "Naked pointer are not supported - use std::shared_ptr or std::unique_ptr"); - } - - static_assert(!std::is_floating_point_v, "Floating point values not supported"); if constexpr (std::is_base_of_v) { solid_assert(_meta.progress_function_); diff --git a/solid/serialization/v3/error.hpp b/solid/serialization/v3/error.hpp index ded37f60..d5ab8d61 100644 --- a/solid/serialization/v3/error.hpp +++ b/solid/serialization/v3/error.hpp @@ -14,7 +14,7 @@ namespace solid { namespace serialization { -namespace v3 { +inline namespace v3 { extern const ErrorConditionT error_limit_container; extern const ErrorConditionT error_limit_string; diff --git a/solid/serialization/v3/serialization.hpp b/solid/serialization/v3/serialization.hpp index fcc170f2..deafc189 100644 --- a/solid/serialization/v3/serialization.hpp +++ b/solid/serialization/v3/serialization.hpp @@ -14,10 +14,3 @@ #include "solid/serialization/v3/binarybasic.hpp" #include "solid/serialization/v3/binarydeserializer.hpp" #include "solid/serialization/v3/binaryserializer.hpp" -namespace solid { -namespace serialization { - -using namespace v3; - -} // namespace serialization -} // namespace solid diff --git a/solid/serialization/v3/src/binarybasic.cpp b/solid/serialization/v3/src/binarybasic.cpp index f079854d..a23ee9a3 100644 --- a/solid/serialization/v3/src/binarybasic.cpp +++ b/solid/serialization/v3/src/binarybasic.cpp @@ -14,7 +14,7 @@ namespace solid { namespace serialization { -namespace v3 { +inline namespace v3 { const LoggerT logger{"solid::serialization"}; diff --git a/solid/serialization/v3/src/binarydeserializer.cpp b/solid/serialization/v3/src/binarydeserializer.cpp index c7ac30d8..91269a2c 100644 --- a/solid/serialization/v3/src/binarydeserializer.cpp +++ b/solid/serialization/v3/src/binarydeserializer.cpp @@ -14,7 +14,7 @@ namespace solid { namespace serialization { -namespace v3 { +inline namespace v3 { namespace binary { //== Deserializer ============================================================== diff --git a/solid/serialization/v3/src/binaryserializer.cpp b/solid/serialization/v3/src/binaryserializer.cpp index b0075005..02697e2a 100644 --- a/solid/serialization/v3/src/binaryserializer.cpp +++ b/solid/serialization/v3/src/binaryserializer.cpp @@ -14,7 +14,7 @@ namespace solid { namespace serialization { -namespace v3 { +inline namespace v3 { namespace binary { //== Serializer ============================================================== diff --git a/solid/serialization/v3/src/error.cpp b/solid/serialization/v3/src/error.cpp index 8196729f..0850c6eb 100644 --- a/solid/serialization/v3/src/error.cpp +++ b/solid/serialization/v3/src/error.cpp @@ -14,7 +14,7 @@ namespace solid { namespace serialization { -namespace v3 { +inline namespace v3 { namespace { diff --git a/solid/serialization/v3/test/CMakeLists.txt b/solid/serialization/v3/test/CMakeLists.txt index e41b4116..92eab2da 100644 --- a/solid/serialization/v3/test/CMakeLists.txt +++ b/solid/serialization/v3/test/CMakeLists.txt @@ -9,6 +9,7 @@ set( SerializationV3TestSuite create_test_sourcelist( SerializationV3Tests test_serialization.cpp ${SerializationV3TestSuite}) add_executable(test_serialization_v3 ${SerializationV3Tests}) +#target_compile_options(test_serialization_v3 PUBLIC "SHELL: -fconcepts-diagnostics-depth=2") target_link_libraries(test_serialization_v3 solid_serialization_v3 diff --git a/solid/serialization/v3/test/test_binary.cpp b/solid/serialization/v3/test/test_binary.cpp index 32d901d0..d8221b66 100644 --- a/solid/serialization/v3/test/test_binary.cpp +++ b/solid/serialization/v3/test/test_binary.cpp @@ -76,6 +76,8 @@ class Test { char blob64[sizeof(uint64_t)]; std::ostringstream oss; + std::istringstream iss; + string* pstr = nullptr; void populate(bool _b) { @@ -208,8 +210,12 @@ class Test { #endif return b == _rt.b && a == _rt.a && v == _rt.v && d == _rt.d && s1 == s2 && m == _rt.m && s == _rt.s && um == _rt.um && us == _rt.us && vb == _rt.vb && bs == _rt.bs && vc == _rt.vc; } - +#ifndef __cpp_explicit_this_parameter SOLID_REFLECT_V1(_rs, _rthis, _rctx) +#else + template + void solidReflectV1(this Self& _rthis, Reflector& _rs, Context& _rctx) +#endif { _rs.add(_rthis.p, _rctx, 1, "p", [](auto& _rmeta) { _rmeta.maxSize(100); }); _rs @@ -271,6 +277,8 @@ class Test { _rctx); _rs.add(_rthis.a3, _rctx, 16, "a3"); + //_rs.add(_rthis.iss, _rctx, 17, "iss"); + //_rs.add(_rthis.pstr, _rctx, 18, "pstr"); //_rs.add(blob, blob_sz, BlobCapacity, _rctx, "blob"); //_rs.add(blob32, blob32_sz, sizeof(uint32_t), _rctx, "blob32"); diff --git a/solid/system/common.hpp b/solid/system/common.hpp index c8b849db..4601d612 100644 --- a/solid/system/common.hpp +++ b/solid/system/common.hpp @@ -27,13 +27,13 @@ using longlong = long long; using ulonglong = unsigned long long; #ifdef __cpp_lib_hardware_interference_size -#ifndef _MSC_VER +#if !defined(_MSC_VER) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Winterference-size" #endif constexpr std::size_t hardware_constructive_interference_size = std::hardware_constructive_interference_size; constexpr std::size_t hardware_destructive_interference_size = std::hardware_destructive_interference_size; -#ifndef _MSC_VER +#if !defined(_MSC_VER) && !defined(__clang__) #pragma GCC diagnostic pop #endif #else diff --git a/solid/system/spinlock.hpp b/solid/system/spinlock.hpp index 703aa7b7..37fc0f9c 100644 --- a/solid/system/spinlock.hpp +++ b/solid/system/spinlock.hpp @@ -11,6 +11,10 @@ #include "solid/system/common.hpp" +#if (defined(__i386__) || defined(__x86_64__)) && defined(__clang__) +#include +#endif + #ifdef SOLID_USE_PTHREAD_SPINLOCK #include @@ -26,10 +30,6 @@ #define _WINSOCKAPI_ #include -#elif defined(__i386__) || defined(__x86_64__) -#if defined(__clang__) -#include -#endif #endif #endif diff --git a/solid/utility/atomic_wait b/solid/utility/atomic_wait index 117a807b..fc9fdd75 100644 --- a/solid/utility/atomic_wait +++ b/solid/utility/atomic_wait @@ -44,326 +44,348 @@ The strategy is chosen this way, by platform: */ -//#define __NO_TABLE -//#define __NO_FUTEX -//#define __NO_CONDVAR -//#define __NO_SLEEP -//#define __NO_IDENT +// #define __NO_TABLE +// #define __NO_FUTEX +// #define __NO_CONDVAR +// #define __NO_SLEEP +// #define __NO_IDENT // To benchmark against spinning -//#define __NO_SPIN -//#define __NO_WAIT +// #define __NO_SPIN +// #define __NO_WAIT #ifndef __ATOMIC_WAIT_INCLUDED #define __ATOMIC_WAIT_INCLUDED -#include -#include #include +#include +#include #include #if defined(__NO_IDENT) - #include - #include +#include +#include - #define __ABI - #define __YIELD() std::this_thread::yield() - #define __SLEEP(x) std::this_thread::sleep_for(std::chrono::microseconds(x)) - #define __YIELD_PROCESSOR() +#define __ABI +#define __YIELD() std::this_thread::yield() +#define __SLEEP(x) std::this_thread::sleep_for(std::chrono::microseconds(x)) +#define __YIELD_PROCESSOR() #else #if defined(__CUSTD__) - #define __NO_FUTEX - #define __NO_CONDVAR - #ifndef __CUDACC__ - #define __host__ - #define __device__ - #endif - #define __ABI __host__ __device__ +#define __NO_FUTEX +#define __NO_CONDVAR +#ifndef __CUDACC__ +#define __host__ +#define __device__ +#endif +#define __ABI __host__ __device__ #else - #define __ABI +#define __ABI #endif -#if defined(__APPLE__) || defined(__linux__) +#if defined(__APPLE__) || defined(__linux__) || defined(__FreeBSD__) - #include - #include - #define __YIELD() sched_yield() - #define __SLEEP(x) usleep(x) +#include +#include +#define __YIELD() sched_yield() +#define __SLEEP(x) usleep(x) - #if defined(__aarch64__) - # define __YIELD_PROCESSOR() asm volatile ("yield" ::: "memory") - #elif defined(__x86_64__) - # define __YIELD_PROCESSOR() asm volatile ("pause" ::: "memory") - #elif defined (__powerpc__) - # define __YIELD_PROCESSOR() asm volatile ("or 27,27,27" ::: "memory") - #endif +#if defined(__aarch64__) +#define __YIELD_PROCESSOR() asm volatile("yield" ::: "memory") +#elif defined(__x86_64__) +#define __YIELD_PROCESSOR() asm volatile("pause" ::: "memory") +#elif defined(__powerpc__) +#define __YIELD_PROCESSOR() asm volatile("or 27,27,27" ::: "memory") +#endif #endif #if defined(__linux__) && !defined(__NO_FUTEX) - #if !defined(__NO_TABLE) - #define __TABLE - #endif - - #include - #include - #include - #include - - #define __FUTEX - #define __FUTEX_TIMED - #define __type_used_directly(_T) (std::is_same::type>::type, __futex_preferred_t>::value) - using __futex_preferred_t = std::int32_t; - template ::type = 1> - void __do_direct_wait(_Tp const* ptr, _Tp val, void const* timeout) { - syscall(SYS_futex, ptr, FUTEX_WAIT_PRIVATE, val, timeout, 0, 0); - } - template ::type = 1> - void __do_direct_wake(_Tp const* ptr, bool all) { - syscall(SYS_futex, ptr, FUTEX_WAKE_PRIVATE, all ? INT_MAX : 1, 0, 0, 0); - } +#if !defined(__NO_TABLE) +#define __TABLE +#endif + +#include +#include +#include +#include + +#define __FUTEX +#define __FUTEX_TIMED +#define __type_used_directly(_T) (std::is_same::type>::type, \ + __futex_preferred_t>::value) +using __futex_preferred_t = std::int32_t; +template ::type = 1> +void __do_direct_wait(_Tp const* ptr, _Tp val, void const* timeout) +{ + syscall(SYS_futex, ptr, FUTEX_WAIT_PRIVATE, val, timeout, 0, 0); +} +template ::type = 1> +void __do_direct_wake(_Tp const* ptr, bool all) +{ + syscall(SYS_futex, ptr, FUTEX_WAKE_PRIVATE, all ? INT_MAX : 1, 0, 0, 0); +} #elif defined(_WIN32) && !defined(__CUSTD__) - #define __NO_CONDVAR - #define __NO_TABLE - - #ifndef NOMINMAX - #define NOMINMAX - #endif - - #define _WINSOCKAPI_ - #include - #define __YIELD() Sleep(0) - #define __SLEEP(x) Sleep(x) - #define __YIELD_PROCESSOR() YieldProcessor() - - #include - template - auto __atomic_load_n(_Tp const* a, int) -> typename std::remove_reference::type { - auto const t = *a; - _ReadWriteBarrier(); - return t; - } - #define __builtin_expect(e, v) (e) - - #if defined(_WIN32_WINNT) && (_WIN32_WINNT >= _WIN32_WINNT_WIN8) && !defined(__NO_FUTEX) - - #define __FUTEX - #define __type_used_directly(_T) (sizeof(_T) <= 8) - using __futex_preferred_t = std::int64_t; - template ::type = 1> - void __do_direct_wait(_Tp const* ptr, _Tp val, void const*) { - WaitOnAddress((PVOID)ptr, (PVOID)&val, sizeof(_Tp), INFINITE); - } - template ::type = 1> - void __do_direct_wake(_Tp const* ptr, bool all) { - if (all) - WakeByAddressAll((PVOID)ptr); - else - WakeByAddressSingle((PVOID)ptr); - } - - #endif +#define __NO_CONDVAR +#define __NO_TABLE + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#define _WINSOCKAPI_ +#include +#define __YIELD() Sleep(0) +#define __SLEEP(x) Sleep(x) +#define __YIELD_PROCESSOR() YieldProcessor() + +#include +template +auto __atomic_load_n(_Tp const* a, int) -> typename std::remove_reference::type +{ + auto const t = *a; + _ReadWriteBarrier(); + return t; +} +#define __builtin_expect(e, v) (e) + +#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= _WIN32_WINNT_WIN8) && !defined(__NO_FUTEX) + +#define __FUTEX +#define __type_used_directly(_T) (sizeof(_T) <= 8) +using __futex_preferred_t = std::int64_t; +template ::type = 1> +void __do_direct_wait(_Tp const* ptr, _Tp val, void const*) +{ + WaitOnAddress((PVOID)ptr, (PVOID)&val, sizeof(_Tp), INFINITE); +} +template ::type = 1> +void __do_direct_wake(_Tp const* ptr, bool all) +{ + if (all) + WakeByAddressAll((PVOID)ptr); + else + WakeByAddressSingle((PVOID)ptr); +} + +#endif #endif // _WIN32 #if !defined(__FUTEX) && !defined(__NO_CONDVAR) - #if defined(__NO_TABLE) - #warning "Condvars always generate a table (ignoring __NO_TABLE)." - #endif - #include - #define __CONDVAR - #define __TABLE +#if defined(__NO_TABLE) +#warning "Condvars always generate a table (ignoring __NO_TABLE)." +#endif +#include +#define __CONDVAR +#define __TABLE #endif #endif // __NO_IDENT #ifdef __TABLE - struct alignas(64) contended_t { - #if defined(__FUTEX) - int waiters = 0; - __futex_preferred_t version = 0; - #elif defined(__CONDVAR) - int credit = 0; - pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - pthread_cond_t condvar = PTHREAD_COND_INITIALIZER; - #else - #error "" - #endif - }; - contended_t * __contention(volatile void const * p); +struct alignas(64) contended_t { +#if defined(__FUTEX) + int waiters = 0; + __futex_preferred_t version = 0; +#elif defined(__CONDVAR) + int credit = 0; + pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + pthread_cond_t condvar = PTHREAD_COND_INITIALIZER; #else - template - __ABI void __cxx_atomic_try_wait_slow_fallback(_Tp const* ptr, _Tp val, int order) { - #ifndef __NO_SLEEP - long history = 10; - do { - __SLEEP(history >> 2); - history += history >> 2; - if (history > (1 << 10)) - history = 1 << 10; - } while (__atomic_load_n(ptr, order) == val); - #else - __YIELD(); - #endif - } +#error "" +#endif +}; +contended_t* __contention(volatile void const* p); +#else +template +__ABI void __cxx_atomic_try_wait_slow_fallback(_Tp const* ptr, _Tp val, int order) +{ +#ifndef __NO_SLEEP + long history = 10; + do { + __SLEEP(history >> 2); + history += history >> 2; + if (history > (1 << 10)) + history = 1 << 10; + } while (__atomic_load_n(ptr, order) == val); +#else + __YIELD(); +#endif +} #endif // __TABLE #if defined(__CONDVAR) - template - void __cxx_atomic_notify_all(volatile _Tp const* ptr) { - auto * const c = __contention(ptr); - __atomic_thread_fence(__ATOMIC_SEQ_CST); - if(__builtin_expect(0 == __atomic_load_n(&c->credit, __ATOMIC_RELAXED), 1)) - return; - if(0 != __atomic_exchange_n(&c->credit, 0, __ATOMIC_RELAXED)) { - pthread_mutex_lock(&c->mutex); - pthread_mutex_unlock(&c->mutex); - pthread_cond_broadcast(&c->condvar); - } - } - template - void __cxx_atomic_notify_one(volatile _Tp const* ptr) { - __cxx_atomic_notify_all(ptr); - } - template - void __cxx_atomic_try_wait_slow(volatile _Tp const* ptr, _Tp const val, int order) { - auto * const c = __contention(ptr); +template +void __cxx_atomic_notify_all(volatile _Tp const* ptr) +{ + auto* const c = __contention(ptr); + __atomic_thread_fence(__ATOMIC_SEQ_CST); + if (__builtin_expect(0 == __atomic_load_n(&c->credit, __ATOMIC_RELAXED), 1)) + return; + if (0 != __atomic_exchange_n(&c->credit, 0, __ATOMIC_RELAXED)) { pthread_mutex_lock(&c->mutex); - __atomic_store_n(&c->credit, 1, __ATOMIC_RELAXED); - __atomic_thread_fence(__ATOMIC_SEQ_CST); - if (val == __atomic_load_n(ptr, order)) - pthread_cond_wait(&c->condvar, &c->mutex); pthread_mutex_unlock(&c->mutex); + pthread_cond_broadcast(&c->condvar); } +} +template +void __cxx_atomic_notify_one(volatile _Tp const* ptr) +{ + __cxx_atomic_notify_all(ptr); +} +template +void __cxx_atomic_try_wait_slow(volatile _Tp const* ptr, _Tp const val, int order) +{ + auto* const c = __contention(ptr); + pthread_mutex_lock(&c->mutex); + __atomic_store_n(&c->credit, 1, __ATOMIC_RELAXED); + __atomic_thread_fence(__ATOMIC_SEQ_CST); + if (val == __atomic_load_n(ptr, order)) + pthread_cond_wait(&c->condvar, &c->mutex); + pthread_mutex_unlock(&c->mutex); +} #elif defined(__FUTEX) - template ::type = 1> - void __cxx_atomic_notify_all(_Tp const* ptr) { - #if defined(__TABLE) - auto * const c = __contention(ptr); - __atomic_fetch_add(&c->version, 1, __ATOMIC_RELAXED); - __atomic_thread_fence(__ATOMIC_SEQ_CST); - if (0 != __atomic_exchange_n(&c->waiters, 0, __ATOMIC_RELAXED)) - __do_direct_wake(&c->version, true); - #endif - } - template ::type = 1> - void __cxx_atomic_notify_one(_Tp const* ptr) { - __cxx_atomic_notify_all(ptr); - } - template ::type = 1> - void __cxx_atomic_try_wait_slow(_Tp const* ptr, _Tp const val, int order) { - #if defined(__TABLE) - auto * const c = __contention(ptr); - __atomic_store_n(&c->waiters, 1, __ATOMIC_RELAXED); - __atomic_thread_fence(__ATOMIC_SEQ_CST); - auto const version = __atomic_load_n(&c->version, __ATOMIC_RELAXED); - if (__builtin_expect(val != __atomic_load_n(ptr, order), 1)) - return; - #ifdef __FUTEX_TIMED - constexpr timespec timeout = { 2, 0 }; // Hedge on rare 'int version' aliasing. - __do_direct_wait(&c->version, version, &timeout); - #else - __do_direct_wait(&c->version, version, nullptr); - #endif - #else - __cxx_atomic_try_wait_slow_fallback(ptr, val, order); - #endif // __TABLE - } - - template ::type = 1> - void __cxx_atomic_try_wait_slow(_Tp const* ptr, _Tp val, int order) { - #ifdef __TABLE - auto * const c = __contention(ptr); - __atomic_fetch_add(&c->waiters, 1, __ATOMIC_RELAXED); - __atomic_thread_fence(__ATOMIC_SEQ_CST); - #endif - __do_direct_wait(ptr, val, nullptr); - #ifdef __TABLE - __atomic_fetch_sub(&c->waiters, 1, __ATOMIC_RELAXED); - #endif - } - template ::type = 1> - void __cxx_atomic_notify_all(_Tp const* ptr) { - #ifdef __TABLE - auto * const c = __contention(ptr); - __atomic_thread_fence(__ATOMIC_SEQ_CST); - if (0 != __atomic_load_n(&c->waiters, __ATOMIC_RELAXED)) - #endif - __do_direct_wake(ptr, true); - } - template ::type = 1> - void __cxx_atomic_notify_one(_Tp const* ptr) { - #ifdef __TABLE - auto * const c = __contention(ptr); - __atomic_thread_fence(__ATOMIC_SEQ_CST); - if (0 != __atomic_load_n(&c->waiters, __ATOMIC_RELAXED)) - #endif - __do_direct_wake(ptr, false); - } +template ::type = 1> +void __cxx_atomic_notify_all(_Tp const* ptr) +{ +#if defined(__TABLE) + auto* const c = __contention(ptr); + __atomic_fetch_add(&c->version, 1, __ATOMIC_RELAXED); + __atomic_thread_fence(__ATOMIC_SEQ_CST); + if (0 != __atomic_exchange_n(&c->waiters, 0, __ATOMIC_RELAXED)) + __do_direct_wake(&c->version, true); +#endif +} +template ::type = 1> +void __cxx_atomic_notify_one(_Tp const* ptr) +{ + __cxx_atomic_notify_all(ptr); +} +template ::type = 1> +void __cxx_atomic_try_wait_slow(_Tp const* ptr, _Tp const val, int order) +{ +#if defined(__TABLE) + auto* const c = __contention(ptr); + __atomic_store_n(&c->waiters, 1, __ATOMIC_RELAXED); + __atomic_thread_fence(__ATOMIC_SEQ_CST); + auto const version = __atomic_load_n(&c->version, __ATOMIC_RELAXED); + if (__builtin_expect(val != __atomic_load_n(ptr, order), 1)) + return; +#ifdef __FUTEX_TIMED + constexpr timespec timeout = {2, 0}; // Hedge on rare 'int version' aliasing. + __do_direct_wait(&c->version, version, &timeout); +#else + __do_direct_wait(&c->version, version, nullptr); +#endif +#else + __cxx_atomic_try_wait_slow_fallback(ptr, val, order); +#endif // __TABLE +} + +template ::type = 1> +void __cxx_atomic_try_wait_slow(_Tp const* ptr, _Tp val, int order) +{ +#ifdef __TABLE + auto* const c = __contention(ptr); + __atomic_fetch_add(&c->waiters, 1, __ATOMIC_RELAXED); + __atomic_thread_fence(__ATOMIC_SEQ_CST); +#endif + __do_direct_wait(ptr, val, nullptr); +#ifdef __TABLE + __atomic_fetch_sub(&c->waiters, 1, __ATOMIC_RELAXED); +#endif +} +template ::type = 1> +void __cxx_atomic_notify_all(_Tp const* ptr) +{ +#ifdef __TABLE + auto* const c = __contention(ptr); + __atomic_thread_fence(__ATOMIC_SEQ_CST); + if (0 != __atomic_load_n(&c->waiters, __ATOMIC_RELAXED)) +#endif + __do_direct_wake(ptr, true); +} +template ::type = 1> +void __cxx_atomic_notify_one(_Tp const* ptr) +{ +#ifdef __TABLE + auto* const c = __contention(ptr); + __atomic_thread_fence(__ATOMIC_SEQ_CST); + if (0 != __atomic_load_n(&c->waiters, __ATOMIC_RELAXED)) +#endif + __do_direct_wake(ptr, false); +} #else // __FUTEX || __CONDVAR - template - __ABI void __cxx_atomic_try_wait_slow(_Tp const* ptr, _Tp val, int order) { - __cxx_atomic_try_wait_slow_fallback(ptr, val, order); - } - template - __ABI void __cxx_atomic_notify_one(_Tp const* ptr) { } - template - __ABI void __cxx_atomic_notify_all(_Tp const* ptr) { } +template +__ABI void __cxx_atomic_try_wait_slow(_Tp const* ptr, _Tp val, int order) +{ + __cxx_atomic_try_wait_slow_fallback(ptr, val, order); +} +template +__ABI void __cxx_atomic_notify_one(_Tp const* ptr) {} +template +__ABI void __cxx_atomic_notify_all(_Tp const* ptr) {} #endif // __FUTEX || __CONDVAR template -__ABI void __cxx_atomic_wait(_Tp const* ptr, _Tp const val, int order) { +__ABI void __cxx_atomic_wait(_Tp const* ptr, _Tp const val, int order) +{ #ifndef __NO_SPIN - if(__builtin_expect(__atomic_load_n(ptr, order) != val,1)) + if (__builtin_expect(__atomic_load_n(ptr, order) != val, 1)) return; - for(int i = 0; i < 16; ++i) { - if(__atomic_load_n(ptr, order) != val) + for (int i = 0; i < 16; ++i) { + if (__atomic_load_n(ptr, order) != val) return; - if(i < 12) + if (i < 12) __YIELD_PROCESSOR(); else __YIELD(); } #endif - while(val == __atomic_load_n(ptr, order)) + while (val == __atomic_load_n(ptr, order)) #ifndef __NO_WAIT __cxx_atomic_try_wait_slow(ptr, val, order) #endif - ; + ; } #include namespace std { - template - __ABI void atomic_wait_explicit(atomic<_Tp> const* a, _Tv val, std::memory_order order) { - __cxx_atomic_wait((const _Tp*)a, (_Tp)val, (int)order); - } - template - __ABI void atomic_wait(atomic<_Tp> const* a, _Tv val) { - atomic_wait_explicit(a, val, std::memory_order_seq_cst); - } - template - __ABI void atomic_notify_one(atomic<_Tp> const* a) { - __cxx_atomic_notify_one((const _Tp*)a); - } - template - __ABI void atomic_notify_all(atomic<_Tp> const* a) { - __cxx_atomic_notify_all((const _Tp*)a); - } +template +__ABI void atomic_wait_explicit(atomic<_Tp> const* a, _Tv val, std::memory_order order) +{ + __cxx_atomic_wait((const _Tp*)a, (_Tp)val, (int)order); +} +template +__ABI void atomic_wait(atomic<_Tp> const* a, _Tv val) +{ + atomic_wait_explicit(a, val, std::memory_order_seq_cst); +} +template +__ABI void atomic_notify_one(atomic<_Tp> const* a) +{ + __cxx_atomic_notify_one((const _Tp*)a); +} +template +__ABI void atomic_notify_all(atomic<_Tp> const* a) +{ + __cxx_atomic_notify_all((const _Tp*)a); } +} // namespace std #endif //__ATOMIC_WAIT_INCLUDED \ No newline at end of file diff --git a/solid/utility/cacheable.hpp b/solid/utility/cacheable.hpp index aaeb892b..448857c7 100644 --- a/solid/utility/cacheable.hpp +++ b/solid/utility/cacheable.hpp @@ -175,7 +175,7 @@ class EnableCacheable : public What { void doCache() override { if constexpr (std::is_base_of_v) { - Cache::store(IntrusivePtr(static_cast(this))); + Cache::store(IntrusivePtr(static_cast(this), true)); } else { static_assert(std::is_base_of_v, "Type must be derived from SharedCacheable"); Cache::store(std::static_pointer_cast(this->shared_from_this())); diff --git a/solid/utility/collapse.hpp b/solid/utility/collapse.hpp index fe56c6db..6d3fbc0f 100644 --- a/solid/utility/collapse.hpp +++ b/solid/utility/collapse.hpp @@ -16,7 +16,7 @@ namespace solid { #ifdef __cpp_concepts template -concept Collapsable = std::is_same_v || is_intrusive_ptr_v; +concept Collapsable = std::is_same_v || is_const_intrusive_ptr_v; template #else @@ -24,10 +24,7 @@ template #endif inline auto collapse(Ptr& _rptr) { - if (_rptr.collapse()) { - return Ptr(std::move(_rptr)); - } - return Ptr(); + return _rptr.collapse(); } } // namespace solid \ No newline at end of file diff --git a/solid/utility/intrusiveptr.hpp b/solid/utility/intrusiveptr.hpp index 3d08df98..3204a8b9 100644 --- a/solid/utility/intrusiveptr.hpp +++ b/solid/utility/intrusiveptr.hpp @@ -14,124 +14,180 @@ namespace solid { -class IntrusiveThreadSafePolicy; +struct IntrusiveThreadSafePolicy; class IntrusiveThreadSafeBase { + friend struct IntrusiveThreadSafePolicy; mutable std::atomic_size_t use_count_{0}; -public: - size_t useCount() const noexcept +protected: + auto useCount() const { return use_count_.load(std::memory_order_relaxed); } +}; -private: - friend class IntrusiveThreadSafePolicy; - void acquire() const noexcept +struct IntrusiveThreadSafePolicy { + + static void acquire(const IntrusiveThreadSafeBase& _r) noexcept { - ++use_count_; + ++_r.use_count_; } - bool release() const noexcept + static bool release(const IntrusiveThreadSafeBase& _r) noexcept { - return use_count_.fetch_sub(1) == 1; + return _r.use_count_.fetch_sub(1) == 1; } -}; - -namespace impl { -template -auto make_policy_intrusive(const P& _policy, Args&&... _args); -} -class IntrusiveThreadSafePolicy { -protected: - void acquire(const IntrusiveThreadSafeBase& _rt) const noexcept + template + static void destroy(const T& _r) { - _rt.acquire(); + static_assert(!std::is_same_v, "cannot destroy object from non virtual base class IntrusiveThreadSafeBase"); + delete &_r; } - auto release(const IntrusiveThreadSafeBase& _rt) const noexcept + static auto use_count(const IntrusiveThreadSafeBase& _r) noexcept { - return _rt.release(); + return _r.use_count_.load(std::memory_order_relaxed); } - auto useCount(const IntrusiveThreadSafeBase& _rt) const noexcept + + template + static T* create(Args&&... _args) { - return _rt.useCount(); + return new T(std::forward(_args)...); } }; -template -class IntrusivePtr : public Policy { +template +struct intrusive_policy_dispatch; + +#ifdef __cpp_concepts +template +concept DefaultIntrusiveC = std::is_base_of_v; + +template +struct intrusive_policy_dispatch { + using type = IntrusiveThreadSafePolicy; +}; + +template +using intrusive_policy_dispatch_t = typename intrusive_policy_dispatch::type; +#endif + +namespace impl { +template > +bool intrusive_ptr_release(const T& _r) +{ + return PolicyT::release(_r); +} + +template > +void intrusive_ptr_destroy(const T& _r) +{ + return PolicyT::destroy(_r); +} + +template > +void intrusive_ptr_acquire(const T& _r) +{ + PolicyT::acquire(_r); +} + +template > +size_t intrusive_ptr_use_count(const T& _r) +{ + return PolicyT::use_count(_r); +} + +template > +T* intrusive_ptr_create(Args&&... _args) +{ + return PolicyT::template create(std::forward(_args)...); +} + +// + +template +class IntrusivePtrBase { + using ThisT = IntrusivePtrBase; + template + friend class IntrusivePtrBase; + +protected: T* ptr_ = nullptr; -public: - using element_type = T; - IntrusivePtr() = default; +protected: + IntrusivePtrBase() = default; - IntrusivePtr(const IntrusivePtr& _other) + IntrusivePtrBase(const IntrusivePtrBase& _other) : ptr_(_other.ptr_) { if (ptr_) { - Policy::acquire(*ptr_); + intrusive_ptr_acquire(*ptr_); } } - IntrusivePtr(IntrusivePtr&& _other) + IntrusivePtrBase(IntrusivePtrBase&& _other) : ptr_(_other.detach()) { } - template - IntrusivePtr(const IntrusivePtr& _other) - : ptr_(_other.get()) + template + IntrusivePtrBase(const IntrusivePtrBase& _other) + : ptr_(static_cast(_other.ptr_)) { if (ptr_) { - Policy::acquire(*ptr_); + intrusive_ptr_acquire(*ptr_); } } - template - IntrusivePtr(IntrusivePtr&& _other) - : ptr_(_other.detach()) + template + IntrusivePtrBase(IntrusivePtrBase&& _other) + : ptr_(static_cast(_other.detach())) { } - template - friend class IntrusivePtr; - - ~IntrusivePtr() + ~IntrusivePtrBase() { - if (ptr_ && Policy::release(*ptr_)) { - delete ptr_; + if (ptr_ && intrusive_ptr_release(*ptr_)) { + intrusive_ptr_destroy(*ptr_); } } - IntrusivePtr& operator=(const IntrusivePtr& _other) noexcept + void doCopy(const IntrusivePtrBase& _other) noexcept { - IntrusivePtr{_other}.swap(*this); - return *this; + IntrusivePtrBase{_other}.swap(*this); } - IntrusivePtr& operator=(IntrusivePtr&& _other) noexcept + void doMove(IntrusivePtrBase&& _other) noexcept { - IntrusivePtr{std::move(_other)}.swap(*this); - return *this; + IntrusivePtrBase{std::move(_other)}.swap(*this); } - template - IntrusivePtr& operator=(const IntrusivePtr& _other) noexcept + template + void doCopy(const IntrusivePtrBase& _other) noexcept { - IntrusivePtr{_other}.swap(*this); - return *this; + IntrusivePtrBase{_other}.swap(*this); } - template - IntrusivePtr& operator=(IntrusivePtr&& _other) noexcept + + template + void doMove(IntrusivePtrBase&& _other) noexcept { - IntrusivePtr{std::move(_other)}.swap(*this); - return *this; + IntrusivePtrBase{std::move(_other)}.swap(*this); } - T* get() const noexcept + IntrusivePtrBase(T* _ptr) + : ptr_(_ptr) { - return ptr_; + if (_ptr) { + intrusive_ptr_acquire(*_ptr); + } + } + + IntrusivePtrBase(T* _ptr, const bool _do_acquire) + : ptr_(_ptr) + { + if (_ptr && _do_acquire) { + intrusive_ptr_acquire(*_ptr); + } } T* detach() noexcept @@ -141,15 +197,15 @@ class IntrusivePtr : public Policy { return ret; } - T& operator*() const noexcept + void swap(IntrusivePtrBase& _other) noexcept { - return *ptr_; + T* tmp = ptr_; + ptr_ = _other.ptr_; + _other.ptr_ = tmp; } - T* operator->() const noexcept - { - return ptr_; - } +public: + using element_type = T; explicit operator bool() const noexcept { @@ -159,118 +215,457 @@ class IntrusivePtr : public Policy { void reset() { if (ptr_) { - if (Policy::release(*ptr_)) { - delete ptr_; + if (intrusive_ptr_release(*ptr_)) { + intrusive_ptr_destroy(*ptr_); } ptr_ = nullptr; } } - void swap(IntrusivePtr& _other) noexcept - { - T* tmp = ptr_; - ptr_ = _other.ptr_; - _other.ptr_ = tmp; - } - size_t useCount() const noexcept { if (ptr_ != nullptr) [[likely]] { - return Policy::useCount(*ptr_); + return intrusive_ptr_use_count(*ptr_); } return 0; } +}; + +} // namespace impl + +template +class IntrusivePtr : public impl::IntrusivePtrBase { + using BaseT = impl::IntrusivePtrBase; + using ThisT = IntrusivePtr; + template + friend class IntrusivePtr; + +public: + IntrusivePtr() = default; - bool collapse() + IntrusivePtr(T* _ptr, const bool _do_acquire) + : BaseT(_ptr, _do_acquire) { - if (ptr_) { - if (Policy::release(*ptr_)) { - Policy::acquire(*ptr_); - return true; - } - ptr_ = nullptr; - } - return false; + } + + IntrusivePtr(const IntrusivePtr& _other) + : BaseT(_other) + { + } + + IntrusivePtr(IntrusivePtr&& _other) + : BaseT(std::move(_other)) + { + } + + template + IntrusivePtr(const IntrusivePtr& _other) + : BaseT(_other) + { + } + + template + IntrusivePtr(IntrusivePtr&& _other) + : BaseT(std::move(_other)) + { + } + + ~IntrusivePtr() = default; + + IntrusivePtr& operator=(const IntrusivePtr& _other) noexcept + { + BaseT::doCopy(_other); + return *this; + } + + IntrusivePtr& operator=(IntrusivePtr&& _other) noexcept + { + BaseT::doMove(std::move(_other)); + return *this; + } + + template + IntrusivePtr& operator=(const IntrusivePtr& _other) noexcept + { + BaseT::doCopy(_other); + return *this; + } + + template + IntrusivePtr& operator=(IntrusivePtr&& _other) noexcept + { + BaseT::doMove(std::move(_other)); + return *this; + } + + T* get() const noexcept + { + return BaseT::ptr_; + } + + T& operator*() const noexcept + { + return *BaseT::ptr_; + } + + T* operator->() const noexcept + { + return BaseT::ptr_; } private: template - friend auto make_intrusive(Args&&... _args); - template - friend auto impl::make_policy_intrusive(const PP&, Args&&... _args); - template - friend IntrusivePtr static_pointer_cast(const IntrusivePtr& _rp) noexcept; - template - friend IntrusivePtr dynamic_pointer_cast(const IntrusivePtr& _rp) noexcept; - template - friend IntrusivePtr static_pointer_cast(IntrusivePtr&& _rp) noexcept; - template - friend IntrusivePtr dynamic_pointer_cast(IntrusivePtr&& _rp) noexcept; - template - friend class EnableCacheable; + friend IntrusivePtr make_intrusive(Args&&... _args); + template + friend IntrusivePtr static_pointer_cast(const IntrusivePtr& _rp) noexcept; + template + friend IntrusivePtr dynamic_pointer_cast(const IntrusivePtr& _rp) noexcept; + template + friend IntrusivePtr static_pointer_cast(IntrusivePtr&& _rp) noexcept; + template + friend IntrusivePtr dynamic_pointer_cast(IntrusivePtr&& _rp) noexcept; IntrusivePtr(T* _ptr) - : ptr_(_ptr) + : BaseT(_ptr) { - if (_ptr) { - Policy::acquire(*_ptr); - } } +}; - IntrusivePtr(const Policy& _policy, T* _ptr) - : Policy(_policy) - , ptr_(_ptr) +//----------------------------------------------------------------------------- +// MutableIntrusivePtr +//----------------------------------------------------------------------------- +template +class ConstIntrusivePtr; + +template +class MutableIntrusivePtr : public impl::IntrusivePtrBase { + using BaseT = impl::IntrusivePtrBase; + using ThisT = MutableIntrusivePtr; + template + friend class MutableIntrusivePtr; + template + friend class ConstIntrusivePtr; + + MutableIntrusivePtr(IntrusivePtr&& _other) + : BaseT(std::move(_other)) { - if (_ptr) { - Policy::acquire(*_ptr); + } + +public: + MutableIntrusivePtr() = default; + + MutableIntrusivePtr(T* _ptr, const bool _do_acquire) + : BaseT(_ptr, _do_acquire) + { + } + + MutableIntrusivePtr(const MutableIntrusivePtr& _other) = delete; + + template + MutableIntrusivePtr(const MutableIntrusivePtr& _other) = delete; + + MutableIntrusivePtr(MutableIntrusivePtr&& _other) + : BaseT(std::move(_other)) + { + } + template + MutableIntrusivePtr(MutableIntrusivePtr&& _other) + : BaseT(std::move(_other)) + { + } + + ~MutableIntrusivePtr() = default; + + MutableIntrusivePtr& operator=(const MutableIntrusivePtr& _other) noexcept = delete; + + template + MutableIntrusivePtr& operator=(const MutableIntrusivePtr& _other) noexcept = delete; + + MutableIntrusivePtr& operator=(MutableIntrusivePtr&& _other) noexcept + { + BaseT::doMove(std::move(_other)); + return *this; + } + template + MutableIntrusivePtr& operator=(MutableIntrusivePtr&& _other) noexcept + { + BaseT::doMove(std::move(_other)); + return *this; + } + + T* get() const noexcept + { + return BaseT::ptr_; + } + + T& operator*() const noexcept + { + return *BaseT::ptr_; + } + + T* operator->() const noexcept + { + return BaseT::ptr_; + } + +private: + template + friend MutableIntrusivePtr make_mutable_intrusive(Args&&... _args); + template + friend MutableIntrusivePtr static_pointer_cast(const MutableIntrusivePtr& _rp) noexcept; + template + friend MutableIntrusivePtr dynamic_pointer_cast(const MutableIntrusivePtr& _rp) noexcept; + template + friend MutableIntrusivePtr static_pointer_cast(MutableIntrusivePtr&& _rp) noexcept; + template + friend MutableIntrusivePtr dynamic_pointer_cast(MutableIntrusivePtr&& _rp) noexcept; + + MutableIntrusivePtr(T* _ptr) + : BaseT(_ptr) + { + } + + MutableIntrusivePtr(ConstIntrusivePtr&& _other) + : BaseT(std::move(_other)) + { + } + + template + MutableIntrusivePtr(ConstIntrusivePtr&& _other) + : BaseT(std::move(_other)) + { + } +}; + +//----------------------------------------------------------------------------- +// ConstIntrusivePtr +//----------------------------------------------------------------------------- + +template +class ConstIntrusivePtr : public impl::IntrusivePtrBase { + using BaseT = impl::IntrusivePtrBase; + using ThisT = ConstIntrusivePtr; + template + friend class ConstIntrusivePtr; + +public: + ConstIntrusivePtr() = default; + + ConstIntrusivePtr(T* _ptr, const bool _do_acquire) + : BaseT(_ptr, _do_acquire) + { + } + + ConstIntrusivePtr(MutableIntrusivePtr&& _other) + : BaseT(std::move(_other)) + { + } + + template + ConstIntrusivePtr(MutableIntrusivePtr&& _other) + : BaseT(std::move(_other)) + { + } + + ConstIntrusivePtr(ConstIntrusivePtr const& _other) + : BaseT(_other) + { + } + + ConstIntrusivePtr(ConstIntrusivePtr&& _other) + : BaseT(std::move(_other)) + { + } + + template + ConstIntrusivePtr(const ConstIntrusivePtr& _other) + : BaseT(_other) + { + } + + template + ConstIntrusivePtr(ConstIntrusivePtr&& _other) + : BaseT(std::move(_other)) + { + } + + ~ConstIntrusivePtr() = default; + + ConstIntrusivePtr& operator=(const ConstIntrusivePtr& _other) noexcept + { + BaseT::doCopy(_other); + return *this; + } + + ConstIntrusivePtr& operator=(ConstIntrusivePtr&& _other) noexcept + { + BaseT::doMove(std::move(_other)); + return *this; + } + + template + ConstIntrusivePtr& operator=(const ConstIntrusivePtr& _other) noexcept + { + BaseT::doCopy(_other); + return *this; + } + + template + ConstIntrusivePtr& operator=(ConstIntrusivePtr&& _other) noexcept + { + BaseT::doMove(std::move(_other)); + return *this; + } + + const T* get() const noexcept + { + return BaseT::ptr_; + } + + T const& operator*() const noexcept + { + return *BaseT::ptr_; + } + + const T* operator->() const noexcept + { + return BaseT::ptr_; + } + + MutableIntrusivePtr collapse() + { + if (BaseT::ptr_) { + if (impl::intrusive_ptr_release(*BaseT::ptr_)) { + impl::intrusive_ptr_acquire(*BaseT::ptr_); + return MutableIntrusivePtr{std::move(*this)}; + } + BaseT::ptr_ = nullptr; } + return {}; + } + +private: + template + friend ConstIntrusivePtr static_pointer_cast(const ConstIntrusivePtr& _rp) noexcept; + template + friend ConstIntrusivePtr dynamic_pointer_cast(const ConstIntrusivePtr& _rp) noexcept; + template + friend ConstIntrusivePtr static_pointer_cast(ConstIntrusivePtr&& _rp) noexcept; + template + friend ConstIntrusivePtr dynamic_pointer_cast(ConstIntrusivePtr&& _rp) noexcept; + + ConstIntrusivePtr(T* _ptr) + : BaseT(_ptr) + { } }; -namespace impl { -template -auto make_policy_intrusive(const P& _policy, Args&&... _args) +//----------------------------------------------------------------------------- +// IntrusivePtr +//----------------------------------------------------------------------------- + +template +inline IntrusivePtr make_intrusive(Args&&... _args) { - return IntrusivePtr(_policy, new T(std::forward(_args)...)); + // return IntrusivePtr(new TT(std::forward(_args)...)); + return IntrusivePtr(impl::intrusive_ptr_create(std::forward(_args)...)); } -} // namespace impl -template -auto make_intrusive(Args&&... _args) +template +inline IntrusivePtr static_pointer_cast(const IntrusivePtr& _rp) noexcept { - if constexpr (std::is_base_of_v) { - return IntrusivePtr(new T(std::forward(_args)...)); - } else { - return impl::make_policy_intrusive(std::forward(_args)...); - } + return IntrusivePtr(static_cast(_rp.get())); +} +template +inline IntrusivePtr dynamic_pointer_cast(const IntrusivePtr& _rp) noexcept +{ + return IntrusivePtr(dynamic_cast(_rp.get())); } -template -IntrusivePtr static_pointer_cast(const IntrusivePtr& _rp) noexcept +template +inline IntrusivePtr static_pointer_cast(IntrusivePtr&& _rp) noexcept { - return IntrusivePtr(static_cast(_rp.get())); + return IntrusivePtr(static_cast(_rp.detach())); } -template -IntrusivePtr static_pointer_cast(IntrusivePtr&& _rp) noexcept +template +inline IntrusivePtr dynamic_pointer_cast(IntrusivePtr&& _rp) noexcept +{ + auto* pt = dynamic_cast(_rp.get()); + if (pt) { + _rp.detach(); + return IntrusivePtr(pt); + } + return IntrusivePtr(); +} + +//----------------------------------------------------------------------------- +// MutableIntrusivePtr +//----------------------------------------------------------------------------- + +template +inline MutableIntrusivePtr make_mutable_intrusive(Args&&... _args) +{ + // return IntrusivePtr(new TT(std::forward(_args)...)); + return MutableIntrusivePtr(make_intrusive(std::forward(_args)...)); +} +template +inline MutableIntrusivePtr static_pointer_cast(const MutableIntrusivePtr& _rp) noexcept { - return IntrusivePtr(static_cast(_rp.detach())); + return MutableIntrusivePtr(static_cast(_rp.get())); +} +template +inline MutableIntrusivePtr dynamic_pointer_cast(const MutableIntrusivePtr& _rp) noexcept +{ + return MutableIntrusivePtr(dynamic_cast(_rp.get())); } -template -IntrusivePtr dynamic_pointer_cast(const IntrusivePtr& _rp) noexcept +template +inline MutableIntrusivePtr static_pointer_cast(MutableIntrusivePtr&& _rp) noexcept { - return IntrusivePtr(dynamic_cast(_rp.get())); + return MutableIntrusivePtr(static_cast(_rp.detach())); } -template -IntrusivePtr dynamic_pointer_cast(IntrusivePtr&& _rp) noexcept +template +inline MutableIntrusivePtr dynamic_pointer_cast(MutableIntrusivePtr&& _rp) noexcept { auto* pt = dynamic_cast(_rp.get()); if (pt) { _rp.detach(); - return IntrusivePtr(pt); + return MutableIntrusivePtr(pt); } - return IntrusivePtr(); + return MutableIntrusivePtr(); } +//----------------------------------------------------------------------------- +// ConstIntrusivePtr +//----------------------------------------------------------------------------- + +template +inline ConstIntrusivePtr static_pointer_cast(const ConstIntrusivePtr& _rp) noexcept +{ + return ConstIntrusivePtr(static_cast(_rp.get())); +} +template +inline ConstIntrusivePtr dynamic_pointer_cast(const ConstIntrusivePtr& _rp) noexcept +{ + return ConstIntrusivePtr(dynamic_cast(_rp.get())); +} + +template +inline ConstIntrusivePtr static_pointer_cast(ConstIntrusivePtr&& _rp) noexcept +{ + return ConstIntrusivePtr(static_cast(_rp.detach())); +} + +template +inline ConstIntrusivePtr dynamic_pointer_cast(ConstIntrusivePtr&& _rp) noexcept +{ + auto* pt = dynamic_cast(_rp.get()); + if (pt) { + _rp.detach(); + return ConstIntrusivePtr(pt); + } + return ConstIntrusivePtr(); +} +//----------------------------------------------------------------------------- } // namespace solid \ No newline at end of file diff --git a/solid/utility/sharedbuffer.hpp b/solid/utility/sharedbuffer.hpp index 7d4df75d..c01ba59f 100644 --- a/solid/utility/sharedbuffer.hpp +++ b/solid/utility/sharedbuffer.hpp @@ -9,85 +9,112 @@ namespace solid { -class SharedBuffer { - struct Data { - std::atomic use_count_; - std::thread::id make_thread_id_; - Data* pnext_ = nullptr; - std::size_t size_ = 0; - std::size_t capacity_ = 0; - char* buffer_ = nullptr; - char data_[8]; - - Data() - : use_count_(1) - { - } - - Data(char* _buffer) - : use_count_(1) - , buffer_(_buffer) - { - } +class BufferManager; - Data& acquire() - { - use_count_.fetch_add(1); - return *this; - } +namespace impl { - char* release(size_t& _previous_use_count); +struct SharedBufferData { + friend class BufferManager; - char* data() - { - return data_; - } - }; + std::atomic use_count_; + std::thread::id make_thread_id_; + SharedBufferData* pnext_ = nullptr; + std::size_t size_ = 0; + std::size_t capacity_ = 0; + char* buffer_ = nullptr; + char data_[8]; - static Data sentinel; + SharedBufferData() + : use_count_(1) + { + } - Data* pdata_; + SharedBufferData(char* _buffer) + : use_count_(1) + , buffer_(_buffer) + { + } - friend SharedBuffer make_shared_buffer(const std::size_t); - friend class BufferManager; + SharedBufferData& acquire() + { + use_count_.fetch_add(1); + return *this; + } - static Data* allocate_data(const std::size_t _cap); + char* release(size_t& _previous_use_count); - SharedBuffer(const std::size_t _cap) - : pdata_(allocate_data(_cap)) + char* data() { + return data_; } +}; + +class SharedBufferBase { +protected: + friend class BufferManager; - SharedBuffer(const std::size_t _cap, const std::thread::id& _thr_id); + static inline SharedBufferData sentinel{}; + static SharedBufferData* allocate_data(const std::size_t _cap); -public: - explicit operator bool() const noexcept + SharedBufferData* pdata_; + +protected: + SharedBufferBase() + : pdata_(&sentinel) { - return pdata_ != &sentinel; } - SharedBuffer() - : pdata_(&sentinel) + SharedBufferBase(const std::size_t _cap) + : pdata_(allocate_data(_cap)) { } - SharedBuffer(const SharedBuffer& _other) + + SharedBufferBase(const std::size_t _cap, const std::thread::id& _thr_id); + + SharedBufferBase(const SharedBufferBase& _other) : pdata_(_other ? &_other.pdata_->acquire() : _other.pdata_) { } - SharedBuffer(SharedBuffer&& _other) + + SharedBufferBase(SharedBufferBase&& _other) : pdata_(_other.pdata_) { _other.pdata_ = &sentinel; } - ~SharedBuffer() + char* data() const { - reset(); + return pdata_->data(); } - char* data() const + void doCopy(const SharedBufferBase& _other) { - return pdata_->data(); + if (pdata_ != _other.pdata_) { + reset(); + + if (_other) { + pdata_ = &_other.pdata_->acquire(); + } + } + } + + void doMove(SharedBufferBase&& _other) + { + if (pdata_ != _other.pdata_) { + reset(); + pdata_ = _other.pdata_; + _other.pdata_ = &sentinel; + } + } + +public: + explicit operator bool() const noexcept + { + return pdata_ != &sentinel; + } + ~SharedBufferBase() + { + reset(); } std::size_t size() const @@ -107,16 +134,6 @@ class SharedBuffer { return pdata_->make_thread_id_; } - void append(const std::size_t _size) - { - pdata_->size_ += _size; - } - - void resize(const std::size_t _size = 0) - { - pdata_->size_ = _size; - } - bool empty() const { return pdata_->size_ == 0; @@ -137,56 +154,274 @@ class SharedBuffer { } return previous_use_count; } +}; + +} // namespace impl + +class MutableSharedBuffer; + +//----------------------------------------------------------------------------- +// SharedBuffer +//----------------------------------------------------------------------------- + +class SharedBuffer : public impl::SharedBufferBase { + friend class BufferManager; + friend SharedBuffer make_shared_buffer(const std::size_t); + + SharedBuffer(const std::size_t _cap) + : SharedBufferBase(_cap) + { + } + + SharedBuffer(const std::size_t _cap, const std::thread::id& _thr_id) + : SharedBufferBase(_cap, _thr_id) + { + } + +public: + SharedBuffer() = default; + + SharedBuffer(const SharedBuffer& _other) + : SharedBufferBase(_other) + { + } + + SharedBuffer(SharedBuffer&& _other) + : SharedBufferBase(std::move(_other)) + { + } + + SharedBuffer(MutableSharedBuffer&& _other); + + ~SharedBuffer() + { + reset(); + } + + char* data() const + { + return pdata_->data(); + } + + void append(const std::size_t _size) + { + pdata_->size_ += _size; + } - bool collapse() + void resize(const std::size_t _size = 0) + { + pdata_->size_ = _size; + } + + SharedBuffer& operator=(const SharedBuffer& _other) + { + doCopy(_other); + return *this; + } + + SharedBuffer& operator=(SharedBuffer&& _other) + { + doMove(std::move(_other)); + return *this; + } + + SharedBuffer& operator=(MutableSharedBuffer&& _other); +}; + +inline SharedBuffer make_shared_buffer(const std::size_t _cap) +{ + return SharedBuffer(_cap); +} + +class ConstSharedBuffer; + +//----------------------------------------------------------------------------- +// MutableSharedBuffer +//----------------------------------------------------------------------------- + +class MutableSharedBuffer : public impl::SharedBufferBase { + friend class ConstSharedBuffer; + friend class BufferManager; + friend MutableSharedBuffer make_mutable_buffer(const std::size_t); + + MutableSharedBuffer(const std::size_t _cap) + : SharedBufferBase(_cap) + { + } + + MutableSharedBuffer(const std::size_t _cap, const std::thread::id& _thr_id) + : SharedBufferBase(_cap, _thr_id) + { + } + + MutableSharedBuffer(ConstSharedBuffer&& _other); + + MutableSharedBuffer(SharedBuffer&& _other) + : SharedBufferBase(std::move(_other)) + { + } + +public: + MutableSharedBuffer() = default; + + MutableSharedBuffer(const MutableSharedBuffer& _other) = delete; + + MutableSharedBuffer(MutableSharedBuffer&& _other) + : SharedBufferBase(std::move(_other)) + { + } + + ~MutableSharedBuffer() + { + reset(); + } + + char* data() + { + return impl::SharedBufferBase::data(); + } + + void append(const std::size_t _size) + { + pdata_->size_ += _size; + } + + void resize(const std::size_t _size = 0) + { + pdata_->size_ = _size; + } + + MutableSharedBuffer& operator=(const MutableSharedBuffer& _other) = delete; + + MutableSharedBuffer& operator=(MutableSharedBuffer&& _other) + { + doMove(std::move(_other)); + return *this; + } + MutableSharedBuffer& operator=(SharedBuffer&& _other) + { + doMove(std::move(_other)); + return *this; + } +}; + +inline MutableSharedBuffer make_mutable_buffer(const std::size_t _cap) +{ + return MutableSharedBuffer(make_shared_buffer(_cap)); +} + +//----------------------------------------------------------------------------- +// ConstSharedBuffer +//----------------------------------------------------------------------------- + +class ConstSharedBuffer : public impl::SharedBufferBase { +public: + ConstSharedBuffer() = default; + + ConstSharedBuffer(const ConstSharedBuffer& _other) + : SharedBufferBase(_other) + { + } + + ConstSharedBuffer(ConstSharedBuffer&& _other) + : SharedBufferBase(std::move(_other)) + { + } + + ConstSharedBuffer(MutableSharedBuffer&& _other) + : SharedBufferBase(std::move(_other)) + { + } + + ~ConstSharedBuffer() + { + reset(); + } + + const char* data() const + { + return impl::SharedBufferBase::data(); + } + + MutableSharedBuffer collapse() { if (*this) { size_t previous_use_count = 0; auto buf = pdata_->release(previous_use_count); if (buf) { pdata_->acquire(); - return true; + return MutableSharedBuffer(std::move(*this)); } else { pdata_ = &sentinel; } } - return false; + return {}; } - SharedBuffer& operator=(const SharedBuffer& _other) + MutableSharedBuffer mutate() { - if (pdata_ != _other.pdata_) { - reset(); - - if (_other) { - pdata_ = &_other.pdata_->acquire(); - } + if (useCount() == 1) { + return MutableSharedBuffer(std::move(*this)); + } else { + return {}; } + } + + ConstSharedBuffer& operator=(const ConstSharedBuffer& _other) + { + doCopy(_other); return *this; } - SharedBuffer& operator=(SharedBuffer&& _other) + ConstSharedBuffer& operator=(ConstSharedBuffer&& _other) { - if (pdata_ != _other.pdata_) { - reset(); - pdata_ = _other.pdata_; - _other.pdata_ = &sentinel; - } + doMove(std::move(_other)); + return *this; + } + + ConstSharedBuffer& operator=(SharedBuffer&& _other) + { + doMove(std::move(_other)); + return *this; + } + + ConstSharedBuffer& operator=(MutableSharedBuffer&& _other) + { + doMove(std::move(_other)); return *this; } }; -inline SharedBuffer make_shared_buffer(const std::size_t _cap) +inline MutableSharedBuffer::MutableSharedBuffer(ConstSharedBuffer&& _other) + : SharedBufferBase(std::move(_other)) { - return SharedBuffer(_cap); } +inline SharedBuffer& SharedBuffer::operator=(MutableSharedBuffer&& _other) +{ + doMove(std::move(_other)); + return *this; +} + +inline SharedBuffer::SharedBuffer(MutableSharedBuffer&& _other) + : SharedBufferBase(std::move(_other)) +{ +} + +//----------------------------------------------------------------------------- +// BufferManager +//----------------------------------------------------------------------------- + class BufferManager : NonCopyable { - friend class SharedBuffer; + friend class impl::SharedBufferData; + friend class impl::SharedBufferBase; struct Data; - PimplT pimpl_; - static char* release(SharedBuffer::Data* _pdata); - static SharedBuffer::Data* allocate(const size_t _cap); + PimplT pimpl_; + + using DataT = impl::SharedBufferData; + + static char* release(DataT* _pdata); + static DataT* allocate(const size_t _cap); public: struct LocalData; @@ -202,6 +437,11 @@ class BufferManager : NonCopyable { return SharedBuffer{_cap, std::this_thread::get_id()}; } + inline static MutableSharedBuffer makeMutable(const size_t _cap) + { + return MutableSharedBuffer{make(_cap)}; + } + static void localMaxCount(const size_t _cap, const size_t _count); static size_t localMaxCount(const size_t _cap); static size_t localCount(const size_t _cap); diff --git a/solid/utility/src/sharedbuffer.cpp b/solid/utility/src/sharedbuffer.cpp index 3a3e06ef..c17785e3 100644 --- a/solid/utility/src/sharedbuffer.cpp +++ b/solid/utility/src/sharedbuffer.cpp @@ -5,19 +5,8 @@ namespace solid { -SharedBuffer::Data SharedBuffer::sentinel; +// SharedBuffer::Data SharedBuffer::sentinel; -char* SharedBuffer::Data::release(size_t& _previous_use_count) -{ - if ((_previous_use_count = use_count_.fetch_sub(1)) == 1) { - if (make_thread_id_ == std::thread::id{}) { - return buffer_; - } else { - return BufferManager::release(this); - } - } - return nullptr; -} namespace { template inline constexpr std::size_t compute_capacity(const std::size_t _cap) @@ -35,41 +24,60 @@ inline constexpr std::size_t compute_capacity(const std::size_t _cap) } } // namespace -/* static */ SharedBuffer::Data* SharedBuffer::allocate_data(const std::size_t _cap) +namespace impl { +char* SharedBufferData::release(size_t& _previous_use_count) +{ + if ((_previous_use_count = use_count_.fetch_sub(1)) == 1) { + if (make_thread_id_ == std::thread::id{}) { + return buffer_; + } else { + return BufferManager::release(this); + } + } + return nullptr; +} + +/* static */ SharedBufferData* SharedBufferBase::allocate_data(const std::size_t _cap) { - const std::size_t new_cap = compute_capacity(_cap); + const std::size_t new_cap = compute_capacity(_cap); auto pbuf = new char[new_cap]; - auto pdata = new (pbuf) Data{pbuf}; + auto pdata = new (pbuf) SharedBufferData{pbuf}; pdata->capacity_ = _cap; //(pbuf + new_cap) - pdata->data(); return pdata; } -SharedBuffer::SharedBuffer(const std::size_t _cap, const std::thread::id& _thr_id) +SharedBufferBase::SharedBufferBase(const std::size_t _cap, const std::thread::id& _thr_id) { pdata_ = BufferManager::allocate(_cap); if (pdata_ == nullptr) [[unlikely]] { - const std::size_t new_cap = compute_capacity(_cap); + const std::size_t new_cap = compute_capacity(_cap); char* pbuf = new char[new_cap]; - pdata_ = new (pbuf) Data{pbuf}; + pdata_ = new (pbuf) SharedBufferData{pbuf}; pdata_->capacity_ = _cap; //(pbuf + new_cap) - pdata_->data(); } pdata_->make_thread_id_ = _thr_id; } -std::size_t SharedBuffer::actualCapacity() const +std::size_t SharedBufferBase::actualCapacity() const { - const std::size_t new_cap = compute_capacity(pdata_->capacity_); - return (pdata_->buffer_ + new_cap) - pdata_->data(); + if (*this) { + const std::size_t new_cap = compute_capacity(pdata_->capacity_); + return (pdata_->buffer_ + new_cap) - pdata_->data(); + } else { + return 0; + } } +} // namespace impl + //----------------------------------------------------------------------------- struct BufferManager::LocalData { struct Entry { - SharedBuffer::Data* ptop_ = nullptr; - size_t max_count_ = BufferManager::configuration().default_local_max_count_; - size_t count_ = 0; + impl::SharedBufferData* ptop_ = nullptr; + size_t max_count_ = BufferManager::configuration().default_local_max_count_; + size_t count_ = 0; inline bool empty() const noexcept { @@ -81,7 +89,7 @@ struct BufferManager::LocalData { return max_count_ != 0 && count_ >= max_count_; } - inline SharedBuffer::Data* pop() noexcept + inline impl::SharedBufferData* pop() noexcept { if (!empty()) { auto* ptmp = ptop_; @@ -92,7 +100,7 @@ struct BufferManager::LocalData { return nullptr; } - inline void push(SharedBuffer::Data* _pnode) noexcept + inline void push(impl::SharedBufferData* _pnode) noexcept { if (_pnode) { _pnode->pnext_ = ptop_; @@ -135,11 +143,11 @@ BufferManager::~BufferManager() {} return ins; } -/* static */ char* BufferManager::release(SharedBuffer::Data* _pdata) +/* static */ char* BufferManager::release(DataT* _pdata) { if (_pdata) { if (_pdata->make_thread_id_ == std::this_thread::get_id()) { - const std::size_t new_cap = compute_capacity(_pdata->capacity_); + const std::size_t new_cap = compute_capacity(_pdata->capacity_); auto& entry = local_data.entry_map_[new_cap]; if (!entry.full()) { @@ -152,9 +160,9 @@ BufferManager::~BufferManager() {} return nullptr; } -/* static */ SharedBuffer::Data* BufferManager::allocate(const size_t _cap) +/* static */ BufferManager::DataT* BufferManager::allocate(const size_t _cap) { - const std::size_t new_cap = compute_capacity(_cap); + const std::size_t new_cap = compute_capacity(_cap); auto& entry = local_data.entry_map_[new_cap]; auto* pdata = entry.pop(); if (pdata) { @@ -168,19 +176,19 @@ BufferManager::~BufferManager() {} /* static */ void BufferManager::localMaxCount(const size_t _cap, const size_t _count) { - const std::size_t new_cap = compute_capacity(_cap); + const std::size_t new_cap = compute_capacity(_cap); local_data.entry_map_[new_cap].max_count_ = _count; } /* static */ size_t BufferManager::localMaxCount(const size_t _cap) { - const std::size_t new_cap = compute_capacity(_cap); + const std::size_t new_cap = compute_capacity(_cap); return local_data.entry_map_[new_cap].max_count_; } /* static */ size_t BufferManager::localCount(const size_t _cap) { - const std::size_t new_cap = compute_capacity(_cap); + const std::size_t new_cap = compute_capacity(_cap); return local_data.entry_map_[new_cap].count_; } diff --git a/solid/utility/test/CMakeLists.txt b/solid/utility/test/CMakeLists.txt index 4ea4dd4e..2dd8220d 100644 --- a/solid/utility/test/CMakeLists.txt +++ b/solid/utility/test/CMakeLists.txt @@ -1,5 +1,7 @@ #============================================================================== set( UtilityTestSuite + test_intrusiveptr.cpp + test_collapse.cpp test_invalid_index.cpp test_innerlist.cpp test_any.cpp @@ -20,8 +22,6 @@ set( UtilityTestSuite test_function_any_speed_full_solid.cpp test_function_any_speed_full_stl.cpp test_shared_buffer.cpp - test_collapse.cpp - test_intrusiveptr.cpp ) set( ThreadPoolTestSuite diff --git a/solid/utility/test/test_collapse.cpp b/solid/utility/test/test_collapse.cpp index 2af573ab..34741732 100644 --- a/solid/utility/test/test_collapse.cpp +++ b/solid/utility/test/test_collapse.cpp @@ -49,7 +49,7 @@ struct Message : IntrusiveThreadSafeBase { }; using CallPoolT = ThreadPool, Function>; -using SharedMessageT = IntrusivePtr; +using SharedMessageT = ConstIntrusivePtr; } // namespace int test_collapse(int argc, char* argv[]) @@ -78,7 +78,7 @@ int test_collapse(int argc, char* argv[]) std::shared_future ready_future(ready_promise.get_future()); std::promise p; auto f = p.get_future(); - auto sm = make_intrusive(); + SharedMessageT sm = make_mutable_intrusive(); vector thr_vec; { auto lambda = [&p, ready_future](SharedMessageT _sm) mutable { @@ -114,17 +114,20 @@ int test_collapse(int argc, char* argv[]) } } } else if (choice == 'p') { - CallPoolT wp{{thread_count, 10000, 100}, + CallPoolT wp{{thread_count, 10000, 100}, [](const size_t) { set_current_thread_affinity(); }, [](const size_t) {}}; - auto sm = make_intrusive(); - const auto start_time = chrono::high_resolution_clock::now(); + SharedMessageT sm = make_mutable_intrusive(); + const auto start_time = chrono::high_resolution_clock::now(); for (size_t i = 0; i < repeat_count; ++i) { std::promise p; auto f = p.get_future(); - auto sm_lock{std::move(sm)}; + SharedMessageT sm_lock{std::move(sm)}; + + solid_check(sm_lock); + wp.pushAll( [&p, sm_lock]() mutable { if (auto tmp_sm = collapse(sm_lock)) { @@ -138,7 +141,7 @@ int test_collapse(int argc, char* argv[]) p.set_value(std::move(tmp_sm)); } { - if (f.wait_for(chrono::seconds(5000)) != future_status::ready) { + if (f.wait_for(chrono::seconds(5)) != future_status::ready) { solid_throw("Waited for too long"); } sm = f.get(); @@ -148,17 +151,18 @@ int test_collapse(int argc, char* argv[]) const auto stop_time = chrono::high_resolution_clock::now(); cout << "Duration: " << chrono::duration_cast(stop_time - start_time).count() << "us" << endl; } else if (choice == 'b') { - CallPoolT wp{{thread_count, 10000, 100}, + CallPoolT wp{{thread_count, 10000, 100}, [](const size_t) { set_current_thread_affinity(); }, [](const size_t) {}}; - auto sm = make_shared_buffer(100); - const auto start_time = chrono::high_resolution_clock::now(); + ConstSharedBuffer sm = make_mutable_buffer(100); + const auto start_time = chrono::high_resolution_clock::now(); for (size_t i = 0; i < repeat_count; ++i) { - std::promise p; - auto f = p.get_future(); - auto sm_lock{std::move(sm)}; + std::promise p; + auto f = p.get_future(); + auto sm_lock{std::move(sm)}; + solid_check(sm_lock); wp.pushAll( [&p, sm_lock]() mutable { if (auto tmp_sm = collapse(sm_lock)) { diff --git a/solid/utility/test/test_function_perf.cpp b/solid/utility/test/test_function_perf.cpp index 4538604e..25dd574b 100644 --- a/solid/utility/test/test_function_perf.cpp +++ b/solid/utility/test/test_function_perf.cpp @@ -111,7 +111,7 @@ TestBase* create_test(const FunctionChoice _fnc_choice, const size_t _closure_si { switch (_fnc_choice) { case FunctionChoice::Solid: - return create_test>(_closure_size); + return create_test>(_closure_size); case FunctionChoice::Standard: return create_test>(_closure_size); } diff --git a/solid/utility/test/test_intrusiveptr.cpp b/solid/utility/test/test_intrusiveptr.cpp index 6858a3e7..4ca3e40c 100644 --- a/solid/utility/test/test_intrusiveptr.cpp +++ b/solid/utility/test/test_intrusiveptr.cpp @@ -7,11 +7,11 @@ using namespace solid; using namespace std; namespace { - -struct Test : IntrusiveThreadSafeBase { +class Test : public IntrusiveThreadSafeBase { string s_; int i_ = 0; +public: Test(const string_view _s, const int _i) : s_(_s) , i_(_i) @@ -39,34 +39,55 @@ struct TestWithCounter { { } + virtual ~TestWithCounter() {} + private: friend class TestIntrusivePolicy; mutable atomic use_count_{0}; }; -class TestIntrusivePolicy { -protected: - void acquire(const TestWithCounter& _p) noexcept +template +concept TestIntrusiveC = std::is_base_of_v; + +struct TestIntrusivePolicy { + static void acquire(const TestWithCounter& _p) noexcept { ++_p.use_count_; } - bool release(const TestWithCounter& _p) noexcept + static bool release(const TestWithCounter& _p) noexcept { return _p.use_count_.fetch_sub(1) == 1; } - size_t useCount(const TestWithCounter& _p) const noexcept + static size_t useCount(const TestWithCounter& _p) noexcept { return _p.use_count_.load(std::memory_order_relaxed); } + + template + static void destroy(const T& _r) + { + delete &_r; + } + + template + static T* create(Args&&... _args) + { + return new T(std::forward(_args)...); + } }; } // namespace +template +struct solid::intrusive_policy_dispatch { + using type = TestIntrusivePolicy; +}; + int test_intrusiveptr(int argc, char* argvp[]) { Test t{"ceva", 10}; auto test_ptr = make_intrusive("ceva", 10); - auto twc_ptr = make_intrusive(TestIntrusivePolicy{}, "ceva2", 11); + auto twc_ptr = make_intrusive("ceva2", 11); cout << "sizeof(test_ptr): " << sizeof(test_ptr) << endl; cout << "sizeof(twc_ptr): " << sizeof(twc_ptr) << endl; @@ -106,6 +127,18 @@ int test_intrusiveptr(int argc, char* argvp[]) IntrusivePtr ptr{static_pointer_cast(first_ptr)}; solid_check(ptr && ptr.useCount() == 2); + + // auto base_ptr = static_pointer_cast(ptr);//must not compile } + + { + MutableIntrusivePtr p1 = make_mutable_intrusive("ceva", 10); + + ConstIntrusivePtr p2 = std::move(p1); + + solid_check(p2); + solid_check(!p1); + } + return 0; } \ No newline at end of file diff --git a/solid/utility/test/test_shared_buffer.cpp b/solid/utility/test/test_shared_buffer.cpp index 679c52d0..69c40b23 100644 --- a/solid/utility/test/test_shared_buffer.cpp +++ b/solid/utility/test/test_shared_buffer.cpp @@ -26,38 +26,74 @@ int test_shared_buffer(int argc, char* argv[]) SharedBuffer sb3 = sb2; // sb3 == sb2 - solid_check(!sb2.collapse()); - solid_check(!sb.collapse()); - - solid_check(sb3.collapse()); solid_check(sb3); } { - std::promise p; - std::future f = p.get_future(); + MutableSharedBuffer sb = make_mutable_buffer(1000); + + cout << sb.capacity() << endl; + cout << sb.size() << endl; + string_view pangram = "the quick brown fox jumps over the lazy dog"; + strncpy(sb.data(), pangram.data(), sb.capacity()); + + sb.resize(pangram.size()); + + sb.data()[0] = 'T'; + + solid_check(sb.size() == pangram.size()); + + // MutableSharedBuffer sbxx{sb};//will not compile + + MutableSharedBuffer sbx{std::move(sb)}; + + solid_check(sbx.size() == pangram.size()); + solid_check(!sb); + + ConstSharedBuffer csb = std::move(sbx); + + solid_check(csb.size() == pangram.size()); + + // csb.data()[0] = 't';//will not compile + + ConstSharedBuffer csb2 = csb; // sb3 == sb2 + + solid_check(csb.size() == pangram.size()); + auto sbc = csb.collapse(); + solid_check(!sbc); + sbc = csb2.collapse(); + solid_check(sbc); + solid_check(!csb2); + solid_check(sbc.size() == pangram.size()); + cout << "Data: " << sbc.data() << endl; + } + { + std::promise p; + std::future f = p.get_future(); vector thr_vec; - void* psb1 = nullptr; + const void* psb1 = nullptr; { - SharedBuffer sb1 = BufferManager::make(1000); - SharedBuffer sb2 = BufferManager::make(2000); + ConstSharedBuffer csb1 = BufferManager::makeMutable(1000); + ConstSharedBuffer csb2 = BufferManager::makeMutable(2000); - cout << static_cast(sb1.data()) << endl; - cout << static_cast(sb2.data()) << endl; + cout << static_cast(csb1.data()) << endl; + cout << static_cast(csb2.data()) << endl; - psb1 = sb1.data(); + psb1 = csb1.data(); solid_check(psb1); - auto lambda = [&p, sb1, sb2]() mutable { + auto lambda = [&p, csb1, csb2]() mutable { this_thread::sleep_for(chrono::milliseconds(200)); - if (sb1.collapse()) { - p.set_value(std::move(sb1)); + auto sbc = csb1.collapse(); + if (sbc) { + p.set_value(std::move(sbc)); } - if (sb2.collapse()) { - sb2.reset(); - solid_check(!sb2); + solid_check(!sbc); + sbc = csb2.collapse(); + if (sbc) { + solid_check(!csb2); solid_check(BufferManager::localCount(2000) == 0); } }; @@ -65,9 +101,9 @@ int test_shared_buffer(int argc, char* argv[]) for (size_t i = 0; i < 4; ++i) { thr_vec.emplace_back(lambda); } - - if (sb1.collapse()) { - p.set_value(std::move(sb1)); + auto sbc = csb1.collapse(); + if (sbc) { + p.set_value(std::move(sbc)); } } @@ -102,6 +138,13 @@ int test_shared_buffer(int argc, char* argv[]) SharedBuffer empty_buf; solid_check(empty_buf.capacity() == 0); cout << "Empty buffer actualCapacity = " << empty_buf.actualCapacity() << endl; + solid_check(empty_buf.actualCapacity() == 0); + } + { + SharedBuffer zero_buf = make_shared_buffer(0); + solid_check(zero_buf.capacity() == 0); + cout << "Zero buffer actualCapacity = " << zero_buf.actualCapacity() << endl; + solid_check(zero_buf.actualCapacity() != 0 && zero_buf.actualCapacity() < 0xffffffff); } return 0; } \ No newline at end of file diff --git a/solid/utility/typetraits.hpp b/solid/utility/typetraits.hpp index c2942af2..4d701019 100644 --- a/solid/utility/typetraits.hpp +++ b/solid/utility/typetraits.hpp @@ -110,6 +110,14 @@ template struct is_intrusive_ptr> : std::true_type { }; +template +struct is_const_intrusive_ptr : std::false_type { +}; + +template +struct is_const_intrusive_ptr> : std::true_type { +}; + template inline constexpr bool is_unique_ptr_v = is_unique_ptr::value; @@ -119,6 +127,9 @@ inline constexpr bool is_shared_ptr_v = is_shared_ptr::value; template inline constexpr bool is_intrusive_ptr_v = is_intrusive_ptr::value; +template +inline constexpr bool is_const_intrusive_ptr_v = is_const_intrusive_ptr::value; + template struct is_bitset : std::false_type { }; diff --git a/tutorials/mprpc_file/CMakeLists.txt b/tutorials/mprpc_file/CMakeLists.txt index 58f88cd8..5618145d 100644 --- a/tutorials/mprpc_file/CMakeLists.txt +++ b/tutorials/mprpc_file/CMakeLists.txt @@ -1,12 +1,7 @@ -if(APPLE OR WIN32) -else() - set(STDFS_LIB stdc++fs) -endif() - include(CheckCXXSymbolExists) -CHECK_CXX_SYMBOL_EXISTS(std::filesystem::path::preferred_separator filesystem cxx17fs) +CHECK_CXX_SYMBOL_EXISTS(std::filesystem::path::preferred_separator filesystem cxx_fs) -if(cxx17fs) +if(cxx_fs) add_executable (tutorial_mprpc_file_server mprpc_file_server.cpp mprpc_file_messages.hpp) target_link_libraries (tutorial_mprpc_file_server @@ -17,7 +12,6 @@ if(cxx17fs) solid_utility solid_system ${SYSTEM_BASIC_LIBRARIES} - ${STDFS_LIB} ) add_executable (tutorial_mprpc_file_client mprpc_file_client.cpp mprpc_file_messages.hpp) @@ -32,5 +26,5 @@ if(cxx17fs) ${SYSTEM_BASIC_LIBRARIES} ) else() - message("C++17 filesystem not found - mprpc_file tutorial not available") + message("C++ filesystem not found - mprpc_file tutorial not available") endif()