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
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