diff --git a/.gitignore b/.gitignore index 5f7f9dd97..51d48cd52 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ cmake_install.cmake *.opensdf *.ipch *.dir +*.lib Win32 Debug Release @@ -30,4 +31,9 @@ autowiring-*-*.dmg autowiring-*-*.deb autowiring-*-*.rpm autowiring-*-*.msi -autowiring.xcodeproj \ No newline at end of file +autowiring.xcodeproj +autowiring/AutowiringConfig.h +*.nupkg +lib/*.* +autowiring-*-*.zip +_CPack_Packages \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 0f32bdb27..d56e4f056 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,39 +1,68 @@ language: cpp compiler: g++ +os: linux + before_install: # Enforce whitespace guidelines - ./scripts/whitespace_check.sh + # Enforce Leap Motion copyright notice + - ./scripts/copyright_check.sh + # g++4.8.1 - - if [ "$CXX" == "g++" ]; then sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; fi - - if [ "$CXX" == "g++" ]; then export CXX; fi + - if [ "$CXX" == "g++" ]; then + sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test + && export CXX; + fi # clang 3.4 - - if [ "$CXX" == "clang++" ]; then sudo add-apt-repository -y ppa:h-rayflood/llvm; fi + - if [ "$CXX" == "clang++" ]; then + sudo add-apt-repository -y ppa:h-rayflood/llvm; + fi - - sudo apt-get remove gcc-4.6 g++-4.6 - - sudo apt-get update -qq + - sudo apt-get remove gcc-4.6 g++-4.6 cmake + - sudo apt-get -qq update install: + # CMake 3.0 + - sudo apt-get -qq install libc6-i386 + && wget http://www.cmake.org/files/v3.0/cmake-3.0.2-Linux-i386.tar.gz + && tar -xzf cmake-3.0.2-Linux-i386.tar.gz + && sudo cp -fR cmake-3.0.2-Linux-i386/* /usr + # g++4.8.1 - - if [ "$CXX" = "g++" ]; then sudo apt-get install -qq gcc-4.8 g++-4.8; fi - - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8"; fi - - sudo ln -s /usr/bin/gcc-4.8 /usr/bin/gcc - - sudo ln -s /usr/bin/g++-4.8 /usr/bin/g++ - + - if [ "$CXX" = "g++" ]; then + sudo apt-get install -qq gcc-4.8 g++-4.8 + && export CXX="g++-4.8" + && sudo ln -s /usr/bin/gcc-4.8 /usr/bin/gcc + && sudo ln -s /usr/bin/g++-4.8 /usr/bin/g++; + fi + # clang 3.4 - - if [ "$CXX" == "clang++" ]; then sudo apt-get install --allow-unauthenticated -qq clang-3.4; fi - - if [ "$CXX" == "clang++" ]; then export CXX="clang++-3.4"; fi - + - if [ "$CXX" == "clang++" ]; then + sudo apt-get install --allow-unauthenticated -qq clang-3.4 + && export CXX="clang++-3.4"; + fi + before_script: - - cmake . -script: - export CPATH=/usr/include/c++/4.8:/usr/include/x86_64-linux-gnu/c++/4.8/:$CPATH - export LD_LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8:$LD_LIBRARY_PATH + - cmake . -DCMAKE_BUILD_TYPE=Release + +script: + # Build Autowriring, run unit tests, and install - make -j 8 || make - - make test + - ctest --output-on-failure + - sudo make install + + # Package + - sudo cpack || (cat _CPack_Packages/Linux/TGZ/InstallOutput.log; exit 1) + + # Build examples from installed Autowiring + - cd examples + && cmake . + && make + && cd .. + after_failure: - cat Testing/Temporary/LastTest.log 2> /dev/null -os: - - linux - - osx \ No newline at end of file diff --git a/Autowiring.nuspec b/Autowiring.nuspec new file mode 100644 index 000000000..adb49d821 --- /dev/null +++ b/Autowiring.nuspec @@ -0,0 +1,192 @@ + + + + autowiring + 0.2.3 + Leap Motion + Leap Motion + http://autowiring.io + false + A C++11 Concurrent Inversion of Control framework + Autowiring is an inversion-of-control framework for C++11. It provides a declarative way to manage resources through dependency injection. + Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. + en-US + c++ ioc concurrent c++11 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AutowiringConfig.h.in b/AutowiringConfig.h.in new file mode 100644 index 000000000..8d666e1a8 --- /dev/null +++ b/AutowiringConfig.h.in @@ -0,0 +1,17 @@ +// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. +#pragma once + +// +// Define preprocessor macros from CMake variables +// + +// Are we building autonet? +#cmakedefine01 AUTOWIRING_BUILD_AUTONET + +// Are we linking with C++11 STL? +#cmakedefine01 USE_LIBCXX +#if USE_LIBCXX +#define AUTOWIRING_USE_LIBCXX 1 +#else +#define AUTOWIRING_USE_LIBCXX 0 +#endif diff --git a/CMakeLists.txt b/CMakeLists.txt index 09ebd224d..c670d7ac7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,8 @@ -cmake_minimum_required(VERSION 2.8) -project(autowiring) +cmake_minimum_required(VERSION 3.0) +include(version.cmake) +project(autowiring VERSION ${autowiring_VERSION}) include(CTest) -# TODO: Use the VERSION attribute for the "project" setting instead after upgrading -# the cmake_minimum_required to version 3.0 -set(autowiring_VERSION_MAJOR 0) -set(autowiring_VERSION_MINOR 1) -set(autowiring_VERSION_PATCH 0) - # Determine whether Autowiring has been embedded in another project if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) set(AUTOWIRING_IS_EMBEDDED) @@ -31,25 +26,22 @@ else() endif() endif() -# Macro for deprecated functionality needed to comply with c++98 std implementations -if(NOT USE_LIBCXX) - add_definitions(-DAUTOWIRING_UNSAFE_HASHTABLE) +if(NOT WIN32) + set(CMAKE_CXX_FLAGS "-std=c++11") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libstdc++") endif() - if(USE_LIBCXX) # Clang needs special additional flags to build with C++11 - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + # Apple needs us to tell it that we're using libc++, or it will try to use libstdc++ instead + message("AppleClang C++11") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") + elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") message("Clang C++11") - if (APPLE) - # Apple needs us to tell it that we're using libc++, or it will try to use libstdc++ instead - set(CMAKE_CXX_FLAGS "-std=c++11 -stdlib=libc++") - else() - set(CMAKE_CXX_FLAGS "-std=c++11") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++") - endif() + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++") elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") message("GCC C++11") - set(CMAKE_CXX_FLAGS "-std=c++11") endif() endif() @@ -64,17 +56,34 @@ set(AUTOWIRING_BUILD_AUTONET_DEFAULT ON) get_filename_component(AUTOWIRING_ROOT_DIR . ABSOLUTE) if(AUTOWIRING_IS_EMBEDDED) set(AUTOWIRING_BUILD_TESTS_DEFAULT OFF) - set(AUTOWIRING_BUILD_EXAMPLES_DEFAULT OFF) else() set(AUTOWIRING_BUILD_TESTS_DEFAULT ON) - set(AUTOWIRING_BUILD_EXAMPLES_DEFAULT ON) # All of our binaries go to one place: The binaries output directory. We only want to tinker # with this if we're building by ourselves, otherwise we just do whatever the enclosing project # wants us to do. - set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + + # Libraries not only all wind up in the libraries directory, but we also keep them all together + # here by putting them in the same place, regardless of whether they are debug or release. This + # makes globbing them together much easier. + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/lib) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/lib) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/lib) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/lib) +endif() + +# 64-bit installations should suffix with 64 +if(${CMAKE_SIZEOF_VOID_P} STREQUAL 8) + set(CMAKE_DEBUG_POSTFIX "64") + set(CMAKE_RELEASE_POSTFIX "64") endif() +# Postfix on all debug libraries should be "d" +set(CMAKE_DEBUG_POSTFIX d${CMAKE_DEBUG_POSTFIX}) + option(AUTOWIRING_BUILD_TESTS "Build Autowiring unit tests" ${AUTOWIRING_BUILD_TESTS_DEFAULT}) function(add_googletest dirname) if(AUTOWIRING_BUILD_TESTS) @@ -107,29 +116,51 @@ include_directories( contrib contrib/websocketpp ) -add_subdirectory(src) -add_subdirectory(contrib) - -# Build examples -option(AUTOWIRING_BUILD_EXAMPLES "Build Autowiring examples" ${AUTOWIRING_BUILD_EXAMPLES_DEFAULT}) -if(AUTOWIRING_BUILD_EXAMPLES) - add_subdirectory(examples) -endif() # CMake configurations +if(${CMAKE_SIZEOF_VOID_P} STREQUAL 8) + set(autowiring_ARCHITECTURE "64") +else() + set(autowiring_ARCHITECTURE "32") +endif() configure_file(autowiring-config.cmake.in autowiring-config.cmake @ONLY) configure_file(autowiring-configVersion.cmake.in autowiring-configVersion.cmake @ONLY) +configure_file(AutowiringConfig.h.in ${PROJECT_SOURCE_DIR}/autowiring/AutowiringConfig.h @ONLY) + +# Recurse through source directories +add_subdirectory(src) + +# Export library +export(EXPORT AutowiringTargets FILE AutowiringTargets.cmake NAMESPACE Autowiring::) # Only attempt to do anything with cpack if we're being built stand-alone if(NOT AUTOWIRING_IS_EMBEDDED) # Install autowiring-config.cmake and autowiring-configVersion.cmake - install (FILES - "${CMAKE_BINARY_DIR}/contrib/autowiring/autowiring-config.cmake" - "${CMAKE_BINARY_DIR}/contrib/autowiring/autowiring-configVersion.cmake" - DESTINATION "${CMAKE_SOURCE_DIR}/cmake" + install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/autowiring-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/autowiring-configVersion.cmake" + DESTINATION "cmake" + COMPONENT autowiring + ) + + # Install public header files + install( + DIRECTORY ${PROJECT_SOURCE_DIR}/autowiring/ + DESTINATION include/autowiring COMPONENT autowiring + FILES_MATCHING PATTERN "*.h" ) + # Targets file is needed in order to describe how to link Autowiring to the rest of the system + install(EXPORT AutowiringTargets FILE AutowiringTargets.cmake COMPONENT autowiring NAMESPACE Autowiring:: DESTINATION cmake CONFIGURATIONS ${CMAKE_CONFIGURATION_TYPES}) + + # 64-bit installations get a different upgrade GUID + if(CMAKE_SIZEOF_VOID_P STREQUAL 8) + set(autowiring_GUID_LAST_CHAR E) + else() + set(autowiring_GUID_LAST_CHAR D) + endif() + # This is the upgrade GUID. Part of the GUID is derived from the major version number. Any time # the major version number is adjusted, the upgrade GUID changes. This allows multiple versions # of the same product to be installed on a user's system at the same time, but also means that @@ -137,15 +168,33 @@ if(NOT AUTOWIRING_IS_EMBEDDED) # # For more information on the rationale for this process, see the discussion on semantic versioning # found at http://semver.org/ - SET(CPACK_WIX_UPGRADE_GUID "{060E5EDD-229${autowiring_VERSION_MAJOR}-4AD8-BAFA-A303D5696A2D}") + SET(CPACK_WIX_UPGRADE_GUID "{060E5EDD-229${autowiring_VERSION_MAJOR}-4AD8-BAFA-A303D5696A2${autowiring_GUID_LAST_CHAR}}") # Need a custom wix installation template so that we update the CMake package registry correctly # Only really needed on Windows; Mac and Linux have pretty good default search behavior, so we # leave those alone. - SET(CPACK_WIX_TEMPLATE autowiring.wxs) + SET(CPACK_WIX_TEMPLATE "${CMAKE_SOURCE_DIR}/autowiring.wxs") + SET(CPACK_MONOLITHIC_INSTALL ON) + + # Run the script that will grab the debug and release configurations and install them during packaging + set(CPACK_INSTALL_COMMANDS + "${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --config Debug" + "${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --config Release" + "${CMAKE_COMMAND} -DBUILD_TYPE=Debug -P \\\"${CMAKE_SOURCE_DIR}/cmake_package.cmake\\\"" + "${CMAKE_COMMAND} -DBUILD_TYPE=Release -P \\\"${CMAKE_SOURCE_DIR}/cmake_package.cmake\\\"" + ) + + # Pick the generator in an appropriate way + if(WIN32) + set(CPACK_GENERATOR WIX ZIP) + elseif(APPLE) + # TODO: Add Bundle as a generator here + set(CPACK_GENERATOR TGZ) + else() + set(CPACK_GENERATOR TGZ DEB) + endif() - # Packaging stuff, if an installer is being made insteadINCLUDE(InstallRequiredSystemLibraries) - SET(CPACK_GENERATOR "WIX") + # Packaging stuff, if an installer is being made instead SET(CPACK_PACKAGE_VENDOR "Leap Motion") SET(CPACK_PACKAGE_CONTACT "cmercenary@gmail.com") SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md") @@ -155,6 +204,6 @@ if(NOT AUTOWIRING_IS_EMBEDDED) SET(CPACK_PACKAGE_VERSION_MINOR "${autowiring_VERSION_MINOR}") SET(CPACK_PACKAGE_VERSION_PATCH "${autowiring_VERSION_PATCH}") SET(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "autowiring") - SET(CPACK_PACKAGE_INSTALL_DIRECTORY "autowiring ${CMake_VERSION_MAJOR}.${CMake_VERSION_MINOR}") + SET(CPACK_PACKAGE_INSTALL_DIRECTORY "autowiring") INCLUDE(CPack) endif() \ No newline at end of file diff --git a/autowiring-config.cmake.in b/autowiring-config.cmake.in index 83b85e68b..66acb7814 100644 --- a/autowiring-config.cmake.in +++ b/autowiring-config.cmake.in @@ -1,7 +1,14 @@ -# - Config file for the websocketpp package +# - Config file for the autowiring package # It defines the following variables -# AUTOWIRING_FOUND - indicates that the module was found -# AUTOWIRING_INCLUDE_DIR - include directories +# autowiring_FOUND - indicates that the module was found +# autowiring_INCLUDE_DIR - include directories +# Check if local build +if ("@CMAKE_CURRENT_BINARY_DIR@" STREQUAL CMAKE_CURRENT_LIST_DIR) + set(autowiring_INCLUDE_DIR "@PROJECT_SOURCE_DIR@") +else() + set(autowiring_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/../include") +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/AutowiringTargets.cmake") set(autowiring_FOUND TRUE) -set(autowiring_INCLUDE_DIR "@INSTALL_INCLUDE_DIR@") diff --git a/autowiring-configVersion.cmake.in b/autowiring-configVersion.cmake.in index 605c67674..fd9678467 100644 --- a/autowiring-configVersion.cmake.in +++ b/autowiring-configVersion.cmake.in @@ -1,4 +1,11 @@ -set(PACKAGE_VERSION "@AUTOWIRING_VERSION@") +set(PACKAGE_VERSION "@autowiring_VERSION@") + +# Verify that we have a bit depth matching the bit depth desired by the customer +if(NOT ${CMAKE_SIZEOF_VOID_P} STREQUAL @CMAKE_SIZEOF_VOID_P@) + set(PACKAGE_VERSION_COMPATIBLE FALSE) + set(PACKAGE_VERSION_UNSUITABLE TRUE) + return() +endif() # Check whether the requested PACKAGE_FIND_VERSION is compatible if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") diff --git a/autowiring/AnySharedPointer.h b/autowiring/AnySharedPointer.h index dafd84310..aea147d59 100644 --- a/autowiring/AnySharedPointer.h +++ b/autowiring/AnySharedPointer.h @@ -8,12 +8,12 @@ struct AnySharedPointer { new (m_space) SharedPointerSlot; } - AnySharedPointer(const AnySharedPointer& rhs) { + explicit AnySharedPointer(const AnySharedPointer& rhs) { new (m_space) SharedPointerSlot(*rhs.slot()); } template - AnySharedPointer(const std::shared_ptr& rhs) { + explicit AnySharedPointer(const std::shared_ptr& rhs) { // Delegate the remainder to the assign operation: new (m_space) SharedPointerSlotT(rhs); } @@ -46,6 +46,11 @@ struct AnySharedPointer { return *slot() == *rhs.slot(); } + template + bool operator==(const std::shared_ptr& rhs) const { + return *slot() == rhs; + } + /// /// Default for std library sorting of unique elements /// @@ -111,4 +116,5 @@ inline bool operator==(const std::shared_ptr& lhs, const AnySharedPointer& rh return rhs == lhs; } -static_assert(!std::is_polymorphic::value, "The shared pointer cannot be polymorphic"); +static_assert(sizeof(AnySharedPointerT) == sizeof(AnySharedPointer), "AnySharedPointer realization cannot have members"); +static_assert(!std::is_polymorphic::value, "The shared pointer cannot be polymorphic, this prevents the root type from being aliased correctly"); diff --git a/autowiring/AutoAnchor.h b/autowiring/AutoAnchor.h deleted file mode 100644 index f567da0ad..000000000 --- a/autowiring/AutoAnchor.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. -#pragma once - -// Marker base class for anchors -struct AutoAnchorBase {}; - -/// -/// Anchors a particular object type or event type to the annotated context sigil -/// -/// -/// The AutoAnchor is a marker type to be applied to a sigil type. When a member of a subcontext of any -/// annotated sigil class attempts to AutoRequire or AutoFire an instance of any sigil type, instead of -/// creating the corresponding object or obtaining the junction box in that child context, the request -/// will be satisfied instead by the anchor. -/// -template -struct AutoAnchor: - AutoAnchorBase -{}; diff --git a/autowiring/AutoCheckout.h b/autowiring/AutoCheckout.h index 0d03cb9e0..1dd136a29 100644 --- a/autowiring/AutoCheckout.h +++ b/autowiring/AutoCheckout.h @@ -1,24 +1,28 @@ // Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. #pragma once +#include +#include MEMORY_HEADER class AutoPacket; template class AutoCheckout { public: - typedef void (AutoPacket::*t_completion)(bool); + typedef void (AutoPacket::*t_completion)(bool, const std::type_info&, const std::type_info&); AutoCheckout(void) : m_parent(nullptr), m_val(nullptr), m_ready(false), + m_source(&typeid(void)), completion(nullptr) {} - AutoCheckout(AutoPacket& parent, const std::shared_ptr& val, t_completion completion) : + AutoCheckout(AutoPacket& parent, const std::shared_ptr& val, t_completion completion, const std::type_info& source = typeid(void)) : m_parent(&parent), m_val(val), m_ready(false), + m_source(&source), completion(completion) {} @@ -26,28 +30,34 @@ class AutoCheckout { m_parent(rhs.m_parent), m_val(rhs.m_val), m_ready(rhs.m_ready), + m_source(rhs.m_source), completion(rhs.completion) { rhs.m_parent = nullptr; - rhs.m_val = nullptr; + rhs.m_val.reset(); + rhs.m_ready = false; } ~AutoCheckout(void) { if(m_val) - (m_parent->*completion)(m_ready); + (m_parent->*completion)(m_ready, typeid(T), *m_source); } private: + // NOTE: m_parent cannot be a shared_ptr since it may be created during the resolution + // of optional_ptr arguments, in which case the shared_ptr to m_parent will be expired. + // Using a regular pointer enables decoration during the final call. AutoPacket* m_parent; std::shared_ptr m_val; - bool m_ready; + mutable bool m_ready; + const std::type_info* m_source; t_completion completion; public: /// /// Causes the wrapped packet to be committed when the checkout is destroyed /// - void Ready(void) { + void Ready() const { m_ready = true; } @@ -58,11 +68,18 @@ class AutoCheckout { operator bool(void) const { return !!m_val; } operator T&(void) const { return *m_val; } + // Assignment by move AutoCheckout& operator=(AutoCheckout&& rhs) { - std::swap(m_parent, rhs.m_parent); - std::swap(m_val, rhs.m_val); + m_parent = rhs.m_parent; + m_val = rhs.m_val; m_ready = rhs.m_ready; + m_source = rhs.m_source; completion = rhs.completion; + + rhs.m_parent = nullptr; + rhs.m_val.reset(); + rhs.m_ready = false; + return *this; } }; diff --git a/autowiring/AutoFilterDescriptor.h b/autowiring/AutoFilterDescriptor.h index 38aaa5df9..d739f918f 100644 --- a/autowiring/AutoFilterDescriptor.h +++ b/autowiring/AutoFilterDescriptor.h @@ -2,185 +2,46 @@ #pragma once #include "AnySharedPointer.h" #include "AutoPacket.h" -#include "auto_out.h" +#include "auto_arg.h" +#include "CallExtractor.h" +#include "DataFlow.h" #include "Decompose.h" #include "has_autofilter.h" -#include "optional_ptr.h" -#include +#include "is_shared_ptr.h" #include MEMORY_HEADER +#include FUNCTIONAL_HEADER +#include STL_UNORDERED_SET +#include STL_UNORDERED_MAP class AutoPacket; class Deferred; -template -class auto_pooled; - -enum eSubscriberInputType { - // Unused type, refers to an unrecognized input - inTypeInvalid, - - // Specifies that this argument is mandatory for the AutoFilter to be called - inTypeRequired, - - // Specifies that this argument is optional--the filter generally may be called - // any time all required arguments are satisfied, no matter how many optional - // arguments remain - inTypeOptional, - - // Specifies that the argument is an output which must be satisfied manually by - // the caller - outTypeRef, - - // Specifies that the argument will automatically be marked ready, unless explicitly - // cancelled by the caller. - outTypeRefAutoReady -}; - -/// -/// Default behavior, which is to just obtain an input for the specified type -/// -template -struct subscriber_traits { - static_assert( - std::is_const::type>::value || - !std::is_reference::value, - "If a decoration is desired, it must either be a const reference, or by value" - ); - - typedef T type; - static const eSubscriberInputType subscriberType = inTypeRequired; - - const T& operator()(AutoPacket& packet) const { - return packet.Get(); - } -}; - -/// -/// Output behavior for the single specified type -/// -template -struct subscriber_traits { - typedef T type; - static const eSubscriberInputType subscriberType = outTypeRef; - - AutoCheckout operator()(AutoPacket& packet) const { - // Inputs by reference are automatically and unconditionally ready: - AutoCheckout rv = packet.Checkout(); - rv.Ready(); - return rv; - } -}; - -/// -/// Mandatory input type -/// -template -struct subscriber_traits: - subscriber_traits -{}; - -/// -/// Optional input type -/// -template -struct subscriber_traits> { - typedef T type; - static const eSubscriberInputType subscriberType = inTypeOptional; - - // Optional pointer overload, tries to satisfy but doesn't throw if there's a miss - optional_ptr operator()(AutoPacket& packet) const { - const typename std::decay::type* out; - if(packet.Get(out)) - return out; - return nullptr; - } -}; - -template -struct subscriber_traits> { - typedef T type; - static const eSubscriberInputType subscriberType = auto_ready ? outTypeRef : outTypeRefAutoReady; - - auto_out operator()(AutoPacket& packet) const { - return auto_out(packet.Checkout()); - } -}; - -template<> -struct subscriber_traits { - typedef AutoPacket type; - static const eSubscriberInputType subscriberType = inTypeRequired; - - AutoPacket& operator()(AutoPacket& packet) const { - return packet; - } -}; - /// -/// Specialization for immediate mode cases +/// AutoFilter argument disposition /// -template -struct CallExtractor; - -template -struct CallExtractor: - Decompose -{ - static const bool deferred = false; - static const size_t N = sizeof...(Args); - - /// - /// Binder struct, lets us refer to an instance of Call by type - /// - template - static void Call(void* pObj, AutoPacket& autoPacket) { - // Handoff - (((T*) pObj)->*memFn)( - subscriber_traits()(autoPacket)... - ); - } -}; - -/// -/// Specialization for deferred cases -/// -template -struct CallExtractor: - Decompose -{ - static const bool deferred = true; - static const size_t N = sizeof...(Args); - - template - static void Call(void* pObj, AutoPacket& autoPacket) { - // Obtain a shared pointer of the AutoPacket in order to ensure the packet - // stays resident when we pend this lambda to the destination object's - // dispatch queue. - auto pAutoPacket = autoPacket.shared_from_this(); - - // Pend the call to this object's dispatch queue: - *(T*) pObj += [pObj, pAutoPacket] { - (((T*) pObj)->*memFn)( - subscriber_traits()(*pAutoPacket)... - ); - }; - } -}; - struct AutoFilterDescriptorInput { AutoFilterDescriptorInput(void) : - ti(nullptr), - subscriberType(inTypeInvalid) + is_input(false), + is_output(false), + is_optional(false), + is_shared(false), + ti(nullptr) {} template - AutoFilterDescriptorInput(subscriber_traits&& traits) : - ti(&typeid(typename subscriber_traits::type)), - subscriberType(subscriber_traits::subscriberType) + AutoFilterDescriptorInput(auto_arg&& traits) : + is_input(auto_arg::is_input), + is_output(auto_arg::is_output), + is_optional(auto_arg::is_optional), + is_shared(auto_arg::is_shared), + ti(&typeid(typename auto_arg::id_type)) {} + const bool is_input; + const bool is_output; + const bool is_optional; + const bool is_shared; const std::type_info* const ti; - const eSubscriberInputType subscriberType; operator bool(void) const { return !!ti; @@ -189,7 +50,7 @@ struct AutoFilterDescriptorInput { template struct rebind { operator AutoFilterDescriptorInput() { - return subscriber_traits(); + return auto_arg(); } }; }; @@ -198,9 +59,6 @@ struct AutoFilterDescriptorInput { /// The unbound part of an AutoFilter, includes everything except the AnySharedPointer /// struct AutoFilterDescriptorStub { - // The type of the call centralizer - typedef void(*t_call)(void*, AutoPacket&); - AutoFilterDescriptorStub(void) : m_pType(nullptr), m_pArgs(nullptr), @@ -214,6 +72,7 @@ struct AutoFilterDescriptorStub { AutoFilterDescriptorStub(const AutoFilterDescriptorStub& rhs) : m_pType(rhs.m_pType), m_pArgs(rhs.m_pArgs), + m_dataMap(rhs.m_dataMap), m_deferred(rhs.m_deferred), m_arity(rhs.m_arity), m_requiredCount(rhs.m_requiredCount), @@ -226,32 +85,32 @@ struct AutoFilterDescriptorStub { /// /// /// The caller is responsible for decomposing the desired routine into the target AutoFilter call. The extractor - /// is required to carry information about the type of the proper member function to be called; t_call is required - /// to be instantiated by the caller and point to the AutoFilter proxy routine. + /// is required to carry information about the type of the proper member function to be called; t_extractedCall is + /// required to be instantiated by the caller and point to the AutoFilter proxy routine. /// - template - AutoFilterDescriptorStub(CallExtractor extractor, t_call pCall) : - m_pType(&typeid(typename Decompose::type)), - m_pArgs(extractor.template Enumerate()), - m_deferred(extractor.deferred), - m_arity(extractor.N), + AutoFilterDescriptorStub(const std::type_info* pType, const AutoFilterDescriptorInput* pArgs, bool deferred, t_extractedCall pCall) : + m_pType(pType), + m_pArgs(pArgs), + m_deferred(deferred), + m_arity(0), m_requiredCount(0), m_optionalCount(0), m_pCall(pCall) { - // Cannot register a subscriber with zero arguments: - static_assert(CallExtractor::N, "Cannot register a subscriber whose AutoFilter method is arity zero"); - for(auto pArg = m_pArgs; *pArg; pArg++) { - switch(pArg->subscriberType) { - case inTypeRequired: - m_requiredCount++; - break; - case inTypeOptional: - m_optionalCount++; - break; - default: - break; + m_arity++; + autowiring::DataFlow& data = m_dataMap[*pArg->ti]; + + // DEFAULT: All data is broadcast + data.broadcast = true; + data.input = pArg->is_input; + data.output = pArg->is_output; + if (pArg->is_input) { + if (pArg->is_optional) { + ++m_optionalCount; + continue; + } + ++m_requiredCount; } } } @@ -261,7 +120,11 @@ struct AutoFilterDescriptorStub { const std::type_info* m_pType; // This subscriber's argument types + // NOTE: This is a reference to a static generated list, + // therefor it MUST be const and MUST be shallow-copied. const AutoFilterDescriptorInput* m_pArgs; + typedef std::unordered_map FlowMap; + FlowMap m_dataMap; // Set if this is a deferred subscriber. Deferred subscribers cannot receive immediate-style // decorations, and have additional handling considerations when dealing with non-copyable @@ -283,10 +146,11 @@ struct AutoFilterDescriptorStub { // that will actually be passed is of a type corresponding to the member function bound // by this operation. Strong guarantees must be made that the types passed into this routine // are identical to the types expected by the corresponding call. - t_call m_pCall; + t_extractedCall m_pCall; public: // Accessor methods: + const std::type_info* GetType() const { return m_pType; } size_t GetArity(void) const { return m_arity; } size_t GetRequiredCount(void) const { return m_requiredCount; } size_t GetOptionalCount(void) const { return m_optionalCount; } @@ -294,13 +158,78 @@ struct AutoFilterDescriptorStub { bool IsDeferred(void) const { return m_deferred; } const std::type_info* GetAutoFilterTypeInfo(void) const { return m_pType; } + /// + /// Orientation (input/output, required/optional) of the argument type. + /// + /// + /// Returns nullptr when no argument is of the requested type. + /// + const AutoFilterDescriptorInput* GetArgumentType(const std::type_info* argType) { + for(auto pArg = m_pArgs; *pArg; pArg++) { + if (pArg->ti == argType) { + return pArg; + } + } + return nullptr; + } + + /// + /// Copies the data flow information for the argument type to the flow argument. + /// + /// true when the argument type is found + autowiring::DataFlow GetDataFlow(const std::type_info* argType) const { + FlowMap::const_iterator data = m_dataMap.find(*argType); + if (data != m_dataMap.end()) { + return data->second; + } + return autowiring::DataFlow(); //DEFAULT: No flow + } + /// A call lambda wrapping the associated subscriber /// /// Parameters for the associated subscriber are obtained by querying the packet. /// The packet must already be decorated with all required parameters for the /// subscribers, or an exception will be thrown. /// - t_call GetCall(void) const { return m_pCall; } + t_extractedCall GetCall(void) const { return m_pCall; } + + /// + /// Sends or receives broadcast instances of the input or output type. + /// + /// + /// The dataType must declared by the AutoFilter method for this call to have an effect. + /// + /// specifies the data type (input or output) to broadcast + /// when false disables broadcasting + void Broadcast(const std::type_info* dataType, bool enable = true) { + FlowMap::iterator flowFind = m_dataMap.find(*dataType); + if (flowFind == m_dataMap.end()) + return; + autowiring::DataFlow& flow = flowFind->second; + flow.broadcast = enable; + } + + /// + /// Creates a data half-pipe from this node to the target node for the specifed data. + /// + /// + /// A complete pipe requires that both the input and output nodes are modified. + /// This method only modifies this node - the other half-pipe requires a call to the other node. + /// The dataType must declared by the AutoFilter method for this call to have an effect. + /// + /// specifies the data type (input or output) to pipe + /// determines the target node that will receive the data + /// when false removes a pipe, if it exists + void HalfPipe(const std::type_info* dataType, const std::type_info* nodeType, bool enable = true) { + FlowMap::iterator flowFind = m_dataMap.find(*dataType); + if (flowFind == m_dataMap.end()) + return; + autowiring::DataFlow& flow = flowFind->second; + if (enable) + flow.halfpipes.insert(*nodeType); + else + flow.halfpipes.erase(*nodeType); + } }; /// @@ -322,6 +251,23 @@ struct AutoFilterDescriptor: m_autoFilter(rhs.m_autoFilter) {} + AutoFilterDescriptor& operator=(const AutoFilterDescriptor& rhs) { + AutoFilterDescriptorStub::operator = (rhs); + m_autoFilter = rhs.m_autoFilter; + return *this; + } + + AutoFilterDescriptor(AutoFilterDescriptor&& rhs) : + AutoFilterDescriptorStub(std::move(rhs)), + m_autoFilter(std::move(rhs.m_autoFilter)) + {} + + AutoFilterDescriptor& operator=(AutoFilterDescriptor&& rhs) { + AutoFilterDescriptorStub::operator = (std::move(rhs)); + m_autoFilter = std::move(rhs.m_autoFilter); + return *this; + } + /// /// Utility constructor, used when there is no proffered AutoFilter method on a class /// @@ -329,6 +275,34 @@ struct AutoFilterDescriptor: m_autoFilter(autoFilter) {} + template + AutoFilterDescriptor(const std::shared_ptr& subscriber) : + AutoFilterDescriptor( + AnySharedPointer(subscriber), + &typeid(T), + Decompose::template Enumerate::types, + CallExtractor::deferred, + &CallExtractor::template Call<&T::AutoFilter> + ) + {} + + /// + /// Adds a function to be called as an AutoFilter for this packet only. + /// + /// + /// Recipients added in this way cannot receive piped data, since they are anonymous. + /// + template + AutoFilterDescriptor(Fn fn): + AutoFilterDescriptor( + AnySharedPointer(std::make_shared(std::forward(fn))), + &typeid(Fn), + CallExtractor::template Enumerate::types, + false, + &CallExtractor::template Call<&Fn::operator()> + ) + {} + /// /// Alternative constructor which can bind a stub /// @@ -343,18 +317,39 @@ struct AutoFilterDescriptor: /// /// This constructor increments the reference count on the passed object until the object is freed. A /// subscriber wraps the templated type, automatically mapping desired arguments into the correct locations, - /// via use of Decompose::Call and a AutoPacket to provide type sources + /// via use of Decompose::Call and a AutoPacket to provide type satisfaction /// /// The caller is responsible for decomposing the desired routine into the target AutoFilter call /// - template - AutoFilterDescriptor(const AnySharedPointer& autoFilter, CallExtractor extractor, t_call pCall) : - AutoFilterDescriptorStub(extractor, pCall), + AutoFilterDescriptor(const AnySharedPointer& autoFilter, const std::type_info* pType, const AutoFilterDescriptorInput* pArgs, bool deferred, t_extractedCall pCall) : + AutoFilterDescriptorStub(pType, pArgs, deferred, pCall), m_autoFilter(autoFilter) - { - // Cannot register a subscriber with zero arguments: - static_assert(CallExtractor::N, "Cannot register a subscriber whose AutoFilter method is arity zero"); - } + {} + + template + AutoFilterDescriptor(RetType(*pfn)(Args...)): + AutoFilterDescriptor( + // Token shared pointer, used to provide a pointer to pfn because we can't + // capture it in a template processing context. Hopefully this can be changed + // once MSVC adopts constexpr. + AnySharedPointer( + std::shared_ptr( + pfn, + [](decltype(pfn)){} + ) + ), + + // The remainder is fairly straightforward + CallExtractor(), + &CallExtractor::Call + ) + {} + + // Convenience overload: + template + AutoFilterDescriptor(RetType(&pfn)(Args...)): + AutoFilterDescriptor(&pfn) + {} protected: // A hold on the enclosed autoFilter @@ -410,26 +405,23 @@ struct AutoFilterDescriptor: template::value> class AutoFilterDescriptorSelect: - public std::true_type, - public AutoFilterDescriptor + public std::true_type { public: AutoFilterDescriptorSelect(const std::shared_ptr& subscriber) : - AutoFilterDescriptor( - subscriber, - CallExtractor(), - &CallExtractor::template Call<&T::AutoFilter> - ) + desc(subscriber) {} + + const AutoFilterDescriptor desc; }; template class AutoFilterDescriptorSelect: - public std::false_type, - public AutoFilterDescriptor + public std::false_type { public: AutoFilterDescriptorSelect(const std::shared_ptr&) {} + const AutoFilterDescriptor desc; }; /// @@ -437,7 +429,7 @@ class AutoFilterDescriptorSelect: /// template AutoFilterDescriptor MakeAutoFilterDescriptor(const std::shared_ptr& ptr) { - return AutoFilterDescriptorSelect(ptr); + return std::move(AutoFilterDescriptorSelect(ptr).desc); } namespace std { diff --git a/autowiring/AutoInjectable.h b/autowiring/AutoInjectable.h index e133a0c4a..a954d2718 100644 --- a/autowiring/AutoInjectable.h +++ b/autowiring/AutoInjectable.h @@ -34,7 +34,7 @@ class AutoInjectableExpression: template std::shared_ptr CallByUnpackingTuple(index_tuple) const { auto ctxt = CoreContext::CurrentContext(); - return ctxt->Construct(std::get(m_args)...); + return ctxt->Inject(std::get(m_args)...); } }; @@ -47,7 +47,7 @@ class AutoInjectableExpression: { public: void operator()(AutoFuture* pFuture) const override { - auto added = CoreContext::CurrentContext()->Construct(); + auto added = CoreContext::CurrentContext()->Inject(); if(pFuture) *pFuture += added; } @@ -138,7 +138,7 @@ class AutoInjectable if(pValue) pValue->operator()(pFuture); if(pFLink) - pFLink->operator()(); + pFLink->operator()(pFuture); } AutoInjectable& operator+=(AutoInjectable&& other) { @@ -201,18 +201,24 @@ AutoInjectable MakeInjectable(Arg1 arg1, Args... args) { } template -AutoInjectable MakeInjectable(void) { - if(sizeof...(Ts) == 0) - return AutoInjectable(); - - AutoInjectable injs [] { - AutoInjectable(new AutoInjectableExpression())... - }; - - AutoInjectable retVal(std::move(injs[0])); - for(auto& cur : injs) - retVal += std::move(cur); - return retVal; +struct SelectInjRoutine { + static AutoInjectable MakeInjectable(void) { + AutoInjectable vals [] = {AutoInjectable(new AutoInjectableExpression())...}; + AutoInjectable retVal(std::move(vals[0])); + for(size_t i = 1; i < sizeof...(Ts); i++) + retVal += std::move(vals[i]); + return retVal; + } +}; + +template<> +struct SelectInjRoutine<> { + static AutoInjectable MakeInjectable(void) { return AutoInjectable(); } +}; + +template +AutoInjectable MakeInjectable(void) { + return SelectInjRoutine::MakeInjectable(); } template diff --git a/autowiring/AutoMerge.h b/autowiring/AutoMerge.h new file mode 100644 index 000000000..35767a6d0 --- /dev/null +++ b/autowiring/AutoMerge.h @@ -0,0 +1,100 @@ +// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. +#pragma once + +#include "AutoPacket.h" +#include STL_UNORDERED_SET + +/// +/// Extracts all instances of a type from a slave-context on each execution of that context. +/// +/// +/// It is expected that the merged data is desired in another context. +/// This process is facilitated the AutoStile and AutoMergeStile. +/// +template +class AutoMerge +{ +protected: + Autowired m_factory; + +public: + typedef std::unordered_map> merge_data; + typedef std::function merge_call; + + /// Final call in slave context to extract all data of the specified type + /// + /// This will only gather data that is directed to this source, identified by the gather type. + /// + void AutoFilter(const AutoPacket& packet, const merge_call& call) { + if (!call) + return; + + // Gather relevant data of the specified type + merge_data unordered = packet.GetAll(typeid(typename SelectTypeUnifier>::type)); + merge_data broadcast = packet.GetAll(typeid(void)); + unordered.insert(broadcast.begin(), broadcast.end()); + + // Merge with prior merged data + if (packet.Has()) { + merge_data priordata = packet.Get(); + unordered.insert(priordata.begin(), priordata.end()); + } + + // Call the master function decorating this packet + call(unordered); + } + + /// Enables a pipe from source to AutoMerge, and disables broadcasting by source. + /// Removes pipe and reinstates broadcast when false + /// + /// Parameters have the same meaning as in AutoPacketFactory PipeData and BroadcastData + /// + template + void PipeToMerge(const std::type_info* dataType = nullptr, bool enable = true) { + if (!m_factory) + return; + m_factory->BroadcastDataOut(dataType, !enable); + m_factory->PipeData>(dataType, enable); + } +}; + +/// +/// This provides a merge extractor function that can be injected in to a slave_context by AutoStile, +/// and constructs a corresponding instance of AutoMerge in the slave_context. +/// +/// +/// In order for the merged data to be extracted, it is necessary that AutoStile include +/// AutoMergeStile::merge_call as an *input*, so that it will be injected. +/// +template +class AutoMergeStile { +protected: + std::weak_ptr> m_merge; + +public: + typedef typename AutoMerge::merge_data merge_data; + typedef typename AutoMerge::merge_call merge_call; + + AutoMergeStile(CoreContext& slave_context) { + CurrentContextPusher pusher(&slave_context); + m_merge = AutoRequired>(); + } + + /// Provides an extractor function that can be injected by AutoStile + void AutoFilter(AutoPacket& packet) { + // IMPORTANT: + // Because the packet reference will be a decoration on packet, + // if it holds a shared_ptr to itself it will never terminate. + std::weak_ptr weak_packet = packet.shared_from_this(); + packet.Decorate(merge_call([weak_packet](const merge_data& unordered){ + std::shared_ptr master_packet = weak_packet.lock(); + if (!master_packet) + return; + master_packet->Decorate(unordered); + })); + } + + std::shared_ptr> GetMerge() { + return m_merge.lock(); + } +}; diff --git a/autowiring/AutoNetServer.h b/autowiring/AutoNetServer.h index 3b0284c2c..d50dab43c 100644 --- a/autowiring/AutoNetServer.h +++ b/autowiring/AutoNetServer.h @@ -6,6 +6,16 @@ class AutoNetServer; extern AutoNetServer* NewAutoNetServerImpl(void); +#ifdef _MSC_VER + #ifdef AUTOWIRING_EXPORT_AUTONET + #define AUTONET_EXPORT __declspec(dllexport) + #else + #define AUTONET_EXPORT __declspec(dllimport) + #endif +#else + #define AUTONET_EXPORT +#endif + class AutoNetServer: public CoreThread { @@ -14,7 +24,8 @@ class AutoNetServer: public: virtual ~AutoNetServer(); - static AutoNetServer* New(void) { + + static AUTONET_EXPORT AutoNetServer* New(void) { return NewAutoNetServerImpl(); } @@ -26,6 +37,4 @@ class AutoNetServer: // Allows a breakpoint previously set with Breakpoint to be resumed virtual void HandleResumeFromBreakpoint(std::string name) = 0; - -protected: }; diff --git a/autowiring/AutoPacket.h b/autowiring/AutoPacket.h index b53f64400..73a976e3a 100644 --- a/autowiring/AutoPacket.h +++ b/autowiring/AutoPacket.h @@ -2,12 +2,16 @@ #pragma once #include "AnySharedPointer.h" #include "at_exit.h" +#include "DataFlow.h" #include "AutoCheckout.h" #include "DecorationDisposition.h" +#include "demangle.h" #include "is_shared_ptr.h" #include "ObjectPool.h" #include "is_any.h" #include "MicroAutoFilter.h" +#include "hash_tuple.h" +#include #include #include #include MEMORY_HEADER @@ -15,16 +19,10 @@ #include STL_UNORDERED_MAP #include EXCEPTION_PTR_HEADER -//DEBUG -#include - class AutoPacketFactory; class AutoPacketProfiler; struct AutoFilterDescriptor; -template -struct subscriber_traits; - /// /// A decorator-style processing packet /// @@ -45,23 +43,56 @@ class AutoPacket: AutoPacket(AutoPacketFactory& factory, const std::shared_ptr& outstanding); public: - ~AutoPacket(void); + ~AutoPacket(); static ObjectPool CreateObjectPool(AutoPacketFactory& factory, const std::shared_ptr& outstanding); private: - // A back-link to the previously issued packet in the packet sequence. May potentially be null, - // if this is the first packet issued by the packet factory. - std::shared_ptr m_prior; + // Lifecycle mutability flag + enum Mutability { + enable_all = 0, + disable_update = 1, //Disables update while resolving optional arguments + disable_decorate = 2 //Disables decorate while resolving final calls + } m_lifecyle; // Saturation counters, constructed when the packet is created and reset each time thereafter - std::vector m_satCounters; + // IMPORTANT: Elements in m_satCounters MUST be stationary, since they will be referenced! + std::list m_satCounters; size_t m_subscriberNum; // The set of decorations currently attached to this object, and the associated lock: - mutable std::mutex m_lock; - typedef std::unordered_map t_decorationMap; + // Decorations are indexed first by type and second by pipe terminating type, if any. + // NOTE: This is a disambiguation of function reference assignment, and avoids use of constexp. + static std::tuple DSIndex(const std::type_index& data, const std::type_index& source) { + return std::make_tuple(data, source); + } + typedef std::unordered_map, DecorationDisposition> t_decorationMap; t_decorationMap m_decorations; + mutable std::mutex m_lock; + + /// + /// Retrieve data flow information for a decoration + /// + /// + /// Broadcast is always true for added or snooping recipients. + /// Pipes are always absent for added or snooping recipients. + /// + autowiring::DataFlow GetDataFlow(const DecorationDisposition& entry) const; + + /// + /// Retrieve data flow information from source + /// + autowiring::DataFlow GetDataFlow(const std::type_info& data, const std::type_info& source); + + /// + /// Adds all AutoFilter argument information for a recipient + /// + void AddSatCounter(SatCounter& satCounter); + + /// + /// Removes all AutoFilter argument information for a recipient + /// + void RemoveSatCounter(SatCounter& satCounter); // Outstanding count local and remote holds: std::shared_ptr m_outstanding; @@ -107,15 +138,10 @@ class AutoPacket: /// void Finalize(void); - /// - /// Adds a recipient for data associated only with this issuance of the packet. - /// - void InitializeRecipient(const AutoFilterDescriptor& descriptor); - /// /// Marks the specified entry as being unsatisfiable /// - void MarkUnsatisfiable(const std::type_info& info); + void MarkUnsatisfiable(const std::type_info& info, const std::type_info& source = typeid(void)); /// /// Updates subscriber statuses given that the specified type information has been satisfied @@ -125,7 +151,7 @@ class AutoPacket: /// This method results in a call to the AutoFilter method on any subscribers which are /// satisfied by this decoration. /// - void UpdateSatisfaction(const std::type_info& info); + void UpdateSatisfaction(const std::type_info& info, const std::type_info& source = typeid(void)); /// /// Performs a "satisfaction pulse", which will avoid notifying any deferred filters @@ -137,55 +163,44 @@ class AutoPacket: /// void PulseSatisfaction(DecorationDisposition* pTypeSubs[], size_t nInfos); - /// - /// Invoked from a checkout when a checkout has completed - /// Ready flag, set to false if the decoration should be marked unsatisfiable - template - void CompleteCheckout(bool ready) { - { - std::lock_guard lk(m_lock); - auto& entry = m_decorations[typeid(T)]; + /// Un-templated & locked component of Has + bool UnsafeHas(const std::type_info& data, const std::type_info& source = typeid(void)) const; - if(!ready) - // Memory must be released, the checkout was cancelled - entry.m_decoration->reset(); + /// Un-templated & locked component of Checkout + void UnsafeCheckout(AnySharedPointer* ptr, const std::type_info& data, const std::type_info& source); - // Reset the checkout flag before releasing the lock: - assert(entry.isCheckedOut); - entry.isCheckedOut = false; - entry.satisfied = true; - } + /// Un-templated & locked component of CompleteCheckout + void UnsafeComplete(bool ready, const std::type_info& data, const std::type_info& source, + DecorationDisposition* &broadDeco, DecorationDisposition* &pipedDeco); - if(ready) { - // Satisfy the base declaration first and then the shared pointer: - UpdateSatisfaction(typeid(T)); - UpdateSatisfaction(typeid(std::shared_ptr)); - } - else - MarkUnsatisfiable(typeid(T)); - } + /// + /// Invoked from a checkout when a checkout has completed + /// Ready flag, set to false if the decoration should be marked unsatisfiable + void CompleteCheckout(bool ready, const std::type_info& data, const std::type_info& source = typeid(void)); public: /// /// True if this packet posesses a decoration of the specified type /// + /// + /// Although "AutoPacket &" and "const AutoPacket&" argument types will be + /// satisfied, the AutoPacket does not "have" these types. + /// template - bool Has(void) const { - auto q = m_decorations.find(typeid(T)); - if(q == m_decorations.end()) - return false; - return q->second.satisfied; + bool Has(const std::type_info& source = typeid(void)) const { + std::lock_guard lk(m_lock); + return UnsafeHas(typeid(T), source); } /// /// Detects the desired type, or throws an exception if such a type cannot be found /// template - const T& Get(void) const { + const T& Get(const std::type_info& source = typeid(void)) const { const T* retVal; - if(!Get(retVal)) { + if(!Get(retVal, source)) { std::stringstream ss; - ss << "Attempted to obtain a type " << typeid(retVal).name() + ss << "Attempted to obtain a type " << autowiring::demangle(retVal) << " which was not decorated on this packet"; throw std::runtime_error(ss.str()); } @@ -195,13 +210,17 @@ class AutoPacket: /// /// Determines whether this pipeline packet contains an entry of the specified type /// + /// + /// This method is also used by DecorateImmediate to extract pointers to data that is + /// valid ONLY during recursive satisfaction calls. + /// template - bool Get(const T*& out) const { + bool Get(const T*& out, const std::type_info& source = typeid(void)) const { std::lock_guard lk(m_lock); - static_assert(!std::is_same::value, "Cannot decorate a packet with another packet"); - auto q = m_decorations.find(typeid(T)); - if(q != m_decorations.end() && q->second.satisfied) { + auto q = m_decorations.find(DSIndex(typeid(T), source)); + if(q != m_decorations.end() && + q->second.satisfied) { auto& disposition = q->second; if(disposition.m_decoration) { out = disposition.m_decoration->as().get(); @@ -220,16 +239,16 @@ class AutoPacket: } /// - /// Shared pointer specialization, used to obtain the underlying shared pointer for some type T + /// Shared pointer specialization of const T*&, used to obtain the underlying shared pointer for some type T /// /// /// This specialization cannot be used to obtain a decoration which has been attached to this packet via - /// DecorateImmediate - /// + /// DecorateImmediate. + /// template - bool Get(const std::shared_ptr*& out) const { + bool Get(const std::shared_ptr*& out, const std::type_info& source = typeid(void)) const { std::lock_guard lk(m_lock); - auto q = m_decorations.find(typeid(T)); + auto q = m_decorations.find(DSIndex(typeid(T), source)); if(q != m_decorations.end() && q->second.satisfied) { auto& disposition = q->second; if(disposition.m_decoration) { @@ -242,6 +261,197 @@ class AutoPacket: return false; } + /// + /// Shared pointer specialization, used to obtain the underlying shared pointer for some type T + /// + /// + /// This method can return an argument of DecorateImmediate as a shared_ptr without a deleter. + /// PROBLEM: This use case implies that holding shared_ptr references to decorations is NOT SAFE. + /// + template + bool Get(std::shared_ptr& out, const std::type_info& source = typeid(void)) const { + std::lock_guard lk(m_lock); + auto deco = m_decorations.find(DSIndex(typeid(T), source)); + if(deco != m_decorations.end() && + deco->second.satisfied) { + auto& disposition = deco->second; + if(disposition.m_decoration) { + out = disposition.m_decoration->as(); + return true; + } else if (disposition.m_pImmediate) { + // Wrap immediate reference in shared_ptr without deleter + out = std::shared_ptr(reinterpret_cast(disposition.m_pImmediate), [](const T*){}); + return true; + } + } + out.reset(); + return false; + } + + /// + /// Transfers ownership of argument to AutoPacket + /// + template + void Put(T* in, const std::type_info& source = typeid(void)) { + const std::type_info& data = typeid(T); + + autowiring::DataFlow flow = GetDataFlow(data, source); + if (flow.broadcast) { + auto& entry = m_decorations[DSIndex(data, typeid(void))]; + if (entry.satisfied || + entry.isCheckedOut) { + std::stringstream ss; + ss << "Cannot put type " << autowiring::demangle(typeid(T)) + << " from source " << autowiring::demangle(source) + << " on AutoPacket, the requested broadcast already exists"; + throw std::runtime_error(ss.str()); + } + + entry.m_decoration = std::shared_ptr(in); + entry.m_type = &data; // Ensure correct type if instantiated here + entry.satisfied = true; + entry.isCheckedOut = false; + + UpdateSatisfaction(data, typeid(void)); + } + if (!flow.halfpipes.empty()) { + auto& entry = m_decorations[DSIndex(data, source)]; + if (entry.satisfied || + entry.isCheckedOut) { + std::stringstream ss; + ss << "Cannot put type " << autowiring::demangle(typeid(T)) + << " from source " << autowiring::demangle(source) + << " on AutoPacket, the requested pipe already exists"; + throw std::runtime_error(ss.str()); + } + + entry.m_decoration = std::shared_ptr(in); + entry.m_type = &data; // Ensure correct type if instantiated here + entry.satisfied = true; + entry.isCheckedOut = false; + + UpdateSatisfaction(data, source); + } + } + + /// + /// Shares ownership of argument with AutoPacket + /// + /// + /// This can be used to: + /// - place data on the AutoPack from an ObjectPool + /// - move data from one AutoPacket to another without copying + /// - alias the type of a decoration on AutoPacket + /// + template + void Put(std::shared_ptr in, const std::type_info& source = typeid(void)) { + const std::type_info& data = typeid(T); + + autowiring::DataFlow flow = GetDataFlow(data, source); + if (flow.broadcast) { + auto& entry = m_decorations[DSIndex(data, typeid(void))]; + if (entry.satisfied || + entry.isCheckedOut) { + std::stringstream ss; + ss << "Cannot put type " << autowiring::demangle(typeid(T)) + << " from source " << autowiring::demangle(source) + << " on AutoPacket, the requested broadcast already exists"; + throw std::runtime_error(ss.str()); + } + + entry.m_decoration = in; + entry.m_type = &data; // Ensure correct type if instantiated here + entry.satisfied = true; + entry.isCheckedOut = false; + + UpdateSatisfaction(data, typeid(void)); + } + if (!flow.halfpipes.empty()) { + auto& entry = m_decorations[DSIndex(data, source)]; + if (entry.satisfied || + entry.isCheckedOut) { + std::stringstream ss; + ss << "Cannot put type " << autowiring::demangle(typeid(T)) + << " from source " << autowiring::demangle(source) + << " on AutoPacket, the requested pipe already exists"; + throw std::runtime_error(ss.str()); + } + + entry.m_decoration = in; + entry.m_type = &data; // Ensure correct type if instantiated here + entry.satisfied = true; + entry.isCheckedOut = false; + + UpdateSatisfaction(data, source); + } + } + + /// + /// The number of sources for the specified type supplied to the specified target + /// + /// Default value counts all broadcast data + /// + /// This method should ONLY be called during the final-call sequence. + /// Before that time, the number of instances of a type may vary. + /// + template + int HasAll(const std::type_info& target = typeid(void)) const { + std::lock_guard lk(m_lock); + + int all = 0; + for (auto& deco : m_decorations) { + if (std::get<0>(deco.first) == typeid(T) && + deco.second.satisfied) { + autowiring::DataFlow flow = GetDataFlow(deco.second); + if (flow.output && + ((flow.broadcast && target == typeid(void)) || + flow.halfpipes.find(target) != flow.halfpipes.end())) { + ++all; + } + } + } + return all; + } + + /// + /// All decorations of the specified type supplied to the specified target, indexed by source + /// + /// + /// Default value retrieves all broadcast data + /// + /// This method should ONLY be called during the final-call sequence. + /// Before that time, the number of instances of a type may vary. + /// + template + std::unordered_map> GetAll(const std::type_info& target = typeid(void)) const { + std::lock_guard lk(m_lock); + + std::unordered_map> all; + for (const auto& deco : m_decorations) { + if (std::get<0>(deco.first) == std::type_index(typeid(T)) && + deco.second.satisfied) { + autowiring::DataFlow flow = GetDataFlow(deco.second); + if (flow.output && + ((flow.broadcast && target == typeid(void)) || + flow.halfpipes.find(target) != flow.halfpipes.end())) { + all[std::get<1>(deco.first)] = deco.second.m_decoration->as(); + } + } + } + return all; + } + + /// Shares all broadcast data from this packet with the recipient packet + /// + /// This method should ONLY be called during the final-call sequence. + /// This method is expected to be used to bridge data to a sibling context. + /// Therefore, only broadcast data will be shared, since pipes between sibling + /// contexts cannot be defined. + /// Furthermore, types that are unsatisfied in this context will not be marked as + /// unsatisfied in the recipient - only present data will be provided. + /// + void ForwardAll(std::shared_ptr recipient) const; + /// /// Checks out the specified type, providing it to the caller to be filled in /// @@ -252,44 +462,32 @@ class AutoPacket: /// when it falls out of scope if so marked. /// template - AutoCheckout Checkout(std::shared_ptr ptr) { - // This allows us to install correct entries for decorated input requests - typedef typename subscriber_traits::type type; + AutoCheckout Checkout(std::shared_ptr ptr, const std::type_info& source = typeid(void)) { + const std::type_info& data = typeid(T); + + /// Injunction to prevent existential loops: + static_assert(!std::is_same::value, "Cannot decorate a packet with another packet"); if(!ptr) throw std::runtime_error("Cannot checkout with shared_ptr == nullptr"); - AnySharedPointer slot; + // This allows us to install correct entries for decorated input requests + AnySharedPointer any_ptr(ptr); { - std::lock_guard lk(m_lock); - auto& entry = m_decorations[typeid(type)]; - if (entry.satisfied) { - std::stringstream ss; - ss << "Cannot decorate this packet with type " << typeid(*ptr).name() - << ", the requested decoration already exists"; - throw std::runtime_error(ss.str()); - } - if(entry.isCheckedOut) { - std::stringstream ss; - ss << "Cannot check out decoration of type " << typeid(*ptr).name() - << ", it is already checked out elsewhere"; - throw std::runtime_error(ss.str()); - } - entry.isCheckedOut = true; - entry.wasCheckedOut = true; - m_decorations[typeid(type)].m_decoration = ptr; + std::lock_guard guard(m_lock); + UnsafeCheckout(&any_ptr, data, source); } - return AutoCheckout( *this, ptr, - &AutoPacket::CompleteCheckout + &AutoPacket::CompleteCheckout, + source ); } template - AutoCheckout Checkout(void) { - return Checkout(std::make_shared()); + AutoCheckout Checkout(const std::type_info& source = typeid(void)) { + return Checkout(std::make_shared(), source); } /// @@ -300,21 +498,22 @@ class AutoPacket: /// input on this type to be called, if the remainder of their inputs are available. /// template - void Unsatisfiable(void) { + void Unsatisfiable(const std::type_info& source = typeid(void)) { { // Insert a null entry at this location: std::lock_guard lk(m_lock); - auto& entry = m_decorations[typeid(T)]; - if(entry.wasCheckedOut) + auto& entry = m_decorations[DSIndex(typeid(T), source)]; + entry.m_type = &typeid(T); // Ensure correct type if instantiated here + if(entry.satisfied || + entry.isCheckedOut) throw std::runtime_error("Cannot mark a decoration as unsatisfiable when that decoration is already present on this packet"); - // Mark the entry as appropriate: - entry.satisfied = true; - entry.wasCheckedOut = true; + // Mark the entry as permanently checked-out + entry.isCheckedOut = true; } // Now trigger a rescan: - MarkUnsatisfiable(typeid(T)); + MarkUnsatisfiable(typeid(T), source); } /// @@ -326,8 +525,8 @@ class AutoPacket: /// value regardless of whether any subscribers exist. /// template - const T& Decorate(T t) { - return Decorate(std::make_shared(std::forward(t))); + const T& Decorate(T t, const std::type_info& source = typeid(void)) { + return Decorate(std::make_shared(std::forward(t)), source); } /// @@ -338,8 +537,8 @@ class AutoPacket: /// shared pointer. /// template - const T& Decorate(std::shared_ptr t) { - Checkout(t).Ready(); + const T& Decorate(std::shared_ptr t, const std::type_info& source = typeid(void)) { + Checkout(t, source).Ready(); return *t; } @@ -350,13 +549,15 @@ class AutoPacket: /// /// /// Unlike Decorate, the arguments of DecorateImmediate are not copied. - /// Each decoration is only valid for AutoFilters which are valid during - /// this call. + /// Each decoration is only valid for AutoFilters which are valid during this call. /// If multiple values are specified, all will be simultaneously made valid and /// then invalidated. /// template void DecorateImmediate(const T& immed, const Ts&... immeds) { + // TODO: DecorateImmediate can only broadcast - change this to allow sourced immediate decoration. + const std::type_info& source = typeid(void); + // None of the inputs may be shared pointers--if any of the inputs are shared pointers, they must be attached // to this packet via Decorate, or else dereferenced and used that way. static_assert( @@ -374,56 +575,63 @@ class AutoPacket: { std::lock_guard lk(m_lock); for(size_t i = 0; i < s_arity; i++) { - pTypeSubs[i] = &m_decorations[*s_argTypes[i]]; - if(pTypeSubs[i]->wasCheckedOut) { + pTypeSubs[i] = &m_decorations[DSIndex(*s_argTypes[i], source)]; + pTypeSubs[i]->m_type = s_argTypes[i]; // Ensure correct type if instantiated here + if(pTypeSubs[i]->satisfied || + pTypeSubs[i]->isCheckedOut) { std::stringstream ss; - ss << "Cannot perform immediate decoration with type " << s_argTypes[i]->name() + ss << "Cannot perform immediate decoration with type " << autowiring::demangle(*s_argTypes[i]) << ", the requested decoration already exists"; throw std::runtime_error(ss.str()); } // Mark the entry as appropriate: + pTypeSubs[i]->isCheckedOut = true; pTypeSubs[i]->satisfied = true; - pTypeSubs[i]->wasCheckedOut = true; - pTypeSubs[i]->m_pImmediate = pvImmeds[i]; } } // Pulse satisfaction: - MakeAtExit([this, &pTypeSubs] { + MakeAtExit([this, &pTypeSubs, &source] { // Mark entries as unsatisfiable: + // IMPORTANT: isCheckedOut = true prevents subsequent decorations of this type for(DecorationDisposition* pEntry : pTypeSubs) { - pEntry->satisfied = false; pEntry->m_pImmediate = nullptr; + pEntry->satisfied = false; } // Now trigger a rescan to hit any deferred, unsatisfiable entries: for(const std::type_info* ti : s_argTypes) - MarkUnsatisfiable(*ti); + MarkUnsatisfiable(*ti, source); }), PulseSatisfaction(pTypeSubs, s_arity); } /// - /// Adds a function to be called as an AutoFilter for this packet only. + /// Adds a recipient for data associated only with this issuance of the packet. /// - template - void AddRecipient(std::function f) { - InitializeRecipient( - MakeAutoFilterDescriptor(std::make_shared>(f)) - ); - } + /// + /// Recipients added in this way cannot receive piped data, since they are anonymous. + /// + void AddRecipient(const AutoFilterDescriptor& descriptor); - /// - /// True if the indicated type has been requested for use by some consumer - /// + /// A reference to the satisfaction counter for the specified type /// - /// This method is used to determine whether an AutoFilter recipient existed - /// for the specified type at the time the packet was created + /// If the type is not a subscriber GetSatisfaction().GetType() == nullptr will be true /// - template - bool HasSubscribers(void) const {return HasSubscribers(typeid(T));} + SatCounter GetSatisfaction(const std::type_info& subscriber) const; + + /// All subscribers to the specified data and source + std::list GetSubscribers(const std::type_info& data, const std::type_info& source = typeid(void)) const; + + /// All decoration dispositions associated with the data type + /// + /// This method is useful for determining whether flow conditions (broadcast, pipes + /// immediate decorations) resulted in missed data. + /// + std::list GetDispositions(const std::type_info& data) const; - bool HasSubscribers(const std::type_info& ti) const; + /// True if the indicated type has been requested for use by some consumer + bool HasSubscribers(const std::type_info& data, const std::type_info& source = typeid(void)) const; }; diff --git a/autowiring/AutoPacketFactory.h b/autowiring/AutoPacketFactory.h index 5b14d9b4e..90320b23d 100644 --- a/autowiring/AutoPacketFactory.h +++ b/autowiring/AutoPacketFactory.h @@ -10,7 +10,7 @@ #include #include TYPE_INDEX_HEADER #include TYPE_TRAITS_HEADER -#include STL_UNORDERED_MAP +#include STL_UNORDERED_SET class Deferred; class DispatchQueue; @@ -88,7 +88,7 @@ class AutoPacketFactory: /// /// /// This method will cause the factory to enter the Stopped state, if it's not there - /// alread. This method is idempotent. + /// already. This method is idempotent. /// void Clear(void); @@ -105,7 +105,7 @@ class AutoPacketFactory: /// template void AddSubscriber(const std::shared_ptr& rhs) { - AddSubscriber(AutoFilterDescriptorSelect(rhs)); + AddSubscriber(MakeAutoFilterDescriptor(rhs)); } /// @@ -114,6 +114,110 @@ class AutoPacketFactory: /// The AutoFilter to be removed void RemoveSubscriber(const AutoFilterDescriptor& autoFilter); + /// + /// Sets the broadcast status for the specified output from the node. + /// + /// + /// When dataType = nullptr the broadcast status is set for all declared input and output data. + /// + template + void BroadcastDataOut(const std::type_info* dataType = nullptr, bool enable = true) { + const std::type_info* nodeType = &typeid(typename SelectTypeUnifier::type); + if (dataType) { + GetContext()->NotifyWhenAutowired( + [this, nodeType, dataType, enable](){ + BroadcastOneDataOut(nodeType, dataType, enable); + }); + } else { + GetContext()->NotifyWhenAutowired( + [this, nodeType, enable](){ + BroadcastAllDataOut(nodeType, enable); + }); + } + } + + /// + /// Sets the broadcast status for the specified output from the node. + /// + /// + /// When dataType = nullptr the broadcast status is set for all declared input and output data. + /// + template + void BroadcastDataIn(const std::type_info* dataType = nullptr, bool enable = true) { + const std::type_info* nodeType = &typeid(typename SelectTypeUnifier::type); + if (dataType) { + GetContext()->NotifyWhenAutowired( + [this, nodeType, dataType, enable](){ + BroadcastOneDataIn(nodeType, dataType, enable); + }); + } else { + GetContext()->NotifyWhenAutowired( + [this, nodeType, enable](){ + BroadcastAllDataIn(nodeType, enable); + }); + } + } + + /// + /// Establishes a data pipe from nodeIn to nodeOut. + /// + /// + /// When dataType = nullptr pipes are established for all declared data + /// that are outputs of nodeOut and inputs to nodeIn + /// If nodeOut includes AutoPacket& as an argument then pipes will be defined + /// for all declared input types of nodeIn. Likewise, if nodeIn declares + /// AutoPacket& or const AutoPacket& as an argument then pipes will be defined + /// for all declared outputs of nodeOut. + /// + template + void PipeData(const std::type_info* dataType = nullptr, bool enable = true) { + const std::type_info* nodeOutType = &typeid(typename SelectTypeUnifier::type); + const std::type_info* nodeInType = &typeid(typename SelectTypeUnifier::type); + if (dataType) { + GetContext()->NotifyWhenAutowired( + [this, nodeOutType, nodeInType, dataType, enable](){ + GetContext()->NotifyWhenAutowired( + [this, nodeOutType, nodeInType, dataType, enable](){ + PipeOneData(nodeOutType, nodeInType, dataType, enable); + }); + }); + } else { + GetContext()->NotifyWhenAutowired( + [this, nodeOutType, nodeInType, enable](){ + GetContext()->NotifyWhenAutowired( + [this, nodeOutType, nodeInType, enable](){ + PipeAllData(nodeOutType, nodeInType, enable); + }); + }); + } + } + +protected: + /// + /// Returns a description of the AutoFilter associated with the type nodeType + /// + /// + /// If a matching description was not found GetTypeDescriptor(type).GetAutoFilterTypeInfo() == nullptr + /// + AutoFilterDescriptor GetTypeDescriptorUnsafe(const std::type_info* nodeType); + + void BroadcastOneDataIn(const std::type_info* nodeType, const std::type_info* dataType, bool enable); + void BroadcastAllDataIn(const std::type_info* nodeType, bool enable); + + void BroadcastOneDataOut(const std::type_info* nodeType, const std::type_info* dataType, bool enable); + void BroadcastAllDataOut(const std::type_info* nodeType, bool enable); + + void PipeOneData(const std::type_info* nodeOutType, const std::type_info* nodeInType, const std::type_info* dataType, bool enable); + void PipeAllData(const std::type_info* nodeOutType, const std::type_info* nodeInType, bool enable); + + static bool IsAutoPacketType(const std::type_info& dataType) { + return + dataType == typeid(AutoPacket) || + dataType == typeid(auto_arg::id_type) || + dataType == typeid(auto_arg::id_type); + } + +public: /// /// Obtains a new packet from the object pool and configures it with the current /// satisfaction graph diff --git a/autowiring/AutoRestarter.h b/autowiring/AutoRestarter.h index 6700e5042..61cfd26a8 100644 --- a/autowiring/AutoRestarter.h +++ b/autowiring/AutoRestarter.h @@ -1,3 +1,4 @@ +// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. #pragma once #include "atomic_object.h" #include "CoreRunnable.h" diff --git a/autowiring/AutoSelfUpdate.h b/autowiring/AutoSelfUpdate.h index fa47f1177..ffd6ad1b3 100644 --- a/autowiring/AutoSelfUpdate.h +++ b/autowiring/AutoSelfUpdate.h @@ -1,52 +1,66 @@ +// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. #pragma once -#include "NewAutoFilter.h" #include "atomic_object.h" +#include "DeclareAutoFilter.h" -/// -///Enables an automatic self-update when a packet is decorated with the object type. -///Provides the prior object (the last decorated instance) to all subsequent packets. -/// -/// -///In order to ensure that this method will be consistent with any other AutoFilter calls, -///the object inherits from atomic_object, which implements basic locking functionality. -/// -template +/// +/// Enables an automatic self-update when a packet is decorated with the object type. +/// Provides the prior object (the last decorated instance) to all subsequent packets. +/// +/// +/// In order to ensure that this method will be consistent with any other AutoFilter calls, +/// the object inherits from atomic_object, which implements basic locking functionality. +/// +template class AutoSelfUpdate: -public atomic_object { +public atomic_object { public: - typedef atomic_object atomic; + typedef atomic_object atomic; typedef typename atomic::object object; typedef typename atomic::lock lock; typedef typename atomic::unlock unlock; typedef typename atomic::shared shared; + typedef BasedAutoFilter, void, const object&> gather; - AutoSelfUpdate() {} - AutoSelfUpdate(const atomic_object& source) : atomic_object(source) {} - AutoSelfUpdate(const object& source) : atomic_object(source) {} - using atomic_object::operator =; - using atomic_object::operator object; - - //The distinct type assigned to the prior value of the object + /// + /// The type assigned to the prior value of the object + /// in order to distinguish it from the AutoFilter argument. + /// class prior_object: public object { public: - prior_object(const object& source): object(source) {} + using object::operator =; }; - //Avoid intermediate copy by defining an explicit cast - operator prior_object() const { - std::lock_guard lock_this(atomic_object::m_lock); - return prior_object(atomic_object::m_object); + AutoSelfUpdate(void) { + // Instanties a BasedAutoFilter for the AutoGather method + m_gather = DeclareAutoFilter(this, &AutoSelfUpdate::AutoGather); + } + using atomic::operator=; + operator object(void) { + // NOTE: This avoids "using" keyword with a cast operator overload + return *(atomic*)this; } - //Decorates all packets with instances of prior_object - void AutoFilter(AutoPacket& packet) { - packet.Decorate(this->operator prior_object()); + /// + /// Decorates all packets with instances of prior_object + /// + void AutoFilter(prior_object& prior) { + std::lock_guard lock_this(atomic::m_lock); + prior = atomic::m_object; } - //Updates this object + /// + /// Updates this object to equal the most recent decoration by object + /// void AutoGather(const object& update) { - atomic_object::operator = (update); + atomic::operator = (update); + } + + /// a reference to the MicroAutoFilter instance calling AutoGather + std::shared_ptr GatherAutoFilter() const { + return m_gather; } - NewAutoFilter::AutoGather), &AutoSelfUpdate::AutoGather> SelfUpdate; +protected: + std::shared_ptr m_gather; }; diff --git a/autowiring/AutoStile.h b/autowiring/AutoStile.h new file mode 100644 index 000000000..dfc635e67 --- /dev/null +++ b/autowiring/AutoStile.h @@ -0,0 +1,127 @@ +// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. +#pragma once + +#include "var_logic.h" +#include "AutoPacket.h" +#include MEMORY_HEADER + +template::is_input> +struct AutoDecorationStile; + +template +struct AutoDecorationStile +{ + /// Decorator for input shares the reference to master_data + static void DecorationStile(std::shared_ptr& slave_packet, std::shared_ptr& master_packet, + data_pipe& master_data, const std::type_info& master_type) { + slave_packet->Decorate(master_data); + } +}; + +template +struct AutoDecorationStile +{ + /// Decorator for output creates an extraction for slave_data + static void DecorationStile(std::shared_ptr& slave_packet, std::shared_ptr& master_packet, + data_pipe& master_data, const std::type_info& master_type) { + static_assert(std::is_same::id_type>>::value, + "Output types must be declared as auto_out"); + + // Reverse argument orientation for AutoFilter in slave context + typedef auto_in::id_type> slave_in_type; + + // HACK: Move sematics do not work with lambdas, so it is necessary to reproduce the auto_out signative using master_packet. + master_data.cancel(); + const std::type_info* stile_source = &master_type; + slave_packet->AddRecipient([master_packet, stile_source](slave_in_type slave_data) { + master_packet->Put(slave_data, *stile_source); + }); + } +}; + +/// +/// AutoStile provides a means of calling a context as though it is an AutoFilter. +/// The AutoStile Args use the same syntax as AutoFilter: +/// - Input types will be injected in to the slave packet +/// - Output types (auto_out only) will be extracted from the slave packet +/// - The absence of any output types indicates that *all* data from the slave context +/// should be output. This is because AutoPacket& is already an argument, and cannot +/// be repeated. +/// ASSERT: Injection and extraction of all data will not yield multiple decorations, +/// since AutoPacket::ForwardAll prevents this from occurring. +/// NOTE: If shared input types are used data copying will be avoided. +/// IMPORTANT: Unshared output types cannot be used, since they will be available +/// before the execution of any Deferred methods in the slave context. +/// +/// +/// When AutoStile::AutoFilter is called it initiates execution of a Slave context, +/// and injects the objects referenced by AutoFilter into the new context. +/// AutoStile also handles the extraction of data requested from the context. +/// +/// If merged data is required from the context then it is necessary to decorate the +/// master_packet with an extraction function, and declare the type of that function +/// as an *input* argument of the AutoStile. The decoration and type are provided by +/// the AutoMergeStile class. +/// +/// +template +class AutoStile +{ +protected: + std::weak_ptr m_slave_factory; + + /// Inject all data from slave_packet into master_packet + static void ForwardStile(std::shared_ptr& slave_packet, std::shared_ptr& master_packet) { + slave_packet->AddRecipient([master_packet](const AutoPacket& slave_packet) { + slave_packet.ForwardAll(master_packet); + }); + } + +public: + AutoStile(std::weak_ptr slave_context = std::weak_ptr()) { + Leash(slave_context); + } + + void AutoFilter(AutoPacket& packet, Args... data) { + std::shared_ptr slave_factory = m_slave_factory.lock(); + if (!slave_factory) + return; + + // Initiate the slave context + std::shared_ptr master_packet = packet.shared_from_this(); + std::shared_ptr slave_packet = slave_factory->NewPacket(); + const std::type_info& master_type = typeid(AutoStile); + (void) std::initializer_list { + (AutoDecorationStile::DecorationStile(slave_packet, master_packet, data, master_type), false)... + }; + + if (var_and::is_input...>::value) { + ForwardStile(slave_packet, master_packet); + } + } + + void Leash(std::weak_ptr slave_context = std::weak_ptr()) { + m_slave_factory.reset(); + + std::shared_ptr strong_context = slave_context.lock(); + if (!strong_context) + return; + + strong_context->NotifyWhenAutowired([this, slave_context](){ + // NOTE: Call-back is not necessarily inside the notifying context. + std::shared_ptr strong_context = slave_context.lock(); + if (!strong_context) + return; + std::shared_ptr slave_factory; + strong_context->FindByTypeRecursive(slave_factory); + m_slave_factory = slave_factory; + }); + } +}; + +/// +/// Zero input argument Stile specialization +/// +template<> +class AutoStile<> +{}; \ No newline at end of file diff --git a/autowiring/AutoTimeStamp.h b/autowiring/AutoTimeStamp.h new file mode 100644 index 000000000..8e643abbf --- /dev/null +++ b/autowiring/AutoTimeStamp.h @@ -0,0 +1,22 @@ +// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. +#pragma once + +#include "auto_out.h" +#include CHRONO_HEADER + +/// +/// Adds a time-of-creation to every AutoPacket created in a context +/// that includes AutoTimeStamp. +/// +class AutoTimeStamp { +public: + /// Alias type for maximum precision timing + class time : public std::chrono::high_resolution_clock::time_point {}; + + /// Records time of issue of AutoPacket + /// + /// Default constructor yielding current time is called by auto_out + void AutoFilter(auto_out