diff --git a/.gitignore b/.gitignore index b0b96e5af..f62e363dc 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,6 @@ lokinet.exe rapidjson/ -.gradle/ \ No newline at end of file +.gradle/ +.idea +.vscode diff --git a/.idea/artifacts/loki_network.xml b/.idea/artifacts/loki_network.xml deleted file mode 100644 index bc864a9a5..000000000 --- a/.idea/artifacts/loki_network.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - $PROJECT_DIR$/android/build/artifacts/loki_network - - - - - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 30aa626c2..000000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/loki-network.iml b/.idea/loki-network.iml deleted file mode 100644 index e28944b24..000000000 --- a/.idea/loki-network.iml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 9db281056..000000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 3f4ab2c12..000000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1ddfb..000000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index 91a7b8a31..000000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,560 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1541522484586 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - loki-network - - - - - - - - Android - - - - - - - - - - - - - - - 1.8 - - - - - - - - loki-network - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index 8c9f05481..000000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "configurations": [ - { - "name": "Linux", - "browse": { - "path": [ - "${workspaceFolder}/llarp", - "${workspaceFolder}/daemon", - "${workspaceFolder}/include", - "${workspaceFolder}/libabyss/src", - "${workspaceFolder}/crypto", - "${workspaceFolder}/vendor/cppbackport-master/lib" - ], - "limitSymbolsToIncludedHeaders": true - }, - "includePath": [ - "${workspaceFolder}/include", - "${workspaceFolder}/llarp", - "${workspaceFolder}/vendor/cppbackport-master/lib", - "${workspaceFolder}/libabyss/include", - "${workspaceFolder}/crypto/include" - ], - "defines": [], - "compilerPath": "/usr/bin/clang", - "cStandard": "c11", - "cppStandard": "c++17", - "intelliSenseMode": "clang-x64" - } - ], - "version": 4 -} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index e1eabfa58..000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [{ - "name": "(lldb) Launch", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceFolder}/build/testAll", - "args": ["--gtest_shuffle", "--gtest_filter=TestThreadPool*"], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": false, - "MIMode": "lldb" - }, - { - "name": "(gdb) Launch", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceFolder}/lokinet", - "args": [], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": true, - "MIMode": "gdb", - "setupCommands": [{ - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - }] - } - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 146ac739a..000000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "editor.formatOnSave": true, - "files.associations": { - "array": "cpp", - "limits": "cpp", - "cctype": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "atomic": "cpp", - "*.tcc": "cpp", - "bitset": "cpp", - "chrono": "cpp", - "codecvt": "cpp", - "condition_variable": "cpp", - "cstdint": "cpp", - "deque": "cpp", - "list": "cpp", - "unordered_map": "cpp", - "unordered_set": "cpp", - "exception": "cpp", - "filesystem": "cpp", - "string_view": "cpp", - "fstream": "cpp", - "functional": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "iosfwd": "cpp", - "iostream": "cpp", - "istream": "cpp", - "memory": "cpp", - "mutex": "cpp", - "optional": "cpp", - "ostream": "cpp", - "ratio": "cpp", - "sstream": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "system_error": "cpp", - "thread": "cpp", - "cinttypes": "cpp", - "type_traits": "cpp", - "tuple": "cpp", - "typeindex": "cpp", - "typeinfo": "cpp", - "utility": "cpp", - "__config": "cpp", - "__nullptr": "cpp", - "algorithm": "cpp", - "io": "cpp", - "strstream": "cpp", - "numeric": "cpp", - "valarray": "cpp", - "*.ipp": "cpp", - "csignal": "cpp", - "future": "cpp", - "map": "cpp", - "vector": "cpp", - "new": "cpp", - "shared_mutex": "cpp", - "complex": "cpp", - "variant": "cpp", - "any": "cpp", - "tuntap.h": "c", - "hashtable": "cpp", - "__mutex_base": "cpp", - "iterator": "cpp" - } -} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 6f34e0472..000000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - "tasks": [ - { - "label": "build", - "type": "shell", - "command": "make -j8 JSONRPC=ON test", - "group": "build", - "problemMatcher": [ - "$gcc" - ] - } - ] -} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 40c139d7f..305f48d94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,8 +45,12 @@ add_compile_options(-Wno-cast-function-type) set(FS_LIB stdc++fs) endif(WIN32) + + if(DEBIAN) add_definitions(-DDEBIAN) +else() + set(CRYPTO_FLAGS -march=native) endif() set(CMAKE_THREAD_PREFER_PTHREAD TRUE) @@ -112,18 +116,6 @@ if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]") add_compile_options( ${DEBUG_FLAGS} ) endif() -if(NOT DEBIAN) -if(NOT ANDROID) -if (NOT USE_AVX2) -set(CRYPTO_FLAGS -march=core2 -mtune=native) -set(CMAKE_ASM_FLAGS "-march=core2") -else() -set(CRYPTO_FLAGS -march=haswell -mtune=native) -set(CMAKE_ASM_FLAGS "-march=haswell -mtune=native ${CMAKE_ASM_FLAGS} $ENV{ASFLAGS}") -endif() -endif() -endif() - if(RPI) add_definitions(-DRPI) set(WITH_STATIC ON) diff --git a/Makefile b/Makefile index d6b29087c..6f6b8a78f 100644 --- a/Makefile +++ b/Makefile @@ -52,12 +52,13 @@ RPI ?= OFF STATIC_LINK ?= OFF NETNS ?= OFF CLANG ?= OFF +CROSS ?= OFF CMAKE_GEN ?= Unix Makefiles BUILD_ROOT = $(REPO)/build -CONFIG_CMD = $(shell /bin/echo -n "cd '$(BUILD_ROOT)' && " ; /bin/echo -n "cmake -G'$(CMAKE_GEN)' -DUSING_CLANG=$(CLANG) -DSTATIC_LINK=$(STATIC_LINK) -DUSE_NETNS=$(NETNS) -DUSE_AVX2=$(AVX2) -DUSE_LIBABYSS=$(JSONRPC) -DRPI=$(RPI) '$(REPO)'") +CONFIG_CMD = $(shell /bin/echo -n "cd '$(BUILD_ROOT)' && " ; /bin/echo -n "cmake -G'$(CMAKE_GEN)' -DCMAKE_CROSSCOMPILING=$(CROSS) -DUSING_CLANG=$(CLANG) -DSTATIC_LINK=$(STATIC_LINK) -DUSE_NETNS=$(NETNS) -DUSE_AVX2=$(AVX2) -DUSE_LIBABYSS=$(JSONRPC) -DRPI=$(RPI) '$(REPO)'") SCAN_BUILD ?= scan-build ANALYZE_CONFIG_CMD = $(shell /bin/echo -n "cd '$(BUILD_ROOT)' && " ; /bin/echo -n "$(SCAN_BUILD) cmake -DUSE_LIBABYSS=$(JSONRPC) '$(REPO)'") diff --git a/docker/ubuntu.Dockerfile b/docker/ubuntu.Dockerfile new file mode 100644 index 000000000..67e511fd0 --- /dev/null +++ b/docker/ubuntu.Dockerfile @@ -0,0 +1,10 @@ +FROM ubuntu:latest + +RUN apt update && \ + apt install -y build-essential cmake git libcap-dev wget rapidjson-dev + +WORKDIR /src/ + +COPY . /src/ + +RUN make -j 8 JSONRPC=ON diff --git a/include/llarp/crypto.hpp b/include/llarp/crypto.hpp index 0ec8cbfec..c7a25a812 100644 --- a/include/llarp/crypto.hpp +++ b/include/llarp/crypto.hpp @@ -17,19 +17,19 @@ namespace llarp const byte_t* pq_keypair_to_secret(const byte_t* keypair); - typedef AlignedBuffer< SHAREDKEYSIZE > SharedSecret; - typedef AlignedBuffer< 32 > KeyExchangeNonce; - typedef AlignedBuffer< PUBKEYSIZE > PubKey; - typedef AlignedBuffer< SECKEYSIZE > SecretKey; - typedef AlignedBuffer< SHORTHASHSIZE > ShortHash; - typedef AlignedBuffer< SIGSIZE > Signature; - typedef AlignedBuffer< TUNNONCESIZE > TunnelNonce; - typedef AlignedBuffer< NONCESIZE > SymmNonce; - typedef AlignedBuffer< 32 > SymmKey; - - typedef AlignedBuffer< PQ_CIPHERTEXTSIZE + 1 > PQCipherBlock; - typedef AlignedBuffer< PQ_PUBKEYSIZE > PQPubKey; - typedef AlignedBuffer< PQ_KEYPAIRSIZE > PQKeyPair; + using SharedSecret = AlignedBuffer< SHAREDKEYSIZE >; + using KeyExchangeNonce = AlignedBuffer< 32 >; + using PubKey = AlignedBuffer< PUBKEYSIZE >; + using SecretKey = AlignedBuffer< SECKEYSIZE >; + using ShortHash = AlignedBuffer< SHORTHASHSIZE >; + using Signature = AlignedBuffer< SIGSIZE >; + using TunnelNonce = AlignedBuffer< TUNNONCESIZE >; + using SymmNonce = AlignedBuffer< NONCESIZE >; + using SymmKey = AlignedBuffer< 32 >; + + using PQCipherBlock = AlignedBuffer< PQ_CIPHERTEXTSIZE + 1 >; + using PQPubKey = AlignedBuffer< PQ_PUBKEYSIZE >; + using PQKeyPair = AlignedBuffer< PQ_KEYPAIRSIZE >; } // namespace llarp diff --git a/include/llarp/dht/bucket.hpp b/include/llarp/dht/bucket.hpp index 276b7173e..5ac3ffacb 100644 --- a/include/llarp/dht/bucket.hpp +++ b/include/llarp/dht/bucket.hpp @@ -14,7 +14,7 @@ namespace llarp template < typename Val_t > struct Bucket { - typedef std::map< Key_t, Val_t, XorMetric > BucketStorage_t; + using BucketStorage_t = std::map< Key_t, Val_t, XorMetric >; Bucket(const Key_t& us) : nodes(XorMetric(us)){}; diff --git a/include/llarp/dht/context.hpp b/include/llarp/dht/context.hpp index acdfeba8a..066b9ba75 100644 --- a/include/llarp/dht/context.hpp +++ b/include/llarp/dht/context.hpp @@ -74,7 +74,7 @@ namespace llarp Validate(const V& value) const = 0; void - OnFound(const Key_t& askedPeer, const V& value) + OnFound(const Key_t askedPeer, const V& value) { peersAsked.insert(askedPeer); if(Validate(value)) @@ -98,18 +98,26 @@ namespace llarp Key_t peer; if(next) { - peer = *next.get(); + // explicit next peer provided + peer = next->data(); } - else + else if(!GetNextPeer(peer, peersAsked)) { - if(!GetNextPeer(peer, peersAsked)) - { - // no more peers - SendReply(); - return false; - } + // no more peers + llarp::LogInfo("no more peers for request asking for", target); + return false; } + const Key_t targetKey = target.data(); + if((prevPeer ^ targetKey) < (peer ^ targetKey)) + { + // next peer is not closer + llarp::LogInfo("next peer ", peer, " is not closer to ", target, + " than ", prevPeer); + return false; + } + else + peersAsked.insert(peer); DoNextRequest(peer); return true; } @@ -118,11 +126,11 @@ namespace llarp SendReply() = 0; }; - typedef std::function< void(const std::vector< service::IntroSet >&) > - IntroSetLookupHandler; + using IntroSetLookupHandler = + std::function< void(const std::vector< service::IntroSet >&) >; - typedef std::function< void(const std::vector< RouterContact >&) > - RouterLookupHandler; + using RouterLookupHandler = + std::function< void(const std::vector< RouterContact >&) >; struct Context { @@ -297,6 +305,7 @@ namespace llarp { (void)whoasked; tx.emplace(askpeer, std::unique_ptr< TX< K, V > >(t)); + auto count = waiting.count(k); waiting.insert(std::make_pair(k, askpeer)); auto itr = timeouts.find(k); @@ -305,7 +314,8 @@ namespace llarp timeouts.insert( std::make_pair(k, time_now_ms() + requestTimeoutMS)); } - t->Start(askpeer); + if(count == 0) + t->Start(askpeer); } /// mark tx as not fond @@ -316,12 +326,11 @@ namespace llarp auto txitr = tx.find(from); if(txitr == tx.end()) return; - if(next) - { - // ask for next peer - if(txitr->second->AskNextPeer(from.node, next)) - sendReply = false; - } + + // ask for next peer + if(txitr->second->AskNextPeer(from.node, next)) + sendReply = false; + llarp::LogWarn("Target key ", txitr->second->target); Inform(from, txitr->second->target, {}, sendReply, sendReply); } diff --git a/include/llarp/dns_dotlokilookup.hpp b/include/llarp/dns_dotlokilookup.hpp index d864b38f5..f5062a419 100644 --- a/include/llarp/dns_dotlokilookup.hpp +++ b/include/llarp/dns_dotlokilookup.hpp @@ -9,8 +9,8 @@ #include "dnsd.hpp" -typedef bool (*map_address_hook_func)(const llarp::service::Address &addr, - uint32_t ip); +using map_address_hook_func = + std::function< bool(const llarp::service::Address &addr, uint32_t ip) >; /// dotLokiLookup context/config struct dotLokiLookup diff --git a/include/llarp/dnsd.hpp b/include/llarp/dnsd.hpp index 6a7f6b58b..5bac27ae1 100644 --- a/include/llarp/dnsd.hpp +++ b/include/llarp/dnsd.hpp @@ -14,8 +14,9 @@ struct dnsd_context; /// sendto hook functor -typedef ssize_t (*sendto_dns_hook_func)(void *sock, const struct sockaddr *from, - const void *buffer, size_t length); +using sendto_dns_hook_func = + std::function< ssize_t(void *sock, const struct sockaddr *from, + const void *buffer, size_t length) >; // FIXME: llarp::Addr /// DNS server query request @@ -89,9 +90,9 @@ writesend_dnss_revresponse(std::string reverse, const struct sockaddr *from, // /// intercept query hook functor -typedef dnsd_query_hook_response *(*intercept_query_hook)( +using intercept_query_hook = std::function< dnsd_query_hook_response *( std::string name, const struct sockaddr *from, - struct dnsd_question_request *request); + struct dnsd_question_request *request) >; // FIXME: llarp::Addr /// DNS Server context diff --git a/include/llarp/encrypted_frame.hpp b/include/llarp/encrypted_frame.hpp index 73c7821ad..6cb2c73cb 100644 --- a/include/llarp/encrypted_frame.hpp +++ b/include/llarp/encrypted_frame.hpp @@ -50,7 +50,7 @@ namespace llarp template < typename User > struct AsyncFrameEncrypter { - typedef void (*EncryptHandler)(EncryptedFrame*, User*); + using EncryptHandler = std::function< void(EncryptedFrame*, User*) >; static void Encrypt(void* user) @@ -96,7 +96,7 @@ namespace llarp template < typename User > struct AsyncFrameDecrypter { - typedef void (*DecryptHandler)(llarp_buffer_t*, User*); + using DecryptHandler = std::function< void(llarp_buffer_t*, User*) >; static void Decrypt(void* user) diff --git a/include/llarp/exit_info.hpp b/include/llarp/exit_info.hpp index 4b7d1b90d..1c564596b 100644 --- a/include/llarp/exit_info.hpp +++ b/include/llarp/exit_info.hpp @@ -64,7 +64,7 @@ namespace llarp return out; out << std::string("/"); #if defined(ANDROID) || defined(RPI) - snprintf(tmp, sizeof(tmp), "%lu", + snprintf(tmp, sizeof(tmp), "%zu", llarp::bits::count_array_bits(xi.netmask.s6_addr)); return out << tmp; #else diff --git a/include/llarp/handlers/tun.hpp b/include/llarp/handlers/tun.hpp index c5a3bbe8f..d1b33c0b9 100644 --- a/include/llarp/handlers/tun.hpp +++ b/include/llarp/handlers/tun.hpp @@ -119,10 +119,9 @@ namespace llarp ObtainIPForAddr(const byte_t* addr); protected: - typedef llarp::util::CoDelQueue< + using PacketQueue_t = llarp::util::CoDelQueue< net::IPv4Packet, net::IPv4Packet::GetTime, net::IPv4Packet::PutTime, - net::IPv4Packet::CompareOrder, net::IPv4Packet::GetNow > - PacketQueue_t; + net::IPv4Packet::CompareOrder, net::IPv4Packet::GetNow >; /// queue for sending packets over the network from us PacketQueue_t m_UserToNetworkPktQueue; /// queue for sending packets to user from network diff --git a/include/llarp/link/server.hpp b/include/llarp/link/server.hpp index a7e348999..0c622ed42 100644 --- a/include/llarp/link/server.hpp +++ b/include/llarp/link/server.hpp @@ -133,8 +133,8 @@ namespace llarp uint32_t tick_id; protected: - typedef util::NullLock Lock; - typedef util::NullMutex Mutex; + using Lock = util::NullLock; + using Mutex = util::NullMutex; void PutSession(ILinkSession* s); diff --git a/include/llarp/link_message.hpp b/include/llarp/link_message.hpp index c8e4b3216..d2222e787 100644 --- a/include/llarp/link_message.hpp +++ b/include/llarp/link_message.hpp @@ -14,7 +14,7 @@ namespace llarp { struct ILinkSession; - typedef std::queue< ILinkMessage* > SendQueue; + using SendQueue = std::queue< ILinkMessage* >; /// parsed link layer message struct ILinkMessage : public IBEncodeMessage diff --git a/include/llarp/nodedb.hpp b/include/llarp/nodedb.hpp index 2d4e88288..eee7b3e6c 100644 --- a/include/llarp/nodedb.hpp +++ b/include/llarp/nodedb.hpp @@ -83,7 +83,8 @@ llarp_nodedb_del_rc(struct llarp_nodedb *n, const llarp::RouterID &pk); /// struct for async rc verification struct llarp_async_verify_rc; -typedef void (*llarp_async_verify_rc_hook_func)(struct llarp_async_verify_rc *); +using llarp_async_verify_rc_hook_func = + std::function< void(struct llarp_async_verify_rc *) >; /// verify rc request struct llarp_async_verify_rc @@ -118,7 +119,8 @@ llarp_nodedb_async_verify(struct llarp_async_verify_rc *job); struct llarp_async_load_rc; -typedef void (*llarp_async_load_rc_hook_func)(struct llarp_async_load_rc *); +using llarp_async_load_rc_hook_func = + std::function< void(struct llarp_async_load_rc *) >; struct llarp_async_load_rc { diff --git a/include/llarp/path.hpp b/include/llarp/path.hpp index a28aa699e..f357ac161 100644 --- a/include/llarp/path.hpp +++ b/include/llarp/path.hpp @@ -280,19 +280,19 @@ namespace llarp /// A path we made struct Path : public IHopHandler, public llarp::routing::IMessageHandler { - typedef std::function< void(Path*) > BuildResultHookFunc; - typedef std::function< bool(Path*, llarp_time_t) > CheckForDeadFunc; - typedef std::function< bool(Path*, const PathID_t&, uint64_t) > - DropHandlerFunc; - typedef std::vector< PathHopConfig > HopList; - typedef std::function< bool(Path*, const service::ProtocolFrame*) > - DataHandlerFunc; - typedef std::function< bool(Path*) > ExitUpdatedFunc; - typedef std::function< bool(Path*) > ExitClosedFunc; - typedef std::function< bool(Path*, llarp_buffer_t) > - ExitTrafficHandlerFunc; + using BuildResultHookFunc = std::function< void(Path*) >; + using CheckForDeadFunc = std::function< bool(Path*, llarp_time_t) >; + using DropHandlerFunc = + std::function< bool(Path*, const PathID_t&, uint64_t) >; + using HopList = std::vector< PathHopConfig >; + using DataHandlerFunc = + std::function< bool(Path*, const service::ProtocolFrame*) >; + using ExitUpdatedFunc = std::function< bool(Path*) >; + using ExitClosedFunc = std::function< bool(Path*) >; + using ExitTrafficHandlerFunc = + std::function< bool(Path*, llarp_buffer_t) >; /// (path, backoff) backoff is 0 on success - typedef std::function< bool(Path*, llarp_time_t) > ObtainedExitHandler; + using ObtainedExitHandler = std::function< bool(Path*, llarp_time_t) >; HopList hops; @@ -612,15 +612,15 @@ namespace llarp void RemovePathSet(PathSet* set); - typedef std::multimap< PathID_t, std::shared_ptr< TransitHop > > - TransitHopsMap_t; + using TransitHopsMap_t = + std::multimap< PathID_t, std::shared_ptr< TransitHop > >; - typedef std::pair< util::Mutex, TransitHopsMap_t > SyncTransitMap_t; + using SyncTransitMap_t = std::pair< util::Mutex, TransitHopsMap_t >; // maps path id -> pathset owner of path - typedef std::map< PathID_t, PathSet* > OwnedPathsMap_t; + using OwnedPathsMap_t = std::map< PathID_t, PathSet* >; - typedef std::pair< util::Mutex, OwnedPathsMap_t > SyncOwnedPathsMap_t; + using SyncOwnedPathsMap_t = std::pair< util::Mutex, OwnedPathsMap_t >; llarp_threadpool* Worker(); diff --git a/include/llarp/path_types.hpp b/include/llarp/path_types.hpp index 77b0fee1a..20830e79f 100644 --- a/include/llarp/path_types.hpp +++ b/include/llarp/path_types.hpp @@ -6,7 +6,7 @@ namespace llarp { - typedef AlignedBuffer< PATHIDSIZE > PathID_t; + using PathID_t = AlignedBuffer< PATHIDSIZE >; } -#endif \ No newline at end of file +#endif diff --git a/include/llarp/pathset.hpp b/include/llarp/pathset.hpp index 07d25bef9..055e964b4 100644 --- a/include/llarp/pathset.hpp +++ b/include/llarp/pathset.hpp @@ -60,6 +60,10 @@ namespace llarp void Tick(llarp_time_t now, llarp_router* r); + /// count the number of paths that will exist at this timestamp in future + size_t + NumPathsExistingAt(llarp_time_t futureTime) const; + void RemovePath(Path* path); @@ -176,7 +180,7 @@ namespace llarp size_t m_NumPaths; private: - typedef std::pair< RouterID, PathID_t > PathInfo_t; + using PathInfo_t = std::pair< RouterID, PathID_t >; struct PathInfoHash { @@ -187,7 +191,7 @@ namespace llarp } }; - typedef std::unordered_map< PathInfo_t, Path*, PathInfoHash > PathMap_t; + using PathMap_t = std::unordered_map< PathInfo_t, Path*, PathInfoHash >; PathMap_t m_Paths; }; diff --git a/include/llarp/profiling.hpp b/include/llarp/profiling.hpp index 120baf6e3..8c348b114 100644 --- a/include/llarp/profiling.hpp +++ b/include/llarp/profiling.hpp @@ -69,8 +69,8 @@ namespace llarp MarkPathSuccess(path::Path* p); private: - typedef llarp::util::Lock lock_t; - typedef llarp::util::Mutex mtx_t; + using lock_t = llarp::util::Lock; + using mtx_t = llarp::util::Mutex; mtx_t m_ProfilesMutex; std::map< RouterID, RouterProfile > m_Profiles; }; diff --git a/include/llarp/router_id.hpp b/include/llarp/router_id.hpp index 00359c07c..ef9a49a62 100644 --- a/include/llarp/router_id.hpp +++ b/include/llarp/router_id.hpp @@ -5,7 +5,7 @@ namespace llarp { - typedef AlignedBuffer< 32 > RouterID; + using RouterID = AlignedBuffer< 32 >; } -#endif \ No newline at end of file +#endif diff --git a/include/llarp/routing_endpoint.hpp b/include/llarp/routing_endpoint.hpp index aa2389ceb..532af81e0 100644 --- a/include/llarp/routing_endpoint.hpp +++ b/include/llarp/routing_endpoint.hpp @@ -6,7 +6,7 @@ namespace llarp { - typedef AlignedBuffer< 32 > RoutingEndpoint_t; + using RoutingEndpoint_t = AlignedBuffer< 32 >; /// Interface for end to end crypto between endpoints struct IRoutingEndpoint @@ -15,4 +15,4 @@ namespace llarp }; } // namespace llarp -#endif \ No newline at end of file +#endif diff --git a/include/llarp/rpc.hpp b/include/llarp/rpc.hpp index 8a08e8b2d..253b3fc47 100644 --- a/include/llarp/rpc.hpp +++ b/include/llarp/rpc.hpp @@ -41,9 +41,12 @@ namespace llarp Start(const std::string& remote); /// test if a router is valid + bool + VerifyRouter(const llarp::PubKey& pk); + + /// do per second tick void - VerifyRouter(llarp::PubKey pkey, - std::function< void(llarp::PubKey, bool) > handler); + Tick(llarp_time_t now); private: CallerImpl* m_Impl; diff --git a/include/llarp/service/config.hpp b/include/llarp/service/config.hpp index 1cf429d56..1f8d9cbe2 100644 --- a/include/llarp/service/config.hpp +++ b/include/llarp/service/config.hpp @@ -9,9 +9,9 @@ namespace llarp { struct Config { - typedef std::list< std::pair< std::string, std::string > > - section_values_t; - typedef std::pair< std::string, section_values_t > section_t; + using section_values_t = + std::list< std::pair< std::string, std::string > >; + using section_t = std::pair< std::string, section_values_t >; std::list< section_t > services; @@ -20,4 +20,4 @@ namespace llarp }; } // namespace service } // namespace llarp -#endif \ No newline at end of file +#endif diff --git a/include/llarp/service/endpoint.hpp b/include/llarp/service/endpoint.hpp index 0458f7669..a8d622c3c 100644 --- a/include/llarp/service/endpoint.hpp +++ b/include/llarp/service/endpoint.hpp @@ -137,8 +137,13 @@ namespace llarp HandleDataMessage(const PathID_t&, ProtocolMessage* msg); virtual bool - ProcessDataMessage(__attribute__((unused)) ProtocolMessage* msg) + ProcessDataMessage(ProtocolMessage* msg) { +#ifdef TESTNET + llarp::LogInfo("Got message from ", msg->sender.Addr()); +#else + (void)msg; +#endif return true; } @@ -188,7 +193,7 @@ namespace llarp bool CheckPathIsDead(path::Path* p, llarp_time_t latency); - typedef std::queue< PendingBuffer > PendingBufferQueue; + using PendingBufferQueue = std::queue< PendingBuffer >; struct SendContext { @@ -326,7 +331,7 @@ namespace llarp // passed a sendto context when we have a path established otherwise // nullptr if the path was not made before the timeout - typedef std::function< void(Address, OutboundContext*) > PathEnsureHook; + using PathEnsureHook = std::function< void(Address, OutboundContext*) >; /// return false if we have already called this function before for this /// address diff --git a/include/llarp/service/handler.hpp b/include/llarp/service/handler.hpp index 5c17ffe08..747580831 100644 --- a/include/llarp/service/handler.hpp +++ b/include/llarp/service/handler.hpp @@ -9,7 +9,7 @@ namespace llarp { namespace service { - typedef llarp::AlignedBuffer< 16 > ConvoTag; + using ConvoTag = llarp::AlignedBuffer< 16 >; struct ProtocolMessage; struct IDataHandler diff --git a/include/llarp/service/protocol.hpp b/include/llarp/service/protocol.hpp index 8d2a1b325..e22743900 100644 --- a/include/llarp/service/protocol.hpp +++ b/include/llarp/service/protocol.hpp @@ -17,7 +17,7 @@ namespace llarp { constexpr std::size_t MAX_PROTOCOL_MESSAGE_SIZE = 2048 * 2; - typedef uint64_t ProtocolType; + using ProtocolType = uint64_t; constexpr ProtocolType eProtocolText = 0UL; constexpr ProtocolType eProtocolTraffic = 1UL; diff --git a/include/llarp/service/vanity.hpp b/include/llarp/service/vanity.hpp index 432225ad0..4f3a92d71 100644 --- a/include/llarp/service/vanity.hpp +++ b/include/llarp/service/vanity.hpp @@ -8,7 +8,7 @@ namespace llarp { /// hidden service address - typedef llarp::AlignedBuffer< 16 > VanityNonce; + using VanityNonce = llarp::AlignedBuffer< 16 >; } // namespace service } // namespace llarp -#endif \ No newline at end of file +#endif diff --git a/include/llarp/string_view.hpp b/include/llarp/string_view.hpp index 7cf1be992..5146664a4 100644 --- a/include/llarp/string_view.hpp +++ b/include/llarp/string_view.hpp @@ -6,7 +6,7 @@ #include namespace llarp { - typedef std::string_view string_view; + using string_view = std::string_view; static std::string string_view_string(const string_view& v) { @@ -17,7 +17,7 @@ namespace llarp #include namespace llarp { - typedef std::string string_view; + using string_view = std::string; static std::string string_view_string(const string_view& v) @@ -26,4 +26,4 @@ namespace llarp }; } // namespace llarp #endif -#endif \ No newline at end of file +#endif diff --git a/include/llarp/threading.hpp b/include/llarp/threading.hpp index fb17ba3ac..5bf0ac944 100644 --- a/include/llarp/threading.hpp +++ b/include/llarp/threading.hpp @@ -34,9 +34,9 @@ namespace llarp } }; - typedef std::mutex mtx_t; - typedef std::unique_lock< std::mutex > lock_t; - typedef std::condition_variable cond_t; + using mtx_t = std::mutex; + using lock_t = std::unique_lock< std::mutex >; + using cond_t = std::condition_variable; struct Mutex { diff --git a/include/llarp/timer.hpp b/include/llarp/timer.hpp index c78e1e23d..5d887cf60 100644 --- a/include/llarp/timer.hpp +++ b/include/llarp/timer.hpp @@ -5,8 +5,11 @@ #include #include +#include + /** called with userptr, original timeout, left */ -typedef void (*llarp_timer_handler_func)(void *, uint64_t, uint64_t); +using llarp_timer_handler_func = + std::function< void(void *, uint64_t, uint64_t) >; struct llarp_timeout_job { diff --git a/include/llarp/types.hpp b/include/llarp/types.hpp index abaaee29e..0705a92b4 100644 --- a/include/llarp/types.hpp +++ b/include/llarp/types.hpp @@ -3,7 +3,7 @@ #include using llarp_proto_version_t = std::uint8_t; -using llarp_time_t = std::uint64_t; -using llarp_seconds_t = std::uint64_t; +using llarp_time_t = std::uint64_t; +using llarp_seconds_t = std::uint64_t; #endif diff --git a/include/tl/optional.hpp b/include/tl/optional.hpp index 88ce30e42..7c6a4d669 100644 --- a/include/tl/optional.hpp +++ b/include/tl/optional.hpp @@ -24,68 +24,78 @@ #include #include -#if (defined(_MSC_VER) && _MSC_VER == 1900) +#if(defined(_MSC_VER) && _MSC_VER == 1900) #define TL_OPTIONAL_MSVC2015 #endif -#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ - !defined(__clang__)) +#if(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 \ + && !defined(__clang__)) #define TL_OPTIONAL_GCC49 #endif -#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ - !defined(__clang__)) +#if(defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 \ + && !defined(__clang__)) #define TL_OPTIONAL_GCC54 #endif -#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ - !defined(__clang__)) +#if(defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 \ + && !defined(__clang__)) #define TL_OPTIONAL_GCC55 #endif -#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ - !defined(__clang__)) +#if(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 \ + && !defined(__clang__)) // GCC < 5 doesn't support overloading on const&& for member functions #define TL_OPTIONAL_NO_CONSTRR // GCC < 5 doesn't support some standard C++11 type traits -#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ - std::has_trivial_copy_constructor::value -#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::has_trivial_copy_assign::value +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::has_trivial_copy_constructor< T >::value +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::has_trivial_copy_assign< T >::value // This one will be different for GCC 5.7 if it's ever supported -#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible::value +#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible< T >::value -// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks std::vector -// for non-copyable types -#elif (defined(__GNUC__) && __GNUC__ < 8 && \ - !defined(__clang__)) +// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks +// std::vector for non-copyable types +#elif(defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__)) #ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX #define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX -namespace tl { - namespace detail { - template - struct is_trivially_copy_constructible : std::is_trivially_copy_constructible{}; +namespace tl +{ + namespace detail + { + template < class T > + struct is_trivially_copy_constructible + : std::is_trivially_copy_constructible< T > + { + }; #ifdef _GLIBCXX_VECTOR - template - struct is_trivially_copy_constructible> - : std::is_trivially_copy_constructible{}; -#endif - } -} + template < class T, class A > + struct is_trivially_copy_constructible< std::vector< T, A > > + : std::is_trivially_copy_constructible< T > + { + }; +#endif + } // namespace detail +} // namespace tl #endif -#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ - tl::detail::is_trivially_copy_constructible::value -#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ - std::is_trivially_copy_assignable::value -#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible::value +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + tl::detail::is_trivially_copy_constructible< T >::value +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable< T >::value +#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible< T >::value #else -#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ - std::is_trivially_copy_constructible::value -#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ - std::is_trivially_copy_assignable::value -#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible::value +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible< T >::value +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable< T >::value +#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible< T >::value #endif #if __cplusplus > 201103L @@ -93,8 +103,8 @@ namespace tl { #endif // constexpr implies const in C++11, not C++14 -#if (__cplusplus == 201103L || defined(TL_OPTIONAL_MSVC2015) || \ - defined(TL_OPTIONAL_GCC49)) +#if(__cplusplus == 201103L || defined(TL_OPTIONAL_MSVC2015) \ + || defined(TL_OPTIONAL_GCC49)) /// \exclude #define TL_OPTIONAL_11_CONSTEXPR #else @@ -102,42 +112,58 @@ namespace tl { #define TL_OPTIONAL_11_CONSTEXPR constexpr #endif -namespace tl { +namespace tl +{ #ifndef TL_MONOSTATE_INPLACE_MUTEX #define TL_MONOSTATE_INPLACE_MUTEX -/// \brief Used to represent an optional with no data; essentially a bool -class monostate {}; - -/// \brief A tag type to tell optional to construct its value in-place -struct in_place_t { - explicit in_place_t() = default; -}; -/// \brief A tag to tell optional to construct its value in-place -static constexpr in_place_t in_place{}; + /// \brief Used to represent an optional with no data; essentially a bool + class monostate + { + }; + + /// \brief A tag type to tell optional to construct its value in-place + struct in_place_t + { + explicit in_place_t() = default; + }; + /// \brief A tag to tell optional to construct its value in-place + static constexpr in_place_t in_place{}; #endif -template class optional; + template < class T > + class optional; -/// \exclude -namespace detail { + /// \exclude + namespace detail + { #ifndef TL_TRAITS_MUTEX #define TL_TRAITS_MUTEX -// C++14-style aliases for brevity -template using remove_const_t = typename std::remove_const::type; -template -using remove_reference_t = typename std::remove_reference::type; -template using decay_t = typename std::decay::type; -template -using enable_if_t = typename std::enable_if::type; -template -using conditional_t = typename std::conditional::type; - -// std::conjunction from C++17 -template struct conjunction : std::true_type {}; -template struct conjunction : B {}; -template -struct conjunction - : std::conditional, B>::type {}; + // C++14-style aliases for brevity + template < class T > + using remove_const_t = typename std::remove_const< T >::type; + template < class T > + using remove_reference_t = typename std::remove_reference< T >::type; + template < class T > + using decay_t = typename std::decay< T >::type; + template < bool E, class T = void > + using enable_if_t = typename std::enable_if< E, T >::type; + template < bool B, class T, class F > + using conditional_t = typename std::conditional< B, T, F >::type; + + // std::conjunction from C++17 + template < class... > + struct conjunction : std::true_type + { + }; + template < class B > + struct conjunction< B > : B + { + }; + template < class B, class... Bs > + struct conjunction< B, Bs... > + : std::conditional< bool(B::value), conjunction< Bs... >, B >::type + { + }; #if defined(_LIBCPP_VERSION) && __cplusplus == 201103L #define TL_OPTIONAL_LIBCXX_MEM_FN_WORKAROUND @@ -147,323 +173,481 @@ struct conjunction // which results in a hard-error when using it in a noexcept expression // in some cases. This is a check to workaround the common failing case. #ifdef TL_OPTIONAL_LIBCXX_MEM_FN_WORKAROUND -template struct is_pointer_to_non_const_member_func : std::false_type{}; -template -struct is_pointer_to_non_const_member_func : std::true_type{}; -template -struct is_pointer_to_non_const_member_func : std::true_type{}; -template -struct is_pointer_to_non_const_member_func : std::true_type{}; -template -struct is_pointer_to_non_const_member_func : std::true_type{}; -template -struct is_pointer_to_non_const_member_func : std::true_type{}; -template -struct is_pointer_to_non_const_member_func : std::true_type{}; - -template struct is_const_or_const_ref : std::false_type{}; -template struct is_const_or_const_ref : std::true_type{}; -template struct is_const_or_const_ref : std::true_type{}; + template < class T > + struct is_pointer_to_non_const_member_func : std::false_type + { + }; + template < class T, class Ret, class... Args > + struct is_pointer_to_non_const_member_func< Ret (T::*)(Args...) > + : std::true_type + { + }; + template < class T, class Ret, class... Args > + struct is_pointer_to_non_const_member_func< Ret (T::*)(Args...) & > + : std::true_type + { + }; + template < class T, class Ret, class... Args > + struct is_pointer_to_non_const_member_func< Ret (T::*)(Args...) && > + : std::true_type + { + }; + template < class T, class Ret, class... Args > + struct is_pointer_to_non_const_member_func< Ret (T::*)(Args...) volatile > + : std::true_type + { + }; + template < class T, class Ret, class... Args > + struct is_pointer_to_non_const_member_func< Ret (T::*)(Args...) volatile & > + : std::true_type + { + }; + template < class T, class Ret, class... Args > + struct is_pointer_to_non_const_member_func< Ret (T::*)( + Args...) volatile && > : std::true_type + { + }; + + template < class T > + struct is_const_or_const_ref : std::false_type + { + }; + template < class T > + struct is_const_or_const_ref< T const & > : std::true_type + { + }; + template < class T > + struct is_const_or_const_ref< T const > : std::true_type + { + }; #endif -// std::invoke from C++17 -// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround -template ::value - && is_const_or_const_ref::value)>, + typename = + enable_if_t< !(is_pointer_to_non_const_member_func< Fn >::value + && is_const_or_const_ref< Args... >::value) >, #endif - typename = enable_if_t>::value>, - int = 0> -constexpr auto invoke(Fn &&f, Args &&... args) noexcept( - noexcept(std::mem_fn(f)(std::forward(args)...))) - -> decltype(std::mem_fn(f)(std::forward(args)...)) { - return std::mem_fn(f)(std::forward(args)...); -} - -template >::value>> -constexpr auto invoke(Fn &&f, Args &&... args) noexcept( - noexcept(std::forward(f)(std::forward(args)...))) - -> decltype(std::forward(f)(std::forward(args)...)) { - return std::forward(f)(std::forward(args)...); -} - -// std::invoke_result from C++17 -template struct invoke_result_impl; - -template -struct invoke_result_impl< - F, decltype(detail::invoke(std::declval(), std::declval()...), void()), - Us...> { - using type = decltype(detail::invoke(std::declval(), std::declval()...)); -}; - -template -using invoke_result = invoke_result_impl; - -template -using invoke_result_t = typename invoke_result::type; + typename = + enable_if_t< std::is_member_pointer< decay_t< Fn > >::value >, + int = 0 > + constexpr auto + invoke(Fn &&f, Args &&... args) noexcept( + noexcept(std::mem_fn(f)(std::forward< Args >(args)...))) + -> decltype(std::mem_fn(f)(std::forward< Args >(args)...)) + { + return std::mem_fn(f)(std::forward< Args >(args)...); + } + + template < typename Fn, typename... Args, + typename = enable_if_t< + !std::is_member_pointer< decay_t< Fn > >::value > > + constexpr auto + invoke(Fn &&f, Args &&... args) noexcept( + noexcept(std::forward< Fn >(f)(std::forward< Args >(args)...))) + -> decltype(std::forward< Fn >(f)(std::forward< Args >(args)...)) + { + return std::forward< Fn >(f)(std::forward< Args >(args)...); + } + + // std::invoke_result from C++17 + template < class F, class, class... Us > + struct invoke_result_impl; + + template < class F, class... Us > + struct invoke_result_impl< F, + decltype(detail::invoke(std::declval< F >(), + std::declval< Us >()...), + void()), + Us... > + { + using type = decltype( + detail::invoke(std::declval< F >(), std::declval< Us >()...)); + }; + + template < class F, class... Us > + using invoke_result = invoke_result_impl< F, void, Us... >; + + template < class F, class... Us > + using invoke_result_t = typename invoke_result< F, Us... >::type; #endif -// std::void_t from C++17 -template struct voider { using type = void; }; -template using void_t = typename voider::type; - -// Trait for checking if a type is a tl::optional -template struct is_optional_impl : std::false_type {}; -template struct is_optional_impl> : std::true_type {}; -template using is_optional = is_optional_impl>; - -// Change void to tl::monostate -template -using fixup_void = conditional_t::value, monostate, U>; - -template > -using get_map_return = optional>>; - -// Check if invoking F for some Us returns void -template struct returns_void_impl; -template -struct returns_void_impl>, U...> - : std::is_void> {}; -template -using returns_void = returns_void_impl; - -template -using enable_if_ret_void = enable_if_t::value>; - -template -using disable_if_ret_void = enable_if_t::value>; - -template -using enable_forward_value = - detail::enable_if_t::value && - !std::is_same, in_place_t>::value && - !std::is_same, detail::decay_t>::value>; - -template -using enable_from_other = detail::enable_if_t< - std::is_constructible::value && - !std::is_constructible &>::value && - !std::is_constructible &&>::value && - !std::is_constructible &>::value && - !std::is_constructible &&>::value && - !std::is_convertible &, T>::value && - !std::is_convertible &&, T>::value && - !std::is_convertible &, T>::value && - !std::is_convertible &&, T>::value>; - -template -using enable_assign_forward = detail::enable_if_t< - !std::is_same, detail::decay_t>::value && - !detail::conjunction, - std::is_same>>::value && - std::is_constructible::value && std::is_assignable::value>; - -template -using enable_assign_from_other = detail::enable_if_t< - std::is_constructible::value && - std::is_assignable::value && - !std::is_constructible &>::value && - !std::is_constructible &&>::value && - !std::is_constructible &>::value && - !std::is_constructible &&>::value && - !std::is_convertible &, T>::value && - !std::is_convertible &&, T>::value && - !std::is_convertible &, T>::value && - !std::is_convertible &&, T>::value && - !std::is_assignable &>::value && - !std::is_assignable &&>::value && - !std::is_assignable &>::value && - !std::is_assignable &&>::value>; + // std::void_t from C++17 + template < class... > + struct voider + { + using type = void; + }; + template < class... Ts > + using void_t = typename voider< Ts... >::type; + + // Trait for checking if a type is a tl::optional + template < class T > + struct is_optional_impl : std::false_type + { + }; + template < class T > + struct is_optional_impl< optional< T > > : std::true_type + { + }; + template < class T > + using is_optional = is_optional_impl< decay_t< T > >; + + // Change void to tl::monostate + template < class U > + using fixup_void = conditional_t< std::is_void< U >::value, monostate, U >; + + template < class F, class U, class = invoke_result_t< F, U > > + using get_map_return = optional< fixup_void< invoke_result_t< F, U > > >; + + // Check if invoking F for some Us returns void + template < class F, class = void, class... U > + struct returns_void_impl; + template < class F, class... U > + struct returns_void_impl< F, void_t< invoke_result_t< F, U... > >, U... > + : std::is_void< invoke_result_t< F, U... > > + { + }; + template < class F, class... U > + using returns_void = returns_void_impl< F, void, U... >; + + template < class T, class... U > + using enable_if_ret_void = enable_if_t< returns_void< T &&, U... >::value >; + + template < class T, class... U > + using disable_if_ret_void = + enable_if_t< !returns_void< T &&, U... >::value >; + + template < class T, class U > + using enable_forward_value = detail::enable_if_t< + std::is_constructible< T, U && >::value + && !std::is_same< detail::decay_t< U >, in_place_t >::value + && !std::is_same< optional< T >, detail::decay_t< U > >::value >; + + template < class T, class U, class Other > + using enable_from_other = detail::enable_if_t< + std::is_constructible< T, Other >::value + && !std::is_constructible< T, optional< U > & >::value + && !std::is_constructible< T, optional< U > && >::value + && !std::is_constructible< T, const optional< U > & >::value + && !std::is_constructible< T, const optional< U > && >::value + && !std::is_convertible< optional< U > &, T >::value + && !std::is_convertible< optional< U > &&, T >::value + && !std::is_convertible< const optional< U > &, T >::value + && !std::is_convertible< const optional< U > &&, T >::value >; + + template < class T, class U > + using enable_assign_forward = detail::enable_if_t< + !std::is_same< optional< T >, detail::decay_t< U > >::value + && !detail::conjunction< + std::is_scalar< T >, + std::is_same< T, detail::decay_t< U > > >::value + && std::is_constructible< T, U >::value + && std::is_assignable< T &, U >::value >; + + template < class T, class U, class Other > + using enable_assign_from_other = detail::enable_if_t< + std::is_constructible< T, Other >::value + && std::is_assignable< T &, Other >::value + && !std::is_constructible< T, optional< U > & >::value + && !std::is_constructible< T, optional< U > && >::value + && !std::is_constructible< T, const optional< U > & >::value + && !std::is_constructible< T, const optional< U > && >::value + && !std::is_convertible< optional< U > &, T >::value + && !std::is_convertible< optional< U > &&, T >::value + && !std::is_convertible< const optional< U > &, T >::value + && !std::is_convertible< const optional< U > &&, T >::value + && !std::is_assignable< T &, optional< U > & >::value + && !std::is_assignable< T &, optional< U > && >::value + && !std::is_assignable< T &, const optional< U > & >::value + && !std::is_assignable< T &, const optional< U > && >::value >; #ifdef _MSC_VER -// TODO make a version which works with MSVC -template struct is_swappable : std::true_type {}; - -template struct is_nothrow_swappable : std::true_type {}; + // TODO make a version which works with MSVC + template < class T, class U = T > + struct is_swappable : std::true_type + { + }; + + template < class T, class U = T > + struct is_nothrow_swappable : std::true_type + { + }; #else -// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept -namespace swap_adl_tests { -// if swap ADL finds this then it would call std::swap otherwise (same -// signature) -struct tag {}; - -template tag swap(T &, T &); -template tag swap(T (&a)[N], T (&b)[N]); - -// helper functions to test if an unqualified swap is possible, and if it -// becomes std::swap -template std::false_type can_swap(...) noexcept(false); -template (), std::declval()))> -std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), - std::declval()))); - -template std::false_type uses_std(...); -template -std::is_same(), std::declval())), tag> -uses_std(int); - -template -struct is_std_swap_noexcept - : std::integral_constant::value && - std::is_nothrow_move_assignable::value> {}; - -template -struct is_std_swap_noexcept : is_std_swap_noexcept {}; - -template -struct is_adl_swap_noexcept - : std::integral_constant(0))> {}; -} // namespace swap_adl_tests - -template -struct is_swappable - : std::integral_constant< - bool, - decltype(detail::swap_adl_tests::can_swap(0))::value && - (!decltype(detail::swap_adl_tests::uses_std(0))::value || - (std::is_move_assignable::value && - std::is_move_constructible::value))> {}; - -template -struct is_swappable - : std::integral_constant< - bool, - decltype(detail::swap_adl_tests::can_swap(0))::value && - (!decltype( - detail::swap_adl_tests::uses_std(0))::value || - is_swappable::value)> {}; - -template -struct is_nothrow_swappable - : std::integral_constant< - bool, - is_swappable::value && - ((decltype(detail::swap_adl_tests::uses_std(0))::value - &&detail::swap_adl_tests::is_std_swap_noexcept::value) || - (!decltype(detail::swap_adl_tests::uses_std(0))::value && - detail::swap_adl_tests::is_adl_swap_noexcept::value))> { -}; + // https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept + namespace swap_adl_tests + { + // if swap ADL finds this then it would call std::swap otherwise (same + // signature) + struct tag + { + }; + + template < class T > + tag + swap(T &, T &); + template < class T, std::size_t N > + tag swap(T (&a)[N], T (&b)[N]); + + // helper functions to test if an unqualified swap is possible, and if it + // becomes std::swap + template < class, class > + std::false_type + can_swap(...) noexcept(false); + template < class T, class U, + class = decltype(swap(std::declval< T & >(), + std::declval< U & >())) > + std::true_type + can_swap(int) noexcept(noexcept(swap(std::declval< T & >(), + std::declval< U & >()))); + + template < class, class > + std::false_type + uses_std(...); + template < class T, class U > + std::is_same< + decltype(swap(std::declval< T & >(), std::declval< U & >())), tag > + uses_std(int); + + template < class T > + struct is_std_swap_noexcept + : std::integral_constant< + bool, + std::is_nothrow_move_constructible< T >::value + && std::is_nothrow_move_assignable< T >::value > + { + }; + + template < class T, std::size_t N > + struct is_std_swap_noexcept< T[N] > : is_std_swap_noexcept< T > + { + }; + + template < class T, class U > + struct is_adl_swap_noexcept + : std::integral_constant< bool, noexcept(can_swap< T, U >(0)) > + { + }; + } // namespace swap_adl_tests + + template < class T, class U = T > + struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap< T, U >(0))::value + && (!decltype( + detail::swap_adl_tests::uses_std< T, U >(0))::value + || (std::is_move_assignable< T >::value + && std::is_move_constructible< T >::value)) > + { + }; + + template < class T, std::size_t N > + struct is_swappable< T[N], T[N] > + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap< T[N], T[N] >(0))::value + && (!decltype(detail::swap_adl_tests::uses_std< T[N], T[N] >( + 0))::value + || is_swappable< T, T >::value) > + { + }; + + template < class T, class U = T > + struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable< T, U >::value + && ((decltype(detail::swap_adl_tests::uses_std< T, U >( + 0))::value &&detail::swap_adl_tests:: + is_std_swap_noexcept< T >::value) + || (!decltype(detail::swap_adl_tests::uses_std< T, U >( + 0))::value &&detail::swap_adl_tests:: + is_adl_swap_noexcept< T, U >::value)) > + { + }; #endif -// The storage base manages the actual storage, and correctly propagates -// trivial destruction from T. This case is for when T is not trivially -// destructible. -template ::value> -struct optional_storage_base { - TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept - : m_dummy(), m_has_value(false) {} - - template - TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u) - : m_value(std::forward(u)...), m_has_value(true) {} - - ~optional_storage_base() { - if (m_has_value) { - m_value.~T(); - m_has_value = false; - } - } - - struct dummy {}; - union { - dummy m_dummy; - T m_value; - }; - - bool m_has_value; -}; - -// This case is for when T is trivially destructible. -template struct optional_storage_base { - TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept - : m_dummy(), m_has_value(false) {} - - template - TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u) - : m_value(std::forward(u)...), m_has_value(true) {} - - // No destructor, so this class is trivially destructible - - struct dummy {}; - union { - dummy m_dummy; - T m_value; - }; + // The storage base manages the actual storage, and correctly propagates + // trivial destruction from T. This case is for when T is not trivially + // destructible. + template < class T, bool = ::std::is_trivially_destructible< T >::value > + struct optional_storage_base + { + TL_OPTIONAL_11_CONSTEXPR + optional_storage_base() noexcept + : m_dummy(), m_has_value(false) + { + } - bool m_has_value = false; -}; + template < class... U > + TL_OPTIONAL_11_CONSTEXPR + optional_storage_base(in_place_t, U &&... u) + : m_value(std::forward< U >(u)...), m_has_value(true) + { + } -// This base class provides some handy member functions which can be used in -// further derived classes -template struct optional_operations_base : optional_storage_base { - using optional_storage_base::optional_storage_base; + ~optional_storage_base() + { + if(m_has_value) + { + m_value.~T(); + m_has_value = false; + } + } - void hard_reset() noexcept { - get().~T(); - this->m_has_value = false; - } + struct dummy + { + }; + union { + dummy m_dummy; + T m_value; + }; + + bool m_has_value; + }; + + // This case is for when T is trivially destructible. + template < class T > + struct optional_storage_base< T, true > + { + TL_OPTIONAL_11_CONSTEXPR + optional_storage_base() noexcept + : m_dummy(), m_has_value(false) + { + } - template void construct(Args &&... args) noexcept { - new (std::addressof(this->m_value)) T(std::forward(args)...); - this->m_has_value = true; - } + template < class... U > + TL_OPTIONAL_11_CONSTEXPR + optional_storage_base(in_place_t, U &&... u) + : m_value(std::forward< U >(u)...), m_has_value(true) + { + } - template void assign(Opt &&rhs) { - if (this->has_value()) { - if (rhs.has_value()) { - this->m_value = std::forward(rhs).get(); - } else { - this->m_value.~T(); + // No destructor, so this class is trivially destructible + + struct dummy + { + }; + union { + dummy m_dummy; + T m_value; + }; + + bool m_has_value = false; + }; + + // This base class provides some handy member functions which can be used in + // further derived classes + template < class T > + struct optional_operations_base : optional_storage_base< T > + { + using optional_storage_base< T >::optional_storage_base; + + void + hard_reset() noexcept + { + get().~T(); this->m_has_value = false; } - } - else if (rhs.has_value()) { - construct(std::forward(rhs).get()); - } - } + template < class... Args > + void + construct(Args &&... args) noexcept + { + new(std::addressof(this->m_value)) T(std::forward< Args >(args)...); + this->m_has_value = true; + } - bool has_value() const { return this->m_has_value; } + template < class Opt > + void + assign(Opt &&rhs) + { + if(this->has_value()) + { + if(rhs.has_value()) + { + this->m_value = std::forward< Opt >(rhs).get(); + } + else + { + this->m_value.~T(); + this->m_has_value = false; + } + } + + else if(rhs.has_value()) + { + construct(std::forward< Opt >(rhs).get()); + } + } - TL_OPTIONAL_11_CONSTEXPR T &get() & { return this->m_value; } - TL_OPTIONAL_11_CONSTEXPR const T &get() const & { return this->m_value; } - TL_OPTIONAL_11_CONSTEXPR T &&get() && { return std::move(this->m_value); } + bool + has_value() const + { + return this->m_has_value; + } + + TL_OPTIONAL_11_CONSTEXPR T & + get() & + { + return this->m_value; + } + TL_OPTIONAL_11_CONSTEXPR const T & + get() const & + { + return this->m_value; + } + TL_OPTIONAL_11_CONSTEXPR T && + get() && + { + return std::move(this->m_value); + } #ifndef TL_OPTIONAL_NO_CONSTRR - constexpr const T &&get() const && { return std::move(this->m_value); } + constexpr const T && + get() const && + { + return std::move(this->m_value); + } #endif -}; - -// This class manages conditionally having a trivial copy constructor -// This specialization is for when T is trivially copy constructible -template -struct optional_copy_base : optional_operations_base { - using optional_operations_base::optional_operations_base; -}; - -// This specialization is for when T is not trivially copy constructible -template -struct optional_copy_base : optional_operations_base { - using optional_operations_base::optional_operations_base; - - optional_copy_base() = default; - optional_copy_base(const optional_copy_base &rhs) { - if (rhs.has_value()) { - this->construct(rhs.get()); - } else { - this->m_has_value = false; - } - } + }; + + // This class manages conditionally having a trivial copy constructor + // This specialization is for when T is trivially copy constructible + template < class T, bool = TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) > + struct optional_copy_base : optional_operations_base< T > + { + using optional_operations_base< T >::optional_operations_base; + }; + + // This specialization is for when T is not trivially copy constructible + template < class T > + struct optional_copy_base< T, false > : optional_operations_base< T > + { + using optional_operations_base< T >::optional_operations_base; + + optional_copy_base() = default; + optional_copy_base(const optional_copy_base &rhs) + { + if(rhs.has_value()) + { + this->construct(rhs.get()); + } + else + { + this->m_has_value = false; + } + } - optional_copy_base(optional_copy_base &&rhs) = default; - optional_copy_base &operator=(const optional_copy_base &rhs) = default; - optional_copy_base &operator=(optional_copy_base &&rhs) = default; -}; + optional_copy_base(optional_copy_base &&rhs) = default; + optional_copy_base & + operator=(const optional_copy_base &rhs) = default; + optional_copy_base & + operator=(optional_copy_base &&rhs) = default; + }; // This class manages conditionally having a trivial move constructor // Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it @@ -471,54 +655,71 @@ struct optional_copy_base : optional_operations_base { // have to make do with a non-trivial move constructor even if T is trivially // move constructible #ifndef TL_OPTIONAL_GCC49 -template ::value> -struct optional_move_base : optional_copy_base { - using optional_copy_base::optional_copy_base; -}; + template < class T, + bool = std::is_trivially_move_constructible< T >::value > + struct optional_move_base : optional_copy_base< T > + { + using optional_copy_base< T >::optional_copy_base; + }; #else -template struct optional_move_base; + template < class T, bool = false > + struct optional_move_base; #endif -template struct optional_move_base : optional_copy_base { - using optional_copy_base::optional_copy_base; - - optional_move_base() = default; - optional_move_base(const optional_move_base &rhs) = default; - - optional_move_base(optional_move_base &&rhs) noexcept( - std::is_nothrow_move_constructible::value) { - if (rhs.has_value()) { - this->construct(std::move(rhs.get())); - } else { - this->m_has_value = false; - } - } - optional_move_base &operator=(const optional_move_base &rhs) = default; - optional_move_base &operator=(optional_move_base &&rhs) = default; -}; - -// This class manages conditionally having a trivial copy assignment operator -template -struct optional_copy_assign_base : optional_move_base { - using optional_move_base::optional_move_base; -}; - -template -struct optional_copy_assign_base : optional_move_base { - using optional_move_base::optional_move_base; - - optional_copy_assign_base() = default; - optional_copy_assign_base(const optional_copy_assign_base &rhs) = default; - - optional_copy_assign_base(optional_copy_assign_base &&rhs) = default; - optional_copy_assign_base &operator=(const optional_copy_assign_base &rhs) { - this->assign(rhs); - return *this; - } - optional_copy_assign_base & - operator=(optional_copy_assign_base &&rhs) = default; -}; + template < class T > + struct optional_move_base< T, false > : optional_copy_base< T > + { + using optional_copy_base< T >::optional_copy_base; + + optional_move_base() = default; + optional_move_base(const optional_move_base &rhs) = default; + + optional_move_base(optional_move_base &&rhs) noexcept( + std::is_nothrow_move_constructible< T >::value) + { + if(rhs.has_value()) + { + this->construct(std::move(rhs.get())); + } + else + { + this->m_has_value = false; + } + } + optional_move_base & + operator=(const optional_move_base &rhs) = default; + optional_move_base & + operator=(optional_move_base &&rhs) = default; + }; + + // This class manages conditionally having a trivial copy assignment + // operator + template < class T, + bool = TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) + && TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) + && TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) > + struct optional_copy_assign_base : optional_move_base< T > + { + using optional_move_base< T >::optional_move_base; + }; + + template < class T > + struct optional_copy_assign_base< T, false > : optional_move_base< T > + { + using optional_move_base< T >::optional_move_base; + + optional_copy_assign_base() = default; + optional_copy_assign_base(const optional_copy_assign_base &rhs) = default; + + optional_copy_assign_base(optional_copy_assign_base &&rhs) = default; + optional_copy_assign_base & + operator=(const optional_copy_assign_base &rhs) + { + this->assign(rhs); + return *this; + } + optional_copy_assign_base & + operator=(optional_copy_assign_base &&rhs) = default; + }; // This class manages conditionally having a trivial move assignment operator // Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it @@ -526,1826 +727,2302 @@ struct optional_copy_assign_base : optional_move_base { // to make do with a non-trivial move assignment operator even if T is trivially // move assignable #ifndef TL_OPTIONAL_GCC49 -template ::value - &&std::is_trivially_move_constructible::value - &&std::is_trivially_move_assignable::value> -struct optional_move_assign_base : optional_copy_assign_base { - using optional_copy_assign_base::optional_copy_assign_base; -}; + template < class T, + bool = std::is_trivially_destructible< T >::value + &&std::is_trivially_move_constructible< T >::value + &&std::is_trivially_move_assignable< T >::value > + struct optional_move_assign_base : optional_copy_assign_base< T > + { + using optional_copy_assign_base< T >::optional_copy_assign_base; + }; #else -template struct optional_move_assign_base; + template < class T, bool = false > + struct optional_move_assign_base; #endif -template -struct optional_move_assign_base : optional_copy_assign_base { - using optional_copy_assign_base::optional_copy_assign_base; + template < class T > + struct optional_move_assign_base< T, false > + : optional_copy_assign_base< T > + { + using optional_copy_assign_base< T >::optional_copy_assign_base; - optional_move_assign_base() = default; - optional_move_assign_base(const optional_move_assign_base &rhs) = default; + optional_move_assign_base() = default; + optional_move_assign_base(const optional_move_assign_base &rhs) = default; - optional_move_assign_base(optional_move_assign_base &&rhs) = default; + optional_move_assign_base(optional_move_assign_base &&rhs) = default; - optional_move_assign_base & - operator=(const optional_move_assign_base &rhs) = default; + optional_move_assign_base & + operator=(const optional_move_assign_base &rhs) = default; - optional_move_assign_base & - operator=(optional_move_assign_base &&rhs) noexcept( - std::is_nothrow_move_constructible::value - &&std::is_nothrow_move_assignable::value) { - this->assign(std::move(rhs)); - return *this; - } -}; - -// optional_delete_ctor_base will conditionally delete copy and move -// constructors depending on whether T is copy/move constructible -template ::value, - bool EnableMove = std::is_move_constructible::value> -struct optional_delete_ctor_base { - optional_delete_ctor_base() = default; - optional_delete_ctor_base(const optional_delete_ctor_base &) = default; - optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = default; - optional_delete_ctor_base & - operator=(const optional_delete_ctor_base &) = default; - optional_delete_ctor_base & - operator=(optional_delete_ctor_base &&) noexcept = default; -}; - -template struct optional_delete_ctor_base { - optional_delete_ctor_base() = default; - optional_delete_ctor_base(const optional_delete_ctor_base &) = default; - optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = delete; - optional_delete_ctor_base & - operator=(const optional_delete_ctor_base &) = default; - optional_delete_ctor_base & - operator=(optional_delete_ctor_base &&) noexcept = default; -}; - -template struct optional_delete_ctor_base { - optional_delete_ctor_base() = default; - optional_delete_ctor_base(const optional_delete_ctor_base &) = delete; - optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = default; - optional_delete_ctor_base & - operator=(const optional_delete_ctor_base &) = default; - optional_delete_ctor_base & - operator=(optional_delete_ctor_base &&) noexcept = default; -}; - -template struct optional_delete_ctor_base { - optional_delete_ctor_base() = default; - optional_delete_ctor_base(const optional_delete_ctor_base &) = delete; - optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = delete; - optional_delete_ctor_base & - operator=(const optional_delete_ctor_base &) = default; - optional_delete_ctor_base & - operator=(optional_delete_ctor_base &&) noexcept = default; -}; - -// optional_delete_assign_base will conditionally delete copy and move -// constructors depending on whether T is copy/move constructible + assignable -template ::value && - std::is_copy_assignable::value), - bool EnableMove = (std::is_move_constructible::value && - std::is_move_assignable::value)> -struct optional_delete_assign_base { - optional_delete_assign_base() = default; - optional_delete_assign_base(const optional_delete_assign_base &) = default; - optional_delete_assign_base(optional_delete_assign_base &&) noexcept = - default; - optional_delete_assign_base & - operator=(const optional_delete_assign_base &) = default; - optional_delete_assign_base & - operator=(optional_delete_assign_base &&) noexcept = default; -}; - -template struct optional_delete_assign_base { - optional_delete_assign_base() = default; - optional_delete_assign_base(const optional_delete_assign_base &) = default; - optional_delete_assign_base(optional_delete_assign_base &&) noexcept = - default; - optional_delete_assign_base & - operator=(const optional_delete_assign_base &) = default; - optional_delete_assign_base & - operator=(optional_delete_assign_base &&) noexcept = delete; -}; - -template struct optional_delete_assign_base { - optional_delete_assign_base() = default; - optional_delete_assign_base(const optional_delete_assign_base &) = default; - optional_delete_assign_base(optional_delete_assign_base &&) noexcept = - default; - optional_delete_assign_base & - operator=(const optional_delete_assign_base &) = delete; - optional_delete_assign_base & - operator=(optional_delete_assign_base &&) noexcept = default; -}; - -template struct optional_delete_assign_base { - optional_delete_assign_base() = default; - optional_delete_assign_base(const optional_delete_assign_base &) = default; - optional_delete_assign_base(optional_delete_assign_base &&) noexcept = - default; - optional_delete_assign_base & - operator=(const optional_delete_assign_base &) = delete; - optional_delete_assign_base & - operator=(optional_delete_assign_base &&) noexcept = delete; -}; - -} // namespace detail - -/// \brief A tag type to represent an empty optional -struct nullopt_t { - struct do_not_use {}; - constexpr explicit nullopt_t(do_not_use, do_not_use) noexcept {} -}; -/// \brief Represents an empty optional -/// \synopsis static constexpr nullopt_t nullopt; -/// -/// *Examples*: -/// ``` -/// tl::optional a = tl::nullopt; -/// void foo (tl::optional); -/// foo(tl::nullopt); //pass an empty optional -/// ``` -static constexpr nullopt_t nullopt{nullopt_t::do_not_use{}, - nullopt_t::do_not_use{}}; - -class bad_optional_access : public std::exception { -public: - bad_optional_access() = default; - const char *what() const noexcept { return "Optional has no value"; } -}; - -/// An optional object is an object that contains the storage for another -/// object and manages the lifetime of this contained object, if any. The -/// contained object may be initialized after the optional object has been -/// initialized, and may be destroyed before the optional object has been -/// destroyed. The initialization state of the contained object is tracked by -/// the optional object. -template -class optional : private detail::optional_move_assign_base, - private detail::optional_delete_ctor_base, - private detail::optional_delete_assign_base { - using base = detail::optional_move_assign_base; - - static_assert(!std::is_same::value, - "instantiation of optional with in_place_t is ill-formed"); - static_assert(!std::is_same, nullopt_t>::value, - "instantiation of optional with nullopt_t is ill-formed"); - -public: + optional_move_assign_base & + operator=(optional_move_assign_base &&rhs) noexcept( + std::is_nothrow_move_constructible< T >::value + &&std::is_nothrow_move_assignable< T >::value) + { + this->assign(std::move(rhs)); + return *this; + } + }; + + // optional_delete_ctor_base will conditionally delete copy and move + // constructors depending on whether T is copy/move constructible + template < class T, + bool EnableCopy = std::is_copy_constructible< T >::value, + bool EnableMove = std::is_move_constructible< T >::value > + struct optional_delete_ctor_base + { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = + default; + optional_delete_ctor_base & + operator=(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base & + operator=(optional_delete_ctor_base &&) noexcept = default; + }; + + template < class T > + struct optional_delete_ctor_base< T, true, false > + { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = delete; + optional_delete_ctor_base & + operator=(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base & + operator=(optional_delete_ctor_base &&) noexcept = default; + }; + + template < class T > + struct optional_delete_ctor_base< T, false, true > + { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base &) = delete; + optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = + default; + optional_delete_ctor_base & + operator=(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base & + operator=(optional_delete_ctor_base &&) noexcept = default; + }; + + template < class T > + struct optional_delete_ctor_base< T, false, false > + { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base &) = delete; + optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = delete; + optional_delete_ctor_base & + operator=(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base & + operator=(optional_delete_ctor_base &&) noexcept = default; + }; + + // optional_delete_assign_base will conditionally delete copy and move + // constructors depending on whether T is copy/move constructible + + // assignable + template < class T, + bool EnableCopy = (std::is_copy_constructible< T >::value + && std::is_copy_assignable< T >::value), + bool EnableMove = (std::is_move_constructible< T >::value + && std::is_move_assignable< T >::value) > + struct optional_delete_assign_base + { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base &) = + default; + optional_delete_assign_base(optional_delete_assign_base &&) noexcept = + default; + optional_delete_assign_base & + operator=(const optional_delete_assign_base &) = default; + optional_delete_assign_base & + operator=(optional_delete_assign_base &&) noexcept = default; + }; + + template < class T > + struct optional_delete_assign_base< T, true, false > + { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base &) = + default; + optional_delete_assign_base(optional_delete_assign_base &&) noexcept = + default; + optional_delete_assign_base & + operator=(const optional_delete_assign_base &) = default; + optional_delete_assign_base & + operator=(optional_delete_assign_base &&) noexcept = delete; + }; + + template < class T > + struct optional_delete_assign_base< T, false, true > + { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base &) = + default; + optional_delete_assign_base(optional_delete_assign_base &&) noexcept = + default; + optional_delete_assign_base & + operator=(const optional_delete_assign_base &) = delete; + optional_delete_assign_base & + operator=(optional_delete_assign_base &&) noexcept = default; + }; + + template < class T > + struct optional_delete_assign_base< T, false, false > + { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base &) = + default; + optional_delete_assign_base(optional_delete_assign_base &&) noexcept = + default; + optional_delete_assign_base & + operator=(const optional_delete_assign_base &) = delete; + optional_delete_assign_base & + operator=(optional_delete_assign_base &&) noexcept = delete; + }; + + } // namespace detail + + /// \brief A tag type to represent an empty optional + struct nullopt_t + { + struct do_not_use + { + }; + constexpr explicit nullopt_t(do_not_use, do_not_use) noexcept + { + } + }; + /// \brief Represents an empty optional + /// \synopsis static constexpr nullopt_t nullopt; + /// + /// *Examples*: + /// ``` + /// tl::optional a = tl::nullopt; + /// void foo (tl::optional); + /// foo(tl::nullopt); //pass an empty optional + /// ``` + static constexpr nullopt_t nullopt{nullopt_t::do_not_use{}, + nullopt_t::do_not_use{}}; + + class bad_optional_access : public std::exception + { + public: + bad_optional_access() = default; + const char * + what() const noexcept + { + return "Optional has no value"; + } + }; + + /// An optional object is an object that contains the storage for another + /// object and manages the lifetime of this contained object, if any. The + /// contained object may be initialized after the optional object has been + /// initialized, and may be destroyed before the optional object has been + /// destroyed. The initialization state of the contained object is tracked by + /// the optional object. + template < class T > + class optional : private detail::optional_move_assign_base< T >, + private detail::optional_delete_ctor_base< T >, + private detail::optional_delete_assign_base< T > + { + using base = detail::optional_move_assign_base< T >; + + static_assert(!std::is_same< T, in_place_t >::value, + "instantiation of optional with in_place_t is ill-formed"); + static_assert(!std::is_same< detail::decay_t< T >, nullopt_t >::value, + "instantiation of optional with nullopt_t is ill-formed"); + + public: // The different versions for C++14 and 11 are needed because deduced return // types are not SFINAE-safe. This provides better support for things like // generic lambdas. C.f. // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html -#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ - !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) - /// \group and_then - /// Carries out some operation which returns an optional on the stored - /// object if there is one. \requires `std::invoke(std::forward(f), - /// value())` returns a `std::optional` for some `U`. \returns Let `U` be - /// the result of `std::invoke(std::forward(f), value())`. Returns a - /// `std::optional`. The return value is empty if `*this` is empty, - /// otherwise the return value of `std::invoke(std::forward(f), value())` - /// is returned. - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) &; - template TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) & { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - return has_value() ? detail::invoke(std::forward(f), **this) - : result(nullopt); - } - - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) &&; - template TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) && { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : result(nullopt); - } +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) \ + && !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// \group and_then + /// Carries out some operation which returns an optional on the stored + /// object if there is one. \requires `std::invoke(std::forward(f), + /// value())` returns a `std::optional` for some `U`. \returns Let `U` be + /// the result of `std::invoke(std::forward(f), value())`. Returns a + /// `std::optional`. The return value is empty if `*this` is empty, + /// otherwise the return value of `std::invoke(std::forward(f), value())` + /// is returned. + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &; + template < class F > + TL_OPTIONAL_11_CONSTEXPR auto + and_then(F &&f) & + { + using result = detail::invoke_result_t< F, T & >; + static_assert(detail::is_optional< result >::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : result(nullopt); + } - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) const &; - template constexpr auto and_then(F &&f) const & { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &&; + template < class F > + TL_OPTIONAL_11_CONSTEXPR auto + and_then(F &&f) && + { + using result = detail::invoke_result_t< F, T && >; + static_assert(detail::is_optional< result >::value, + "F must return an optional"); + + return has_value() + ? detail::invoke(std::forward< F >(f), std::move(**this)) + : result(nullopt); + } - return has_value() ? detail::invoke(std::forward(f), **this) - : result(nullopt); - } + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &; + template < class F > + constexpr auto + and_then(F &&f) const & + { + using result = detail::invoke_result_t< F, const T & >; + static_assert(detail::is_optional< result >::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : result(nullopt); + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; - template constexpr auto and_then(F &&f) const && { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : result(nullopt); - } + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; + template < class F > + constexpr auto + and_then(F &&f) const && + { + using result = detail::invoke_result_t< F, const T && >; + static_assert(detail::is_optional< result >::value, + "F must return an optional"); + + return has_value() + ? detail::invoke(std::forward< F >(f), std::move(**this)) + : result(nullopt); + } #endif #else - /// \group and_then - /// Carries out some operation which returns an optional on the stored - /// object if there is one. \requires `std::invoke(std::forward(f), - /// value())` returns a `std::optional` for some `U`. - /// \returns Let `U` be the result of `std::invoke(std::forward(f), - /// value())`. Returns a `std::optional`. The return value is empty if - /// `*this` is empty, otherwise the return value of - /// `std::invoke(std::forward(f), value())` is returned. - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) &; - template - TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) & { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - return has_value() ? detail::invoke(std::forward(f), **this) - : result(nullopt); - } - - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) &&; - template - TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) && { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : result(nullopt); - } + /// \group and_then + /// Carries out some operation which returns an optional on the stored + /// object if there is one. \requires `std::invoke(std::forward(f), + /// value())` returns a `std::optional` for some `U`. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. Returns a `std::optional`. The return value is empty if + /// `*this` is empty, otherwise the return value of + /// `std::invoke(std::forward(f), value())` is returned. + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &; + template < class F > + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t< F, T & > + and_then(F &&f) & + { + using result = detail::invoke_result_t< F, T & >; + static_assert(detail::is_optional< result >::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : result(nullopt); + } - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) const &; - template - constexpr detail::invoke_result_t and_then(F &&f) const & { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &&; + template < class F > + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t< F, T && > + and_then(F &&f) && + { + using result = detail::invoke_result_t< F, T && >; + static_assert(detail::is_optional< result >::value, + "F must return an optional"); + + return has_value() + ? detail::invoke(std::forward< F >(f), std::move(**this)) + : result(nullopt); + } - return has_value() ? detail::invoke(std::forward(f), **this) - : result(nullopt); - } + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &; + template < class F > + constexpr detail::invoke_result_t< F, const T & > + and_then(F &&f) const & + { + using result = detail::invoke_result_t< F, const T & >; + static_assert(detail::is_optional< result >::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : result(nullopt); + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; - template - constexpr detail::invoke_result_t and_then(F &&f) const && { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : result(nullopt); - } + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; + template < class F > + constexpr detail::invoke_result_t< F, const T && > + and_then(F &&f) const && + { + using result = detail::invoke_result_t< F, const T && >; + static_assert(detail::is_optional< result >::value, + "F must return an optional"); + + return has_value() + ? detail::invoke(std::forward< F >(f), std::move(**this)) + : result(nullopt); + } #endif #endif -#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ - !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) - /// \brief Carries out some operation on the stored object if there is one. - /// \returns Let `U` be the result of `std::invoke(std::forward(f), - /// value())`. Returns a `std::optional`. The return value is empty if - /// `*this` is empty, otherwise an `optional` is constructed from the - /// return value of `std::invoke(std::forward(f), value())` and is - /// returned. - /// - /// \group map - /// \synopsis template constexpr auto map(F &&f) &; - template TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) & { - return optional_map_impl(*this, std::forward(f)); - } +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) \ + && !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// \brief Carries out some operation on the stored object if there is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. Returns a `std::optional`. The return value is empty if + /// `*this` is empty, otherwise an `optional` is constructed from the + /// return value of `std::invoke(std::forward(f), value())` and is + /// returned. + /// + /// \group map + /// \synopsis template constexpr auto map(F &&f) &; + template < class F > + TL_OPTIONAL_11_CONSTEXPR auto + map(F &&f) & + { + return optional_map_impl(*this, std::forward< F >(f)); + } - /// \group map - /// \synopsis template constexpr auto map(F &&f) &&; - template TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) && { - return optional_map_impl(std::move(*this), std::forward(f)); - } + /// \group map + /// \synopsis template constexpr auto map(F &&f) &&; + template < class F > + TL_OPTIONAL_11_CONSTEXPR auto + map(F &&f) && + { + return optional_map_impl(std::move(*this), std::forward< F >(f)); + } - /// \group map - /// \synopsis template constexpr auto map(F &&f) const&; - template constexpr auto map(F &&f) const & { - return optional_map_impl(*this, std::forward(f)); - } + /// \group map + /// \synopsis template constexpr auto map(F &&f) const&; + template < class F > + constexpr auto + map(F &&f) const & + { + return optional_map_impl(*this, std::forward< F >(f)); + } - /// \group map - /// \synopsis template constexpr auto map(F &&f) const&&; - template constexpr auto map(F &&f) const && { - return optional_map_impl(std::move(*this), std::forward(f)); - } + /// \group map + /// \synopsis template constexpr auto map(F &&f) const&&; + template < class F > + constexpr auto + map(F &&f) const && + { + return optional_map_impl(std::move(*this), std::forward< F >(f)); + } #else - /// \brief Carries out some operation on the stored object if there is one. - /// \returns Let `U` be the result of `std::invoke(std::forward(f), - /// value())`. Returns a `std::optional`. The return value is empty if - /// `*this` is empty, otherwise an `optional` is constructed from the - /// return value of `std::invoke(std::forward(f), value())` and is - /// returned. - /// - /// \group map - /// \synopsis template auto map(F &&f) &; - template - TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval(), - std::declval())) - map(F &&f) & { - return optional_map_impl(*this, std::forward(f)); - } + /// \brief Carries out some operation on the stored object if there is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. Returns a `std::optional`. The return value is empty if + /// `*this` is empty, otherwise an `optional` is constructed from the + /// return value of `std::invoke(std::forward(f), value())` and is + /// returned. + /// + /// \group map + /// \synopsis template auto map(F &&f) &; + template < class F > + TL_OPTIONAL_11_CONSTEXPR decltype( + optional_map_impl(std::declval< optional & >(), std::declval< F && >())) + map(F &&f) & + { + return optional_map_impl(*this, std::forward< F >(f)); + } - /// \group map - /// \synopsis template auto map(F &&f) &&; - template - TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval(), - std::declval())) - map(F &&f) && { - return optional_map_impl(std::move(*this), std::forward(f)); - } + /// \group map + /// \synopsis template auto map(F &&f) &&; + template < class F > + TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl( + std::declval< optional && >(), std::declval< F && >())) + map(F &&f) && + { + return optional_map_impl(std::move(*this), std::forward< F >(f)); + } - /// \group map - /// \synopsis template auto map(F &&f) const&; - template - constexpr decltype(optional_map_impl(std::declval(), - std::declval())) - map(F &&f) const & { - return optional_map_impl(*this, std::forward(f)); - } + /// \group map + /// \synopsis template auto map(F &&f) const&; + template < class F > + constexpr decltype(optional_map_impl(std::declval< const optional & >(), + std::declval< F && >())) + map(F &&f) const & + { + return optional_map_impl(*this, std::forward< F >(f)); + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \group map - /// \synopsis template auto map(F &&f) const&&; - template - constexpr decltype(optional_map_impl(std::declval(), - std::declval())) - map(F &&f) const && { - return optional_map_impl(std::move(*this), std::forward(f)); - } + /// \group map + /// \synopsis template auto map(F &&f) const&&; + template < class F > + constexpr decltype(optional_map_impl(std::declval< const optional && >(), + std::declval< F && >())) + map(F &&f) const && + { + return optional_map_impl(std::move(*this), std::forward< F >(f)); + } #endif #endif - /// \brief Calls `f` if the optional is empty - /// \requires `std::invoke_result_t` must be void or convertible to - /// `optional`. - /// \effects If `*this` has a value, returns `*this`. - /// Otherwise, if `f` returns `void`, calls `std::forward(f)` and returns - /// `std::nullopt`. Otherwise, returns `std::forward(f)()`. - /// - /// \group or_else - /// \synopsis template optional or_else (F &&f) &; - template * = nullptr> - optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & { - if (has_value()) - return *this; - - std::forward(f)(); - return nullopt; - } - - /// \exclude - template * = nullptr> - optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & { - return has_value() ? *this : std::forward(f)(); - } - - /// \group or_else - /// \synopsis template optional or_else (F &&f) &&; - template * = nullptr> - optional or_else(F &&f) && { - if (has_value()) - return std::move(*this); + /// \brief Calls `f` if the optional is empty + /// \requires `std::invoke_result_t` must be void or convertible to + /// `optional`. + /// \effects If `*this` has a value, returns `*this`. + /// Otherwise, if `f` returns `void`, calls `std::forward(f)` and returns + /// `std::nullopt`. Otherwise, returns `std::forward(f)()`. + /// + /// \group or_else + /// \synopsis template optional or_else (F &&f) &; + template < class F, detail::enable_if_ret_void< F > * = nullptr > + optional< T > TL_OPTIONAL_11_CONSTEXPR + or_else(F &&f) & + { + if(has_value()) + return *this; + + std::forward< F >(f)(); + return nullopt; + } - std::forward(f)(); - return nullopt; - } + /// \exclude + template < class F, detail::disable_if_ret_void< F > * = nullptr > + optional< T > TL_OPTIONAL_11_CONSTEXPR + or_else(F &&f) & + { + return has_value() ? *this : std::forward< F >(f)(); + } - /// \exclude - template * = nullptr> - optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) && { - return has_value() ? std::move(*this) : std::forward(f)(); - } + /// \group or_else + /// \synopsis template optional or_else (F &&f) &&; + template < class F, detail::enable_if_ret_void< F > * = nullptr > + optional< T > + or_else(F &&f) && + { + if(has_value()) + return std::move(*this); + + std::forward< F >(f)(); + return nullopt; + } - /// \group or_else - /// \synopsis template optional or_else (F &&f) const &; - template * = nullptr> - optional or_else(F &&f) const & { - if (has_value()) - return *this; + /// \exclude + template < class F, detail::disable_if_ret_void< F > * = nullptr > + optional< T > TL_OPTIONAL_11_CONSTEXPR + or_else(F &&f) && + { + return has_value() ? std::move(*this) : std::forward< F >(f)(); + } - std::forward(f)(); - return nullopt; - } + /// \group or_else + /// \synopsis template optional or_else (F &&f) const &; + template < class F, detail::enable_if_ret_void< F > * = nullptr > + optional< T > + or_else(F &&f) const & + { + if(has_value()) + return *this; + + std::forward< F >(f)(); + return nullopt; + } - /// \exclude - template * = nullptr> - optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) const & { - return has_value() ? *this : std::forward(f)(); - } + /// \exclude + template < class F, detail::disable_if_ret_void< F > * = nullptr > + optional< T > TL_OPTIONAL_11_CONSTEXPR + or_else(F &&f) const & + { + return has_value() ? *this : std::forward< F >(f)(); + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \exclude - template * = nullptr> - optional or_else(F &&f) const && { - if (has_value()) - return std::move(*this); - - std::forward(f)(); - return nullopt; - } + /// \exclude + template < class F, detail::enable_if_ret_void< F > * = nullptr > + optional< T > + or_else(F &&f) const && + { + if(has_value()) + return std::move(*this); + + std::forward< F >(f)(); + return nullopt; + } - /// \exclude - template * = nullptr> - optional or_else(F &&f) const && { - return has_value() ? std::move(*this) : std::forward(f)(); - } + /// \exclude + template < class F, detail::disable_if_ret_void< F > * = nullptr > + optional< T > + or_else(F &&f) const && + { + return has_value() ? std::move(*this) : std::forward< F >(f)(); + } #endif - /// \brief Maps the stored value with `f` if there is one, otherwise returns - /// `u`. - /// - /// \details If there is a value stored, then `f` is called with `**this` - /// and the value is returned. Otherwise `u` is returned. - /// - /// \group map_or - template U map_or(F &&f, U &&u) & { - return has_value() ? detail::invoke(std::forward(f), **this) - : std::forward(u); - } + /// \brief Maps the stored value with `f` if there is one, otherwise returns + /// `u`. + /// + /// \details If there is a value stored, then `f` is called with `**this` + /// and the value is returned. Otherwise `u` is returned. + /// + /// \group map_or + template < class F, class U > + U + map_or(F &&f, U &&u) & + { + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : std::forward< U >(u); + } - /// \group map_or - template U map_or(F &&f, U &&u) && { - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : std::forward(u); - } + /// \group map_or + template < class F, class U > + U + map_or(F &&f, U &&u) && + { + return has_value() + ? detail::invoke(std::forward< F >(f), std::move(**this)) + : std::forward< U >(u); + } - /// \group map_or - template U map_or(F &&f, U &&u) const & { - return has_value() ? detail::invoke(std::forward(f), **this) - : std::forward(u); - } + /// \group map_or + template < class F, class U > + U + map_or(F &&f, U &&u) const & + { + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : std::forward< U >(u); + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \group map_or - template U map_or(F &&f, U &&u) const && { - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : std::forward(u); - } + /// \group map_or + template < class F, class U > + U + map_or(F &&f, U &&u) const && + { + return has_value() + ? detail::invoke(std::forward< F >(f), std::move(**this)) + : std::forward< U >(u); + } #endif - /// \brief Maps the stored value with `f` if there is one, otherwise calls - /// `u` and returns the result. - /// - /// \details If there is a value stored, then `f` is - /// called with `**this` and the value is returned. Otherwise - /// `std::forward(u)()` is returned. - /// - /// \group map_or_else - /// \synopsis template \nauto map_or_else(F &&f, U &&u) &; - template - detail::invoke_result_t map_or_else(F &&f, U &&u) & { - return has_value() ? detail::invoke(std::forward(f), **this) - : std::forward(u)(); - } + /// \brief Maps the stored value with `f` if there is one, otherwise calls + /// `u` and returns the result. + /// + /// \details If there is a value stored, then `f` is + /// called with `**this` and the value is returned. Otherwise + /// `std::forward(u)()` is returned. + /// + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) &; + template < class F, class U > + detail::invoke_result_t< U > + map_or_else(F &&f, U &&u) & + { + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : std::forward< U >(u)(); + } - /// \group map_or_else - /// \synopsis template \nauto map_or_else(F &&f, U &&u) - /// &&; - template - detail::invoke_result_t map_or_else(F &&f, U &&u) && { - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : std::forward(u)(); - } + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// &&; + template < class F, class U > + detail::invoke_result_t< U > + map_or_else(F &&f, U &&u) && + { + return has_value() + ? detail::invoke(std::forward< F >(f), std::move(**this)) + : std::forward< U >(u)(); + } - /// \group map_or_else - /// \synopsis template \nauto map_or_else(F &&f, U &&u) - /// const &; - template - detail::invoke_result_t map_or_else(F &&f, U &&u) const & { - return has_value() ? detail::invoke(std::forward(f), **this) - : std::forward(u)(); - } + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// const &; + template < class F, class U > + detail::invoke_result_t< U > + map_or_else(F &&f, U &&u) const & + { + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : std::forward< U >(u)(); + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \group map_or_else - /// \synopsis template \nauto map_or_else(F &&f, U &&u) - /// const &&; - template - detail::invoke_result_t map_or_else(F &&f, U &&u) const && { - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : std::forward(u)(); - } + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// const &&; + template < class F, class U > + detail::invoke_result_t< U > + map_or_else(F &&f, U &&u) const && + { + return has_value() + ? detail::invoke(std::forward< F >(f), std::move(**this)) + : std::forward< U >(u)(); + } #endif - /// \returns `u` if `*this` has a value, otherwise an empty optional. - template - constexpr optional::type> conjunction(U &&u) const { - using result = optional>; - return has_value() ? result{u} : result{nullopt}; - } + /// \returns `u` if `*this` has a value, otherwise an empty optional. + template < class U > + constexpr optional< typename std::decay< U >::type > + conjunction(U &&u) const + { + using result = optional< detail::decay_t< U > >; + return has_value() ? result{u} : result{nullopt}; + } - /// \returns `rhs` if `*this` is empty, otherwise the current value. - /// \group disjunction - TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) & { - return has_value() ? *this : rhs; - } + /// \returns `rhs` if `*this` is empty, otherwise the current value. + /// \group disjunction + TL_OPTIONAL_11_CONSTEXPR optional + disjunction(const optional &rhs) & + { + return has_value() ? *this : rhs; + } - /// \group disjunction - constexpr optional disjunction(const optional &rhs) const & { - return has_value() ? *this : rhs; - } + /// \group disjunction + constexpr optional + disjunction(const optional &rhs) const & + { + return has_value() ? *this : rhs; + } - /// \group disjunction - TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) && { - return has_value() ? std::move(*this) : rhs; - } + /// \group disjunction + TL_OPTIONAL_11_CONSTEXPR optional + disjunction(const optional &rhs) && + { + return has_value() ? std::move(*this) : rhs; + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \group disjunction - constexpr optional disjunction(const optional &rhs) const && { - return has_value() ? std::move(*this) : rhs; - } + /// \group disjunction + constexpr optional + disjunction(const optional &rhs) const && + { + return has_value() ? std::move(*this) : rhs; + } #endif - /// \group disjunction - TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) & { - return has_value() ? *this : std::move(rhs); - } + /// \group disjunction + TL_OPTIONAL_11_CONSTEXPR optional + disjunction(optional &&rhs) & + { + return has_value() ? *this : std::move(rhs); + } - /// \group disjunction - constexpr optional disjunction(optional &&rhs) const & { - return has_value() ? *this : std::move(rhs); - } + /// \group disjunction + constexpr optional + disjunction(optional &&rhs) const & + { + return has_value() ? *this : std::move(rhs); + } - /// \group disjunction - TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) && { - return has_value() ? std::move(*this) : std::move(rhs); - } + /// \group disjunction + TL_OPTIONAL_11_CONSTEXPR optional + disjunction(optional &&rhs) && + { + return has_value() ? std::move(*this) : std::move(rhs); + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \group disjunction - constexpr optional disjunction(optional &&rhs) const && { - return has_value() ? std::move(*this) : std::move(rhs); - } + /// \group disjunction + constexpr optional + disjunction(optional &&rhs) const && + { + return has_value() ? std::move(*this) : std::move(rhs); + } #endif - /// Takes the value out of the optional, leaving it empty - /// \group take - optional take() & { - optional ret = *this; - reset(); - return ret; - } + /// Takes the value out of the optional, leaving it empty + /// \group take + optional + take() & + { + optional ret = *this; + reset(); + return ret; + } - /// \group take - optional take() const & { - optional ret = *this; - reset(); - return ret; - } + /// \group take + optional + take() const & + { + optional ret = *this; + reset(); + return ret; + } - /// \group take - optional take() && { - optional ret = std::move(*this); - reset(); - return ret; - } + /// \group take + optional + take() && + { + optional ret = std::move(*this); + reset(); + return ret; + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \group take - optional take() const && { - optional ret = std::move(*this); - reset(); - return ret; - } + /// \group take + optional + take() const && + { + optional ret = std::move(*this); + reset(); + return ret; + } #endif - using value_type = T; - - /// Constructs an optional that does not contain a value. - /// \group ctor_empty - constexpr optional() noexcept = default; + using value_type = T; - /// \group ctor_empty - constexpr optional(nullopt_t) noexcept {} + /// Constructs an optional that does not contain a value. + /// \group ctor_empty + constexpr optional() noexcept = default; - /// Copy constructor - /// - /// If `rhs` contains a value, the stored value is direct-initialized with - /// it. Otherwise, the constructed optional is empty. - TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) = default; - - /// Move constructor - /// - /// If `rhs` contains a value, the stored value is direct-initialized with - /// it. Otherwise, the constructed optional is empty. - TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) = default; - - /// Constructs the stored value in-place using the given arguments. - /// \group in_place - /// \synopsis template constexpr explicit optional(in_place_t, Args&&... args); - template - constexpr explicit optional( - detail::enable_if_t::value, in_place_t>, - Args &&... args) - : base(in_place, std::forward(args)...) {} - - /// \group in_place - /// \synopsis template \nconstexpr explicit optional(in_place_t, std::initializer_list&, Args&&... args); - template - TL_OPTIONAL_11_CONSTEXPR explicit optional( - detail::enable_if_t &, - Args &&...>::value, - in_place_t>, - std::initializer_list il, Args &&... args) { - this->construct(il, std::forward(args)...); - } + /// \group ctor_empty + constexpr optional(nullopt_t) noexcept + { + } - /// Constructs the stored value with `u`. - /// \synopsis template constexpr optional(U &&u); - template < - class U = T, - detail::enable_if_t::value> * = nullptr, - detail::enable_forward_value * = nullptr> - constexpr optional(U &&u) : base(in_place, std::forward(u)) {} + /// Copy constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + TL_OPTIONAL_11_CONSTEXPR + optional(const optional &rhs) = default; + + /// Move constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + TL_OPTIONAL_11_CONSTEXPR + optional(optional &&rhs) = default; + + /// Constructs the stored value in-place using the given arguments. + /// \group in_place + /// \synopsis template constexpr explicit + /// optional(in_place_t, Args&&... args); + template < class... Args > + constexpr explicit optional( + detail::enable_if_t< std::is_constructible< T, Args... >::value, + in_place_t >, + Args &&... args) + : base(in_place, std::forward< Args >(args)...) + { + } - /// \exclude - template < - class U = T, - detail::enable_if_t::value> * = nullptr, - detail::enable_forward_value * = nullptr> - constexpr explicit optional(U &&u) : base(in_place, std::forward(u)) {} + /// \group in_place + /// \synopsis template \nconstexpr explicit + /// optional(in_place_t, std::initializer_list&, Args&&... args); + template < class U, class... Args > + TL_OPTIONAL_11_CONSTEXPR explicit optional( + detail::enable_if_t< + std::is_constructible< T, std::initializer_list< U > &, + Args &&... >::value, + in_place_t >, + std::initializer_list< U > il, Args &&... args) + { + this->construct(il, std::forward< Args >(args)...); + } - /// Converting copy constructor. - /// \synopsis template optional(const optional &rhs); - template < - class U, detail::enable_from_other * = nullptr, - detail::enable_if_t::value> * = nullptr> - optional(const optional &rhs) { - this->construct(*rhs); - } + /// Constructs the stored value with `u`. + /// \synopsis template constexpr optional(U &&u); + template < class U = T, + detail::enable_if_t< std::is_convertible< U &&, T >::value > * = + nullptr, + detail::enable_forward_value< T, U > * = nullptr > + constexpr optional(U &&u) : base(in_place, std::forward< U >(u)) + { + } - /// \exclude - template * = nullptr, - detail::enable_if_t::value> * = - nullptr> - explicit optional(const optional &rhs) { - this->construct(*rhs); - } + /// \exclude + template < class U = T, + detail::enable_if_t< !std::is_convertible< U &&, T >::value > * = + nullptr, + detail::enable_forward_value< T, U > * = nullptr > + constexpr explicit optional(U &&u) : base(in_place, std::forward< U >(u)) + { + } - /// Converting move constructor. - /// \synopsis template optional(optional &&rhs); - template < - class U, detail::enable_from_other * = nullptr, - detail::enable_if_t::value> * = nullptr> - optional(optional &&rhs) { - this->construct(std::move(*rhs)); - } + /// Converting copy constructor. + /// \synopsis template optional(const optional &rhs); + template < class U, + detail::enable_from_other< T, U, const U & > * = nullptr, + detail::enable_if_t< std::is_convertible< const U &, T >::value > + * = nullptr > + optional(const optional< U > &rhs) + { + this->construct(*rhs); + } - /// \exclude - template < - class U, detail::enable_from_other * = nullptr, - detail::enable_if_t::value> * = nullptr> - explicit optional(optional &&rhs) { - this->construct(std::move(*rhs)); - } + /// \exclude + template < + class U, detail::enable_from_other< T, U, const U & > * = nullptr, + detail::enable_if_t< !std::is_convertible< const U &, T >::value > * = + nullptr > + explicit optional(const optional< U > &rhs) + { + this->construct(*rhs); + } - /// Destroys the stored value if there is one. - ~optional() = default; + /// Converting move constructor. + /// \synopsis template optional(optional &&rhs); + template < class U, detail::enable_from_other< T, U, U && > * = nullptr, + detail::enable_if_t< std::is_convertible< U &&, T >::value > * = + nullptr > + optional(optional< U > &&rhs) + { + this->construct(std::move(*rhs)); + } - /// Assignment to empty. - /// - /// Destroys the current value if there is one. - optional &operator=(nullopt_t) noexcept { - if (has_value()) { - this->m_value.~T(); - this->m_has_value = false; + /// \exclude + template < class U, detail::enable_from_other< T, U, U && > * = nullptr, + detail::enable_if_t< !std::is_convertible< U &&, T >::value > * = + nullptr > + explicit optional(optional< U > &&rhs) + { + this->construct(std::move(*rhs)); } - return *this; - } + /// Destroys the stored value if there is one. + ~optional() = default; - /// Copy assignment. - /// - /// Copies the value from `rhs` if there is one. Otherwise resets the stored - /// value in `*this`. - optional &operator=(const optional &rhs) = default; + /// Assignment to empty. + /// + /// Destroys the current value if there is one. + optional &operator=(nullopt_t) noexcept + { + if(has_value()) + { + this->m_value.~T(); + this->m_has_value = false; + } - /// Move assignment. - /// - /// Moves the value from `rhs` if there is one. Otherwise resets the stored - /// value in `*this`. - optional &operator=(optional &&rhs) = default; - - /// Assigns the stored value from `u`, destroying the old value if there was - /// one. - /// \synopsis optional &operator=(U &&u); - template * = nullptr> - optional &operator=(U &&u) { - if (has_value()) { - this->m_value = std::forward(u); - } else { - this->construct(std::forward(u)); - } - - return *this; - } + return *this; + } - /// Converting copy assignment operator. - /// - /// Copies the value from `rhs` if there is one. Otherwise resets the stored - /// value in `*this`. - /// \synopsis optional &operator=(const optional & rhs); - template * = nullptr> - optional &operator=(const optional &rhs) { - if (has_value()) { - if (rhs.has_value()) { - this->m_value = *rhs; - } else { - this->hard_reset(); + /// Copy assignment. + /// + /// Copies the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + optional & + operator=(const optional &rhs) = default; + + /// Move assignment. + /// + /// Moves the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + optional & + operator=(optional &&rhs) = default; + + /// Assigns the stored value from `u`, destroying the old value if there was + /// one. + /// \synopsis optional &operator=(U &&u); + template < class U = T, detail::enable_assign_forward< T, U > * = nullptr > + optional & + operator=(U &&u) + { + if(has_value()) + { + this->m_value = std::forward< U >(u); + } + else + { + this->construct(std::forward< U >(u)); } - } - if (rhs.has_value()) { - this->construct(*rhs); + return *this; } - return *this; - } + /// Converting copy assignment operator. + /// + /// Copies the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + /// \synopsis optional &operator=(const optional & rhs); + template < class U, + detail::enable_assign_from_other< T, U, const U & > * = nullptr > + optional & + operator=(const optional< U > &rhs) + { + if(has_value()) + { + if(rhs.has_value()) + { + this->m_value = *rhs; + } + else + { + this->hard_reset(); + } + } - // TODO check exception guarantee - /// Converting move assignment operator. - /// - /// Moves the value from `rhs` if there is one. Otherwise resets the stored - /// value in `*this`. - /// \synopsis optional &operator=(optional && rhs); - template * = nullptr> - optional &operator=(optional &&rhs) { - if (has_value()) { - if (rhs.has_value()) { - this->m_value = std::move(*rhs); - } else { - this->hard_reset(); + if(rhs.has_value()) + { + this->construct(*rhs); } - } - if (rhs.has_value()) { - this->construct(std::move(*rhs)); + return *this; } - return *this; - } + // TODO check exception guarantee + /// Converting move assignment operator. + /// + /// Moves the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + /// \synopsis optional &operator=(optional && rhs); + template < class U, + detail::enable_assign_from_other< T, U, U > * = nullptr > + optional & + operator=(optional< U > &&rhs) + { + if(has_value()) + { + if(rhs.has_value()) + { + this->m_value = std::move(*rhs); + } + else + { + this->hard_reset(); + } + } - /// Constructs the value in-place, destroying the current one if there is - /// one. - /// \group emplace - template T &emplace(Args &&... args) { - static_assert(std::is_constructible::value, - "T must be constructible with Args"); + if(rhs.has_value()) + { + this->construct(std::move(*rhs)); + } - *this = nullopt; - this->construct(std::forward(args)...); - return value(); - } + return *this; + } - /// \group emplace - /// \synopsis template \nT& emplace(std::initializer_list il, Args &&... args); - template - detail::enable_if_t< - std::is_constructible &, Args &&...>::value, - T &> - emplace(std::initializer_list il, Args &&... args) { - *this = nullopt; - this->construct(il, std::forward(args)...); - return value(); - } + /// Constructs the value in-place, destroying the current one if there is + /// one. + /// \group emplace + template < class... Args > + T & + emplace(Args &&... args) + { + static_assert(std::is_constructible< T, Args &&... >::value, + "T must be constructible with Args"); + + *this = nullopt; + this->construct(std::forward< Args >(args)...); + return value(); + } - /// Swaps this optional with the other. - /// - /// If neither optionals have a value, nothing happens. - /// If both have a value, the values are swapped. - /// If one has a value, it is moved to the other and the movee is left - /// valueless. - void - swap(optional &rhs) noexcept(std::is_nothrow_move_constructible::value - &&detail::is_nothrow_swappable::value) { - if (has_value()) { - if (rhs.has_value()) { - using std::swap; - swap(**this, *rhs); - } else { - new (std::addressof(rhs.m_value)) T(std::move(this->m_value)); - this->m_value.T::~T(); + /// \group emplace + /// \synopsis template \nT& + /// emplace(std::initializer_list il, Args &&... args); + template < class U, class... Args > + detail::enable_if_t< std::is_constructible< T, std::initializer_list< U > &, + Args &&... >::value, + T & > + emplace(std::initializer_list< U > il, Args &&... args) + { + *this = nullopt; + this->construct(il, std::forward< Args >(args)...); + return value(); + } + + /// Swaps this optional with the other. + /// + /// If neither optionals have a value, nothing happens. + /// If both have a value, the values are swapped. + /// If one has a value, it is moved to the other and the movee is left + /// valueless. + void + swap(optional &rhs) noexcept(std::is_nothrow_move_constructible< T >::value + &&detail::is_nothrow_swappable< T >::value) + { + if(has_value()) + { + if(rhs.has_value()) + { + using std::swap; + swap(**this, *rhs); + } + else + { + new(std::addressof(rhs.m_value)) T(std::move(this->m_value)); + this->m_value.T::~T(); + } + } + else if(rhs.has_value()) + { + new(std::addressof(this->m_value)) T(std::move(rhs.m_value)); + rhs.m_value.T::~T(); } - } else if (rhs.has_value()) { - new (std::addressof(this->m_value)) T(std::move(rhs.m_value)); - rhs.m_value.T::~T(); } - } - /// \returns a pointer to the stored value - /// \requires a value is stored - /// \group pointer - /// \synopsis constexpr const T *operator->() const; - constexpr const T *operator->() const { - return std::addressof(this->m_value); - } + /// \returns a pointer to the stored value + /// \requires a value is stored + /// \group pointer + /// \synopsis constexpr const T *operator->() const; + constexpr const T *operator->() const + { + return std::addressof(this->m_value); + } - /// \group pointer - /// \synopsis constexpr T *operator->(); - TL_OPTIONAL_11_CONSTEXPR T *operator->() { - return std::addressof(this->m_value); - } + /// \group pointer + /// \synopsis constexpr T *operator->(); + TL_OPTIONAL_11_CONSTEXPR T *operator->() + { + return std::addressof(this->m_value); + } - /// \returns the stored value - /// \requires a value is stored - /// \group deref - /// \synopsis constexpr T &operator*(); - TL_OPTIONAL_11_CONSTEXPR T &operator*() & { return this->m_value; } + /// \returns the stored value + /// \requires a value is stored + /// \group deref + /// \synopsis constexpr T &operator*(); + TL_OPTIONAL_11_CONSTEXPR T &operator*() & + { + return this->m_value; + } - /// \group deref - /// \synopsis constexpr const T &operator*() const; - constexpr const T &operator*() const & { return this->m_value; } + /// \group deref + /// \synopsis constexpr const T &operator*() const; + constexpr const T &operator*() const & + { + return this->m_value; + } - /// \exclude - TL_OPTIONAL_11_CONSTEXPR T &&operator*() && { - return std::move(this->m_value); - } + /// \exclude + TL_OPTIONAL_11_CONSTEXPR T &&operator*() && + { + return std::move(this->m_value); + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \exclude - constexpr const T &&operator*() const && { return std::move(this->m_value); } + /// \exclude + constexpr const T &&operator*() const && + { + return std::move(this->m_value); + } #endif - /// \returns whether or not the optional has a value - /// \group has_value - constexpr bool has_value() const noexcept { return this->m_has_value; } + /// \returns whether or not the optional has a value + /// \group has_value + constexpr bool + has_value() const noexcept + { + return this->m_has_value; + } - /// \group has_value - constexpr explicit operator bool() const noexcept { - return this->m_has_value; - } + /// \group has_value + constexpr explicit operator bool() const noexcept + { + return this->m_has_value; + } - /// \returns the contained value if there is one, otherwise throws - /// [bad_optional_access] - /// \group value - /// \synopsis constexpr T &value(); - TL_OPTIONAL_11_CONSTEXPR T &value() & { - if (has_value()) - return this->m_value; - throw bad_optional_access(); - } - /// \group value - /// \synopsis constexpr const T &value() const; - TL_OPTIONAL_11_CONSTEXPR const T &value() const & { - if (has_value()) - return this->m_value; - throw bad_optional_access(); - } - /// \exclude - TL_OPTIONAL_11_CONSTEXPR T &&value() && { - if (has_value()) - return std::move(this->m_value); - throw bad_optional_access(); - } + /// \returns the contained value if there is one, otherwise throws + /// [bad_optional_access] + /// \group value + /// \synopsis constexpr T &value(); + TL_OPTIONAL_11_CONSTEXPR T & + value() & + { + if(has_value()) + return this->m_value; + throw bad_optional_access(); + } + /// \group value + /// \synopsis constexpr const T &value() const; + TL_OPTIONAL_11_CONSTEXPR const T & + value() const & + { + if(has_value()) + return this->m_value; + throw bad_optional_access(); + } + /// \exclude + TL_OPTIONAL_11_CONSTEXPR T && + value() && + { + if(has_value()) + return std::move(this->m_value); + throw bad_optional_access(); + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \exclude - TL_OPTIONAL_11_CONSTEXPR const T &&value() const && { - if (has_value()) - return std::move(this->m_value); - throw bad_optional_access(); - } + /// \exclude + TL_OPTIONAL_11_CONSTEXPR const T && + value() const && + { + if(has_value()) + return std::move(this->m_value); + throw bad_optional_access(); + } #endif - /// \returns the stored value if there is one, otherwise returns `u` - /// \group value_or - template constexpr T value_or(U &&u) const & { - static_assert(std::is_copy_constructible::value && - std::is_convertible::value, - "T must be copy constructible and convertible from U"); - return has_value() ? **this : static_cast(std::forward(u)); + /// \returns the stored value if there is one, otherwise returns `u` + /// \group value_or + template < class U > + constexpr T + value_or(U &&u) const & + { + static_assert(std::is_copy_constructible< T >::value + && std::is_convertible< U &&, T >::value, + "T must be copy constructible and convertible from U"); + return has_value() ? **this : static_cast< T >(std::forward< U >(u)); + } + + /// \group value_or + template < class U > + TL_OPTIONAL_11_CONSTEXPR T + value_or(U &&u) && + { + static_assert(std::is_move_constructible< T >::value + && std::is_convertible< U &&, T >::value, + "T must be move constructible and convertible from U"); + return has_value() ? **this : static_cast< T >(std::forward< U >(u)); + } + + /// Destroys the stored value if one exists, making the optional empty + void + reset() noexcept + { + if(has_value()) + { + this->m_value.~T(); + this->m_has_value = false; + } + } + }; // namespace tl + + /// \group relop + /// \brief Compares two optional objects + /// \details If both optionals contain a value, they are compared with `T`s + /// relational operators. Otherwise `lhs` and `rhs` are equal only if they are + /// both empty, and `lhs` is less than `rhs` only if `rhs` is empty and `lhs` + /// is not. + template < class T, class U > + inline constexpr bool + operator==(const optional< T > &lhs, const optional< U > &rhs) + { + return lhs.has_value() == rhs.has_value() + && (!lhs.has_value() || *lhs == *rhs); + } + /// \group relop + template < class T, class U > + inline constexpr bool + operator!=(const optional< T > &lhs, const optional< U > &rhs) + { + return lhs.has_value() != rhs.has_value() + || (lhs.has_value() && *lhs != *rhs); + } + /// \group relop + template < class T, class U > + inline constexpr bool + operator<(const optional< T > &lhs, const optional< U > &rhs) + { + return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs); + } + /// \group relop + template < class T, class U > + inline constexpr bool + operator>(const optional< T > &lhs, const optional< U > &rhs) + { + return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs); + } + /// \group relop + template < class T, class U > + inline constexpr bool + operator<=(const optional< T > &lhs, const optional< U > &rhs) + { + return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs); + } + /// \group relop + template < class T, class U > + inline constexpr bool + operator>=(const optional< T > &lhs, const optional< U > &rhs) + { + return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs); + } + + /// \group relop_nullopt + /// \brief Compares an optional to a `nullopt` + /// \details Equivalent to comparing the optional to an empty optional + template < class T > + inline constexpr bool + operator==(const optional< T > &lhs, nullopt_t) noexcept + { + return !lhs.has_value(); + } + /// \group relop_nullopt + template < class T > + inline constexpr bool + operator==(nullopt_t, const optional< T > &rhs) noexcept + { + return !rhs.has_value(); + } + /// \group relop_nullopt + template < class T > + inline constexpr bool + operator!=(const optional< T > &lhs, nullopt_t) noexcept + { + return lhs.has_value(); + } + /// \group relop_nullopt + template < class T > + inline constexpr bool + operator!=(nullopt_t, const optional< T > &rhs) noexcept + { + return rhs.has_value(); + } + /// \group relop_nullopt + template < class T > + inline constexpr bool + operator<(const optional< T > &, nullopt_t) noexcept + { + return false; + } + /// \group relop_nullopt + template < class T > + inline constexpr bool + operator<(nullopt_t, const optional< T > &rhs) noexcept + { + return rhs.has_value(); + } + /// \group relop_nullopt + template < class T > + inline constexpr bool + operator<=(const optional< T > &lhs, nullopt_t) noexcept + { + return !lhs.has_value(); + } + /// \group relop_nullopt + template < class T > + inline constexpr bool + operator<=(nullopt_t, const optional< T > &) noexcept + { + return true; + } + /// \group relop_nullopt + template < class T > + inline constexpr bool + operator>(const optional< T > &lhs, nullopt_t) noexcept + { + return lhs.has_value(); + } + /// \group relop_nullopt + template < class T > + inline constexpr bool + operator>(nullopt_t, const optional< T > &) noexcept + { + return false; + } + /// \group relop_nullopt + template < class T > + inline constexpr bool + operator>=(const optional< T > &, nullopt_t) noexcept + { + return true; + } + /// \group relop_nullopt + template < class T > + inline constexpr bool + operator>=(nullopt_t, const optional< T > &rhs) noexcept + { + return !rhs.has_value(); + } + + /// \group relop_t + /// \brief Compares the optional with a value. + /// \details If the optional has a value, it is compared with the other value + /// using `T`s relational operators. Otherwise, the optional is considered + /// less than the value. + template < class T, class U > + inline constexpr bool + operator==(const optional< T > &lhs, const U &rhs) + { + return lhs.has_value() ? *lhs == rhs : false; + } + /// \group relop_t + template < class T, class U > + inline constexpr bool + operator==(const U &lhs, const optional< T > &rhs) + { + return rhs.has_value() ? lhs == *rhs : false; + } + /// \group relop_t + template < class T, class U > + inline constexpr bool + operator!=(const optional< T > &lhs, const U &rhs) + { + return lhs.has_value() ? *lhs != rhs : true; + } + /// \group relop_t + template < class T, class U > + inline constexpr bool + operator!=(const U &lhs, const optional< T > &rhs) + { + return rhs.has_value() ? lhs != *rhs : true; + } + /// \group relop_t + template < class T, class U > + inline constexpr bool + operator<(const optional< T > &lhs, const U &rhs) + { + return lhs.has_value() ? *lhs < rhs : true; + } + /// \group relop_t + template < class T, class U > + inline constexpr bool + operator<(const U &lhs, const optional< T > &rhs) + { + return rhs.has_value() ? lhs < *rhs : false; + } + /// \group relop_t + template < class T, class U > + inline constexpr bool + operator<=(const optional< T > &lhs, const U &rhs) + { + return lhs.has_value() ? *lhs <= rhs : true; + } + /// \group relop_t + template < class T, class U > + inline constexpr bool + operator<=(const U &lhs, const optional< T > &rhs) + { + return rhs.has_value() ? lhs <= *rhs : false; + } + /// \group relop_t + template < class T, class U > + inline constexpr bool + operator>(const optional< T > &lhs, const U &rhs) + { + return lhs.has_value() ? *lhs > rhs : false; + } + /// \group relop_t + template < class T, class U > + inline constexpr bool + operator>(const U &lhs, const optional< T > &rhs) + { + return rhs.has_value() ? lhs > *rhs : true; + } + /// \group relop_t + template < class T, class U > + inline constexpr bool + operator>=(const optional< T > &lhs, const U &rhs) + { + return lhs.has_value() ? *lhs >= rhs : false; + } + /// \group relop_t + template < class T, class U > + inline constexpr bool + operator>=(const U &lhs, const optional< T > &rhs) + { + return rhs.has_value() ? lhs >= *rhs : true; + } + + /// \synopsis template \nvoid swap(optional &lhs, optional + /// &rhs); + template < + class T, + detail::enable_if_t< std::is_move_constructible< T >::value > * = nullptr, + detail::enable_if_t< detail::is_swappable< T >::value > * = nullptr > + void + swap(optional< T > &lhs, optional< T > &rhs) noexcept(noexcept(lhs.swap(rhs))) + { + return lhs.swap(rhs); } - /// \group value_or - template TL_OPTIONAL_11_CONSTEXPR T value_or(U &&u) && { - static_assert(std::is_move_constructible::value && - std::is_convertible::value, - "T must be move constructible and convertible from U"); - return has_value() ? **this : static_cast(std::forward(u)); + namespace detail + { + struct i_am_secret + { + }; + } // namespace detail + + template < class T = detail::i_am_secret, class U, + class Ret = detail::conditional_t< + std::is_same< T, detail::i_am_secret >::value, + detail::decay_t< U >, T > > + inline constexpr optional< Ret > + make_optional(U &&v) + { + return optional< Ret >(std::forward< U >(v)); } - /// Destroys the stored value if one exists, making the optional empty - void reset() noexcept { - if (has_value()) { - this->m_value.~T(); - this->m_has_value = false; - } + template < class T, class... Args > + inline constexpr optional< T > + make_optional(Args &&... args) + { + return optional< T >(in_place, std::forward< Args >(args)...); + } + template < class T, class U, class... Args > + inline constexpr optional< T > + make_optional(std::initializer_list< U > il, Args &&... args) + { + return optional< T >(in_place, il, std::forward< Args >(args)...); } -}; // namespace tl - -/// \group relop -/// \brief Compares two optional objects -/// \details If both optionals contain a value, they are compared with `T`s -/// relational operators. Otherwise `lhs` and `rhs` are equal only if they are -/// both empty, and `lhs` is less than `rhs` only if `rhs` is empty and `lhs` -/// is not. -template -inline constexpr bool operator==(const optional &lhs, - const optional &rhs) { - return lhs.has_value() == rhs.has_value() && - (!lhs.has_value() || *lhs == *rhs); -} -/// \group relop -template -inline constexpr bool operator!=(const optional &lhs, - const optional &rhs) { - return lhs.has_value() != rhs.has_value() || - (lhs.has_value() && *lhs != *rhs); -} -/// \group relop -template -inline constexpr bool operator<(const optional &lhs, - const optional &rhs) { - return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs); -} -/// \group relop -template -inline constexpr bool operator>(const optional &lhs, - const optional &rhs) { - return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs); -} -/// \group relop -template -inline constexpr bool operator<=(const optional &lhs, - const optional &rhs) { - return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs); -} -/// \group relop -template -inline constexpr bool operator>=(const optional &lhs, - const optional &rhs) { - return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs); -} - -/// \group relop_nullopt -/// \brief Compares an optional to a `nullopt` -/// \details Equivalent to comparing the optional to an empty optional -template -inline constexpr bool operator==(const optional &lhs, nullopt_t) noexcept { - return !lhs.has_value(); -} -/// \group relop_nullopt -template -inline constexpr bool operator==(nullopt_t, const optional &rhs) noexcept { - return !rhs.has_value(); -} -/// \group relop_nullopt -template -inline constexpr bool operator!=(const optional &lhs, nullopt_t) noexcept { - return lhs.has_value(); -} -/// \group relop_nullopt -template -inline constexpr bool operator!=(nullopt_t, const optional &rhs) noexcept { - return rhs.has_value(); -} -/// \group relop_nullopt -template -inline constexpr bool operator<(const optional &, nullopt_t) noexcept { - return false; -} -/// \group relop_nullopt -template -inline constexpr bool operator<(nullopt_t, const optional &rhs) noexcept { - return rhs.has_value(); -} -/// \group relop_nullopt -template -inline constexpr bool operator<=(const optional &lhs, nullopt_t) noexcept { - return !lhs.has_value(); -} -/// \group relop_nullopt -template -inline constexpr bool operator<=(nullopt_t, const optional &) noexcept { - return true; -} -/// \group relop_nullopt -template -inline constexpr bool operator>(const optional &lhs, nullopt_t) noexcept { - return lhs.has_value(); -} -/// \group relop_nullopt -template -inline constexpr bool operator>(nullopt_t, const optional &) noexcept { - return false; -} -/// \group relop_nullopt -template -inline constexpr bool operator>=(const optional &, nullopt_t) noexcept { - return true; -} -/// \group relop_nullopt -template -inline constexpr bool operator>=(nullopt_t, const optional &rhs) noexcept { - return !rhs.has_value(); -} - -/// \group relop_t -/// \brief Compares the optional with a value. -/// \details If the optional has a value, it is compared with the other value -/// using `T`s relational operators. Otherwise, the optional is considered -/// less than the value. -template -inline constexpr bool operator==(const optional &lhs, const U &rhs) { - return lhs.has_value() ? *lhs == rhs : false; -} -/// \group relop_t -template -inline constexpr bool operator==(const U &lhs, const optional &rhs) { - return rhs.has_value() ? lhs == *rhs : false; -} -/// \group relop_t -template -inline constexpr bool operator!=(const optional &lhs, const U &rhs) { - return lhs.has_value() ? *lhs != rhs : true; -} -/// \group relop_t -template -inline constexpr bool operator!=(const U &lhs, const optional &rhs) { - return rhs.has_value() ? lhs != *rhs : true; -} -/// \group relop_t -template -inline constexpr bool operator<(const optional &lhs, const U &rhs) { - return lhs.has_value() ? *lhs < rhs : true; -} -/// \group relop_t -template -inline constexpr bool operator<(const U &lhs, const optional &rhs) { - return rhs.has_value() ? lhs < *rhs : false; -} -/// \group relop_t -template -inline constexpr bool operator<=(const optional &lhs, const U &rhs) { - return lhs.has_value() ? *lhs <= rhs : true; -} -/// \group relop_t -template -inline constexpr bool operator<=(const U &lhs, const optional &rhs) { - return rhs.has_value() ? lhs <= *rhs : false; -} -/// \group relop_t -template -inline constexpr bool operator>(const optional &lhs, const U &rhs) { - return lhs.has_value() ? *lhs > rhs : false; -} -/// \group relop_t -template -inline constexpr bool operator>(const U &lhs, const optional &rhs) { - return rhs.has_value() ? lhs > *rhs : true; -} -/// \group relop_t -template -inline constexpr bool operator>=(const optional &lhs, const U &rhs) { - return lhs.has_value() ? *lhs >= rhs : false; -} -/// \group relop_t -template -inline constexpr bool operator>=(const U &lhs, const optional &rhs) { - return rhs.has_value() ? lhs >= *rhs : true; -} - -/// \synopsis template \nvoid swap(optional &lhs, optional &rhs); -template ::value> * = nullptr, - detail::enable_if_t::value> * = nullptr> -void swap(optional &lhs, - optional &rhs) noexcept(noexcept(lhs.swap(rhs))) { - return lhs.swap(rhs); -} - -namespace detail { -struct i_am_secret {}; -} // namespace detail - -template ::value, - detail::decay_t, T>> -inline constexpr optional make_optional(U &&v) { - return optional(std::forward(v)); -} - -template -inline constexpr optional make_optional(Args &&... args) { - return optional(in_place, std::forward(args)...); -} -template -inline constexpr optional make_optional(std::initializer_list il, - Args &&... args) { - return optional(in_place, il, std::forward(args)...); -} #if __cplusplus >= 201703L -template optional(T)->optional; + template < class T > + optional(T)->optional< T >; #endif -/// \exclude -namespace detail { + /// \exclude + namespace detail + { #ifdef TL_OPTIONAL_CXX14 -template (), - *std::declval())), - detail::enable_if_t::value> * = nullptr> -constexpr auto optional_map_impl(Opt &&opt, F &&f) { - return opt.has_value() - ? detail::invoke(std::forward(f), *std::forward(opt)) - : optional(nullopt); -} - -template (), - *std::declval())), - detail::enable_if_t::value> * = nullptr> -auto optional_map_impl(Opt &&opt, F &&f) { - if (opt.has_value()) { - detail::invoke(std::forward(f), *std::forward(opt)); - return make_optional(monostate{}); - } + template < class Opt, class F, + class Ret = decltype(detail::invoke(std::declval< F >(), + *std::declval< Opt >())), + detail::enable_if_t< !std::is_void< Ret >::value > * = nullptr > + constexpr auto + optional_map_impl(Opt &&opt, F &&f) + { + return opt.has_value() + ? detail::invoke(std::forward< F >(f), *std::forward< Opt >(opt)) + : optional< Ret >(nullopt); + } + + template < class Opt, class F, + class Ret = decltype(detail::invoke(std::declval< F >(), + *std::declval< Opt >())), + detail::enable_if_t< std::is_void< Ret >::value > * = nullptr > + auto + optional_map_impl(Opt &&opt, F &&f) + { + if(opt.has_value()) + { + detail::invoke(std::forward< F >(f), *std::forward< Opt >(opt)); + return make_optional(monostate{}); + } - return optional(nullopt); -} + return optional< monostate >(nullopt); + } #else -template (), - *std::declval())), - detail::enable_if_t::value> * = nullptr> - -constexpr auto optional_map_impl(Opt &&opt, F &&f) -> optional { - return opt.has_value() - ? detail::invoke(std::forward(f), *std::forward(opt)) - : optional(nullopt); -} - -template (), - *std::declval())), - detail::enable_if_t::value> * = nullptr> - -auto optional_map_impl(Opt &&opt, F &&f) -> optional { - if (opt.has_value()) { - detail::invoke(std::forward(f), *std::forward(opt)); - return monostate{}; - } + template < class Opt, class F, + class Ret = decltype(detail::invoke(std::declval< F >(), + *std::declval< Opt >())), + detail::enable_if_t< !std::is_void< Ret >::value > * = nullptr > + + constexpr auto + optional_map_impl(Opt &&opt, F &&f) -> optional< Ret > + { + return opt.has_value() + ? detail::invoke(std::forward< F >(f), *std::forward< Opt >(opt)) + : optional< Ret >(nullopt); + } - return nullopt; -} + template < class Opt, class F, + class Ret = decltype(detail::invoke(std::declval< F >(), + *std::declval< Opt >())), + detail::enable_if_t< std::is_void< Ret >::value > * = nullptr > + + auto + optional_map_impl(Opt &&opt, F &&f) -> optional< monostate > + { + if(opt.has_value()) + { + detail::invoke(std::forward< F >(f), *std::forward< Opt >(opt)); + return monostate{}; + } + + return nullopt; + } #endif -} // namespace detail + } // namespace detail -/// Specialization for when `T` is a reference. `optional` acts similarly -/// to a `T*`, but provides more operations and shows intent more clearly. -/// -/// *Examples*: -/// -/// ``` -/// int i = 42; -/// tl::optional o = i; -/// *o == 42; //true -/// i = 12; -/// *o = 12; //true -/// &*o == &i; //true -/// ``` -/// -/// Assignment has rebind semantics rather than assign-through semantics: -/// -/// ``` -/// int j = 8; -/// o = j; -/// -/// &*o == &j; //true -/// ``` -template class optional { -public: + /// Specialization for when `T` is a reference. `optional` acts similarly + /// to a `T*`, but provides more operations and shows intent more clearly. + /// + /// *Examples*: + /// + /// ``` + /// int i = 42; + /// tl::optional o = i; + /// *o == 42; //true + /// i = 12; + /// *o = 12; //true + /// &*o == &i; //true + /// ``` + /// + /// Assignment has rebind semantics rather than assign-through semantics: + /// + /// ``` + /// int j = 8; + /// o = j; + /// + /// &*o == &j; //true + /// ``` + template < class T > + class optional< T & > + { + public: // The different versions for C++14 and 11 are needed because deduced return // types are not SFINAE-safe. This provides better support for things like // generic lambdas. C.f. // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html -#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ - !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) - /// \group and_then - /// Carries out some operation which returns an optional on the stored - /// object if there is one. \requires `std::invoke(std::forward(f), - /// value())` returns a `std::optional` for some `U`. \returns Let `U` be - /// the result of `std::invoke(std::forward(f), value())`. Returns a - /// `std::optional`. The return value is empty if `*this` is empty, - /// otherwise the return value of `std::invoke(std::forward(f), value())` - /// is returned. - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) &; - template TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) & { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - return has_value() ? detail::invoke(std::forward(f), **this) - : result(nullopt); - } - - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) &&; - template TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) && { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - return has_value() ? detail::invoke(std::forward(f), **this) - : result(nullopt); - } +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) \ + && !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// \group and_then + /// Carries out some operation which returns an optional on the stored + /// object if there is one. \requires `std::invoke(std::forward(f), + /// value())` returns a `std::optional` for some `U`. \returns Let `U` be + /// the result of `std::invoke(std::forward(f), value())`. Returns a + /// `std::optional`. The return value is empty if `*this` is empty, + /// otherwise the return value of `std::invoke(std::forward(f), value())` + /// is returned. + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &; + template < class F > + TL_OPTIONAL_11_CONSTEXPR auto + and_then(F &&f) & + { + using result = detail::invoke_result_t< F, T & >; + static_assert(detail::is_optional< result >::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : result(nullopt); + } - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) const &; - template constexpr auto and_then(F &&f) const & { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &&; + template < class F > + TL_OPTIONAL_11_CONSTEXPR auto + and_then(F &&f) && + { + using result = detail::invoke_result_t< F, T & >; + static_assert(detail::is_optional< result >::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : result(nullopt); + } - return has_value() ? detail::invoke(std::forward(f), **this) - : result(nullopt); - } + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &; + template < class F > + constexpr auto + and_then(F &&f) const & + { + using result = detail::invoke_result_t< F, const T & >; + static_assert(detail::is_optional< result >::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : result(nullopt); + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; - template constexpr auto and_then(F &&f) const && { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - return has_value() ? detail::invoke(std::forward(f), **this) - : result(nullopt); - } + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; + template < class F > + constexpr auto + and_then(F &&f) const && + { + using result = detail::invoke_result_t< F, const T & >; + static_assert(detail::is_optional< result >::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : result(nullopt); + } #endif #else - /// \group and_then - /// Carries out some operation which returns an optional on the stored - /// object if there is one. \requires `std::invoke(std::forward(f), - /// value())` returns a `std::optional` for some `U`. \returns Let `U` be - /// the result of `std::invoke(std::forward(f), value())`. Returns a - /// `std::optional`. The return value is empty if `*this` is empty, - /// otherwise the return value of `std::invoke(std::forward(f), value())` - /// is returned. - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) &; - template - TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) & { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - return has_value() ? detail::invoke(std::forward(f), **this) - : result(nullopt); - } - - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) &&; - template - TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) && { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - return has_value() ? detail::invoke(std::forward(f), **this) - : result(nullopt); - } + /// \group and_then + /// Carries out some operation which returns an optional on the stored + /// object if there is one. \requires `std::invoke(std::forward(f), + /// value())` returns a `std::optional` for some `U`. \returns Let `U` be + /// the result of `std::invoke(std::forward(f), value())`. Returns a + /// `std::optional`. The return value is empty if `*this` is empty, + /// otherwise the return value of `std::invoke(std::forward(f), value())` + /// is returned. + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &; + template < class F > + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t< F, T & > + and_then(F &&f) & + { + using result = detail::invoke_result_t< F, T & >; + static_assert(detail::is_optional< result >::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : result(nullopt); + } - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) const &; - template - constexpr detail::invoke_result_t and_then(F &&f) const & { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) &&; + template < class F > + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t< F, T & > + and_then(F &&f) && + { + using result = detail::invoke_result_t< F, T & >; + static_assert(detail::is_optional< result >::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : result(nullopt); + } - return has_value() ? detail::invoke(std::forward(f), **this) - : result(nullopt); - } + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &; + template < class F > + constexpr detail::invoke_result_t< F, const T & > + and_then(F &&f) const & + { + using result = detail::invoke_result_t< F, const T & >; + static_assert(detail::is_optional< result >::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : result(nullopt); + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \group and_then - /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; - template - constexpr detail::invoke_result_t and_then(F &&f) const && { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - return has_value() ? detail::invoke(std::forward(f), **this) - : result(nullopt); - } + /// \group and_then + /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; + template < class F > + constexpr detail::invoke_result_t< F, const T & > + and_then(F &&f) const && + { + using result = detail::invoke_result_t< F, const T & >; + static_assert(detail::is_optional< result >::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : result(nullopt); + } #endif #endif -#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ - !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) - /// \brief Carries out some operation on the stored object if there is one. - /// \returns Let `U` be the result of `std::invoke(std::forward(f), - /// value())`. Returns a `std::optional`. The return value is empty if - /// `*this` is empty, otherwise an `optional` is constructed from the - /// return value of `std::invoke(std::forward(f), value())` and is - /// returned. - /// - /// \group map - /// \synopsis template constexpr auto map(F &&f) &; - template TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) & { - return detail::optional_map_impl(*this, std::forward(f)); - } +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) \ + && !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// \brief Carries out some operation on the stored object if there is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. Returns a `std::optional`. The return value is empty if + /// `*this` is empty, otherwise an `optional` is constructed from the + /// return value of `std::invoke(std::forward(f), value())` and is + /// returned. + /// + /// \group map + /// \synopsis template constexpr auto map(F &&f) &; + template < class F > + TL_OPTIONAL_11_CONSTEXPR auto + map(F &&f) & + { + return detail::optional_map_impl(*this, std::forward< F >(f)); + } - /// \group map - /// \synopsis template constexpr auto map(F &&f) &&; - template TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) && { - return detail::optional_map_impl(std::move(*this), std::forward(f)); - } + /// \group map + /// \synopsis template constexpr auto map(F &&f) &&; + template < class F > + TL_OPTIONAL_11_CONSTEXPR auto + map(F &&f) && + { + return detail::optional_map_impl(std::move(*this), std::forward< F >(f)); + } - /// \group map - /// \synopsis template constexpr auto map(F &&f) const&; - template constexpr auto map(F &&f) const & { - return detail::optional_map_impl(*this, std::forward(f)); - } + /// \group map + /// \synopsis template constexpr auto map(F &&f) const&; + template < class F > + constexpr auto + map(F &&f) const & + { + return detail::optional_map_impl(*this, std::forward< F >(f)); + } - /// \group map - /// \synopsis template constexpr auto map(F &&f) const&&; - template constexpr auto map(F &&f) const && { - return detail::optional_map_impl(std::move(*this), std::forward(f)); - } + /// \group map + /// \synopsis template constexpr auto map(F &&f) const&&; + template < class F > + constexpr auto + map(F &&f) const && + { + return detail::optional_map_impl(std::move(*this), std::forward< F >(f)); + } #else - /// \brief Carries out some operation on the stored object if there is one. - /// \returns Let `U` be the result of `std::invoke(std::forward(f), - /// value())`. Returns a `std::optional`. The return value is empty if - /// `*this` is empty, otherwise an `optional` is constructed from the - /// return value of `std::invoke(std::forward(f), value())` and is - /// returned. - /// - /// \group map - /// \synopsis template auto map(F &&f) &; - template - TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval(), - std::declval())) - map(F &&f) & { - return detail::optional_map_impl(*this, std::forward(f)); - } + /// \brief Carries out some operation on the stored object if there is one. + /// \returns Let `U` be the result of `std::invoke(std::forward(f), + /// value())`. Returns a `std::optional`. The return value is empty if + /// `*this` is empty, otherwise an `optional` is constructed from the + /// return value of `std::invoke(std::forward(f), value())` and is + /// returned. + /// + /// \group map + /// \synopsis template auto map(F &&f) &; + template < class F > + TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl( + std::declval< optional & >(), std::declval< F && >())) + map(F &&f) & + { + return detail::optional_map_impl(*this, std::forward< F >(f)); + } - /// \group map - /// \synopsis template auto map(F &&f) &&; - template - TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval(), - std::declval())) - map(F &&f) && { - return detail::optional_map_impl(std::move(*this), std::forward(f)); - } + /// \group map + /// \synopsis template auto map(F &&f) &&; + template < class F > + TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl( + std::declval< optional && >(), std::declval< F && >())) + map(F &&f) && + { + return detail::optional_map_impl(std::move(*this), std::forward< F >(f)); + } - /// \group map - /// \synopsis template auto map(F &&f) const&; - template - constexpr decltype(detail::optional_map_impl(std::declval(), - std::declval())) - map(F &&f) const & { - return detail::optional_map_impl(*this, std::forward(f)); - } + /// \group map + /// \synopsis template auto map(F &&f) const&; + template < class F > + constexpr decltype(detail::optional_map_impl( + std::declval< const optional & >(), std::declval< F && >())) + map(F &&f) const & + { + return detail::optional_map_impl(*this, std::forward< F >(f)); + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \group map - /// \synopsis template auto map(F &&f) const&&; - template - constexpr decltype(detail::optional_map_impl(std::declval(), - std::declval())) - map(F &&f) const && { - return detail::optional_map_impl(std::move(*this), std::forward(f)); - } + /// \group map + /// \synopsis template auto map(F &&f) const&&; + template < class F > + constexpr decltype(detail::optional_map_impl( + std::declval< const optional && >(), std::declval< F && >())) + map(F &&f) const && + { + return detail::optional_map_impl(std::move(*this), std::forward< F >(f)); + } #endif #endif - /// \brief Calls `f` if the optional is empty - /// \requires `std::invoke_result_t` must be void or convertible to - /// `optional`. \effects If `*this` has a value, returns `*this`. - /// Otherwise, if `f` returns `void`, calls `std::forward(f)` and returns - /// `std::nullopt`. Otherwise, returns `std::forward(f)()`. - /// - /// \group or_else - /// \synopsis template optional or_else (F &&f) &; - template * = nullptr> - optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & { - if (has_value()) - return *this; - - std::forward(f)(); - return nullopt; - } - - /// \exclude - template * = nullptr> - optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & { - return has_value() ? *this : std::forward(f)(); - } - - /// \group or_else - /// \synopsis template optional or_else (F &&f) &&; - template * = nullptr> - optional or_else(F &&f) && { - if (has_value()) - return std::move(*this); + /// \brief Calls `f` if the optional is empty + /// \requires `std::invoke_result_t` must be void or convertible to + /// `optional`. \effects If `*this` has a value, returns `*this`. + /// Otherwise, if `f` returns `void`, calls `std::forward(f)` and returns + /// `std::nullopt`. Otherwise, returns `std::forward(f)()`. + /// + /// \group or_else + /// \synopsis template optional or_else (F &&f) &; + template < class F, detail::enable_if_ret_void< F > * = nullptr > + optional< T > TL_OPTIONAL_11_CONSTEXPR + or_else(F &&f) & + { + if(has_value()) + return *this; + + std::forward< F >(f)(); + return nullopt; + } - std::forward(f)(); - return nullopt; - } + /// \exclude + template < class F, detail::disable_if_ret_void< F > * = nullptr > + optional< T > TL_OPTIONAL_11_CONSTEXPR + or_else(F &&f) & + { + return has_value() ? *this : std::forward< F >(f)(); + } - /// \exclude - template * = nullptr> - optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) && { - return has_value() ? std::move(*this) : std::forward(f)(); - } + /// \group or_else + /// \synopsis template optional or_else (F &&f) &&; + template < class F, detail::enable_if_ret_void< F > * = nullptr > + optional< T > + or_else(F &&f) && + { + if(has_value()) + return std::move(*this); + + std::forward< F >(f)(); + return nullopt; + } - /// \group or_else - /// \synopsis template optional or_else (F &&f) const &; - template * = nullptr> - optional or_else(F &&f) const & { - if (has_value()) - return *this; + /// \exclude + template < class F, detail::disable_if_ret_void< F > * = nullptr > + optional< T > TL_OPTIONAL_11_CONSTEXPR + or_else(F &&f) && + { + return has_value() ? std::move(*this) : std::forward< F >(f)(); + } - std::forward(f)(); - return nullopt; - } + /// \group or_else + /// \synopsis template optional or_else (F &&f) const &; + template < class F, detail::enable_if_ret_void< F > * = nullptr > + optional< T > + or_else(F &&f) const & + { + if(has_value()) + return *this; + + std::forward< F >(f)(); + return nullopt; + } - /// \exclude - template * = nullptr> - optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) const & { - return has_value() ? *this : std::forward(f)(); - } + /// \exclude + template < class F, detail::disable_if_ret_void< F > * = nullptr > + optional< T > TL_OPTIONAL_11_CONSTEXPR + or_else(F &&f) const & + { + return has_value() ? *this : std::forward< F >(f)(); + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \exclude - template * = nullptr> - optional or_else(F &&f) const && { - if (has_value()) - return std::move(*this); - - std::forward(f)(); - return nullopt; - } + /// \exclude + template < class F, detail::enable_if_ret_void< F > * = nullptr > + optional< T > + or_else(F &&f) const && + { + if(has_value()) + return std::move(*this); + + std::forward< F >(f)(); + return nullopt; + } - /// \exclude - template * = nullptr> - optional or_else(F &&f) const && { - return has_value() ? std::move(*this) : std::forward(f)(); - } + /// \exclude + template < class F, detail::disable_if_ret_void< F > * = nullptr > + optional< T > + or_else(F &&f) const && + { + return has_value() ? std::move(*this) : std::forward< F >(f)(); + } #endif - /// \brief Maps the stored value with `f` if there is one, otherwise returns - /// `u`. - /// - /// \details If there is a value stored, then `f` is called with `**this` - /// and the value is returned. Otherwise `u` is returned. - /// - /// \group map_or - template U map_or(F &&f, U &&u) & { - return has_value() ? detail::invoke(std::forward(f), **this) - : std::forward(u); - } + /// \brief Maps the stored value with `f` if there is one, otherwise returns + /// `u`. + /// + /// \details If there is a value stored, then `f` is called with `**this` + /// and the value is returned. Otherwise `u` is returned. + /// + /// \group map_or + template < class F, class U > + U + map_or(F &&f, U &&u) & + { + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : std::forward< U >(u); + } - /// \group map_or - template U map_or(F &&f, U &&u) && { - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : std::forward(u); - } + /// \group map_or + template < class F, class U > + U + map_or(F &&f, U &&u) && + { + return has_value() + ? detail::invoke(std::forward< F >(f), std::move(**this)) + : std::forward< U >(u); + } - /// \group map_or - template U map_or(F &&f, U &&u) const & { - return has_value() ? detail::invoke(std::forward(f), **this) - : std::forward(u); - } + /// \group map_or + template < class F, class U > + U + map_or(F &&f, U &&u) const & + { + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : std::forward< U >(u); + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \group map_or - template U map_or(F &&f, U &&u) const && { - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : std::forward(u); - } + /// \group map_or + template < class F, class U > + U + map_or(F &&f, U &&u) const && + { + return has_value() + ? detail::invoke(std::forward< F >(f), std::move(**this)) + : std::forward< U >(u); + } #endif - /// \brief Maps the stored value with `f` if there is one, otherwise calls - /// `u` and returns the result. - /// - /// \details If there is a value stored, then `f` is - /// called with `**this` and the value is returned. Otherwise - /// `std::forward(u)()` is returned. - /// - /// \group map_or_else - /// \synopsis template \nauto map_or_else(F &&f, U &&u) &; - template - detail::invoke_result_t map_or_else(F &&f, U &&u) & { - return has_value() ? detail::invoke(std::forward(f), **this) - : std::forward(u)(); - } + /// \brief Maps the stored value with `f` if there is one, otherwise calls + /// `u` and returns the result. + /// + /// \details If there is a value stored, then `f` is + /// called with `**this` and the value is returned. Otherwise + /// `std::forward(u)()` is returned. + /// + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) &; + template < class F, class U > + detail::invoke_result_t< U > + map_or_else(F &&f, U &&u) & + { + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : std::forward< U >(u)(); + } - /// \group map_or_else - /// \synopsis template \nauto map_or_else(F &&f, U &&u) - /// &&; - template - detail::invoke_result_t map_or_else(F &&f, U &&u) && { - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : std::forward(u)(); - } + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// &&; + template < class F, class U > + detail::invoke_result_t< U > + map_or_else(F &&f, U &&u) && + { + return has_value() + ? detail::invoke(std::forward< F >(f), std::move(**this)) + : std::forward< U >(u)(); + } - /// \group map_or_else - /// \synopsis template \nauto map_or_else(F &&f, U &&u) - /// const &; - template - detail::invoke_result_t map_or_else(F &&f, U &&u) const & { - return has_value() ? detail::invoke(std::forward(f), **this) - : std::forward(u)(); - } + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// const &; + template < class F, class U > + detail::invoke_result_t< U > + map_or_else(F &&f, U &&u) const & + { + return has_value() ? detail::invoke(std::forward< F >(f), **this) + : std::forward< U >(u)(); + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \group map_or_else - /// \synopsis template \nauto map_or_else(F &&f, U &&u) - /// const &&; - template - detail::invoke_result_t map_or_else(F &&f, U &&u) const && { - return has_value() ? detail::invoke(std::forward(f), std::move(**this)) - : std::forward(u)(); - } + /// \group map_or_else + /// \synopsis template \nauto map_or_else(F &&f, U &&u) + /// const &&; + template < class F, class U > + detail::invoke_result_t< U > + map_or_else(F &&f, U &&u) const && + { + return has_value() + ? detail::invoke(std::forward< F >(f), std::move(**this)) + : std::forward< U >(u)(); + } #endif - /// \returns `u` if `*this` has a value, otherwise an empty optional. - template - constexpr optional::type> conjunction(U &&u) const { - using result = optional>; - return has_value() ? result{u} : result{nullopt}; - } + /// \returns `u` if `*this` has a value, otherwise an empty optional. + template < class U > + constexpr optional< typename std::decay< U >::type > + conjunction(U &&u) const + { + using result = optional< detail::decay_t< U > >; + return has_value() ? result{u} : result{nullopt}; + } - /// \returns `rhs` if `*this` is empty, otherwise the current value. - /// \group disjunction - TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) & { - return has_value() ? *this : rhs; - } + /// \returns `rhs` if `*this` is empty, otherwise the current value. + /// \group disjunction + TL_OPTIONAL_11_CONSTEXPR optional + disjunction(const optional &rhs) & + { + return has_value() ? *this : rhs; + } - /// \group disjunction - constexpr optional disjunction(const optional &rhs) const & { - return has_value() ? *this : rhs; - } + /// \group disjunction + constexpr optional + disjunction(const optional &rhs) const & + { + return has_value() ? *this : rhs; + } - /// \group disjunction - TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) && { - return has_value() ? std::move(*this) : rhs; - } + /// \group disjunction + TL_OPTIONAL_11_CONSTEXPR optional + disjunction(const optional &rhs) && + { + return has_value() ? std::move(*this) : rhs; + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \group disjunction - constexpr optional disjunction(const optional &rhs) const && { - return has_value() ? std::move(*this) : rhs; - } + /// \group disjunction + constexpr optional + disjunction(const optional &rhs) const && + { + return has_value() ? std::move(*this) : rhs; + } #endif - /// \group disjunction - TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) & { - return has_value() ? *this : std::move(rhs); - } + /// \group disjunction + TL_OPTIONAL_11_CONSTEXPR optional + disjunction(optional &&rhs) & + { + return has_value() ? *this : std::move(rhs); + } - /// \group disjunction - constexpr optional disjunction(optional &&rhs) const & { - return has_value() ? *this : std::move(rhs); - } + /// \group disjunction + constexpr optional + disjunction(optional &&rhs) const & + { + return has_value() ? *this : std::move(rhs); + } - /// \group disjunction - TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) && { - return has_value() ? std::move(*this) : std::move(rhs); - } + /// \group disjunction + TL_OPTIONAL_11_CONSTEXPR optional + disjunction(optional &&rhs) && + { + return has_value() ? std::move(*this) : std::move(rhs); + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \group disjunction - constexpr optional disjunction(optional &&rhs) const && { - return has_value() ? std::move(*this) : std::move(rhs); - } + /// \group disjunction + constexpr optional + disjunction(optional &&rhs) const && + { + return has_value() ? std::move(*this) : std::move(rhs); + } #endif - /// Takes the value out of the optional, leaving it empty - /// \group take - optional take() & { - optional ret = *this; - reset(); - return ret; - } + /// Takes the value out of the optional, leaving it empty + /// \group take + optional + take() & + { + optional ret = *this; + reset(); + return ret; + } - /// \group take - optional take() const & { - optional ret = *this; - reset(); - return ret; - } + /// \group take + optional + take() const & + { + optional ret = *this; + reset(); + return ret; + } - /// \group take - optional take() && { - optional ret = std::move(*this); - reset(); - return ret; - } + /// \group take + optional + take() && + { + optional ret = std::move(*this); + reset(); + return ret; + } #ifndef TL_OPTIONAL_NO_CONSTRR - /// \group take - optional take() const && { - optional ret = std::move(*this); - reset(); - return ret; - } + /// \group take + optional + take() const && + { + optional ret = std::move(*this); + reset(); + return ret; + } #endif - using value_type = T &; - - /// Constructs an optional that does not contain a value. - /// \group ctor_empty - constexpr optional() noexcept : m_value(nullptr) {} + using value_type = T &; - /// \group ctor_empty - constexpr optional(nullopt_t) noexcept : m_value(nullptr) {} + /// Constructs an optional that does not contain a value. + /// \group ctor_empty + constexpr optional() noexcept : m_value(nullptr) + { + } - /// Copy constructor - /// - /// If `rhs` contains a value, the stored value is direct-initialized with - /// it. Otherwise, the constructed optional is empty. - TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) noexcept = default; + /// \group ctor_empty + constexpr optional(nullopt_t) noexcept : m_value(nullptr) + { + } - /// Move constructor - /// - /// If `rhs` contains a value, the stored value is direct-initialized with - /// it. Otherwise, the constructed optional is empty. - TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) = default; - - /// Constructs the stored value with `u`. - /// \synopsis template constexpr optional(U &&u); - template >::value> - * = nullptr> - constexpr optional(U &&u) : m_value(std::addressof(u)) { - static_assert(std::is_lvalue_reference::value, "U must be an lvalue"); - } + /// Copy constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + TL_OPTIONAL_11_CONSTEXPR + optional(const optional &rhs) noexcept = default; + + /// Move constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + TL_OPTIONAL_11_CONSTEXPR + optional(optional &&rhs) = default; + + /// Constructs the stored value with `u`. + /// \synopsis template constexpr optional(U &&u); + template < class U = T, + detail::enable_if_t< !detail::is_optional< + detail::decay_t< U > >::value > * = nullptr > + constexpr optional(U &&u) : m_value(std::addressof(u)) + { + static_assert(std::is_lvalue_reference< U >::value, + "U must be an lvalue"); + } - /// \exclude - template - constexpr explicit optional(const optional &rhs) : optional(*rhs) {} + /// \exclude + template < class U > + constexpr explicit optional(const optional< U > &rhs) : optional(*rhs) + { + } - /// No-op - ~optional() = default; + /// No-op + ~optional() = default; - /// Assignment to empty. - /// - /// Destroys the current value if there is one. - optional &operator=(nullopt_t) noexcept { - m_value = nullptr; - return *this; - } + /// Assignment to empty. + /// + /// Destroys the current value if there is one. + optional &operator=(nullopt_t) noexcept + { + m_value = nullptr; + return *this; + } - /// Copy assignment. - /// - /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise - /// resets the stored value in `*this`. - optional &operator=(const optional &rhs) = default; + /// Copy assignment. + /// + /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise + /// resets the stored value in `*this`. + optional & + operator=(const optional &rhs) = default; + + /// Rebinds this optional to `u`. + /// + /// \requires `U` must be an lvalue reference. + /// \synopsis optional &operator=(U &&u); + template < class U = T, + detail::enable_if_t< !detail::is_optional< + detail::decay_t< U > >::value > * = nullptr > + optional & + operator=(U &&u) + { + static_assert(std::is_lvalue_reference< U >::value, + "U must be an lvalue"); + m_value = std::addressof(u); + return *this; + } - /// Rebinds this optional to `u`. - /// - /// \requires `U` must be an lvalue reference. - /// \synopsis optional &operator=(U &&u); - template >::value> - * = nullptr> - optional &operator=(U &&u) { - static_assert(std::is_lvalue_reference::value, "U must be an lvalue"); - m_value = std::addressof(u); - return *this; - } + /// Converting copy assignment operator. + /// + /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise + /// resets the stored value in `*this`. + template < class U > + optional & + operator=(const optional< U > &rhs) + { + m_value = std::addressof(rhs.value()); + return *this; + } - /// Converting copy assignment operator. - /// - /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise - /// resets the stored value in `*this`. - template optional &operator=(const optional &rhs) { - m_value = std::addressof(rhs.value()); - return *this; - } + /// Constructs the value in-place, destroying the current one if there is + /// one. + /// + /// \group emplace + template < class... Args > + T & + emplace(Args &&... args) noexcept + { + static_assert(std::is_constructible< T, Args &&... >::value, + "T must be constructible with Args"); + + *this = nullopt; + this->construct(std::forward< Args >(args)...); + } - /// Constructs the value in-place, destroying the current one if there is - /// one. - /// - /// \group emplace - template T &emplace(Args &&... args) noexcept { - static_assert(std::is_constructible::value, - "T must be constructible with Args"); + /// Swaps this optional with the other. + /// + /// If neither optionals have a value, nothing happens. + /// If both have a value, the values are swapped. + /// If one has a value, it is moved to the other and the movee is left + /// valueless. + void + swap(optional &rhs) noexcept + { + std::swap(m_value, rhs.m_value); + } - *this = nullopt; - this->construct(std::forward(args)...); - } + /// \returns a pointer to the stored value + /// \requires a value is stored + /// \group pointer + /// \synopsis constexpr const T *operator->() const; + constexpr const T *operator->() const + { + return m_value; + } - /// Swaps this optional with the other. - /// - /// If neither optionals have a value, nothing happens. - /// If both have a value, the values are swapped. - /// If one has a value, it is moved to the other and the movee is left - /// valueless. - void swap(optional &rhs) noexcept { std::swap(m_value, rhs.m_value); } - - /// \returns a pointer to the stored value - /// \requires a value is stored - /// \group pointer - /// \synopsis constexpr const T *operator->() const; - constexpr const T *operator->() const { return m_value; } - - /// \group pointer - /// \synopsis constexpr T *operator->(); - TL_OPTIONAL_11_CONSTEXPR T *operator->() { return m_value; } - - /// \returns the stored value - /// \requires a value is stored - /// \group deref - /// \synopsis constexpr T &operator*(); - TL_OPTIONAL_11_CONSTEXPR T &operator*() { return *m_value; } - - /// \group deref - /// \synopsis constexpr const T &operator*() const; - constexpr const T &operator*() const { return *m_value; } - - /// \returns whether or not the optional has a value - /// \group has_value - constexpr bool has_value() const noexcept { return m_value != nullptr; } - - /// \group has_value - constexpr explicit operator bool() const noexcept { - return m_value != nullptr; - } + /// \group pointer + /// \synopsis constexpr T *operator->(); + TL_OPTIONAL_11_CONSTEXPR T *operator->() + { + return m_value; + } - /// \returns the contained value if there is one, otherwise throws - /// [bad_optional_access] - /// \group value - /// synopsis constexpr T &value(); - TL_OPTIONAL_11_CONSTEXPR T &value() { - if (has_value()) - return *m_value; - throw bad_optional_access(); - } - /// \group value - /// \synopsis constexpr const T &value() const; - TL_OPTIONAL_11_CONSTEXPR const T &value() const { - if (has_value()) + /// \returns the stored value + /// \requires a value is stored + /// \group deref + /// \synopsis constexpr T &operator*(); + TL_OPTIONAL_11_CONSTEXPR T &operator*() + { return *m_value; - throw bad_optional_access(); - } - - /// \returns the stored value if there is one, otherwise returns `u` - /// \group value_or - template constexpr T value_or(U &&u) const & { - static_assert(std::is_copy_constructible::value && - std::is_convertible::value, - "T must be copy constructible and convertible from U"); - return has_value() ? **this : static_cast(std::forward(u)); - } + } - /// \group value_or - template TL_OPTIONAL_11_CONSTEXPR T value_or(U &&u) && { - static_assert(std::is_move_constructible::value && - std::is_convertible::value, - "T must be move constructible and convertible from U"); - return has_value() ? **this : static_cast(std::forward(u)); - } + /// \group deref + /// \synopsis constexpr const T &operator*() const; + constexpr const T &operator*() const + { + return *m_value; + } - /// Destroys the stored value if one exists, making the optional empty - void reset() noexcept { m_value = nullptr; } + /// \returns whether or not the optional has a value + /// \group has_value + constexpr bool + has_value() const noexcept + { + return m_value != nullptr; + } -private: - T *m_value; -}; // namespace tl + /// \group has_value + constexpr explicit operator bool() const noexcept + { + return m_value != nullptr; + } + /// \returns the contained value if there is one, otherwise throws + /// [bad_optional_access] + /// \group value + /// synopsis constexpr T &value(); + TL_OPTIONAL_11_CONSTEXPR T & + value() + { + if(has_value()) + return *m_value; + throw bad_optional_access(); + } + /// \group value + /// \synopsis constexpr const T &value() const; + TL_OPTIONAL_11_CONSTEXPR const T & + value() const + { + if(has_value()) + return *m_value; + throw bad_optional_access(); + } + /// \returns the stored value if there is one, otherwise returns `u` + /// \group value_or + template < class U > + constexpr T + value_or(U &&u) const & + { + static_assert(std::is_copy_constructible< T >::value + && std::is_convertible< U &&, T >::value, + "T must be copy constructible and convertible from U"); + return has_value() ? **this : static_cast< T >(std::forward< U >(u)); + } -} // namespace tl + /// \group value_or + template < class U > + TL_OPTIONAL_11_CONSTEXPR T + value_or(U &&u) && + { + static_assert(std::is_move_constructible< T >::value + && std::is_convertible< U &&, T >::value, + "T must be move constructible and convertible from U"); + return has_value() ? **this : static_cast< T >(std::forward< U >(u)); + } -namespace std { -// TODO SFINAE -template struct hash> { - ::std::size_t operator()(const tl::optional &o) const { - if (!o.has_value()) - return 0; + /// Destroys the stored value if one exists, making the optional empty + void + reset() noexcept + { + m_value = nullptr; + } - return std::hash>()(*o); - } -}; -} // namespace std + private: + T *m_value; + }; // namespace tl + +} // namespace tl + +namespace std +{ + // TODO SFINAE + template < class T > + struct hash< tl::optional< T > > + { + ::std::size_t + operator()(const tl::optional< T > &o) const + { + if(!o.has_value()) + return 0; + + return std::hash< tl::detail::remove_const_t< T > >()(*o); + } + }; +} // namespace std #endif diff --git a/libabyss/include/abyss/client.hpp b/libabyss/include/abyss/client.hpp index 6b793fb24..3702f4b63 100644 --- a/libabyss/include/abyss/client.hpp +++ b/libabyss/include/abyss/client.hpp @@ -14,10 +14,10 @@ namespace abyss { namespace http { - typedef std::string RPC_Method_t; - typedef json::Value RPC_Params; - typedef json::Document RPC_Response; - typedef std::unordered_multimap< std::string, std::string > Headers_t; + using RPC_Method_t = std::string; + using RPC_Params = json::Value; + using RPC_Response = json::Document; + using Headers_t = std::unordered_multimap< std::string, std::string >; struct ConnImpl; /// jsonrpc response handler for client @@ -30,7 +30,7 @@ namespace abyss /// return true on successful handling /// return false on errors while handling virtual bool - HandleResponse(const RPC_Response& response) = 0; + HandleResponse(RPC_Response response) = 0; /// populate http request headers virtual void @@ -55,7 +55,7 @@ namespace abyss /// jsonrpc client struct JSONRPC { - typedef std::function< IRPCClientHandler*(ConnImpl*) > HandlerFactory; + using HandlerFactory = std::function< IRPCClientHandler*(ConnImpl*) >; JSONRPC(); ~JSONRPC(); @@ -115,4 +115,4 @@ namespace abyss } // namespace http } // namespace abyss -#endif \ No newline at end of file +#endif diff --git a/libabyss/include/abyss/http.hpp b/libabyss/include/abyss/http.hpp index 86d7447d3..cf0de9666 100644 --- a/libabyss/include/abyss/http.hpp +++ b/libabyss/include/abyss/http.hpp @@ -11,7 +11,7 @@ namespace abyss { struct RequestHeader { - typedef std::unordered_multimap< std::string, std::string > Headers_t; + using Headers_t = std::unordered_multimap< std::string, std::string >; Headers_t Headers; std::string Method; std::string Path; @@ -33,4 +33,4 @@ namespace abyss } // namespace http } // namespace abyss -#endif \ No newline at end of file +#endif diff --git a/libabyss/include/abyss/json.hpp b/libabyss/include/abyss/json.hpp index e87bdbe29..045374624 100644 --- a/libabyss/include/abyss/json.hpp +++ b/libabyss/include/abyss/json.hpp @@ -10,7 +10,7 @@ namespace abyss { namespace json { - typedef std::unordered_map< std::string, std::any > Object; + using Object = std::unordered_map< std::string, std::any >; } } // namespace abyss #else @@ -19,17 +19,17 @@ namespace abyss { namespace json { - typedef rapidjson::Document Document; - typedef rapidjson::Value Value; + using Document = rapidjson::Document; + using Value = rapidjson::Value; } // namespace json } // namespace abyss #endif namespace abyss { #if __cplusplus >= 201703L - typedef std::string_view string_view; + using string_view = std::string_view; #else - typedef std::string string_view; + using string_view = std::string; #endif namespace json { diff --git a/libabyss/include/abyss/server.hpp b/libabyss/include/abyss/server.hpp index 2de7add48..860a3ae02 100644 --- a/libabyss/include/abyss/server.hpp +++ b/libabyss/include/abyss/server.hpp @@ -19,9 +19,9 @@ namespace abyss struct IRPCHandler { - typedef std::string Method_t; - typedef json::Value Params; - typedef json::Document Response; + using Method_t = std::string; + using Params = json::Value; + using Response = json::Document; IRPCHandler(ConnImpl* impl); diff --git a/libabyss/main.cpp b/libabyss/main.cpp index 7e936f16f..9c45a59a4 100644 --- a/libabyss/main.cpp +++ b/libabyss/main.cpp @@ -29,7 +29,7 @@ struct DemoCall : public abyss::http::IRPCClientHandler } bool - HandleResponse(const abyss::http::RPC_Response& resp) override + HandleResponse(abyss::http::RPC_Response resp) override { std::string body; abyss::json::ToString(resp, body); diff --git a/libabyss/src/client.cpp b/libabyss/src/client.cpp index 7ea363d26..ae3c34ce1 100644 --- a/libabyss/src/client.cpp +++ b/libabyss/src/client.cpp @@ -125,7 +125,7 @@ namespace abyss case json::IParser::eNeedData: return true; case json::IParser::eDone: - handler->HandleResponse(m_Response); + handler->HandleResponse(std::move(m_Response)); Close(); return true; case json::IParser::eParseError: diff --git a/llarp/config.cpp b/llarp/config.cpp index 0c4d003fd..3a74da899 100644 --- a/llarp/config.cpp +++ b/llarp/config.cpp @@ -36,6 +36,7 @@ namespace llarp services = find_section(top, "services", section_t{}); system = find_section(top, "system", section_t{}); api = find_section(top, "api", section_t{}); + lokid = find_section(top, "lokid", section_t{}); return true; } return false; @@ -135,11 +136,7 @@ llarp_generic_ensure_config(std::ofstream &f, std::string basepath) f << "# authkey=insertpubkey1here" << std::endl; f << "# authkey=insertpubkey2here" << std::endl; f << "# authkey=insertpubkey3here" << std::endl; -#ifdef _WIN32 f << "bind=127.0.0.1:1190" << std::endl; -#else - f << "bind=unix:" << basepath << "api.socket" << std::endl; -#endif f << std::endl << std::endl; f << "# system settings for priviledges and such" << std::endl; @@ -165,6 +162,7 @@ llarp_generic_ensure_config(std::ofstream &f, std::string basepath) // Make auto-config smarter // will this break reproducibility rules? +// (probably) #ifdef __linux__ f << "bind=127.3.2.1:53" << std::endl; #else @@ -187,6 +185,11 @@ llarp_generic_ensure_config(std::ofstream &f, std::string basepath) void llarp_ensure_router_config(std::ofstream &f, std::string basepath) { + f << "# lokid settings (disabled by default)" << std::endl; + f << "[lokid]" << std::endl; + f << "enabled=false" << std::endl; + f << "jsonrpc=127.0.0.1:22023" << std::endl; + f << std::endl; f << "# network settings " << std::endl; f << "[network]" << std::endl; f << "profiles=" << basepath << "profiles.dat" << std::endl; diff --git a/llarp/config.hpp b/llarp/config.hpp index f8bff7afc..469fe737c 100644 --- a/llarp/config.hpp +++ b/llarp/config.hpp @@ -9,7 +9,7 @@ namespace llarp { struct Config { - typedef std::list< std::pair< std::string, std::string > > section_t; + using section_t = std::list< std::pair< std::string, std::string > >; section_t router; section_t network; @@ -20,6 +20,7 @@ namespace llarp section_t services; section_t system; section_t api; + section_t lokid; bool Load(const char *fname); diff --git a/llarp/dht/context.cpp b/llarp/dht/context.cpp index 0244c8a99..d89bf8e50 100644 --- a/llarp/dht/context.cpp +++ b/llarp/dht/context.cpp @@ -47,32 +47,32 @@ namespace llarp } bool - Validate(const RouterID &) const + Validate(const RouterID &) const override { // TODO: check with lokid return true; } void - Start(const TXOwner &peer) + Start(const TXOwner &peer) override { parent->DHTSendTo(peer.node, new FindRouterMessage(parent->OurKey(), peer.txid)); } bool - GetNextPeer(Key_t &, const std::set< Key_t > &) + GetNextPeer(Key_t &, const std::set< Key_t > &) override { return false; } void - DoNextRequest(const Key_t &) + DoNextRequest(const Key_t &) override { } void - SendReply() + SendReply() override { llarp::LogInfo("got ", valuesFound.size(), " routers from exploration"); for(const auto &pk : valuesFound) @@ -324,7 +324,7 @@ namespace llarp } bool - Validate(const service::IntroSet &value) const + Validate(const service::IntroSet &value) const override { if(!value.Verify(parent->Crypto(), parent->Now())) { @@ -339,52 +339,33 @@ namespace llarp return true; } - void - DoNextRequest(const Key_t &nextPeer) - { - // iterate to next peer - parent->LookupIntroSetIterative( - target, whoasked.node, whoasked.txid, nextPeer, - std::bind(&ServiceAddressLookup::HandleNextRequestResult, this, - std::placeholders::_1)); - } - - void - HandleNextRequestResult(const std::vector< service::IntroSet > &results) - { - // merge results - std::set< service::IntroSet > found; - - for(const auto &introset : valuesFound) - found.insert(introset); - - for(const auto &introset : results) - found.insert(introset); - - valuesFound.clear(); - for(const auto &introset : found) - valuesFound.push_back(introset); - - // send reply - SendReply(); - } - bool - GetNextPeer(Key_t &next, const std::set< Key_t > &exclude) + GetNextPeer(Key_t &next, const std::set< Key_t > &exclude) override { Key_t k = target.data(); return parent->nodes->FindCloseExcluding(k, next, exclude); } void - Start(const TXOwner &peer) + Start(const TXOwner &peer) override { parent->DHTSendTo(peer.node, new FindIntroMessage(peer.txid, target, R)); } + void + DoNextRequest(const Key_t &ask) override + { + if(R) + parent->LookupIntroSetRecursive(target, whoasked.node, whoasked.txid, + ask, R - 1); + else + parent->LookupIntroSetIterative(target, whoasked.node, whoasked.txid, + ask); + } + virtual void - SendReply() + SendReply() override { if(handleResult) handleResult(valuesFound); @@ -408,7 +389,7 @@ namespace llarp } void - SendReply() + SendReply() override { auto path = parent->router->paths.GetByUpstream(parent->OurKey(), localPath); @@ -461,7 +442,7 @@ namespace llarp } bool - Validate(const service::IntroSet &introset) const + Validate(const service::IntroSet &introset) const override { if(I.A != introset.A) { @@ -473,7 +454,7 @@ namespace llarp } void - Start(const TXOwner &peer) + Start(const TXOwner &peer) override { std::vector< Key_t > exclude; for(const auto &router : dontTell) @@ -483,18 +464,18 @@ namespace llarp } bool - GetNextPeer(Key_t &, const std::set< Key_t > &) + GetNextPeer(Key_t &, const std::set< Key_t > &) override { return false; } void - DoNextRequest(const Key_t &) + DoNextRequest(const Key_t &) override { } void - SendReply() + SendReply() override { // don't need this } @@ -550,7 +531,7 @@ namespace llarp } bool - Validate(const service::IntroSet &introset) const + Validate(const service::IntroSet &introset) const override { if(!introset.Verify(parent->Crypto(), parent->Now())) { @@ -566,26 +547,25 @@ namespace llarp } void - Start(const TXOwner &peer) + Start(const TXOwner &peer) override { parent->DHTSendTo(peer.node, new FindIntroMessage(target, peer.txid, R)); } bool - GetNextPeer(__attribute__((unused)) Key_t &nextpeer, - __attribute__((unused)) const std::set< Key_t > &exclude) + GetNextPeer(Key_t &, const std::set< Key_t > &) override { return false; } void - DoNextRequest(__attribute__((unused)) const Key_t &nextPeer) + DoNextRequest(const Key_t &) override { } void - SendReply() + SendReply() override { std::set< service::IntroSet > found; for(const auto &remoteTag : valuesFound) @@ -636,7 +616,7 @@ namespace llarp } void - SendReply() + SendReply() override { auto path = parent->router->paths.GetByUpstream(parent->OurKey(), localPath); @@ -717,7 +697,7 @@ namespace llarp } bool - Validate(const RouterContact &rc) const + Validate(const RouterContact &rc) const override { if(!rc.Verify(parent->Crypto())) { @@ -728,20 +708,18 @@ namespace llarp } bool - GetNextPeer(__attribute__((unused)) Key_t &next, - __attribute__((unused)) const std::set< Key_t > &exclude) + GetNextPeer(Key_t &, const std::set< Key_t > &) override { - // TODO: implement iterative (?) return false; } void - DoNextRequest(__attribute__((unused)) const Key_t &next) + DoNextRequest(const Key_t &) override { } void - Start(const TXOwner &peer) + Start(const TXOwner &peer) override { parent->DHTSendTo( peer.node, @@ -749,7 +727,7 @@ namespace llarp } virtual void - SendReply() + SendReply() override { if(resultHandler) { @@ -777,7 +755,7 @@ namespace llarp } void - SendReply() + SendReply() override { auto path = parent->router->paths.GetByUpstream(parent->OurKey(), localPath); diff --git a/llarp/dns.cpp b/llarp/dns.cpp index 5236ba1d2..156c4abc9 100644 --- a/llarp/dns.cpp +++ b/llarp/dns.cpp @@ -242,8 +242,8 @@ extern "C" // llarp::LogDebug("Advancing to pos ", std::to_string(*pos)); moveable += (*pos); // advance to position - //hexDump(moveable, 12); - //hexDumpAt(buffer, *pos, 12); + // hexDump(moveable, 12); + // hexDumpAt(buffer, *pos, 12); if(*moveable == '\xc0') { @@ -267,10 +267,10 @@ extern "C" /* uint32_t readAt32 = *pos; answer->name = getDNSstring(buffer, &readAt32); - llarp::LogInfo("Parsed string ", answer->name, " read ", std::to_string(readAt32)); - moveable += readAt32; (*pos) += readAt32; + llarp::LogInfo("Parsed string ", answer->name, " read ", + std::to_string(readAt32)); moveable += readAt32; (*pos) += readAt32; */ - //moveable++; (*pos)++; + // moveable++; (*pos)++; } /* hexDump(moveable, 10); @@ -330,12 +330,12 @@ extern "C" // FIXME: move this out of here, this shouldn't be responsible for decode switch(answer->type) { - case 2: // NS + case 2: // NS // don't really need to do anything here moveable += answer->rdLen; (*pos) += answer->rdLen; // advance the length - break; - case 5: + break; + case 5: moveable += answer->rdLen; (*pos) += answer->rdLen; // advance the length break; @@ -371,10 +371,10 @@ extern "C" { std::string revname = getDNSstring(buffer, pos); llarp::LogInfo("revDNSname: ", revname); - //answer->rData = new uint8_t[answer->rdLen + 1]; + // answer->rData = new uint8_t[answer->rdLen + 1]; answer->rData.resize(answer->rdLen); memcpy(answer->rData.data(), revname.c_str(), answer->rdLen); - //answer->rData = (uint8_t *)strdup(revname.c_str()); // safer? nope + // answer->rData = (uint8_t *)strdup(revname.c_str()); // safer? nope moveable += answer->rdLen; //(*pos) += answer->rdLen; // advance the length } diff --git a/llarp/dns_dotlokilookup.cpp b/llarp/dns_dotlokilookup.cpp index fff58140b..2aea964a6 100644 --- a/llarp/dns_dotlokilookup.cpp +++ b/llarp/dns_dotlokilookup.cpp @@ -229,9 +229,10 @@ ReverseHandlerIter(struct llarp::service::Context::endpoint_iter *endpointCfg) + tokensCheck[2] + "." + tokensCheck[3]; llarp::LogDebug(searchIp, " vs ", checkIp); - llarp::IPRange range = llarp::iprange_ipv4( - std::stoi(tokensCheck[0]), std::stoi(tokensCheck[1]), std::stoi(tokensCheck[2]), - std::stoi(tokensCheck[3]), tunEndpoint->tunif.netmask); // create range + llarp::IPRange range = + llarp::iprange_ipv4(std::stoi(tokensCheck[0]), std::stoi(tokensCheck[1]), + std::stoi(tokensCheck[2]), std::stoi(tokensCheck[3]), + tunEndpoint->tunif.netmask); // create range // hack atm to work around limitations in ipaddr_ipv4_bits and llarp::IPRange llarp::huint32_t searchIPv4_fixed = llarp::ipaddr_ipv4_bits( std::stoi(tokensSearch[searchTokens - 6]), diff --git a/llarp/dnsc.cpp b/llarp/dnsc.cpp index 583411b49..fe5501dd8 100644 --- a/llarp/dnsc.cpp +++ b/llarp/dnsc.cpp @@ -224,13 +224,14 @@ generic_handle_dnsc_recvfrom(dnsc_answer_request *request, for(uint32_t i = 0; i < hdr->qdCount; i++) { question = decode_question(castBufc, &pos); - //llarp::LogDebug("Read a question, now at ", std::to_string(pos)); + // llarp::LogDebug("Read a question, now at ", std::to_string(pos)); // 1 dot: 1 byte for length + length // 4 bytes for class/type // castBuf += question->name.length() + 1 + 4; // castBuf += 2; // skip answer label } - llarp::LogDebug("Question ", std::to_string(question->type), " ", question->name); + llarp::LogDebug("Question ", std::to_string(question->type), " ", + question->name); // FIXME: only handling one atm std::vector< dns_msg_answer * > answers; @@ -296,15 +297,15 @@ generic_handle_dnsc_recvfrom(dnsc_answer_request *request, // pos = 0; // reset pos answer = decode_answer(castBufc, &pos); // answers.push_back(answer); - llarp::LogDebug("Read an authority for ", - request->question.name, " at ", std::to_string(pos)); + llarp::LogDebug("Read an authority for ", request->question.name, " at ", + std::to_string(pos)); // castBuf += answer->name.length() + 4 + 4 + 4 + answer->rdLen; - if((ssize_t)pos > sz) - { - llarp::LogWarn("Would read past end of dns packet. for ", - request->question.name); - break; - } + if((ssize_t)pos > sz) + { + llarp::LogWarn("Would read past end of dns packet. for ", + request->question.name); + break; + } } /* @@ -393,14 +394,14 @@ generic_handle_dnsc_recvfrom(dnsc_answer_request *request, int ip = 0; - // if no answer, just bail now - if (!answer) - { - request->found = false; - request->resolved(request); - return; - } - + // if no answer, just bail now + if(!answer) + { + request->found = false; + request->resolved(request); + return; + } + /* search for and print IPv4 addresses */ // if(dnsQuery->reqType == 0x01) /* @@ -603,7 +604,7 @@ llarp_handle_dnsc_recvfrom(struct llarp_udp_io *const udp, llarp::LogDebug("Header got client responses for id: ", hdr->id); // if we sent this out, then there's an id - struct dns_tracker *tracker = (struct dns_tracker *)udp->user; + struct dns_tracker *tracker = (struct dns_tracker *)udp->user; struct dnsc_answer_request *request = tracker->client_request[hdr->id].get(); // sometimes we'll get double responses @@ -694,7 +695,7 @@ void llarp_host_resolved(dnsc_answer_request *const request) { dns_tracker *tracker = (dns_tracker *)request->context->tracker; - auto val = std::find_if( + auto val = std::find_if( tracker->client_request.begin(), tracker->client_request.end(), [request]( std::pair< const uint32_t, std::unique_ptr< dnsc_answer_request > > @@ -735,7 +736,7 @@ llarp_dnsc_init(struct dnsc_context *const dnsc, llarp::LogInfo("DNSc adding relay ", dnsc_sockaddr); dnsc->resolvers.push_back(dnsc_sockaddr); dnsc->tracker = &dns_udp_tracker; - dnsc->logic = logic; + dnsc->logic = logic; return true; } diff --git a/llarp/ev.hpp b/llarp/ev.hpp index 0b5cdb509..23ce9e02c 100644 --- a/llarp/ev.hpp +++ b/llarp/ev.hpp @@ -83,13 +83,13 @@ namespace llarp }; }; - typedef llarp::util::CoDelQueue< WriteBuffer, WriteBuffer::GetTime, - WriteBuffer::PutTime, WriteBuffer::Compare, - llarp::util::NullMutex, - llarp::util::NullLock, 5, 100, 128 > - LossyWriteQueue_t; + using LossyWriteQueue_t = + llarp::util::CoDelQueue< WriteBuffer, WriteBuffer::GetTime, + WriteBuffer::PutTime, WriteBuffer::Compare, + llarp::util::NullMutex, llarp::util::NullLock, + 5, 100, 128 >; - typedef std::deque< WriteBuffer > LosslessWriteQueue_t; + using LosslessWriteQueue_t = std::deque< WriteBuffer >; // on windows, tcp/udp event loops are socket fds // and TUN device is a plain old fd @@ -319,13 +319,13 @@ namespace llarp }; }; - typedef llarp::util::CoDelQueue< - WriteBuffer, WriteBuffer::GetTime, WriteBuffer::PutTime, - WriteBuffer::Compare, WriteBuffer::GetNow, llarp::util::NullMutex, - llarp::util::NullLock, 5, 100, 1024 > - LossyWriteQueue_t; + using LossyWriteQueue_t = + llarp::util::CoDelQueue< WriteBuffer, WriteBuffer::GetTime, + WriteBuffer::PutTime, WriteBuffer::Compare, + WriteBuffer::GetNow, llarp::util::NullMutex, + llarp::util::NullLock, 5, 100, 1024 >; - typedef std::deque< WriteBuffer > LosslessWriteQueue_t; + using LosslessWriteQueue_t = std::deque< WriteBuffer >; int fd; int flags = 0; diff --git a/llarp/ev_kqueue.hpp b/llarp/ev_kqueue.hpp index 7fc3bb775..993574327 100644 --- a/llarp/ev_kqueue.hpp +++ b/llarp/ev_kqueue.hpp @@ -215,7 +215,7 @@ namespace llarp llarp_tun_io* t; device* tunif; tun(llarp_tun_io* tio, llarp_ev_loop* l) - : ev_io(-1, new LossyWriteQueue_t("kqueue_tun_write", l)) + : ev_io(-1, new LossyWriteQueue_t("kqueue_tun_write", l, l)) , t(tio) , tunif(tuntap_init()){}; diff --git a/llarp/exit/session.cpp b/llarp/exit/session.cpp index 2dd5ace1b..1458581ba 100644 --- a/llarp/exit/session.cpp +++ b/llarp/exit/session.cpp @@ -22,8 +22,12 @@ namespace llarp bool BaseSession::ShouldBuildMore(llarp_time_t now) const { - return AvailablePaths(llarp::path::ePathRoleExit) == 0 - || path::Builder::ShouldBuildMore(now); + const size_t expect = (1 + (m_NumPaths / 2)); + if(NumPathsExistingAt(now + (10 * 1000)) < expect) + return true; + if(AvailablePaths(llarp::path::ePathRoleExit) < expect) + return true; + return false; } bool diff --git a/llarp/fs.hpp b/llarp/fs.hpp index 39ec656e8..01934bc65 100644 --- a/llarp/fs.hpp +++ b/llarp/fs.hpp @@ -22,8 +22,8 @@ namespace llarp { namespace util { - typedef std::function< bool(const fs::path &) > PathVisitor; - typedef std::function< void(const fs::path &, PathVisitor) > PathIter; + using PathVisitor = std::function< bool(const fs::path &) >; + using PathIter = std::function< void(const fs::path &, PathVisitor) >; static PathIter IterDir = [](const fs::path &path, PathVisitor visit) { DIR *d = opendir(path.string().c_str()); diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index 0a8b88613..ff42293c4 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -300,7 +300,9 @@ namespace llarp return false; huint32_t ip = GetIPForIdent(pk); m_ActiveExits.insert(std::make_pair( - pk, new llarp::exit::Endpoint(pk, path, !wantInternet, ip, this))); + pk, + std::unique_ptr< llarp::exit::Endpoint >( + new llarp::exit::Endpoint(pk, path, !wantInternet, ip, this)))); m_Paths[path] = pk; return HasLocalMappedAddrFor(pk); } diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index a2ab29575..cf75577cb 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -335,7 +335,7 @@ namespace llarp TunEndpoint::Tick(llarp_time_t now) { // call tun code in endpoint logic in case of network isolation - llarp_logic_queue_job(EndpointLogic(), {this, handleTickTun}); + //llarp_logic_queue_job(EndpointLogic(), {this, handleTickTun}); FlushSend(); Endpoint::Tick(now); } diff --git a/llarp/ini.hpp b/llarp/ini.hpp index aa465f3d3..94b4bd09d 100644 --- a/llarp/ini.hpp +++ b/llarp/ini.hpp @@ -48,9 +48,9 @@ namespace ini { } - typedef std::list< std::pair< std::string, std::string > > value_map_t; - typedef std::map< std::string, Level > section_map_t; - typedef std::list< section_map_t::const_iterator > sections_t; + using value_map_t = std::list< std::pair< std::string, std::string > >; + using section_map_t = std::map< std::string, Level >; + using sections_t = std::list< section_map_t::const_iterator >; value_map_t values; section_map_t sections; sections_t ordered_sections; diff --git a/llarp/link/utp.cpp b/llarp/link/utp.cpp index 1b9b35b39..e37c029e2 100644 --- a/llarp/link/utp.cpp +++ b/llarp/link/utp.cpp @@ -869,12 +869,12 @@ namespace llarp BaseSession::EnterState(State st) { state = st; + Alive(); if(st == eSessionReady) { parent->MapAddr(remoteRC.pubkey, this); - Router()->HandleLinkSessionEstablished(remoteRC); + Router()->HandleLinkSessionEstablished(remoteRC, parent); } - Alive(); } bool diff --git a/llarp/net.cpp b/llarp/net.cpp index 50685415f..bc0594779 100644 --- a/llarp/net.cpp +++ b/llarp/net.cpp @@ -152,12 +152,12 @@ _llarp_nt_getadaptersaddresses(struct llarp_nt_ifaddrs_t** ifap) fprintf(stderr, "IP_ADAPTER_ADDRESSES buffer length %lu bytes.\n", dwSize); #endif pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)_llarp_nt_heap_alloc(dwSize); - dwRet = GetAdaptersAddresses( - AF_UNSPEC, - GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST - | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME - | GAA_FLAG_SKIP_MULTICAST, - nullptr, pAdapterAddresses, &dwSize); + dwRet = GetAdaptersAddresses(AF_UNSPEC, + GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST + | GAA_FLAG_SKIP_DNS_SERVER + | GAA_FLAG_SKIP_FRIENDLY_NAME + | GAA_FLAG_SKIP_MULTICAST, + nullptr, pAdapterAddresses, &dwSize); if(ERROR_BUFFER_OVERFLOW == dwRet) { _llarp_nt_heap_free(pAdapterAddresses); @@ -378,7 +378,7 @@ _llarp_nt_getadaptersaddresses(struct llarp_nt_ifaddrs_t** ifap) /* default IPv6 route */ if(AF_INET6 == lpSockaddr->sa_family && 0 == prefix->PrefixLength && IN6_IS_ADDR_UNSPECIFIED( - &((struct sockaddr_in6*)(lpSockaddr))->sin6_addr)) + &((struct sockaddr_in6*)(lpSockaddr))->sin6_addr)) { #ifdef DEBUG fprintf(stderr, @@ -447,10 +447,10 @@ _llarp_nt_getadaptersaddresses(struct llarp_nt_ifaddrs_t** ifap) ift->_ifa.ifa_next = (struct llarp_nt_ifaddrs_t*)(ift + 1); ift = (struct _llarp_nt_ifaddrs_t*)(ift->_ifa.ifa_next); } - else + else { - ift->_ifa.ifa_next = nullptr; - } + ift->_ifa.ifa_next = nullptr; + } } } @@ -464,7 +464,7 @@ static unsigned _llarp_nt_getadaptersaddresses_nametoindex(const char* ifname) { ULONG ifIndex; - DWORD dwSize = 4096, dwRet; + DWORD dwSize = 4096, dwRet; IP_ADAPTER_ADDRESSES *pAdapterAddresses = nullptr, *adapter; char szAdapterName[256]; @@ -487,7 +487,7 @@ _llarp_nt_getadaptersaddresses_nametoindex(const char* ifname) for(unsigned i = 3; i; i--) { pAdapterAddresses = (IP_ADAPTER_ADDRESSES*)_llarp_nt_heap_alloc(dwSize); - dwRet = GetAdaptersAddresses( + dwRet = GetAdaptersAddresses( AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_MULTICAST, @@ -524,7 +524,8 @@ _llarp_nt_getadaptersaddresses_nametoindex(const char* ifname) { if(0 == strcmp(szAdapterName, adapter->AdapterName)) { - //ifIndex = AF_INET6 == iffamily ? adapter->Ipv6IfIndex : adapter->IfIndex; + // ifIndex = AF_INET6 == iffamily ? adapter->Ipv6IfIndex : + // adapter->IfIndex; _llarp_nt_heap_free(pAdapterAddresses); return ifIndex; } diff --git a/llarp/pathset.cpp b/llarp/pathset.cpp index 8165025e3..8b461dba3 100644 --- a/llarp/pathset.cpp +++ b/llarp/pathset.cpp @@ -41,6 +41,18 @@ namespace llarp return 0; } + size_t + PathSet::NumPathsExistingAt(llarp_time_t futureTime) const + { + size_t num = 0; + for(const auto & item : m_Paths) + { + if(!item.second->Expired(futureTime)) + ++num; + } + return num; + } + void PathSet::Tick(llarp_time_t now, llarp_router* r) { diff --git a/llarp/queue_manager.cpp b/llarp/queue_manager.cpp index 2bcff082d..a9d2b7bfb 100644 --- a/llarp/queue_manager.cpp +++ b/llarp/queue_manager.cpp @@ -335,7 +335,7 @@ namespace llarp // Queue is full. assert(state == ElementState::Reading); assert( - 1 == (circularDifference(currGen, elemGen, m_maxGeneration) + 1)); + 1 == (circularDifference(currGen, elemGen, m_maxGeneration + 1))); return QueueReturn::QueueEmpty; } diff --git a/llarp/queue_manager.hpp b/llarp/queue_manager.hpp index dc428b077..f0ddb332d 100644 --- a/llarp/queue_manager.hpp +++ b/llarp/queue_manager.hpp @@ -70,7 +70,7 @@ namespace llarp public: static constexpr size_t Alignment = 64; - using AtomicIndex = std::atomic; + using AtomicIndex = std::atomic< std::uint32_t >; private: AtomicIndex m_pushIndex; // Index in the buffer that the next @@ -90,7 +90,7 @@ namespace llarp const uint32_t m_maxCombinedIndex; // Maximum combined value of index and // generation for this object. - std::atomic* m_states; // Array of index states. + std::atomic< std::uint32_t >* m_states; // Array of index states. AtomicIndex& pushIndex(); diff --git a/llarp/router.cpp b/llarp/router.cpp index 1850501eb..56cedb308 100644 --- a/llarp/router.cpp +++ b/llarp/router.cpp @@ -121,9 +121,10 @@ llarp_router_try_connect(struct llarp_router *router, } void -llarp_router::HandleLinkSessionEstablished(llarp::RouterContact rc) +llarp_router::HandleLinkSessionEstablished(llarp::RouterContact rc, + llarp::ILinkLayer *link) { - async_verify_RC(rc); + async_verify_RC(rc, link); } llarp_router::llarp_router() @@ -162,13 +163,7 @@ llarp_router::PersistSessionUntil(const llarp::RouterID &remote, llarp_time_t until) { llarp::LogDebug("persist session to ", remote, " until ", until); - if(m_PersistingSessions.find(remote) == m_PersistingSessions.end()) - m_PersistingSessions[remote] = until; - else - { - if(m_PersistingSessions[remote] < until) - m_PersistingSessions[remote] = until; - } + m_PersistingSessions[remote] = std::max(until, m_PersistingSessions[remote]); } constexpr size_t MaxPendingSendQueueSize = 8; @@ -240,9 +235,15 @@ llarp_router::HandleDHTLookupForSendTo( { if(results.size()) { - llarp_nodedb_put_rc(nodedb, results[0]); - llarp_router_try_connect(this, results[0], 10); - async_verify_RC(results[0]); + if(whitelistRouters && lokinetRouters.find(remote) == lokinetRouters.end()) + { + return; + } + if(results[0].Verify(&crypto)) + { + llarp_nodedb_put_rc(nodedb, results[0]); + llarp_router_try_connect(this, results[0], 10); + } } else { @@ -362,7 +363,7 @@ llarp_router::on_verify_client_rc(llarp_async_verify_rc *job) llarp::PubKey pk(job->rc.pubkey); router->FlushOutboundFor(pk, router->GetLinkWithSessionByPubkey(pk)); delete ctx; - delete job; + router->pendingVerifyRC.erase(pk); } void @@ -374,15 +375,15 @@ llarp_router::on_verify_server_rc(llarp_async_verify_rc *job) llarp::PubKey pk(job->rc.pubkey); if(!job->valid) { - llarp::LogWarn("invalid server RC"); if(ctx->establish_job) { // was an outbound attempt ctx->establish_job->Failed(); } delete ctx; - delete job; router->DiscardOutboundFor(pk); + router->pendingVerifyRC.erase(pk); + return; } // we're valid, which means it's already been committed to the nodedb @@ -412,7 +413,7 @@ llarp_router::on_verify_server_rc(llarp_async_verify_rc *job) else router->FlushOutboundFor(pk, router->GetLinkWithSessionByPubkey(pk)); delete ctx; - delete job; + router->pendingVerifyRC.erase(pk); } void @@ -473,9 +474,11 @@ llarp_router::HandleDHTLookupForTryEstablishTo( } for(const auto &result : results) { + if(whitelistRouters + && lokinetRouters.find(result.pubkey) == lokinetRouters.end()) + continue; llarp_nodedb_put_rc(nodedb, result); llarp_router_try_connect(this, result, 10); - async_verify_RC(result); } } @@ -537,6 +540,8 @@ llarp_router::Tick() } paths.TickPaths(now); exitContext.Tick(now); + if(rpcCaller) + rpcCaller->Tick(now); } void @@ -606,19 +611,21 @@ llarp_router::GetLinkWithSessionByPubkey(const llarp::RouterID &pubkey) } void -llarp_router::FlushOutboundFor(const llarp::RouterID remote, +llarp_router::FlushOutboundFor(llarp::RouterID remote, llarp::ILinkLayer *chosen) { llarp::LogDebug("Flush outbound for ", remote); - pendingEstablishJobs.erase(remote); + auto itr = outboundMessageQueue.find(remote); if(itr == outboundMessageQueue.end()) { + pendingEstablishJobs.erase(remote); return; } if(!chosen) { DiscardOutboundFor(remote); + pendingEstablishJobs.erase(remote); return; } while(itr->second.size()) @@ -630,6 +637,7 @@ llarp_router::FlushOutboundFor(const llarp::RouterID remote, itr->second.pop(); } + pendingEstablishJobs.erase(remote); } void @@ -654,9 +662,21 @@ llarp_router::GetRandomConnectedRouter(llarp::RouterContact &result) const } void -llarp_router::async_verify_RC(const llarp::RouterContact &rc) +llarp_router::async_verify_RC(const llarp::RouterContact &rc, + llarp::ILinkLayer *link) { - llarp_async_verify_rc *job = new llarp_async_verify_rc(); + if(pendingVerifyRC.count(rc.pubkey)) + return; + if(rc.IsPublicRouter() && whitelistRouters) + { + if(lokinetRouters.find(rc.pubkey) == lokinetRouters.end()) + { + llarp::LogInfo(rc.pubkey, " is NOT a valid service node, rejecting"); + link->CloseSessionTo(rc.pubkey); + return; + } + } + llarp_async_verify_rc *job = &pendingVerifyRC[rc.pubkey]; llarp::async_verify_context *ctx = new llarp::async_verify_context(); ctx->router = this; ctx->establish_job = nullptr; @@ -679,25 +699,8 @@ llarp_router::async_verify_RC(const llarp::RouterContact &rc) job->hook = &llarp_router::on_verify_server_rc; else job->hook = &llarp_router::on_verify_client_rc; - if(rpcCaller && rc.IsPublicRouter()) - { - rpcCaller->VerifyRouter(rc.pubkey, [job](llarp::PubKey pk, bool valid) { - if(valid) - { - llarp::LogDebug("lokid says ", pk, " is valid"); - llarp_nodedb_async_verify(job); - } - else - { - llarp::LogDebug("lokid says ", pk, " is NOT valid"); - job->hook(job); - } - }); - } - else - { - llarp_nodedb_async_verify(job); - } + + llarp_nodedb_async_verify(job); } void @@ -1236,6 +1239,17 @@ namespace llarp llarp::LogWarn("failed to load hidden service config for ", key); } } + else if(StrEq(section, "lokid")) + { + if(StrEq(key, "enabled")) + { + self->whitelistRouters = IsTrueValue(val); + } + if(StrEq(key, "jsonrpc")) + { + self->lokidRPCAddr = val; + } + } else if(StrEq(section, "dns")) { if(StrEq(key, "upstream")) diff --git a/llarp/router.hpp b/llarp/router.hpp index 828e36113..28159a118 100644 --- a/llarp/router.hpp +++ b/llarp/router.hpp @@ -134,7 +134,7 @@ struct llarp_router llarp::Profiling routerProfiling; std::string routerProfilesFile = "profiles.dat"; - typedef std::queue< std::vector< byte_t > > MessageQueue; + using MessageQueue = std::queue< std::vector< byte_t > >; /// outbound message queue std::unordered_map< llarp::RouterID, MessageQueue, llarp::RouterID::Hash > @@ -150,6 +150,11 @@ struct llarp_router llarp::RouterID::Hash > pendingEstablishJobs; + // pending RCs to be verified by pubkey + std::unordered_map< llarp::RouterID, llarp_async_verify_rc, + llarp::RouterID::Hash > + pendingVerifyRC; + // sessions to persist -> timestamp to end persist at std::unordered_map< llarp::RouterID, llarp_time_t, llarp::RouterID::Hash > m_PersistingSessions; @@ -162,7 +167,8 @@ struct llarp_router llarp_router(); ~llarp_router(); - void HandleLinkSessionEstablished(llarp::RouterContact); + void + HandleLinkSessionEstablished(llarp::RouterContact, llarp::ILinkLayer *); bool HandleRecvLinkMessageBuffer(llarp::ILinkSession *from, llarp_buffer_t msg); @@ -245,8 +251,7 @@ struct llarp_router /// manually flush outbound message queue for just 1 router void - FlushOutboundFor(const llarp::RouterID remote, - llarp::ILinkLayer *chosen = nullptr); + FlushOutboundFor(llarp::RouterID remote, llarp::ILinkLayer *chosen = nullptr); /// manually discard all pending messages to remote router void @@ -296,7 +301,7 @@ struct llarp_router GetRandomConnectedRouter(llarp::RouterContact &result) const; void - async_verify_RC(const llarp::RouterContact &rc); + async_verify_RC(const llarp::RouterContact &rc, llarp::ILinkLayer *link); void HandleDHTLookupForSendTo(llarp::RouterID remote, diff --git a/llarp/rpc.cpp b/llarp/rpc.cpp index e35e38d15..832e909f5 100644 --- a/llarp/rpc.cpp +++ b/llarp/rpc.cpp @@ -21,11 +21,31 @@ namespace llarp { } + virtual bool + HandleJSONResult(const ::abyss::json::Value& val) = 0; + + bool + HandleResponse(::abyss::http::RPC_Response response) + { + if(!response.IsObject()) + { + return HandleJSONResult({}); + } + const auto itr = response.FindMember("result"); + if(itr == response.MemberEnd()) + { + return HandleJSONResult({}); + } + if(itr->value.IsObject()) + return HandleJSONResult(itr->value); + return false; + } + void PopulateReqHeaders(abyss::http::Headers_t& hdr) { (void)hdr; - // TODO: add http auth + // TODO: add http auth (?) } }; @@ -45,17 +65,44 @@ namespace llarp } bool - HandleResponse(const ::abyss::http::RPC_Response& response) + HandleJSONResult(const ::abyss::json::Value& result) override { - (void)response; - // TODO: get keys from response PubkeyList_t keys; + if(!result.IsObject()) + { + handler({}, false); + return false; + } + const auto itr = result.FindMember("keys"); + if(itr == result.MemberEnd()) + { + handler({}, false); + return false; + } + if(!itr->value.IsArray()) + { + handler({}, false); + return false; + } + auto key_itr = itr->value.Begin(); + while(key_itr != itr->value.End()) + { + if(key_itr->IsString()) + { + keys.emplace_back(); + if(!HexDecode(key_itr->GetString(), keys.back(), PUBKEYSIZE)) + { + keys.pop_back(); + } + } + ++key_itr; + } handler(keys, true); return true; } void - HandleError() + HandleError() override { handler({}, false); } @@ -73,9 +120,8 @@ namespace llarp } void - Tick() + Tick(llarp_time_t now) { - llarp_time_t now = router->Now(); if(now >= m_NextKeyUpdate) { AsyncUpdatePubkeyList(); @@ -90,7 +136,7 @@ namespace llarp llarp::LogInfo("Updating service node list"); ::abyss::json::Value params; params.SetObject(); - QueueRPC("get_all_service_node_keys", std::move(params), + QueueRPC("/get_all_service_node_keys", std::move(params), std::bind(&CallerImpl::NewAsyncUpdatePubkeyListConn, this, std::placeholders::_1)); } @@ -110,21 +156,6 @@ namespace llarp std::placeholders::_1, std::placeholders::_2)); } - void - VerifyRouter(llarp::PubKey pk, - std::function< void(llarp::PubKey, bool) > handler) - { - if(router->whitelistRouters) - { - auto itr = router->lokinetRouters.find(pk); - handler(pk, itr != router->lokinetRouters.end()); - } - else - { - handler(pk, true); - } - } - void HandleServiceNodeListUpdated(const PubkeyList_t& list, bool updated) { @@ -132,7 +163,7 @@ namespace llarp { router->lokinetRouters.clear(); for(const auto& pk : list) - router->lokinetRouters.emplace( + router->lokinetRouters.insert( std::make_pair(pk, std::numeric_limits< llarp_time_t >::max())); llarp::LogInfo("updated service node list, we have ", router->lokinetRouters.size(), " authorized routers"); @@ -301,16 +332,9 @@ namespace llarp } void - Tick() - { - } - - void - VerifyRouter(llarp::PubKey pk, - std::function< void(llarp::PubKey, bool) > result) + Tick(llarp_time_t now) { - // always allow routers when not using libabyss - result(pk, true); + (void)now; } }; @@ -332,10 +356,9 @@ namespace llarp } void - Caller::VerifyRouter(llarp::PubKey pk, - std::function< void(llarp::PubKey, bool) > handler) + Caller::Tick(llarp_time_t now) { - m_Impl->VerifyRouter(pk, handler); + m_Impl->Tick(now); } Server::Server(llarp_router* r) : m_Impl(new ServerImpl(r)) diff --git a/llarp/service/endpoint.cpp b/llarp/service/endpoint.cpp index 651f0a37b..64e67404c 100644 --- a/llarp/service/endpoint.cpp +++ b/llarp/service/endpoint.cpp @@ -94,7 +94,7 @@ namespace llarp llarp::LogWarn("could not publish descriptors for endpoint ", Name(), " because we couldn't get enough valid introductions"); if(ShouldBuildMore(now) || forceRebuild) - ManualRebuild(1, llarp::path::ePathRoleInboundHS); + ManualRebuild(1); return; } m_IntroSet.I.clear(); @@ -197,7 +197,7 @@ namespace llarp continue; byte_t tmp[1024] = {0}; auto buf = StackBuffer< decltype(tmp) >(tmp); - if(!SendToOrQueue(introset.A.Addr(), buf, eProtocolText)) + if(!SendToOrQueue(introset.A.Addr().data(), buf, eProtocolText)) { llarp::LogWarn(Name(), " failed to send/queue data to ", introset.A.Addr(), " for tag ", tag.ToString()); @@ -1113,9 +1113,12 @@ namespace llarp } } // no converstation - EnsurePathToService(remote, [](Address, OutboundContext*) {}, 5000, - false); - return false; + return EnsurePathToService(remote, + [](Address, OutboundContext* c) { + if(c) + c->UpdateIntroSet(true); + }, + 5000, false); } bool @@ -1159,7 +1162,7 @@ namespace llarp } return false; } - Build(hops, llarp::path::ePathRoleOutboundHS); + Build(hops); return true; } @@ -1529,8 +1532,7 @@ namespace llarp } } (void)roles; - return path::Builder::SelectHop(db, prev, cur, hop, - llarp::path::ePathRoleOutboundHS); + return path::Builder::SelectHop(db, prev, cur, hop, roles); } uint64_t @@ -1547,8 +1549,7 @@ namespace llarp { if(markedBad) return false; - bool should = path::Builder::ShouldBuildMoreForRoles( - now, llarp::path::ePathRoleOutboundHS); + bool should = path::Builder::ShouldBuildMore(now); // determinte newest intro Introduction intro; if(!GetNewestIntro(intro)) diff --git a/llarp/threadpool.hpp b/llarp/threadpool.hpp index 6d6398c78..ba5bd804c 100644 --- a/llarp/threadpool.hpp +++ b/llarp/threadpool.hpp @@ -10,8 +10,8 @@ namespace llarp { namespace thread { - typedef util::Mutex mtx_t; - typedef util::Lock lock_t; + using mtx_t = util::Mutex; + using lock_t = util::Lock; using Pool = ThreadPool; diff --git a/lokinet-bootstrap b/lokinet-bootstrap old mode 100644 new mode 100755 index 701608ae9..b92856a25 --- a/lokinet-bootstrap +++ b/lokinet-bootstrap @@ -4,4 +4,7 @@ if [ "X$1" = "X" ] ; then url="https://i2p.rocks/i2procks.signed" ; else url="$1" ; fi echo "downloading $url" -wget -O $HOME/.lokinet/bootstrap.signed "$url" || echo "failed to download bootstrap from $url" \ No newline at end of file +if [ ! -d $HOME/.lokinet/]; then + mkdir $HOME/.lokinet +fi +wget -O $HOME/.lokinet/bootstrap.signed "$url" || echo "failed to download bootstrap from $url" diff --git a/test/jsonrpc_unittest.cpp b/test/jsonrpc_unittest.cpp index b1bce93e6..3fade3a4f 100644 --- a/test/jsonrpc_unittest.cpp +++ b/test/jsonrpc_unittest.cpp @@ -102,8 +102,7 @@ struct ClientHandler : public abyss::http::IRPCClientHandler } bool - HandleResponse(__attribute__((unused)) - const abyss::http::RPC_Response& response) + HandleResponse(__attribute__((unused)) abyss::http::RPC_Response response) { test->Stop(); return true; diff --git a/test/test_llarp_queue.cpp b/test/test_llarp_queue.cpp index f46f09cd0..05d6cce4c 100644 --- a/test/test_llarp_queue.cpp +++ b/test/test_llarp_queue.cpp @@ -82,7 +82,7 @@ popFrontTester(Args& args) args.startCond.notify_one(); - args.runCond.wait(guard, [&args]() { return args.runSignal; }); + args.runCond.wait(guard, [&args]() { return !!args.runSignal; }); } for(;;) @@ -104,7 +104,7 @@ pushBackTester(Args& args) args.startCond.notify_one(); - args.runCond.wait(guard, [&args]() { return args.runSignal; }); + args.runCond.wait(guard, [&args]() { return !!args.runSignal; }); } for(size_t i = 0; i < args.iterations; ++i) @@ -322,7 +322,7 @@ TEST(TestQueue, manyProducerManyConsumer) { threads[i] = std::thread(std::bind(&popFrontTester, std::ref(args))); - args.startCond.wait(lock, [&args, i]() { return args.count == (i + 1); }); + args.startCond.wait(lock, [&]() { return args.count == (i + 1); }); } for(size_t i = 0; i < numThreads; ++i) @@ -330,8 +330,8 @@ TEST(TestQueue, manyProducerManyConsumer) threads[i + numThreads] = std::thread(std::bind(&pushBackTester, std::ref(args))); - args.startCond.wait( - lock, [&args, i]() { return args.count == (numThreads + i + 1); }); + args.startCond.wait(lock, + [&]() { return args.count == (numThreads + i + 1); }); } args.runSignal++; diff --git a/test/test_llarp_queue_manager.cpp b/test/test_llarp_queue_manager.cpp index 401c04df6..016c4baf3 100644 --- a/test/test_llarp_queue_manager.cpp +++ b/test/test_llarp_queue_manager.cpp @@ -139,7 +139,8 @@ struct QueueData std::uint32_t* m_states; // Array of index states. }; -static_assert(sizeof(QueueData) == sizeof(QueueManager)); +static_assert(sizeof(QueueData) == sizeof(QueueManager), + "QueueData not updated"); static constexpr uint32_t GENERATION_COUNT_SHIFT = 0x2; static constexpr uint32_t ELEMENT_STATE_MASK = 0x3;