diff --git a/.travis.yml b/.travis.yml
index 90e1375b3..11e1363b4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,6 +9,9 @@ before_install:
# Enforce Leap Motion copyright notice
- ./scripts/copyright_check.sh
+ # Verify that our version numbers all line up
+ - ./scripts/version_number_updated.sh
+
# g++4.8.1
- if [ "$CXX" == "g++" ]; then
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
diff --git a/AutowiringConfig.h.in b/AutowiringConfig.h.in
index 43d230fc6..686ac171b 100644
--- a/AutowiringConfig.h.in
+++ b/AutowiringConfig.h.in
@@ -10,11 +10,3 @@
// Building for ARM?
#cmakedefine01 autowiring_BUILD_ARM
-
-// Are we linking with C++11 STL?
-#cmakedefine01 autowiring_USE_LIBCXX
-#if autowiring_USE_LIBCXX
-#define AUTOWIRING_USE_LIBCXX 1
-#else
-#define AUTOWIRING_USE_LIBCXX 0
-#endif
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c80ba66b6..fa25903a8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -40,33 +40,6 @@ if(EXTERNAL_LIBRARY_DIR)
list(APPEND CMAKE_INCLUDE_PATH ${EXTERNAL_LIBRARY_DIR})
endif()
-if(APPLE)
- # Offer option for autowiring_USE_LIBCXX
- # Check for existing definition of autowiring_USE_LIBCXX
- if(NOT DEFINED autowiring_USE_LIBCXX)
- option(autowiring_USE_LIBCXX "Build Autowiring using c++11" ON)
- else()
- if(NOT autowiring_USE_LIBCXX)
- message("Parent project has set autowiring_USE_LIBCXX = OFF -> Build Autowiring using c++98")
- endif()
- endif()
-
- # Install autoboost when using libstdc++
- if(autowiring_USE_LIBCXX)
- set(autowiring_INSTALL_AUTOBOOST OFF)
- else()
- set(autowiring_INSTALL_AUTOBOOST ON)
- endif()
-else()
- # Always use libc++ on other platforms
- set(autowiring_USE_LIBCXX ON)
-
- # Don't install autoboost unless otherwise specified
- if(NOT DEFINED autowiring_INSTALL_AUTOBOOST)
- set(autowiring_INSTALL_AUTOBOOST OFF)
- endif()
-endif()
-
if(CMAKE_COMPILER_IS_GNUCC)
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.8")
message(FATAL_ERROR "GCC version 4.8 minimum is required to build Autowiring")
@@ -86,16 +59,9 @@ endif()
# Clang needs special additional flags to build with C++11
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
- if(autowiring_USE_LIBCXX)
- message(STATUS "AppleClang C++11")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
- set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
- else()
- message(STATUS "AppleClang C++11 with libstdc++")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libstdc++")
- set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libstdc++")
- endif()
+ message(STATUS "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(STATUS "Clang C++11")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lstdc++")
@@ -223,9 +189,9 @@ if(NOT AUTOWIRING_IS_EMBEDDED)
COMPONENT autowiring
FILES_MATCHING PATTERN "*.h"
)
-
- # Install autoboost headers
- if(autowiring_INSTALL_AUTOBOOST)
+
+ # Install autoboost headers on ARM, which still requires them
+ if(autowiring_BUILD_ARM)
install(
DIRECTORY ${PROJECT_SOURCE_DIR}/contrib/autoboost/autoboost
DESTINATION include
diff --git a/Doxyfile b/Doxyfile
index 8bea9e696..38bd60e9f 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -32,7 +32,7 @@ PROJECT_NAME = Autowiring
# This could be handy for archiving the generated documentation or
# if some version control system is used.
-PROJECT_NUMBER = "0.4.2"
+PROJECT_NUMBER = "0.5.0"
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer
diff --git a/autowiring-configVersion.cmake.in b/autowiring-configVersion.cmake.in
index 64bca57a4..02552c97a 100644
--- a/autowiring-configVersion.cmake.in
+++ b/autowiring-configVersion.cmake.in
@@ -12,9 +12,6 @@ if(autowiring_DEBUG)
message(STATUS "Installed CMAKE_SIZEOF_VOID_P: @CMAKE_SIZEOF_VOID_P@")
message(STATUS "Configured CMAKE_SIZEOF_VOID_P: ${CMAKE_SIZEOF_VOID_P}")
-
- message(STATUS "Installed using autowiring_USE_LIBCXX: @autowiring_USE_LIBCXX@")
- message(STATUS "Configured using autowiring_USE_LIBCXX: ${autowiring_USE_LIBCXX}")
endif()
# If the customer has an override architecture requirement, use that
@@ -73,26 +70,22 @@ endforeach()
# Determine whether the user's request (either implied or explicit) for libstdc++ can
# be met by this verison of Autowiring
if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
- # If this value isn't defined, then we assume the user's request is "on"
- if(NOT DEFINED autowiring_USE_LIBCXX)
- SET(autowiring_USE_LIBCXX ON)
+ # Require that the user either omit autowiring_USE_LIBCXX or leave it off
+ if(DEFINED autowiring_USE_LIBCXX)
+ if(autowiring_USE_LIBCXX)
+ message(WARNING "Autowiring no longer supports libstdc++, autowiring_USE_LIBCXX is therefore a deprecated flag")
+ else()
+ message(STATUS "Autowiring no longer supports libstdc++")
+ set(PACKAGE_VERSION_COMPATIBLE FALSE)
+ set(PACKAGE_VERSION_UNSUITABLE TRUE)
+ return()
+ endif()
endif()
if(autowiring_DEBUG)
message(STATUS "Installed autowiring_USE_LIBCXX: @autowiring_USE_LIBCXX@")
message(STATUS "Configured autowiring_USE_LIBCXX: ${autowiring_USE_LIBCXX}")
endif()
-
- # Our built version must be the same as the requested version. If it's not, then we are
- # not a match for the user's request
- if((NOT ${autowiring_USE_LIBCXX} AND @autowiring_USE_LIBCXX@) OR (${autowiring_USE_LIBCXX} AND NOT @autowiring_USE_LIBCXX@))
- if(autowiring_DEBUG)
- message("User C++ runtime library request incompatible with locally built version")
- endif()
- set(PACKAGE_VERSION_COMPATIBLE FALSE)
- set(PACKAGE_VERSION_UNSUITABLE TRUE)
- return()
- endif()
endif()
if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
diff --git a/autowiring/AutoPacket.h b/autowiring/AutoPacket.h
index be79fb5a8..a56f1a8f9 100644
--- a/autowiring/AutoPacket.h
+++ b/autowiring/AutoPacket.h
@@ -161,6 +161,11 @@ class AutoPacket:
///
static void ThrowNotDecoratedException(const DecorationKey& key);
+ ///
+ /// Throws a formatted runtime error corresponding to the case where a decoration was demanded and more than one such decoration was present
+ ///
+ static void ThrowMultiplyDecoratedException(const DecorationKey& key);
+
public:
///
/// The number of distinct decoration types on this packet
@@ -210,11 +215,20 @@ class AutoPacket:
///
template
bool Get(const T*& out, int tshift=0) const {
- const DecorationDisposition* pDisposition = GetDisposition(DecorationKey(auto_id::key(), false, tshift));
+ DecorationKey key(auto_id::key(), false, tshift);
+ const DecorationDisposition* pDisposition = GetDisposition(key);
if (pDisposition) {
- if (pDisposition->m_decorations.size() == 1) {
+ switch (pDisposition->m_decorations.size()) {
+ case 0:
+ // No shared pointer decorations available, we have to try something else
+ break;
+ case 1:
+ // Single decoration, we can do what the user is asking
out = static_cast(pDisposition->m_decorations[0]->ptr());
return true;
+ default:
+ ThrowMultiplyDecoratedException(key);
+ break;
}
// Second-chance satisfaction with an immediate
@@ -238,18 +252,31 @@ class AutoPacket:
///
/// This specialization cannot be used to obtain a decoration which has been attached to this packet via
/// DecorateImmediate.
+ ///
+ /// This method will throw an exception if the requested decoration is multiply present on the packet
///
template
bool Get(const std::shared_ptr*& out, int tshift=0) const {
// Decoration must be present and the shared pointer itself must also be present
- const DecorationDisposition* pDisposition = GetDisposition(DecorationKey(auto_id::key(), true, tshift));
- if (!pDisposition || pDisposition->m_decorations.size() != 1) {
+ DecorationKey key(auto_id::key(), true, tshift);
+ const DecorationDisposition* pDisposition = GetDisposition(key);
+ if (!pDisposition) {
out = nullptr;
return false;
}
-
- out = &pDisposition->m_decorations[0]->as_unsafe();
- return true;
+ switch (pDisposition->m_decorations.size()) {
+ case 0:
+ // Simple non-availability, trivial return
+ out = nullptr;
+ return false;
+ case 1:
+ // Single decoration available, we can return here
+ out = &pDisposition->m_decorations[0]->as_unsafe();
+ return true;
+ default:
+ ThrowMultiplyDecoratedException(key);
+ return false;
+ }
}
///
@@ -431,18 +458,10 @@ class AutoPacket:
}
// Now trigger a rescan to hit any deferred, unsatisfiable entries:
-#if autowiring_USE_LIBCXX
for (const std::type_info* ti : {&auto_id::key(), &auto_id::key()...}) {
MarkUnsatisfiable(DecorationKey(*ti, true, 0));
MarkUnsatisfiable(DecorationKey(*ti, false, 0));
}
-#else
- bool dummy[] = {
- (MarkUnsatisfiable(DecorationKey(auto_id::key(), false, 0)), false),
- (MarkUnsatisfiable(DecorationKey(auto_id::key(), false, 0)), false)...
- };
- (void)dummy;
-#endif
}),
PulseSatisfaction(pTypeSubs, 1 + sizeof...(Ts));
}
diff --git a/autowiring/AutowirableSlot.h b/autowiring/AutowirableSlot.h
index 69d76ef92..01fc6f7bd 100644
--- a/autowiring/AutowirableSlot.h
+++ b/autowiring/AutowirableSlot.h
@@ -84,6 +84,11 @@ class DeferrableAutowiring:
m_pFlink = pFlink;
}
+ ///
+ /// The context corresponding to this slot, if it hasn't already expired
+ ///
+ std::shared_ptr GetContext(void) const { return m_context.lock(); }
+
///
/// The type on which this deferred slot is bound
///
@@ -184,15 +189,23 @@ class AutowirableSlot:
// where we can guarantee that the type will be completely defined, because the user is about
// to make use of this type.
(void) autowiring::fast_pointer_cast_initializer::sc_init;
- return get();
+
+ auto retVal = get();
+ if (!retVal)
+ throw autowiring_error("Attempted to dereference a null autowired field");
+ return retVal;
}
T& operator*(void) const {
+ auto retVal = get();
+ if (!retVal)
+ throw autowiring_error("Attempted to dereference a null autowired field");
+
// We have to initialize here, in the operator context, because we don't actually know if the
// user will be making use of this type.
(void) autowiring::fast_pointer_cast_initializer::sc_init;
- return *get();
+ return *retVal;
}
using AnySharedPointer::operator=;
diff --git a/autowiring/Autowired.h b/autowiring/Autowired.h
index ec13671c7..c8a695be6 100644
--- a/autowiring/Autowired.h
+++ b/autowiring/Autowired.h
@@ -1,5 +1,6 @@
// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved.
#pragma once
+#include "auto_signal.h"
#include "AutowirableSlot.h"
#include "Decompose.h"
#include "GlobalCoreContext.h"
@@ -120,11 +121,15 @@ class Autowired:
}
~Autowired(void) {
+ // Unlink all signal entries
+ for (auto& unlinkEntry : m_unlinkEntries)
+ *unlinkEntry.relay -= unlinkEntry.node;
+
if(m_pFirstChild == this)
// Tombstoned, nothing to do:
return;
- // Need to ensure that nobody tries to autowire us while we are tearing down:
+ // Need to ensure that nobody tries to fill us while we are tearing down:
this->CancelAutowiring();
// And now we destroy our deferrable autowiring collection:
@@ -139,6 +144,23 @@ class Autowired:
// which will be the first member registered via NotifyWhenAutowired.
std::atomic*> m_pFirstChild;
+ struct unlink_entry
+ {
+ unlink_entry(
+ autowiring::signal_relay* relay,
+ autowiring::internal::signal_node_base* node
+ ) :
+ relay(relay),
+ node(node)
+ {}
+
+ autowiring::signal_relay* relay;
+ autowiring::internal::signal_node_base* node;
+ };
+
+ // The set of all nodes that will have to be unlinked when this field is torn down
+ std::vector m_unlinkEntries;
+
public:
operator const std::shared_ptr&(void) const {
return
@@ -156,7 +178,37 @@ class Autowired:
operator T*(void) const {
return this->operator const std::shared_ptr&().get();
}
+
+ template
+ struct signal_relay {
+ signal_relay(Autowired& owner, autowiring::signal_relay_t& relay) :
+ owner(owner),
+ relay(relay)
+ {}
+
+ private:
+ Autowired& owner;
+ autowiring::signal_relay_t& relay;
+
+ public:
+ void operator+=(std::function&& fn) {
+ owner.m_unlinkEntries.push_back(
+ unlink_entry(
+ &relay,
+ relay += std::move(fn)
+ )
+ );
+ }
+ };
+ template
+ signal_relay operator()(autowiring::signal T::*handler) {
+ auto ctxt = AutowirableSlot::GetContext();
+ if (!ctxt)
+ throw std::runtime_error("Attempted to attach a signal to an Autowired field in a context that was already destroyed");
+ return {*this, ctxt->RelayForType(handler)};
+ }
+
///
/// Assigns a lambda function to be called when the dependency for this slot is autowired.
///
@@ -331,6 +383,20 @@ class AutowiredFast:
return std::shared_ptr::get();
}
+ T* operator->(void) const {
+ auto* retVal = std::shared_ptr::operator->();
+ if (!retVal)
+ throw autowiring_error("Attempted to dereference a null autowired field");
+ return retVal;
+ }
+
+ T& operator*(void) const {
+ T* retVal = std::shared_ptr::get();
+ if (!retVal)
+ throw autowiring_error("Attempted to dereference a null autowired field");
+ return *retVal;
+ }
+
bool IsAutowired(void) const { return std::shared_ptr::get() != nullptr; }
};
diff --git a/autowiring/C++11/cpp11.h b/autowiring/C++11/cpp11.h
index 8efa94198..842f0ead4 100644
--- a/autowiring/C++11/cpp11.h
+++ b/autowiring/C++11/cpp11.h
@@ -131,11 +131,7 @@
#endif
#if TYPE_TRAITS_AVAILABLE
- #if defined(_MSC_VER) || defined(_LIBCPP_VERSION)
- #define TYPE_TRAITS_HEADER
- #else
- #define TYPE_TRAITS_HEADER
- #endif
+ #define TYPE_TRAITS_HEADER
#else
#define TYPE_TRAITS_HEADER
#endif
diff --git a/autowiring/CoreContext.h b/autowiring/CoreContext.h
index 9cb862fba..c1cf9c06d 100644
--- a/autowiring/CoreContext.h
+++ b/autowiring/CoreContext.h
@@ -1,6 +1,7 @@
// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved.
#pragma once
#include "AnySharedPointer.h"
+#include "auto_signal.h"
#include "AutoFilterDescriptor.h"
#include "AutowirableSlot.h"
#include "AutowiringEvents.h"
@@ -52,6 +53,11 @@ class CoreContextT;
template
class JunctionBox;
+namespace autowiring {
+ template
+ struct signal_relay_t;
+}
+
/// \file
/// CoreContext definitions.
@@ -161,7 +167,8 @@ class CoreContext:
// The first deferrable autowiring which requires this type, if one exists:
DeferrableAutowiring* pFirst;
- // A back reference to the concrete type from which this memo was generated:
+ // A back reference to the concrete type from which this memo was generated. This field may be null
+ // if there is no corresponding concrete type.
const CoreObjectDescriptor* pObjTraits;
// Once this memo entry is satisfied, this will contain the AnySharedPointer instance that performs
@@ -295,11 +302,7 @@ class CoreContext:
// Enables a boltable class
template
void EnableInternal(T*, Boltable*) {
- bool dummy[] = {
- false, // Ensure non-zero array size
- (AutoRequireMicroBolt(), false)...
- };
- (void) dummy;
+ [](...){}((AutoRequireMicroBolt(),false)...);
}
void EnableInternal(...) {}
@@ -325,7 +328,22 @@ class CoreContext:
/// \internal
///
- /// Invokes all deferred autowiring fields, generally called after a new member has been added
+ /// Satisfies all slots associated with the passed memo entry and causes finalizers to be invoked
+ ///
+ ///
+ /// The passed lock will be unlocked when this function returns
+ ///
+ void SatisfyAutowiring(std::unique_lock& lk, MemoEntry& entry);
+
+ /// \internal
+ ///
+ /// Updates slots related to a single autowired field
+ ///
+ void UpdateDeferredElement(std::unique_lock&& lk, MemoEntry& entry);
+
+ /// \internal
+ ///
+ /// Updates all deferred autowiring fields, generally called after a new member has been added
///
void UpdateDeferredElements(std::unique_lock&& lk, const CoreObjectDescriptor& entry);
@@ -400,6 +418,12 @@ class CoreContext:
///
void AddInternal(const CoreObjectDescriptor& traits);
+ /// \internal
+ ///
+ /// Internal specific interface introduction routine
+ ///
+ void AddInternal(const AnySharedPointer& ptr);
+
/// \internal
///
/// Scans the memo collection for the specified entry, or adds a deferred resolution marker if resolution was not possible
@@ -634,6 +658,22 @@ class CoreContext:
EnableInternal((T*)nullptr, (Boltable*)nullptr);
}
+ ///
+ /// Introduces the specified pointer to this context explicitly
+ ///
+ /// The interface to make available in this context
+ ///
+ /// Add is similar in behavior to Inject, except that the passed pointer is not treated as a concrete
+ /// type. This means that other interfaces implemented by ptr will not be available for autowiring
+ /// unless explicitly made discoverable by another call to Add.
+ ///
+ /// It is an error to add a type which is already autowirable in a context.
+ ///
+ template
+ void Add(const std::shared_ptr& ptr) {
+ AddInternal(AnySharedPointer(ptr));
+ }
+
///
/// Injects the specified types into this context.
///
@@ -1144,6 +1184,36 @@ class CoreContext:
///
void CancelAutowiringNotification(DeferrableAutowiring* pDeferrable);
+ ///
+ /// A slots-and-signals type relay for a specific type
+ ///
+ template
+ autowiring::signal_relay_t& RelayForType(autowiring::signal T::*handler) {
+ typedef typename SelectTypeUnifier::type TActual;
+
+ // Get the table first
+ auto registration = Inject();
+
+ // Find the basis offset between T and TActual. This is the address of the first member of T
+ // relative to the base of TActual.
+ size_t basis =
+ reinterpret_cast(
+ static_cast(
+ reinterpret_cast(1)
+ )
+ ) - 1;
+
+ // Find the offset and return the relay
+ return
+ static_cast&>(
+ *registration->GetSignalRelay(
+ typeid(TActual),
+ basis +
+ reinterpret_cast(&(static_cast(nullptr)->*handler))
+ )
+ );
+ }
+
///
/// Utility debug method for writing a snapshot of this context to the specified output stream
///
diff --git a/autowiring/CoreObjectDescriptor.h b/autowiring/CoreObjectDescriptor.h
index b5629d179..95fa92242 100644
--- a/autowiring/CoreObjectDescriptor.h
+++ b/autowiring/CoreObjectDescriptor.h
@@ -2,6 +2,7 @@
#pragma once
#include "AnySharedPointer.h"
#include "AutoFilterDescriptor.h"
+#include "AutowiringEvents.h"
#include "BoltBase.h"
#include "ContextMember.h"
#include "CoreRunnable.h"
@@ -24,9 +25,9 @@
struct CoreObjectDescriptor {
template
CoreObjectDescriptor(const std::shared_ptr& value, T*) :
- type(typeid(T)),
- actual_type(typeid(*value)),
- stump(SlotInformationStump::s_stump),
+ type(&typeid(T)),
+ actual_type(&typeid(*value)),
+ stump(&SlotInformationStump::s_stump),
value(value),
subscriber(MakeAutoFilterDescriptor(value)),
pCoreObject(autowiring::fast_pointer_cast(value)),
@@ -36,12 +37,14 @@ struct CoreObjectDescriptor {
pFilter(autowiring::fast_pointer_cast(value)),
pBoltBase(autowiring::fast_pointer_cast(value)),
receivesEvents(
- [this]{
- for (auto evt = g_pFirstEventEntry; evt; evt = evt->pFlink) {
- auto identifier = evt->NewTypeIdentifier();
- if (identifier->IsSameAs(pCoreObject.get()))
+ [this, value]{
+ // Because we manually added AutowiringEvents to the JunctionBoxManager, check here also
+ if (autowiring::fast_pointer_cast(value))
+ return true;
+
+ for (auto evt = g_pFirstEventEntry; evt; evt = evt->pFlink)
+ if (evt->IsSameAs(pCoreObject.get()))
return true;
- }
// "T" not found in event registry
return false;
}()
@@ -63,12 +66,12 @@ struct CoreObjectDescriptor {
{}
// The type of the passed pointer
- const std::type_info& type;
+ const std::type_info* type;
// The "actual type" used by Autowiring. This type may differ from CoreObjectDescriptor::type in cases
// where a type unifier is used, or if the concrete type is defined in an external module--for
// instance, by a class factory.
- const std::type_info& actual_type;
+ const std::type_info* actual_type;
///
/// Used to obtain a list of slots defined on this type, for reflection purposes
@@ -98,23 +101,23 @@ struct CoreObjectDescriptor {
///
/// The linked list is guaranteed to be in reverse-sorted order
///
- const SlotInformationStumpBase& stump;
+ const SlotInformationStumpBase* stump;
// A holder to store the original shared pointer, to ensure that type information propagates
// correctly on the right-hand side of our map
- const AnySharedPointer value;
+ AnySharedPointer value;
// The packet subscriber introduction method, if appropriate:
- const AutoFilterDescriptor subscriber;
+ AutoFilterDescriptor subscriber;
// There are a lot of interfaces we support, here they all are:
- const std::shared_ptr pCoreObject;
- const std::shared_ptr pContextMember;
- const std::shared_ptr pCoreRunnable;
- const std::shared_ptr pBasicThread;
- const std::shared_ptr pFilter;
- const std::shared_ptr pBoltBase;
+ std::shared_ptr pCoreObject;
+ std::shared_ptr pContextMember;
+ std::shared_ptr pCoreRunnable;
+ std::shared_ptr pBasicThread;
+ std::shared_ptr pFilter;
+ std::shared_ptr pBoltBase;
// Does this type receive events?
- const bool receivesEvents;
+ bool receivesEvents;
};
diff --git a/autowiring/CreationRules.h b/autowiring/CreationRules.h
index 24ca00965..5ad53712e 100644
--- a/autowiring/CreationRules.h
+++ b/autowiring/CreationRules.h
@@ -111,7 +111,7 @@ struct crh {
try {
// Push a new stack location so that all constructors from here know the injected type under construction
SlotInformationStackLocation loc(
- &SlotInformationStump::s_stump,
+ SlotInformationStump::s_stump,
pSpace,
sizeof(TActual)
);
diff --git a/autowiring/EventRegistry.h b/autowiring/EventRegistry.h
index 61271b97e..ad701e2e3 100644
--- a/autowiring/EventRegistry.h
+++ b/autowiring/EventRegistry.h
@@ -6,7 +6,9 @@
class JunctionBoxBase;
-struct EventRegistryEntry {
+struct EventRegistryEntry:
+ public TypeIdentifierBase
+{
EventRegistryEntry(const std::type_info& ti);
// Next entry in the list:
@@ -15,20 +17,10 @@ struct EventRegistryEntry {
// Type of this entry:
const std::type_info& ti;
- ///
- /// The runtime type information corresponding to this entry
- ///
- virtual const std::type_info& GetTypeInfo(void) const = 0;
-
///
/// Constructor method, used to generate a new junction box
///
virtual std::shared_ptr NewJunctionBox(void) const = 0;
-
- ///
- /// Used to create a type identifier value, for use with AutoNet
- ///
- virtual std::shared_ptr NewTypeIdentifier(void) const = 0;
};
template
@@ -38,20 +30,16 @@ struct EventRegistryEntryT:
EventRegistryEntryT(void):
EventRegistryEntry(typeid(T))
{}
-
- virtual const std::type_info& GetTypeInfo(void) const override { return typeid(T); }
-
+ virtual const std::type_info& GetTypeInfo(void) const override { return typeid(T); }
+
virtual std::shared_ptr NewJunctionBox(void) const override {
- return std::static_pointer_cast(
- std::make_shared>()
- );
+ return std::make_shared>();
}
- std::shared_ptr NewTypeIdentifier(void) const override {
- return std::static_pointer_cast(
- std::make_shared>()
- );
+ // true if "obj" is an event receiver for T
+ bool IsSameAs(const CoreObject* obj) const override {
+ return !!dynamic_cast(obj);
}
};
diff --git a/autowiring/MicroBolt.h b/autowiring/MicroBolt.h
index 4e93356da..6b3b3a27b 100644
--- a/autowiring/MicroBolt.h
+++ b/autowiring/MicroBolt.h
@@ -32,11 +32,7 @@ struct MicroBolt:
// multiple calls to Inject() if a matching context
// is created during traversal.
const auto ctxt = CoreContext::CurrentContext();
- const bool dummy [] = {
- LoopInject(ctxt)...,
- false
- };
- (void)dummy;
+ [](...){}(LoopInject(ctxt)...);
}
void ContextCreated(void) override;
};
diff --git a/autowiring/SlotInformation.h b/autowiring/SlotInformation.h
index c49a82e97..0c3d765a9 100644
--- a/autowiring/SlotInformation.h
+++ b/autowiring/SlotInformation.h
@@ -110,46 +110,49 @@ SlotInformationStump SlotInformationStump::s_stump;
///
class SlotInformationStackLocation {
public:
- SlotInformationStackLocation(const SlotInformationStackLocation& rhs) = delete;
- SlotInformationStackLocation(SlotInformationStackLocation&& rhs) = delete;
-
///
/// Registers a new stack location on the current thread, used to provide slot reflection services in Autowiring
///
- SlotInformationStackLocation(SlotInformationStumpBase* pStump, const void* pObj = nullptr, size_t extent = 0);
+ SlotInformationStackLocation(SlotInformationStumpBase& stump, const void* pObj = nullptr, size_t extent = 0);
~SlotInformationStackLocation(void);
-private:
- // The prior stack location, made current when this slot goes out of scope
- SlotInformationStackLocation* m_pPrior;
-
// The pointer location where the stump will be stored:
- SlotInformationStumpBase* m_pStump;
+ SlotInformationStumpBase& stump;
+
+ // Information about the object being constructed while this stack location is valid:
+ const void* const pObj;
+ const size_t extent;
+
+ // The prior stack location, made current when this slot goes out of scope
+ SlotInformationStackLocation& prior;
+private:
// Current slot information:
SlotInformation* m_pCur;
// Most recent AutoFilter descriptor link:
AutoFilterDescriptorStubLink* m_pLastLink;
- // Information about the object being constructed while this stack location is valid:
- const void* m_pObj;
- size_t m_extent;
-
public:
///
/// True if the passed pointer is inside of the object currently under construction at this stack location
///
- bool Encloses(const void* ptr) {
- return m_pObj < ptr && ptr < (char*) m_pObj + m_extent;
+ bool Encloses(const void* ptr) const {
+ return pObj < ptr && ptr < (char*) pObj + extent;
}
- ///
- /// The slot information stump for this stack location
- ///
- SlotInformationStumpBase* GetStump(void) const {
- return m_pStump;
+ ///
+ /// The offset of the specified pointer from the beginning of the object under construction
+ ///
+ /// The object whose offset is to be found
+ ///
+ /// The return value is undefined if the passed pointer is not within the object under construction. This
+ /// function is intend to be used during field construction to find the offset of the passed field within
+ /// its enclosing type.
+ ///
+ size_t Offset(const void* ptr) const {
+ return (char*) ptr - (char*) pObj;
}
///
@@ -157,11 +160,6 @@ class SlotInformationStackLocation {
///
static SlotInformationStackLocation* CurrentStackLocation(void);
- ///
- /// Returns the stump in the current stack location, or null
- ///
- static SlotInformationStumpBase* CurrentStump(void);
-
///
/// Registers the named slot with the current stack location
///
@@ -171,8 +169,4 @@ class SlotInformationStackLocation {
/// Registers a NewAutoFilter with this SlotInformation
///
static void RegisterSlot(const AutoFilterDescriptorStub& stub);
-
- // Operator overloads:
- void operator=(SlotInformationStackLocation&& rhs) = delete;
- void operator=(const SlotInformationStackLocation& rhs) = delete;
};
diff --git a/autowiring/TypeIdentifier.h b/autowiring/TypeIdentifier.h
index 73db0b5f9..e41898711 100644
--- a/autowiring/TypeIdentifier.h
+++ b/autowiring/TypeIdentifier.h
@@ -5,20 +5,13 @@
// Checks if an Object* listens to a event T;
struct TypeIdentifierBase {
- virtual bool IsSameAs(const CoreObject* obj) = 0;
- virtual const std::type_info& Type() = 0;
-};
+ ///
+ /// The runtime type information corresponding to this identifier
+ ///
+ virtual const std::type_info& GetTypeInfo(void) const = 0;
-template
- struct TypeIdentifier:
-public TypeIdentifierBase
-{
- // true if "obj" is an event receiver for T
- bool IsSameAs(const CoreObject* obj) override {
- return !!dynamic_cast(obj);
- }
-
- const std::type_info& Type() override {
- return typeid(T);
- }
+ ///
+ /// True if this type identifier can cast the specified CoreObject
+ ///
+ virtual bool IsSameAs(const CoreObject* obj) const = 0;
};
diff --git a/autowiring/TypeRegistry.h b/autowiring/TypeRegistry.h
index 8295aacd3..6ad9be58a 100644
--- a/autowiring/TypeRegistry.h
+++ b/autowiring/TypeRegistry.h
@@ -10,7 +10,9 @@ namespace autowiring {
void InjectCurrent(void);
}
-struct TypeRegistryEntry {
+struct TypeRegistryEntry:
+ public TypeIdentifierBase
+{
TypeRegistryEntry(const std::type_info& ti);
// Next entry in the list:
@@ -19,16 +21,6 @@ struct TypeRegistryEntry {
// Type of this entry:
const std::type_info& ti;
- ///
- /// The runtime type information corresponding to this entry
- ///
- virtual const std::type_info& GetTypeInfo(void) const = 0;
-
- ///
- /// Used to create a type identifier value, for use with AutoNet
- ///
- virtual std::shared_ptr NewTypeIdentifier(void) const = 0;
-
///
/// Returns true if an injection is possible on the described type
///
@@ -57,10 +49,9 @@ struct TypeRegistryEntryT:
virtual const std::type_info& GetTypeInfo(void) const override { return typeid(T); }
- std::shared_ptr NewTypeIdentifier(void) const override {
- return std::static_pointer_cast(
- std::make_shared>()
- );
+ // true if "obj" is an event receiver for T
+ bool IsSameAs(const CoreObject* obj) const override {
+ return !!dynamic_cast(obj);
}
bool CanInject(void) const override {
diff --git a/autowiring/auto_future_win.h b/autowiring/auto_future_win.h
index 041d9d125..1fda66f6e 100644
--- a/autowiring/auto_future_win.h
+++ b/autowiring/auto_future_win.h
@@ -4,20 +4,39 @@
#include
namespace autowiring {
+ #pragma pack(push, _CRT_PACKING)
+
template
class _Packaged_state_unwrap:
- public std::_Associated_state<_Ret*>
+ public std::_Associated_state<_Ret>
{
public:
std::function<_Ret(_ArgTypes...)> _Fn;
};
+ template
+ class _Packaged_state_unwrap<_Ret&, _ArgTypes...>:
+ public std::_Associated_state<_Ret*>
+ {
+ public:
+ std::function<_Ret&(_ArgTypes...)> _Fn;
+ };
+
+ template
+ class _Packaged_state_unwrap:
+ public std::_Associated_state
+ {
+ public:
+ std::function _Fn;
+ };
+
class _Task_async_state_unwrap:
public std::_Packaged_state
{
public:
::Concurrency::task _Task;
};
+ #pragma pack(pop)
///
/// Platform-specific operation appending routine
@@ -36,6 +55,11 @@ namespace autowiring {
auto* packagedState = static_cast*>(deferredAsync);
auto* unwrap = reinterpret_cast<_Packaged_state_unwrap*>(packagedState);
+ static_assert(
+ sizeof(*packagedState) == sizeof(*unwrap),
+ "Size of unwrapped version differs from underlying version, internal API has changed"
+ );
+
// New function which consists of a call to the original then a call to the continuation
unwrap->_Fn = std::bind(
[] (const std::function& orig, const Fn& fn) {
diff --git a/autowiring/auto_signal.h b/autowiring/auto_signal.h
new file mode 100644
index 000000000..5f7823e32
--- /dev/null
+++ b/autowiring/auto_signal.h
@@ -0,0 +1,216 @@
+// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved.
+#pragma once
+#include
+#include
+#include
+#include
+#include
+
+namespace autowiring {
+ struct signal_relay_registration_table;
+}
+
+///
+/// Implements an asynchronous signal concept as an AutoFired alternative
+///
+namespace autowiring {
+ template
+ struct signal;
+
+ struct signal_relay;
+
+ namespace internal {
+ // Linked list entry
+ struct signal_node_base {
+ signal_node_base(void) :
+ pFlink(nullptr),
+ pBlink(nullptr)
+ {}
+
+ virtual ~signal_node_base(void) {}
+
+ // Forward and backward linkages:
+ signal_node_base* pFlink;
+ signal_node_base* pBlink;
+ };
+
+ // Holds a reference to one of the signal holders
+ template
+ struct signal_node:
+ signal_node_base
+ {
+ signal_node(const signal_node& rhs) = delete;
+
+ signal_node(std::function&& fn) :
+ fn(std::move(fn))
+ {}
+
+ const std::function fn;
+ };
+
+
+ struct signal_registration_base {
+ signal_registration_base(void);
+
+ // Associates signal entries with their corresponding relays
+ std::unordered_map> entries;
+ };
+
+ ///
+ /// Associates offsets in type T with signals in that type
+ ///
+ template
+ struct signal_registration:
+ signal_registration_base
+ {};
+
+ ///
+ /// Obtains the signal relay for the specified member type:
+ ///
+ std::shared_ptr ObtainRelay(void* pMember);
+ }
+
+ ///
+ /// Associates abstract offsets with signal relays
+ ///
+ ///
+ /// One of these registration tables exists per context.
+ ///
+ struct signal_relay_registration_table {
+ struct registration {
+ registration(const std::type_info* ti, size_t offset) :
+ ti(ti),
+ offset(offset)
+ {}
+
+ const std::type_info* ti;
+ size_t offset;
+
+ bool operator==(const registration& rhs) const {
+ return *ti == *rhs.ti && offset == rhs.offset;
+ }
+ };
+
+ struct registration_hash {
+ size_t operator()(const registration& reg) const {
+ return reg.ti->hash_code() + reg.offset;
+ }
+ };
+
+ // Lock for the table:
+ std::mutex lock;
+
+ ///
+ /// Associates an offset in the relay registration with a signal relay
+ ///
+ ///
+ /// This map assumes that users have some other way of inferring the types on the key side.
+ ///
+ std::unordered_map, registration_hash> table;
+
+ ///
+ /// Creates or returns the relay at the specified offset
+ ///
+ std::shared_ptr GetSignalRelay(const std::type_info& ti, size_t offset);
+ };
+
+ ///
+ /// Stores a signal
+ ///
+ struct signal_relay
+ {
+ public:
+ signal_relay(void) :
+ pHead(nullptr)
+ {}
+
+ ~signal_relay(void) {
+ // Standard linked list cleaup
+ internal::signal_node_base* next = nullptr;
+ for (auto cur = pHead; cur; cur = next) {
+ next = cur->pFlink;
+ delete cur;
+ }
+ }
+
+ protected:
+ // First entry on the list:
+ internal::signal_node_base* pHead;
+
+ public:
+ void operator-=(internal::signal_node_base* rhs) {
+ // Clear linkage
+ if (rhs->pBlink)
+ rhs->pBlink->pFlink = rhs->pFlink;
+ if (rhs->pFlink)
+ rhs->pFlink->pBlink = rhs->pBlink;
+
+ // If we're the head pointer then unlink
+ if (rhs == pHead)
+ pHead = rhs->pFlink;
+
+ // Fully unlinked, delete
+ delete rhs;
+ }
+ };
+
+ ///
+ /// Descriptor type used to add entries that will be constructed in a signal relay
+ ///
+ template
+ struct signal_relay_t:
+ signal_relay
+ {
+ internal::signal_node* GetHead(void) const {
+ return static_cast*>(pHead);
+ }
+
+ ///
+ /// Attaches the specified handler to this signal
+ ///
+ internal::signal_node* operator+=(std::function&& fn) {
+ // Standard singly linked list insert:
+ auto retVal = new internal::signal_node(std::move(fn));
+ retVal->pFlink = pHead;
+ if (pHead)
+ pHead->pBlink = retVal;
+ pHead = retVal;
+ return retVal;
+ }
+ };
+
+ ///
+ /// A signal registration entry, for use as an embedded member variable of a context member.
+ ///
+ template
+ struct signal
+ {
+ public:
+ signal(void) :
+ m_relay(
+ std::static_pointer_cast>(
+ internal::ObtainRelay(this)
+ )
+ )
+ {}
+
+ private:
+ const std::shared_ptr> m_relay;
+
+ public:
+ internal::signal_node* operator+=(std::function&& fn) { return *m_relay += std::move(fn); }
+ void operator-=(internal::signal_node* node) { return *m_relay -= node; }
+
+ ///
+ /// Raises the signal and invokes all attached handlers
+ ///
+ void operator()(Args... args) const {
+ for (
+ auto cur = m_relay->GetHead();
+ cur;
+ cur = static_cast(cur->pFlink)
+ )
+ cur->fn(args...);
+ }
+ };
+}
diff --git a/publicDoxyfile.conf b/publicDoxyfile.conf
index a5dfcb4b8..c56e74b41 100644
--- a/publicDoxyfile.conf
+++ b/publicDoxyfile.conf
@@ -38,7 +38,7 @@ PROJECT_NAME = Autowiring
# could be handy for archiving the generated documentation or if some version
# control system is used.
-PROJECT_NUMBER = 0.4.2
+PROJECT_NUMBER = 0.5.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
diff --git a/scripts/version_number_updated.sh b/scripts/version_number_updated.sh
new file mode 100755
index 000000000..80c9c4d1b
--- /dev/null
+++ b/scripts/version_number_updated.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+#
+# Ensure that the version.cmake version number is the same everywhere
+#
+
+# Get the version number from version.cmake first
+if ! version=$(grep -oE "[0-9]+.[0-9]+.[0-9]+" version.cmake); then
+ echo "Version number not found in version.cmake"
+ exit 1
+fi
+
+# Verify that this identical version number appears in our doxygen files
+if ! grep $version Doxyfile; then
+ echo "Expected to find version $version in Doxyfile"
+ exit 1
+fi
+if ! grep $version publicDoxyfile.conf; then
+ echo "Expected to find version $version in publicDoxyfile.conf"
+ exit 1
+fi
diff --git a/src/autonet/AutoNetServerImpl.cpp b/src/autonet/AutoNetServerImpl.cpp
index 123f9ebbc..4d697cd8d 100644
--- a/src/autonet/AutoNetServerImpl.cpp
+++ b/src/autonet/AutoNetServerImpl.cpp
@@ -57,7 +57,7 @@ AutoNetServerImpl::AutoNetServerImpl(std::unique_ptr&& transpo
// Generate list of all events from event registry
for(auto event = g_pFirstEventEntry; event; event = event->pFlink)
- m_EventTypes.insert(event->NewTypeIdentifier());
+ m_EventTypes.insert(event);
}
AutoNetServerImpl::~AutoNetServerImpl(){}
@@ -184,7 +184,7 @@ void AutoNetServerImpl::NewObject(CoreContext& ctxt, const CoreObjectDescriptor&
// Add slots for this object
{
Json::array slots;
- for(auto slot = object.stump.pHead; slot; slot = slot->pFlink) {
+ for(auto slot = object.stump->pHead; slot; slot = slot->pFlink) {
slots.push_back(Json::object{
{"id", autowiring::demangle(slot->type)},
{"autoRequired", slot->autoRequired},
@@ -233,8 +233,8 @@ void AutoNetServerImpl::NewObject(CoreContext& ctxt, const CoreObjectDescriptor&
{
Json::array listenerTypes;
for(const auto& event : m_EventTypes) {
- if(event->IsSameAs(object.pCoreObject.get()))
- listenerTypes.push_back(autowiring::demangle(event->Type()));
+ if (object.receivesEvents)
+ listenerTypes.push_back(autowiring::demangle(event->GetTypeInfo()));
}
if(!listenerTypes.empty())
diff --git a/src/autonet/AutoNetServerImpl.hpp b/src/autonet/AutoNetServerImpl.hpp
index 132a0dc85..c8b98dd3b 100644
--- a/src/autonet/AutoNetServerImpl.hpp
+++ b/src/autonet/AutoNetServerImpl.hpp
@@ -145,7 +145,7 @@ class AutoNetServerImpl:
std::map m_ContextPtrs;
// All event types
- std::set> m_EventTypes;
+ std::set m_EventTypes;
// All ContextMembers
std::map> m_AllTypes;
diff --git a/src/autonet/CMakeLists.txt b/src/autonet/CMakeLists.txt
index 66f863e53..a43d7430d 100644
--- a/src/autonet/CMakeLists.txt
+++ b/src/autonet/CMakeLists.txt
@@ -1,8 +1,3 @@
-if(NOT WIN32 AND NOT autowiring_USE_LIBCXX)
- message("Cannot build Autonet, requires proper C++11 support")
- return()
-endif()
-
add_googletest(test)
include_directories(
${PROJECT_SOURCE_DIR}/contrib/autoboost
diff --git a/src/autowiring/AutoPacket.cpp b/src/autowiring/AutoPacket.cpp
index c2b8ab395..18ce30c67 100644
--- a/src/autowiring/AutoPacket.cpp
+++ b/src/autowiring/AutoPacket.cpp
@@ -324,6 +324,12 @@ void AutoPacket::ThrowNotDecoratedException(const DecorationKey& key) {
throw std::runtime_error(ss.str());
}
+void AutoPacket::ThrowMultiplyDecoratedException(const DecorationKey& key) {
+ std::stringstream ss;
+ ss << "Attempted to obtain a type " << autowiring::demangle(key.ti) << " which was decorated more than once on this packet";
+ throw std::runtime_error(ss.str());
+}
+
size_t AutoPacket::GetDecorationTypeCount(void) const
{
std::lock_guard lk(m_lock);
diff --git a/src/autowiring/AutoPacketInternal.cpp b/src/autowiring/AutoPacketInternal.cpp
index 525aaddcb..a3e6a8183 100644
--- a/src/autowiring/AutoPacketInternal.cpp
+++ b/src/autowiring/AutoPacketInternal.cpp
@@ -43,12 +43,7 @@ void AutoPacketInternal::Initialize(bool isFirstPacket) {
call->GetCall()(call->GetAutoFilter(), *this);
// First-call indicated by argumument type AutoPacket&:
-#if autowiring_USE_LIBCXX
for (bool is_shared : {false, true}) {
-#else
- for (int num = 0; num < 2; ++num) {
- bool is_shared = (bool)num;
-#endif
std::unique_lock lk(m_lock);
// Don't modify the decorations set if nobody expects an AutoPacket input
diff --git a/src/autowiring/CMakeLists.txt b/src/autowiring/CMakeLists.txt
index f502fdc18..dc9031e6c 100644
--- a/src/autowiring/CMakeLists.txt
+++ b/src/autowiring/CMakeLists.txt
@@ -16,6 +16,8 @@ set(Autowiring_SRCS
at_exit.h
auto_id.h
auto_future.h
+ auto_signal.h
+ auto_signal.cpp
AutoConfig.cpp
AutoConfig.h
AutoConfigManager.cpp
diff --git a/src/autowiring/CoreContext.cpp b/src/autowiring/CoreContext.cpp
index 651dd54fa..2a27d24b6 100644
--- a/src/autowiring/CoreContext.cpp
+++ b/src/autowiring/CoreContext.cpp
@@ -195,7 +195,7 @@ const std::type_info& CoreContext::GetAutoTypeId(const AnySharedPointer& ptr) co
throw autowiring_error("Attempted to obtain the true type of a shared pointer that was not a member of this context");
const CoreObjectDescriptor* pObjTraits = q->second.pObjTraits;
- return pObjTraits->type;
+ return *pObjTraits->type;
}
std::shared_ptr CoreContext::IncrementOutstandingThreadCount(void) {
@@ -246,7 +246,7 @@ void CoreContext::AddInternal(const CoreObjectDescriptor& traits) {
// concrete type defined in another context or potentially a unifier type. Creating a slot here
// is also undesirable because the complete type is not available and we can't create a dynaimc
// caster to identify when this slot gets satisfied.
- auto q = m_typeMemos.find(traits.actual_type);
+ auto q = m_typeMemos.find(*traits.actual_type);
if(q != m_typeMemos.end()) {
auto& v = q->second;
if(*v.m_value == traits.pCoreObject)
@@ -290,7 +290,7 @@ void CoreContext::AddInternal(const CoreObjectDescriptor& traits) {
}
// Subscribers, if applicable:
- const auto& stump = traits.stump;
+ const auto& stump = *traits.stump;
if(!traits.subscriber.empty()) {
AddPacketSubscriber(traits.subscriber);
@@ -307,6 +307,19 @@ void CoreContext::AddInternal(const CoreObjectDescriptor& traits) {
GetGlobal()->Invoke(&AutowiringEvents::NewObject)(*this, traits);
}
+void CoreContext::AddInternal(const AnySharedPointer& ptr) {
+ std::unique_lock lk(m_stateBlock->m_lock);
+
+ // Verify that this type isn't already satisfied
+ MemoEntry& entry = m_typeMemos[ptr->type()];
+ if (entry.m_value)
+ throw autowiring_error("This interface is already present in the context");
+
+ // Now we can satisfy it:
+ entry.m_value = ptr;
+ UpdateDeferredElement(std::move(lk), entry);
+}
+
void CoreContext::FindByType(AnySharedPointer& reference) const {
std::lock_guard lk(m_stateBlock->m_lock);
FindByTypeUnsafe(reference);
@@ -736,76 +749,145 @@ void CoreContext::BroadcastContextCreationNotice(const std::type_info& sigil) co
m_pParent->BroadcastContextCreationNotice(sigil);
}
-void CoreContext::UpdateDeferredElements(std::unique_lock&& lk, const CoreObjectDescriptor& entry) {
- // Collection of satisfiable lists:
- std::vector satisfiable;
-
- // Notify any autowired field whose autowiring was deferred. We do this by processing each entry
- // in the entire type memos collection. These entries are keyed on the type of the memo, and the
- // value is a linked list of trees of deferred autowiring instances that will need to be called
- // if the corresponding memo type has been satisfied.
- //
- // A tree data structure is used, here, specifically because there are cases where child nodes
- // on a tree should only be called if and only if the root node is still present. For instance,
- // creating an Autowired field adds a tree to this list with the root node referring to the
- // Autowired field itself, and then invoking Autowired::NotifyWhenAutowired attaches a child to
- // this tree. If the Autowired instance is destroyed, the lambda registered for notification is
- // also removed at the same time.
- //
- // Each connected nonroot deferrable autowiring is referred to as a "dependant chain".
+void CoreContext::SatisfyAutowiring(std::unique_lock& lk, MemoEntry& entry) {
+ std::vector requiresFinalize;
+
+ // Now we need to take on the responsibility of satisfying this deferral. We will do this by
+ // nullifying the flink, and by ensuring that the memo is satisfied at the point where we
+ // release the lock.
std::stack stk;
- for(auto& cur : m_typeMemos) {
- MemoEntry& value = cur.second;
+ stk.push(entry.pFirst);
+ entry.pFirst = nullptr;
+
+ // Depth-first search
+ while (!stk.empty()) {
+ auto top = stk.top();
+ stk.pop();
+
+ for (DeferrableAutowiring* pCur = top; pCur; pCur = pCur->GetFlink()) {
+ pCur->SatisfyAutowiring(entry.m_value);
+
+ // See if there's another chain we need to process:
+ auto child = pCur->ReleaseDependentChain();
+ if (child)
+ stk.push(child);
+
+ // Not everyone needs to be finalized. The entities that don't require finalization
+ // are identified by an empty strategy, and we just skip them.
+ auto strategy = pCur->GetStrategy();
+ if (strategy)
+ requiresFinalize.push_back(strategy);
+ }
+ }
- if(value.m_value)
- // This entry is already satisfied, no need to process it
- continue;
+ lk.unlock();
- // Determine whether the current candidate element satisfies the autowiring we are considering.
- // This is done internally via a dynamic cast on the interface type for which this polymorphic
- // base type was constructed.
- if(!value.m_value->try_assign(entry.pCoreObject))
+ // Run through everything else and finalize it all:
+ for (const auto& cur : requiresFinalize)
+ cur->Finalize();
+}
+
+void CoreContext::UpdateDeferredElement(std::unique_lock&& lk, MemoEntry& entry) {
+ // Satisfy what needs to be satisfied:
+ SatisfyAutowiring(lk, entry);
+
+ // Give children a chance to also update their deferred elements:
+ lk.lock();
+ for (const auto& weak_child : m_children) {
+ // Hold reference to prevent this iterator from becoming invalidated:
+ auto ctxt = weak_child.lock();
+ if (!ctxt)
continue;
- // Success, assign the traits
- value.pObjTraits = &entry;
-
- // Now we need to take on the responsibility of satisfying this deferral. We will do this by
- // nullifying the flink, and by ensuring that the memo is satisfied at the point where we
- // release the lock.
- stk.push(value.pFirst);
- value.pFirst = nullptr;
-
- // Finish satisfying the remainder of the chain while we hold the lock:
- while(!stk.empty()) {
- auto top = stk.top();
- stk.pop();
-
- for(DeferrableAutowiring* pNext = top; pNext; pNext = pNext->GetFlink()) {
- pNext->SatisfyAutowiring(value.m_value);
-
- // See if there's another chain we need to process:
- auto child = pNext->ReleaseDependentChain();
- if(child)
- stk.push(child);
-
- // Not everyone needs to be finalized. The entities that don't require finalization
- // are identified by an empty strategy, and we just skip them.
- auto strategy = pNext->GetStrategy();
- if(strategy)
- satisfiable.push_back(strategy);
+ // Reverse lock before satisfying children:
+ lk.unlock();
+ ctxt->UpdateDeferredElement(
+ std::unique_lock(ctxt->m_stateBlock->m_lock),
+ entry
+ );
+ lk.lock();
+ }
+ lk.unlock();
+}
+
+void CoreContext::UpdateDeferredElements(std::unique_lock&& lk, const CoreObjectDescriptor& entry) {
+ {
+ // Collection of items needing finalization:
+ std::vector delayedFinalize;
+
+ // Notify any autowired field whose autowiring was deferred. We do this by processing each entry
+ // in the entire type memos collection. These entries are keyed on the type of the memo, and the
+ // value is a linked list of trees of deferred autowiring instances that will need to be called
+ // if the corresponding memo type has been satisfied.
+ //
+ // A tree data structure is used, here, specifically because there are cases where child nodes
+ // on a tree should only be called if and only if the root node is still present. For instance,
+ // creating an Autowired field adds a tree to this list with the root node referring to the
+ // Autowired field itself, and then invoking Autowired::NotifyWhenAutowired attaches a child to
+ // this tree. If the Autowired instance is destroyed, the lambda registered for notification is
+ // also removed at the same time.
+ //
+ // Each connected nonroot deferrable autowiring is referred to as a "dependant chain".
+ std::stack stk;
+ for (auto& cur : m_typeMemos) {
+ MemoEntry& value = cur.second;
+
+ if (value.m_value)
+ // This entry is already satisfied, no need to process it
+ continue;
+
+ // Determine whether the current candidate element satisfies the autowiring we are considering.
+ // This is done internally via a dynamic cast on the interface type for which this polymorphic
+ // base type was constructed.
+ if (!value.m_value->try_assign(entry.pCoreObject))
+ continue;
+
+ // Success, assign the traits
+ value.pObjTraits = &entry;
+
+ // Now we need to take on the responsibility of satisfying this deferral. We will do this by
+ // nullifying the flink, and by ensuring that the memo is satisfied at the point where we
+ // release the lock.
+ stk.push(value.pFirst);
+ value.pFirst = nullptr;
+
+ // Finish satisfying the remainder of the chain while we hold the lock:
+ while (!stk.empty()) {
+ auto top = stk.top();
+ stk.pop();
+
+ for (DeferrableAutowiring* pNext = top; pNext; pNext = pNext->GetFlink()) {
+ pNext->SatisfyAutowiring(value.m_value);
+
+ // See if there's another chain we need to process:
+ auto child = pNext->ReleaseDependentChain();
+ if (child)
+ stk.push(child);
+
+ // Not everyone needs to be finalized. The entities that don't require finalization
+ // are identified by an empty strategy, and we just skip them.
+ auto strategy = pNext->GetStrategy();
+ if (strategy)
+ delayedFinalize.push_back(strategy);
+ }
}
}
+ lk.unlock();
+
+ // Run through everything else and finalize it all:
+ for (const auto& cur : delayedFinalize)
+ cur->Finalize();
}
// Give children a chance to also update their deferred elements:
- for(const auto& weak_child : m_children) {
+ lk.lock();
+ for (const auto& weak_child : m_children) {
// Hold reference to prevent this iterator from becoming invalidated:
auto ctxt = weak_child.lock();
if(!ctxt)
continue;
- // Reverse lock before satisfying children:
+ // Reverse lock before handing off control:
lk.unlock();
ctxt->UpdateDeferredElements(
std::unique_lock(ctxt->m_stateBlock->m_lock),
@@ -813,11 +895,6 @@ void CoreContext::UpdateDeferredElements(std::unique_lock&& lk, cons
);
lk.lock();
}
- lk.unlock();
-
- // Run through everything else and finalize it all:
- for(const auto& cur : satisfiable)
- cur->Finalize();
}
void CoreContext::AddEventReceiver(const JunctionBoxEntry& entry) {
diff --git a/src/autowiring/SlotInformation.cpp b/src/autowiring/SlotInformation.cpp
index ae8b6080f..a6b069df4 100644
--- a/src/autowiring/SlotInformation.cpp
+++ b/src/autowiring/SlotInformation.cpp
@@ -9,13 +9,13 @@
// Special file-level allocation with a no-op dtor, because all stack locations are stack-allocated
static autowiring::thread_specific_ptr tss([](SlotInformationStackLocation*) {});
-SlotInformationStackLocation::SlotInformationStackLocation(SlotInformationStumpBase* pStump, const void* pObj, size_t extent) :
- m_pPrior(tss.get()),
- m_pStump(pStump),
+SlotInformationStackLocation::SlotInformationStackLocation(SlotInformationStumpBase& stump, const void* pObj, size_t extent) :
+ prior(*tss),
+ stump(stump),
m_pCur(nullptr),
m_pLastLink(nullptr),
- m_pObj(pObj),
- m_extent(extent)
+ pObj(pObj),
+ extent(extent)
{
tss.reset(this);
}
@@ -33,35 +33,26 @@ void UpdateOrCascadeDelete(T* ptr, const T*& dest) {
}
SlotInformationStackLocation::~SlotInformationStackLocation(void) {
- if(!m_pStump)
- // Rvalue moved, end here
- return;
-
// Replace the prior stack location, we were pushed
- tss.reset(m_pPrior);
+ tss.reset(&prior);
- UpdateOrCascadeDelete(m_pCur, m_pStump->pHead);
- UpdateOrCascadeDelete(m_pLastLink, m_pStump->pFirstAutoFilter);
+ UpdateOrCascadeDelete(m_pCur, stump.pHead);
+ UpdateOrCascadeDelete(m_pLastLink, stump.pFirstAutoFilter);
// Unconditionally update to true, no CAS needed
- m_pStump->bInitialized = true;
+ stump.bInitialized = true;
}
SlotInformationStackLocation* SlotInformationStackLocation::CurrentStackLocation(void) {
return tss.get();
}
-SlotInformationStumpBase* SlotInformationStackLocation::CurrentStump(void) {
- // Trivial null defaulting:
- return tss.get() ? tss->m_pStump : nullptr;
-}
-
void SlotInformationStackLocation::RegisterSlot(DeferrableAutowiring* pDeferrable) {
if(!tss.get())
// Nothing to do, this slot entry is missing
return;
- if(tss->m_pStump->bInitialized)
+ if(tss->stump.bInitialized)
// No reason to continue, stump already initialized
return;
@@ -73,13 +64,13 @@ void SlotInformationStackLocation::RegisterSlot(DeferrableAutowiring* pDeferrabl
tss->m_pCur,
pDeferrable->GetType(),
reinterpret_cast(pDeferrable) -
- reinterpret_cast(tss->m_pObj),
+ reinterpret_cast(tss->pObj),
false
);
}
void SlotInformationStackLocation::RegisterSlot(const AutoFilterDescriptorStub& stub) {
- if(!tss.get() || tss->m_pStump->bInitialized)
+ if(!tss.get() || tss->stump.bInitialized)
return;
tss->m_pLastLink = new AutoFilterDescriptorStubLink(stub, tss->m_pLastLink);
diff --git a/src/autowiring/auto_signal.cpp b/src/autowiring/auto_signal.cpp
new file mode 100644
index 000000000..29465cd0b
--- /dev/null
+++ b/src/autowiring/auto_signal.cpp
@@ -0,0 +1,35 @@
+// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved.
+#include "stdafx.h"
+#include "auto_signal.h"
+#include "Autowired.h"
+#include "SlotInformation.h"
+
+using namespace autowiring;
+using namespace autowiring::internal;
+
+signal_registration_base::signal_registration_base(void) {
+
+}
+
+std::shared_ptr signal_relay_registration_table::GetSignalRelay(const std::type_info& ti, size_t offset)
+{
+ std::lock_guard lk(lock);
+ auto& retVal = table[registration(&ti, offset)];
+ if (!retVal)
+ retVal = std::make_shared();
+ return retVal;
+}
+
+std::shared_ptr autowiring::internal::ObtainRelay(void* pMember)
+{
+ auto location = SlotInformationStackLocation::CurrentStackLocation();
+ if (!location || !location->Encloses(pMember))
+ // Signal is being constructed outside of an autowired member, we need to create a default relay
+ return std::make_shared();
+
+ // Signal is being constructed as a member, the registration table should know where the relay is
+ return AutoRequired()->GetSignalRelay(
+ location->stump.ti,
+ location->Offset(pMember)
+ );
+}
\ No newline at end of file
diff --git a/src/autowiring/test/AutoPacketTest.cpp b/src/autowiring/test/AutoPacketTest.cpp
index 0bc1638b3..653255518 100644
--- a/src/autowiring/test/AutoPacketTest.cpp
+++ b/src/autowiring/test/AutoPacketTest.cpp
@@ -42,4 +42,23 @@ TEST_F(AutoPacketTest, FactoryCallTest) {
);
ASSERT_TRUE(bCalled);
-}
\ No newline at end of file
+}
+
+TEST_F(AutoPacketTest, MultipleDecorateGetFailures) {
+ // Decorate with two integer types:
+ *factory += [](int& arg) { arg = 0; };
+ *factory += [](int& arg) { arg = 1; };
+
+ // Now issue the packet:
+ auto packet = factory->NewPacket();
+
+ // Any type of "Get" call made on int should fail now
+ {
+ const int* out;
+ ASSERT_ANY_THROW(packet->Get(out));
+ }
+ {
+ const std::shared_ptr* out;
+ ASSERT_ANY_THROW(packet->Get(out));
+ }
+}
diff --git a/src/autowiring/test/AutoSignalTest.cpp b/src/autowiring/test/AutoSignalTest.cpp
new file mode 100644
index 000000000..7cba85b91
--- /dev/null
+++ b/src/autowiring/test/AutoSignalTest.cpp
@@ -0,0 +1,124 @@
+// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved.
+#include "stdafx.h"
+#include
+
+class AutoSignalTest:
+ public testing::Test
+{};
+
+class RaisesASignal {
+public:
+ autowiring::signal signal;
+};
+
+TEST_F(AutoSignalTest, SimpleSignalTest) {
+ autowiring::signal signal;
+
+ bool handler_called = false;
+ int val = 0;
+
+ // Register a handler directly on the signal:
+ auto* registration =
+ signal += [&](int v) {
+ handler_called = true;
+ val = v;
+ };
+
+ // Trivially raise the signal:
+ signal(101);
+ ASSERT_TRUE(handler_called) << "Handler was not called on a stack-allocated signal";
+ ASSERT_EQ(101, val) << "Handler did not correctly copy a passed value";
+
+ // Unregister and verify we can still raise the signal:
+ signal -= registration;
+
+ handler_called = false;
+ signal(102);
+ ASSERT_FALSE(handler_called) << "Handler was called after being unregistered";
+}
+
+TEST_F(AutoSignalTest, SignalWithAutowiring) {
+ bool handler_called = false;
+ int val = 202;
+
+ {
+ Autowired ras;
+
+ // Register a signal handler:
+ ras(&RaisesASignal::signal) += [&](int v) {
+ handler_called = true;
+ val = v;
+ };
+
+ // Inject type type after the signal has been registered
+ AutoRequired();
+
+ // Now raise the signal, see what happens:
+ ras->signal(55);
+
+ // Verify that the handler got called with the correct value:
+ ASSERT_TRUE(handler_called) << "Signal handler was not invoked";
+ ASSERT_EQ(55, val) << "Signal handler not called with the correct parameter as expected";
+ }
+
+ // Raise the signal again, this should not cause anything to break:
+ Autowired ras;
+ handler_called = false;
+ ras->signal(99);
+ ASSERT_FALSE(handler_called) << "A handler was unexpectedly called after it should have been destroyed";
+}
+
+TEST_F(AutoSignalTest, MultipleSlotsTest) {
+ autowiring::signal signal;
+
+ bool handler_called1 = false;
+ bool handler_called2 = false;
+
+ auto* registration1 =
+ signal += [&] {
+ handler_called1 = true;
+ };
+
+ // Registration 2
+ signal += [&] {
+ handler_called2 = true;
+ };
+
+ // Trivially raise the signal:
+ signal();
+ ASSERT_TRUE(handler_called1) << "Handler 1 was not called on a stack-allocated signal";
+ ASSERT_TRUE(handler_called2) << "Handler 2 was not called on a stack-allocated signal";
+
+ // Unregister the first signal and reset the variables
+ signal -= registration1;
+
+ handler_called1 = false;
+ handler_called2 = false;
+
+ // Verify that registration 2 can still receive the signals
+ signal();
+ ASSERT_FALSE(handler_called1) << "Handler 1 was called after being unregistered";
+ ASSERT_TRUE(handler_called2) << "Handler 2 was inadvertently unregistered";
+}
+
+TEST_F(AutoSignalTest, RaiseASignalWithinASlotTest) {
+ autowiring::signal signal1;
+ autowiring::signal signal2;
+
+ bool handler_called1 = false;
+ bool handler_called2 = false;
+
+ signal1 += [&] {
+ handler_called1 = true;
+ signal2();
+ };
+
+ signal2 += [&] {
+ handler_called2 = true;
+ };
+
+ // Trivially raise the signal:
+ signal1();
+ ASSERT_TRUE(handler_called1) << "Handler 1 was not called on a stack-allocated signal";
+ ASSERT_TRUE(handler_called2) << "Handler 2 was not called on a stack-allocated signal";
+}
diff --git a/src/autowiring/test/AutowiringTest.cpp b/src/autowiring/test/AutowiringTest.cpp
index 48181d0b0..b12d71489 100644
--- a/src/autowiring/test/AutowiringTest.cpp
+++ b/src/autowiring/test/AutowiringTest.cpp
@@ -178,3 +178,15 @@ TEST_F(AutowiringTest, StaticNewWithArgs) {
ctxt->SignalShutdown(true);
}
}
+
+TEST_F(AutowiringTest, NullDereferenceAttempt) {
+ Autowired co;
+ ASSERT_ANY_THROW(*co) << "A dereference attempt on a CoreObject did not throw an exception as expected";
+ ASSERT_ANY_THROW(co->one) << "A dereference attempt on a CoreObject did not throw an exception as expected";
+}
+
+TEST_F(AutowiringTest, FastNullDereferenceAttempt) {
+ AutowiredFast co;
+ ASSERT_ANY_THROW(*co) << "A dereference attempt on a CoreObject did not throw an exception as expected";
+ ASSERT_ANY_THROW(co->one) << "A dereference attempt on a CoreObject did not throw an exception as expected";
+}
\ No newline at end of file
diff --git a/src/autowiring/test/CMakeLists.txt b/src/autowiring/test/CMakeLists.txt
index ef348d204..56ef33af5 100644
--- a/src/autowiring/test/CMakeLists.txt
+++ b/src/autowiring/test/CMakeLists.txt
@@ -5,11 +5,15 @@ set(AutowiringTest_SRCS
AutoConstructTest.cpp
AutoFilterCollapseRulesTest.cpp
AutoFilterDiagnosticsTest.cpp
+ AutoFilterFunctionTest.cpp
+ AutoFilterSequencing.cpp
+ AutoFilterTest.cpp
AutoInjectableTest.cpp
AutoPacketTest.cpp
AutoPacketFactoryTest.cpp
AutoParameterTest.cpp
AutoRestarterTest.cpp
+ AutoSignalTest.cpp
AutowiringTest.cpp
AutowiringUtilitiesTest.cpp
BasicThreadTest.cpp
@@ -66,14 +70,6 @@ add_windows_sources(
AutoFutureTest.cpp
)
-if(autowiring_USE_LIBCXX)
- set(AutowiringTest_SRCS ${AutowiringTest_SRCS}
- AutoFilterFunctionTest.cpp
- AutoFilterSequencing.cpp
- AutoFilterTest.cpp
- )
-endif()
-
set(AutowiringFixture_SRCS
HasForwardOnlyType.hpp
HasForwardOnlyType.cpp
diff --git a/src/autowiring/test/CoreContextTest.cpp b/src/autowiring/test/CoreContextTest.cpp
index 953d797e1..779257299 100644
--- a/src/autowiring/test/CoreContextTest.cpp
+++ b/src/autowiring/test/CoreContextTest.cpp
@@ -350,3 +350,14 @@ TEST_F(CoreContextTest, InitiateMultipleChildren) {
outerCtxt->SignalShutdown(true);
}
}
+
+class CoreContextAddTestClass {};
+
+TEST_F(CoreContextTest, CoreContextAdd) {
+ auto myClass = std::make_shared();
+ AutoCurrentContext ctxt;
+ ctxt->Add(myClass);
+
+ Autowired mc;
+ ASSERT_TRUE(mc.IsAutowired()) << "Manually registered interface was not detected as expected";
+}
\ No newline at end of file
diff --git a/version.cmake b/version.cmake
index 116908e6e..006a6503f 100644
--- a/version.cmake
+++ b/version.cmake
@@ -1 +1 @@
-set(autowiring_VERSION 0.4.4)
+set(autowiring_VERSION 0.5.0)