diff --git a/Autowiring.nuspec b/Autowiring.nuspec index e50092ac3..053839b2f 100644 --- a/Autowiring.nuspec +++ b/Autowiring.nuspec @@ -2,7 +2,7 @@ autowiring - 0.2.4 + 0.2.5 Leap Motion Leap Motion http://autowiring.io diff --git a/CMakeLists.txt b/CMakeLists.txt index 804a045c7..7d6d8671c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,11 @@ if(USE_LIBCXX) elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") message("GCC C++11") endif() + + # Also need position-independent code to make things work correctly on certain 64-bit machines + if(${CMAKE_SIZEOF_VOID_P} STREQUAL 8) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + endif() endif() set_property(GLOBAL PROPERTY USE_FOLDERS ON) diff --git a/autowiring/AnySharedPointer.h b/autowiring/AnySharedPointer.h index aea147d59..b10f730a2 100644 --- a/autowiring/AnySharedPointer.h +++ b/autowiring/AnySharedPointer.h @@ -4,13 +4,8 @@ struct AnySharedPointer { public: - AnySharedPointer(void) { - new (m_space) SharedPointerSlot; - } - - explicit AnySharedPointer(const AnySharedPointer& rhs) { - new (m_space) SharedPointerSlot(*rhs.slot()); - } + AnySharedPointer(void); + explicit AnySharedPointer(const AnySharedPointer& rhs); template explicit AnySharedPointer(const std::shared_ptr& rhs) { @@ -18,10 +13,7 @@ struct AnySharedPointer { new (m_space) SharedPointerSlotT(rhs); } - ~AnySharedPointer(void) { - // Pass control to the *real* destructor: - slot()->~SharedPointerSlot(); - } + ~AnySharedPointer(void); protected: unsigned char m_space[sizeof(SharedPointerSlot)]; @@ -115,6 +107,3 @@ template inline bool operator==(const std::shared_ptr& lhs, const AnySharedPointer& rhs) { return rhs == lhs; } - -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/C++11/boost_tuple.h b/autowiring/C++11/boost_tuple.h index eca51e63e..ab8c0e9ce 100644 --- a/autowiring/C++11/boost_tuple.h +++ b/autowiring/C++11/boost_tuple.h @@ -2,10 +2,36 @@ #pragma once #include +#include namespace std { - using boost::tuple; - using boost::get; - using boost::make_tuple; - using boost::tie; -} + template + class tuple { + public: + tuple(const Ts&... ele): + m_tuple(ele...) + {} + virtual ~tuple(void){} + + bool operator==(const tuple& other) const { + return m_tuple == other.m_tuple; + } + + bool operator<(const tuple& other) const { + return m_tuple < other.m_tuple; + } + + boost::tuple m_tuple; + }; + + template + auto get(const ::std::tuple& tup) -> decltype(boost::get(tup.m_tuple)) { + return boost::get(tup.m_tuple); + } + + template + ::std::tuple make_tuple(const Ts&... ele) { + return ::std::tuple(ele...); + } + +}//namespace std diff --git a/autowiring/C++11/type_index.h b/autowiring/C++11/type_index.h index 033684ca6..051d39fb3 100644 --- a/autowiring/C++11/type_index.h +++ b/autowiring/C++11/type_index.h @@ -11,8 +11,7 @@ class type_index { public: type_index(const type_info& info): _info(&info) - { - } + {} bool operator==(const type_index& rhs) const { return *_info == *rhs._info; @@ -22,6 +21,10 @@ class type_index { return (_info->before(*rhs._info) != 0); } + bool operator!=(const type_index& rhs) const { + return !operator==(rhs); + } + size_t hash_code() const { return (size_t)_info; } diff --git a/autowiring/CoreContext.h b/autowiring/CoreContext.h index facb8fbb7..2c3cdef97 100644 --- a/autowiring/CoreContext.h +++ b/autowiring/CoreContext.h @@ -1,7 +1,6 @@ // Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. #pragma once #include "AnySharedPointer.h" -#include "ObjectTraits.h" #include "AutoFilterDescriptor.h" #include "AutowirableSlot.h" #include "AutowiringEvents.h" @@ -12,16 +11,18 @@ #include "ContextMember.h" #include "CreationRules.h" #include "CurrentContextPusher.h" +#include "EventOutputStream.h" +#include "EventInputStream.h" +#include "EventRegistry.h" +#include "ExceptionFilter.h" #include "fast_pointer_cast.h" #include "has_autoinit.h" #include "InvokeRelay.h" -#include "result_or_default.h" #include "JunctionBoxManager.h" -#include "EventOutputStream.h" -#include "EventInputStream.h" -#include "ExceptionFilter.h" +#include "member_new_type.h" +#include "ObjectTraits.h" +#include "result_or_default.h" #include "TeardownNotifier.h" -#include "EventRegistry.h" #include "TypeRegistry.h" #include "TypeUnifier.h" @@ -146,8 +147,22 @@ class CoreContext: AnySharedPointer m_value; }; + /// + /// A proxy context member that knows how to create a factory for a particular type + /// + /// + /// This is an internal routine! Don't try to use it yourself! If you would like to + /// register yourself as a factory producing a certain type, use the static new method + /// which has one of the signatures defined factorytype + /// + template + class AutoFactory; + + template + class AutoFactoryFn; + // This is a list of concrete types, indexed by the true type of each element. - std::vector m_concreteTypes; + std::list m_concreteTypes; // This is a memoization map used to memoize any already-detected interfaces. mutable std::unordered_map m_typeMemos; @@ -177,6 +192,10 @@ class CoreContext: // Destructor does nothing; this is by design. std::weak_ptr m_outstanding; + // Creation rules are allowed to refer to private methods in this type + template + friend struct autowiring::crh; + protected: // Delayed creation routine typedef std::shared_ptr (*t_pfnCreate)( @@ -312,7 +331,7 @@ class CoreContext: /// /// Unsynchronized version of FindByType /// - void FindByTypeUnsafe(AnySharedPointer& reference) const; + MemoEntry& FindByTypeUnsafe(AnySharedPointer& reference) const; /// /// Recursive locking for Autowire satisfaction search @@ -353,6 +372,27 @@ class CoreContext: /// void UnsnoopAutoPacket(const ObjectTraits& traits); + /// + /// Registers a factory _function_, a lambda which is capable of constructing decltype(fn()) + /// + template + void RegisterFactoryFn(Fn&& fn) { + Inject::type, Fn>>(std::forward(fn)); + } + + /// + /// Registers a new foreign factory type without explicitly specifying the returned value type + /// + /// The factory type to be added + /// A reference to the factory proper + template + void RegisterFactory(Factory& obj, autowiring::member_new_type) { + RegisterFactoryFn([&obj] { return obj.New(); }); + } + + template + void RegisterFactory(const Factory&, autowiring::member_new_type) {} + public: // Accessor methods: bool IsGlobalContext(void) const { return !m_pParent; } @@ -360,6 +400,13 @@ class CoreContext: size_t GetChildCount(void) const; virtual const std::type_info& GetSigilType(void) const = 0; t_childList::iterator GetBackReference(void) const { return m_backReference; } + const std::shared_ptr& GetParentContext(void) const { return m_pParent; } + + /// + /// True if the sigil type of this CoreContext matches the specified sigil type + /// + template + bool Is(void) const { return GetSigilType() == typeid(Sigil); } /// /// The first child in the set of this context's children @@ -443,28 +490,21 @@ class CoreContext: /// template std::shared_ptr Inject(Args&&... args) { + // Creator proxy, knows how to create the type we intend to inject + typedef autowiring::CreationRules CreationRules; + // Add this type to the TypeRegistry (void) RegType::r; - - // If T doesn't inherit Object, then we need to compose a unifying type which does - typedef typename SelectTypeUnifier::type TActual; - static_assert(std::is_base_of::value, "Constructive type does not implement Object as expected"); - static_assert( - std::is_base_of::value || !has_static_new::value, - "If type T provides a static New method, then the constructed type MUST directly inherit Object" - ); // First see if the object has already been injected: - std::shared_ptr retVal; + std::shared_ptr retVal; FindByType(retVal); if(retVal) return retVal; // We must make ourselves current for the remainder of this call: CurrentContextPusher pshr(shared_from_this()); - - // Cannot safely inject while holding the lock, so we have to unlock and then inject - retVal.reset(CreationRules::New(std::forward(args)...)); + retVal.reset(CreationRules::New(*this, std::forward(args)...)); // AutoInit if sensible to do so: CallAutoInit(*retVal, has_autoinit()); @@ -481,6 +521,10 @@ class CoreContext: // Construct. FindByType(retVal); } + + // Factory registration if sensible to do so, but only after the underlying type has been + // added, so that the proper type can succeed + RegisterFactory(*retVal, autowiring::member_new_type()); return retVal; } @@ -507,12 +551,6 @@ class CoreContext: return m_junctionBoxManager->CheckEventOutputStream(typeid(T)); } - /// - /// True if the sigil type of this CoreContext matches the specified sigil type - /// - template - bool Is(void) const { return GetSigilType() == typeid(Sigil); } - /// /// Sends AutowiringEvents to build current state /// @@ -663,11 +701,6 @@ class CoreContext: /// static std::shared_ptr CurrentContext(void); - /// - /// Obtains a pointer to the parent context - /// - const std::shared_ptr& GetParentContext(void) const {return m_pParent;} - /// /// Filters std::current_exception using any registered exception filters, or rethrows. /// @@ -771,7 +804,7 @@ class CoreContext: /// Identical to Autowire, but will not register the passed slot for deferred resolution /// template - void FindByTypeRecursive(std::shared_ptr& ptr) { + void FindByTypeRecursive(std::shared_ptr& ptr) const { AnySharedPointerT slot; FindByTypeRecursive(slot, AutoSearchLambdaDefault()); ptr = slot.slot()->get(); @@ -781,7 +814,7 @@ class CoreContext: /// Identical to Autowire, but will not register the passed slot for deferred resolution /// template - void FindByTypeRecursive(AnySharedPointerT& slot) { + void FindByTypeRecursive(AnySharedPointerT& slot) const { FindByTypeRecursive(slot, AutoSearchLambdaDefault()); } @@ -957,3 +990,49 @@ template void CoreContext::AutoRequireMicroBolt(void) { Inject>(); } + +template +class CoreContext::AutoFactory +{ +public: + virtual T* operator()(CoreContext& ctxt) const = 0; +}; + +template +class CoreContext::AutoFactoryFn : + public Object, + public CoreContext::AutoFactory +{ +public: + AutoFactoryFn(Fn&& fn) : + fn(std::move(fn)) + {} + + const Fn fn; + + T* operator()(CoreContext&) const override { return fn(); } +}; + +template +class CoreContext::AutoFactoryFn, Fn> : + public Object, + CoreContext::AutoFactory... +{ +public: + AutoFactoryFn(Fn&& fn) : + fn(std::move(fn)) + {} + + const Fn fn; +}; + +template +T* autowiring::crh::New(CoreContext& ctxt, Args&&... args) { + AnySharedPointerT> af; + ctxt.FindByType(af); + if(!af) + throw autowiring_error("Attempted to AutoRequire an interface, but failed to find a factory for this interface in the current context"); + + // Standard factory invocation: + return (*af)(ctxt); +} diff --git a/autowiring/CreationRules.h b/autowiring/CreationRules.h index 9146a9e9a..3ec81c43d 100644 --- a/autowiring/CreationRules.h +++ b/autowiring/CreationRules.h @@ -15,69 +15,136 @@ struct is_injectable static const bool value = has_simple_constructor::value || has_static_new::value; }; +namespace autowiring { +enum class construction_strategy { + // Just use new + standard, + + // Use the static new defined by the type + factory_new, + + // Type is abstract, use an enclosing factory context + foreign_factory +}; + +template +struct select_strategy { + static const construction_strategy value = + // If a factory new is defined, then use it + has_static_new::value ? + construction_strategy::factory_new : + + // If the type is abstract, then we will try to find a foreign factory + std::is_abstract::value ? + construction_strategy::foreign_factory : + + // Otherwise we give up and just try to compose the type directly + construction_strategy::standard; +}; + +template +struct strategy_impl {}; + +template +struct alloc_fn {}; + +template +static U* Allocate(alloc_fn<&U::operator new>*) { + return (U*) U::operator new(sizeof(U)); +} + +template +static U* Allocate(...) { + return (U*) ::operator new(sizeof(U)); +} + +template +struct free_fn {}; + +template +static void Free(void* ptr, free_fn<&U::operator delete>*) { + U::operator delete(ptr); +} + +template +static void Free(void* ptr, ...) { + ::operator delete(ptr); +} + /// -/// Simple structure to centralize knowledge about how to create types with various declarations +/// Creation rules helper type, used to select one of the creation strategies based on an arglist /// -namespace CreationRules { - template - struct alloc_fn {}; - - template - U* Allocate(alloc_fn<&U::operator new>*) { - return (U*) U::operator new(sizeof(U)); - } - - template - U* Allocate(...) { - return (U*) ::operator new(sizeof(U)); - } - - template - struct free_fn {}; - - template - void Free(void* ptr, free_fn<&U::operator delete>*) { - U::operator delete(ptr); - } - - template - void Free(void* ptr, ...) { - ::operator delete(ptr); - } +template +struct crh; + +template +struct crh +{ + // Actual constructed type is directly the specified type + typedef T TActual; - template - typename std::enable_if::value, U*>::type New(Args&&... args) { - auto retVal = U::New(std::forward(args)...); + static_assert( + std::is_base_of::value || !has_static_new::value, + "If type T provides a static New method, then the constructed type MUST directly inherit Object" + ); + + static T* New(const CoreContext&, Args&&... args) { + auto* retVal = T::New(std::forward(args)...); static_assert( - std::is_convertible::value, + std::is_convertible::value, "Attempted to create T using T::New, but the type of T::New() is not derived from T" ); return retVal; } +}; + +template +struct crh { + // If T doesn't inherit Object, then we need to compose a unifying type which does + typedef typename SelectTypeUnifier::type TActual; - template - typename std::enable_if::value, U*>::type New(Args&&... args) { - static_assert(!std::is_abstract::value, "Cannot create a type which is abstract"); - static_assert(!has_static_new::value, "Can't inject member with arguments if it has a static New"); + static_assert(!std::is_abstract::value, "Cannot create a type which is abstract"); + static_assert(!has_static_new::value, "Can't inject member with arguments if it has a static New"); + static TActual* New(const CoreContext&, Args&&... args) { // Allocate slot first before registration - auto* pSpace = Allocate(nullptr); + auto* pSpace = Allocate(nullptr); 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(U) + sizeof(TActual) ); // And now we create our space - return ::new (pSpace) U(std::forward(args)...); + return ::new (pSpace) TActual(std::forward(args)...); } catch(...) { // Don't want memory leaks--but we also want to avoid calling the destructor, here, so we cast to void before freeing - Free(pSpace, nullptr); + Free(pSpace, nullptr); throw; } } -} +}; + +template +struct crh { + typedef T TActual; + + // The definition of this method has to be provided in CoreContext.h because it actually makes use + // of the CoreContext argument + static T* New(CoreContext& ctxt, Args&&... args); +}; + +/// +/// Simple structure to centralize knowledge about how to create types with various declarations +/// +template +struct CreationRules : + crh::value, T, Args...> +{ +}; + +} \ No newline at end of file diff --git a/autowiring/GlobalCoreContext.h b/autowiring/GlobalCoreContext.h index 1292dbb3f..ff6de95c3 100644 --- a/autowiring/GlobalCoreContext.h +++ b/autowiring/GlobalCoreContext.h @@ -48,23 +48,12 @@ class GlobalCoreContext: /// thread holds a reference to the current global context via SetCurrent, it is /// the caller's responsibility to manually release that reference via EvictCurrent. /// - static void Release(void) { - // Release local: - std::lock_guard lk(getInitLock()); - getGlobalContextSharedPtr().reset(); - } + static void Release(void); private: // Global context shared pointer and lock: - static inline std::mutex& getInitLock() { - static std::mutex s_initLock; - return s_initLock; - } - - static inline std::shared_ptr& getGlobalContextSharedPtr() { - static std::shared_ptr s_globalContextSharedPtr; - return s_globalContextSharedPtr; - } + static std::mutex& getInitLock(); + static std::shared_ptr& getGlobalContextSharedPtr(); }; /// diff --git a/autowiring/InvokeRelay.h b/autowiring/InvokeRelay.h index 82f68835a..4f3673a8d 100644 --- a/autowiring/InvokeRelay.h +++ b/autowiring/InvokeRelay.h @@ -3,6 +3,7 @@ #include "is_any.h" #include "index_tuple.h" #include "JunctionBox.h" +#include STL_TUPLE_HEADER class Deferred; diff --git a/autowiring/ObjectTraits.h b/autowiring/ObjectTraits.h index 2ada06652..f49945fe6 100644 --- a/autowiring/ObjectTraits.h +++ b/autowiring/ObjectTraits.h @@ -1,33 +1,31 @@ // Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. #pragma once -#include -#include MEMORY_HEADER - -#include "fast_pointer_cast.h" #include "AnySharedPointer.h" #include "AutoFilterDescriptor.h" - #include "AutowiringEvents.h" -#include "Object.h" +#include "BoltBase.h" #include "ContextMember.h" #include "CoreRunnable.h" #include "BasicThread.h" #include "ExceptionFilter.h" -#include "BoltBase.h" - #include "EventRegistry.h" +#include "fast_pointer_cast.h" +#include "Object.h" + +#include +#include MEMORY_HEADER /// /// Mapping and extraction structure used to provide a runtime version of an Object-implementing shared pointer /// struct ObjectTraits { - template - ObjectTraits(const std::shared_ptr::type>& value, T*) : + template + ObjectTraits(const std::shared_ptr& value, T*) : type(typeid(T)), + actual_type(typeid(*value)), stump(SlotInformationStump::s_stump), value(value), - cast_offset(this->value->cast_offset()), subscriber(MakeAutoFilterDescriptor(value)), pObject(autowiring::fast_pointer_cast(value)), pContextMember(autowiring::fast_pointer_cast(value)), @@ -46,14 +44,16 @@ struct ObjectTraits { return !!dynamic_cast(pObject.get()); }() ) - { - if(!pObject) - throw autowiring_error("Cannot add a type which does not implement Object"); - } + {} - // The declared original type: + // The type of the passed pointer const std::type_info& type; + // The "actual type" used by Autowiring. This type may differ from ObjectTraits::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; + /// /// Used to obtain a list of slots defined on this type, for reflection purposes /// @@ -88,9 +88,6 @@ struct ObjectTraits { // correctly on the right-hand side of our map const AnySharedPointer value; - // Offset of the void pointer to the embedded Object - const size_t cast_offset; - // The packet subscriber introduction method, if appropriate: const AutoFilterDescriptor subscriber; diff --git a/autowiring/SharedPointerSlot.h b/autowiring/SharedPointerSlot.h index 3ddf8d532..72d96b6b2 100644 --- a/autowiring/SharedPointerSlot.h +++ b/autowiring/SharedPointerSlot.h @@ -134,16 +134,6 @@ struct SharedPointerSlot { /// virtual void reset(void) {} - /// - /// Utility service method, used to perform a semidynamic cast - /// - /// - /// The number of bytes that must be added to the return value of this->ptr() in order to obtain an Object* - /// - virtual int cast_offset(void) const { - return 0; - } - /// /// Attempts to coerce this type to the specified type /// @@ -231,24 +221,6 @@ struct SharedPointerSlot { } }; -struct SharedPointerSlotTOffsetBase { - static int cast_offset() { - throw std::bad_cast(); - } -}; - -template::value> -struct SharedPointerSlotTOffset { - static int cast_offset() { - return static_cast(reinterpret_cast( - static_cast(reinterpret_cast(1)) - )) - 1; - } -}; - -template -struct SharedPointerSlotTOffset : SharedPointerSlotTOffsetBase {}; - template struct SharedPointerSlotT: SharedPointerSlot @@ -293,11 +265,6 @@ struct SharedPointerSlotT: return autowiring::fast_pointer_cast(get()); } - int cast_offset(void) const override { - // Should be safe, unless we expect sizeof(T) > 2^32 - return SharedPointerSlotTOffset::cast_offset(); - } - virtual void* ptr(void) override { // At this level, because this type is runtime, we can only provide runtime detection of misuse if(std::is_const::value) diff --git a/autowiring/demangle.h b/autowiring/demangle.h index b4cfe4c1c..aeb071f6c 100644 --- a/autowiring/demangle.h +++ b/autowiring/demangle.h @@ -14,31 +14,11 @@ // Just returns type_info.name() on windows // namespace autowiring { -#if __GNUG__ // Mac and linux - - static inline std::string demangle(const std::type_info& ti) { - int status; - std::unique_ptr res{ - abi::__cxa_demangle(ti.name(), nullptr, nullptr, &status), - std::free - }; - return std::string(status == 0 ? res.get() : ti.name()); - } - -#else // Windows - - static inline std::string demangle(const std::type_info& ti) { - return std::string(ti.name()); - } - -#endif - - static inline std::string demangle(const std::type_info* ti) { - return demangle(*ti); - } + std::string demangle(const std::type_info& ti); + std::string demangle(const std::type_info* ti); template - static inline std::string demangle(const T&) { + std::string demangle(const T&) { return demangle(typeid(T)); } }//namespace autowiring diff --git a/autowiring/hash_tuple.h b/autowiring/hash_tuple.h index 36beffe78..03e82f3bb 100644 --- a/autowiring/hash_tuple.h +++ b/autowiring/hash_tuple.h @@ -1,37 +1,38 @@ // Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. #pragma once #include STL_TUPLE_HEADER +#include "index_tuple.h" namespace std { - template + template struct hash> { - static uint64_t hash_combine(const size_t& lhs, const size_t& rhs) { - //return lhs ^ rhs; - //static const size_t salt = 0x9e3779b9; + + template + static uint64_t hash_combine(const T& head, const Ts&... tail) { + // Get hash for head and tail + uint64_t head_hash = std::hash()(head); + uint64_t tail_hash = hash_combine(tail...); + + // Add a pinch of salt and stir static const uint64_t salt = 0x278dde6e5fd29e00; - return rhs ^ (salt + (lhs << 6) + (lhs >> 2)); - // Adding pseudo-random bits reduces collisions between consecutive values - // 0 < 1/phi < 1 so the denominator includes a shift. - // phi = (1 + sqrt(5)) / 2 - // 2^32 / phi = 0x9e3779b9 - // 2^64 / phi = 0x278dde6e5fd29e00 + return head_hash ^ (salt + (tail_hash << 6) + (tail_hash >> 2)); + } + + // base case to hash std::tuple<> + static uint64_t hash_combine(void) { + return 0; } - // Base: recursion from empty tail - template - static size_t hash_tuple(const std::tuple& value) { return 0; } - - // General: combine hash with recursion on tail - template - static size_t hash_tuple(const std::tuple& value) { - static const int I = N - sizeof...(Tail) - 1; - return (size_t)hash_combine(std::hash()(std::get(value)), hash_tuple(value)); + // Hash each element in the tuple and mix them together + template + static size_t hash_tuple(index_tuple, const std::tuple& value) { + return static_cast(hash_combine(std::get(value)...)); } public: size_t operator()(const std::tuple& value) const { - return hash_tuple(value); + return hash_tuple(typename make_index_tuple::type(), value); } }; } diff --git a/autowiring/member_new_type.h b/autowiring/member_new_type.h new file mode 100644 index 000000000..1a5b9ac09 --- /dev/null +++ b/autowiring/member_new_type.h @@ -0,0 +1,100 @@ +// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. +#pragma once +#include "Decompose.h" + +namespace autowiring { + +enum class factorytype { + // No factory method in this class + none, + + // IType* New(void); + // std::tuple New(void); + ret_val, + + // void New(IType1*&, IType2*&); + multi_byref +}; + +/// +/// Decomposes the member function type into a specific supported return type +/// +template +struct sel_valuetype { + static const factorytype value = factorytype::none; +}; + +template +struct sel_valuetype { + static const factorytype value = factorytype::ret_val; +}; + +template +struct sel_valuetype { + static const factorytype value = factorytype::ret_val; +}; + +template +struct sel_valuetype(U::*)()> { + static const factorytype value = factorytype::ret_val; +}; + +template +struct sel_valuetype(U::*)() const> { + static const factorytype value = factorytype::ret_val; +}; + +template +struct sel_valuetype { + static const factorytype value = factorytype::multi_byref; +}; + +template +struct sel_valuetype { + static const factorytype value = factorytype::multi_byref; +}; + +template +struct factorytype_const { + static const factorytype value = ft; +}; + +template +struct member_new_type_helper { + template + static factorytype_const select(...); + + template + static factorytype_const::value> select(void*); + + // Conveninece typedef used externally: + typedef decltype(select(nullptr)) factory_value_type; + + // Evaluates to true only if T includes a unique AutoFilter method with at least one argument. + static const factorytype value = factory_value_type::value; +}; + +/// +/// Detects whether the specified type T has a nonstatic factory New method +/// +/// +/// An auto-initializer routine is called after a shared pointer assignment has taken place, but before +/// an entity is injected into the context. This affords a context member an opportunity to obtain and, +/// optionally , distribute its own shared pointer before that entity becomes discoverable in that +/// context. +/// +/// Note that this call is actually made before the type is actually injected in the context, so an +/// attempt to perform Autowired from AutoInit will necessarily fail. +/// +template::value> +struct member_new_type +{ + static const factorytype factory = ft; +}; + +template +struct member_new_type { + static const factorytype factory = factorytype::ret_val; +}; + +} \ No newline at end of file diff --git a/src/autonet/CMakeLists.txt b/src/autonet/CMakeLists.txt index 011068d93..aad2c66e1 100644 --- a/src/autonet/CMakeLists.txt +++ b/src/autonet/CMakeLists.txt @@ -1,4 +1,3 @@ -set(Boost_USE_STATIC_LIBS ON) find_package(Boost COMPONENTS system QUIET) if(NOT Boost_FOUND) message("Cannot build Autonet, boost not installed on this system") @@ -36,6 +35,7 @@ ADD_MSVC_PRECOMPILED_HEADER("stdafx.h" "stdafx.cpp" AutoNet_SRCS) add_library(AutoNet SHARED ${AutoNet_SRCS}) target_link_libraries(AutoNet Autowiring ${Boost_LIBRARIES}) set_target_properties(AutoNet PROPERTIES INTERFACE_LINK_LIBRARIES Autowiring) + set_property(TARGET AutoNet PROPERTY FOLDER "Autowiring") # diff --git a/src/autowiring/AnySharedPointer.cpp b/src/autowiring/AnySharedPointer.cpp new file mode 100644 index 000000000..75264a572 --- /dev/null +++ b/src/autowiring/AnySharedPointer.cpp @@ -0,0 +1,19 @@ +// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. +#include "stdafx.h" +#include "AnySharedPointer.h" + +AnySharedPointer::AnySharedPointer(void) { + new (m_space) SharedPointerSlot; +} + +AnySharedPointer::AnySharedPointer(const AnySharedPointer& rhs) { + new (m_space) SharedPointerSlot(*rhs.slot()); +} + +AnySharedPointer::~AnySharedPointer(void) { + // Pass control to the *real* destructor: + slot()->~SharedPointerSlot(); +} + +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/src/autowiring/CMakeLists.txt b/src/autowiring/CMakeLists.txt index ef69817e3..95ef04300 100644 --- a/src/autowiring/CMakeLists.txt +++ b/src/autowiring/CMakeLists.txt @@ -16,6 +16,7 @@ set(Autowiring_SRCS auto_arg.h auto_arg.cpp AnySharedPointer.h + AnySharedPointer.cpp AutoCheckout.h AutoRestarter.h AutoFuture.h @@ -80,8 +81,8 @@ set(Autowiring_SRCS DecorationDisposition.h Deserialize.h Deferred.h - DefaultAutoNetServer.cpp demangle.h + demangle.cpp DispatchQueue.h DispatchQueue.cpp DispatchThunk.h @@ -112,7 +113,7 @@ set(Autowiring_SRCS InterlockedExchange.h InvokeRelay.h atomic_object.h - unlock_object.h + member_new_type.h Object.h Object.cpp ObjectPool.h @@ -124,7 +125,6 @@ set(Autowiring_SRCS MicroAutoFilter.h MicroBolt.h NewAutoFilter.h - NewAutoFilter.cpp SharedPointerSlot.h SlotInformation.h SlotInformation.cpp @@ -137,10 +137,15 @@ set(Autowiring_SRCS TypeRegistry.h TypeRegistry.cpp TypeUnifier.h + unlock_object.h uuid.h var_logic.h ) +if(NOT AUTOWIRING_BUILD_AUTONET) + list(APPEND Autowiring_SRCS DefaultAutoNetServer.cpp) +endif() + if(NOT APPLE) # avoid warning: has no symbols set(Autowiring_SRCS @@ -195,7 +200,15 @@ ADD_MSVC_PRECOMPILED_HEADER("stdafx.h" "stdafx.cpp" Autowiring_SRCS) # add_library(Autowiring STATIC ${Autowiring_SRCS}) -set_target_properties(Autowiring PROPERTIES COMPILE_FLAGS "-fvisibility=hidden") + +# On Unix, 64-bit code will need to be compiled as position-independent code +if(UNIX) + if(${CMAKE_SIZEOF_VOID_P} STREQUAL 8) + set_target_properties(Autowiring PROPERTIES COMPILE_FLAGS "-fPIC -fvisibility=hidden") + else() + set_target_properties(Autowiring PROPERTIES COMPILE_FLAGS "-fvisibility=hidden") + endif() +endif() target_include_directories(Autowiring INTERFACE "$" diff --git a/src/autowiring/CoreContext.cpp b/src/autowiring/CoreContext.cpp index 26edb9734..935b90ea2 100644 --- a/src/autowiring/CoreContext.cpp +++ b/src/autowiring/CoreContext.cpp @@ -192,12 +192,12 @@ void CoreContext::AddInternal(const ObjectTraits& traits) { { std::unique_lock lk(m_stateBlock->m_lock); - // Validate that this addition does not generate an ambiguity. We need to use the proper type of - // pObject, rather than the type passed in via traits.type, because the proper type might be a + // Validate that this addition does not generate an ambiguity. We need to use the actual type of + // the value, rather than the type passed in via traits.type, because the proper type might be a // 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(typeid(*traits.pObject)); + auto q = m_typeMemos.find(traits.actual_type); if(q != m_typeMemos.end()) { auto& v = q->second; if(*v.m_value == traits.pObject) @@ -262,7 +262,7 @@ void CoreContext::FindByType(AnySharedPointer& reference) const { FindByTypeUnsafe(reference); } -void CoreContext::FindByTypeUnsafe(AnySharedPointer& reference) const { +CoreContext::MemoEntry& CoreContext::FindByTypeUnsafe(AnySharedPointer& reference) const { const std::type_info& type = reference->type(); // If we've attempted to search for this type before, we will return the value of the memo immediately: @@ -270,7 +270,7 @@ void CoreContext::FindByTypeUnsafe(AnySharedPointer& reference) const { if(q != m_typeMemos.end()) { // We can copy over and return here reference = q->second.m_value; - return; + return q->second; } // Resolve based on iterated dynamic casts for each concrete type: @@ -289,7 +289,9 @@ void CoreContext::FindByTypeUnsafe(AnySharedPointer& reference) const { } // This entry was not formerly memoized. Memoize unconditionally. - m_typeMemos[type].m_value = reference; + MemoEntry& retVal = m_typeMemos[type]; + retVal.m_value = reference; + return retVal; } void CoreContext::FindByTypeRecursive(AnySharedPointer& reference, const AutoSearchLambda& searchFn) const { diff --git a/src/autowiring/DefaultAutoNetServer.cpp b/src/autowiring/DefaultAutoNetServer.cpp index 89c24e6b1..0e340191d 100644 --- a/src/autowiring/DefaultAutoNetServer.cpp +++ b/src/autowiring/DefaultAutoNetServer.cpp @@ -16,8 +16,6 @@ class DefaultAutoNetServer: void HandleResumeFromBreakpoint(std::string name) {} }; -#if !AUTOWIRING_BUILD_AUTONET AutoNetServer* NewAutoNetServerImpl(void) { return new DefaultAutoNetServer; } -#endif diff --git a/src/autowiring/GlobalCoreContext.cpp b/src/autowiring/GlobalCoreContext.cpp index 6e61b4a29..3afda5763 100644 --- a/src/autowiring/GlobalCoreContext.cpp +++ b/src/autowiring/GlobalCoreContext.cpp @@ -41,6 +41,22 @@ std::shared_ptr GlobalCoreContext::Get() { return ptr; } +void GlobalCoreContext::Release(void) { + // Release local: + std::lock_guard lk(getInitLock()); + getGlobalContextSharedPtr().reset(); +} + +std::mutex& GlobalCoreContext::getInitLock() { + static std::mutex s_initLock; + return s_initLock; +} + +std::shared_ptr& GlobalCoreContext::getGlobalContextSharedPtr() { + static std::shared_ptr s_globalContextSharedPtr; + return s_globalContextSharedPtr; +} + std::shared_ptr GetGlobalContext(void) { return GlobalCoreContext::Get(); } diff --git a/src/autowiring/demangle.cpp b/src/autowiring/demangle.cpp new file mode 100644 index 000000000..db43ed548 --- /dev/null +++ b/src/autowiring/demangle.cpp @@ -0,0 +1,26 @@ +// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. +#include "stdafx.h" +#include "demangle.h" + +#if __GNUG__ // Mac and linux + +std::string autowiring::demangle(const std::type_info& ti) { + int status; + std::unique_ptr res{ + abi::__cxa_demangle(ti.name(), nullptr, nullptr, &status), + std::free + }; + return std::string(status == 0 ? res.get() : ti.name()); +} + +#else // Windows + +std::string autowiring::demangle(const std::type_info& ti) { + return std::string(ti.name()); +} + +#endif + +std::string autowiring::demangle(const std::type_info* ti) { + return autowiring::demangle(*ti); +} diff --git a/src/autowiring/test/AnySharedPointerTest.cpp b/src/autowiring/test/AnySharedPointerTest.cpp index 1e0c2816f..d0e292827 100644 --- a/src/autowiring/test/AnySharedPointerTest.cpp +++ b/src/autowiring/test/AnySharedPointerTest.cpp @@ -104,7 +104,7 @@ TEST_F(AnySharedPointerTest, SlotsInVector) { auto sharedPtr = std::make_shared(); { - std::vector slots; + std::list slots; // Initialize with a lot of copies of sharedPtr for(size_t i = 0; i < 10; i++) { @@ -191,50 +191,3 @@ TEST_F(AnySharedPointerTest, VoidReturnExpected) { // Validate equivalence of the void operator: ASSERT_EQ(v.get(), slot->ptr()) << "Shared pointer slot did not return a void* with an expected value"; } - -struct WiredType{}; -struct RequiredType{}; -struct WiredFastType{}; -struct CombinedWireType{ - Autowired wt; - AutoRequired rt; - AutowiredFast wft; -}; - -#pragma pack(1) -class ConfoundingBaseClass { -public: - virtual ~ConfoundingBaseClass(void) {} - - int padding; -}; - -class MyKnownBaseObj: - public ConfoundingBaseClass, - public Object -{ -}; -#pragma pack() - -TEST_F(AnySharedPointerTest, CorrectBaseOffsetReporting) { - auto myObj = std::make_shared(); - - // Perform standard assignment - AnySharedPointer ptr(myObj); - - // Verify that no unexpected type collapse has taken place. We really do need the address of Object* to be different - // from the address of the most derived type, because if the offsets are the same then we are just testing against zero, - // and it's possible that this could yield a higher false positive rate - ConfoundingBaseClass* pConfound = myObj.get(); - Object* pObj = myObj.get(); - ASSERT_NE((void*) pConfound, (void*) pObj) << "Objects were expected to have different offsets, test cannot proceed"; - ASSERT_NE((void*) myObj.get(), (void*) pObj) << "Object offset was coincident with the base type, this test will not produce meaningful results"; - - // An in-place multiple cast shouldn't result in aliasing either: - Object* pCastedObject = static_cast(reinterpret_cast(1)); - ASSERT_NE(1, static_cast(reinterpret_cast(pCastedObject))) << "In-place cast of a dummy pointer was degenerate"; - - // Now verify that everything is packed as we expect - size_t realOffset = reinterpret_cast(pObj) - reinterpret_cast(myObj.get()); - ASSERT_EQ(realOffset, ptr->cast_offset()) << "Offset to the base type was not correctly computed"; -} \ No newline at end of file diff --git a/src/autowiring/test/CMakeLists.txt b/src/autowiring/test/CMakeLists.txt index dcfc52953..9ca30e077 100644 --- a/src/autowiring/test/CMakeLists.txt +++ b/src/autowiring/test/CMakeLists.txt @@ -2,10 +2,6 @@ set(AutowiringTest_SRCS AnySharedPointerTest.cpp ArgumentTypeTest.cpp AutoConstructTest.cpp - AutoFilterFunctionTest.cpp - AutoFilterPipeTest.cpp - AutoFilterSequencing.cpp - AutoFilterTest.cpp AutoInjectableTest.cpp AutoPacketFactoryTest.cpp AutoRestarterTest.cpp @@ -32,6 +28,7 @@ set(AutowiringTest_SRCS EventReceiverTest.cpp EventRegistryTest.cpp FactoryTest.cpp + ForeignTest.cpp GlobalInitTest.hpp GlobalInitTest.cpp GuardObjectTest.cpp @@ -58,6 +55,15 @@ set(AutowiringTest_SRCS UuidTest.cpp ) +if(USE_LIBCXX) + set(AutowiringTest_SRCS ${AutowiringTest_SRCS} + AutoFilterFunctionTest.cpp + AutoFilterPipeTest.cpp + AutoFilterSequencing.cpp + AutoFilterTest.cpp + ) +endif() + set(AutowiringFixture_SRCS SelfSelectingFixture.hpp SelfSelectingFixture.cpp diff --git a/src/autowiring/test/ForeignTest.cpp b/src/autowiring/test/ForeignTest.cpp new file mode 100644 index 000000000..ac16b45a7 --- /dev/null +++ b/src/autowiring/test/ForeignTest.cpp @@ -0,0 +1,83 @@ +// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved. +#include "stdafx.h" +#include + +class ForeignType { +public: + typedef void base; + virtual ~ForeignType(void) {} + + virtual void foo(void) = 0; +}; + +class ForeignTypeImpl: + public ForeignType +{ + typedef ForeignType base; + + virtual void foo(void) {} +}; + +class ForeignFactoryA { +public: + ForeignType* New(void) { + return new ForeignTypeImpl; + } +}; + +class ForeignFactoryB { +public: + std::tuple New(void) { + ForeignTypeImpl* rv; + return std::tuple(rv, rv); + } +}; + +class ForeignFactoryC { +public: + void New(ForeignType*& rv) { + rv = new ForeignTypeImpl; + } +}; + +// These include statements placed here intentionally to ensure that the above test fixture classes +// do not come to rely on anything actually defined in autowiring. +#include +#include + +using namespace autowiring; + +static_assert(member_new_type::factory == factorytype::ret_val, "Single return member return type not correctly extracted"); +static_assert(member_new_type::factory == factorytype::ret_val, "Multi-ret member return type not correctly extracted"); +static_assert(member_new_type::factory == factorytype::multi_byref, "Multi-ret byref return member return type not correctly extracted"); + +class ForeignTest: + public testing::Test +{}; + + + +TEST_F(ForeignTest, ForeignFactoryATest) { + // Inject the factory, this should cause it to be registered as a factory + AutoRequired factory; + + // Now try to use the factory to construct our unrelated type. + // Formerly, attempting to AutoRequire an interface would fail; this succeeds because we + // now permit a factory to satisfy the request at runtime rather than requiring definite + // knowledge on how to perform the satisfaction at compile time. + AutoRequired ft; + ASSERT_TRUE(ft.IsAutowired()) << "Factory-constructed type was not autowired as expected"; +} + +// Not currently supported! Maybe someday if someone needs it +TEST_F(ForeignTest, DISABLED_ForeignFactoryBTest) { + AutoRequired factory; + + // Now try to use the factory to construct our unrelated type, as before + AutoRequired ft; + ASSERT_TRUE(ft.IsAutowired()) << "Factory-constructed type was not autowired as expected"; + + // Now see if we can also obtain the implementation pointer this time, because of the tuple-style return + Autowired impl; + ASSERT_TRUE(impl.IsAutowired()) << "Tuple-returning factory's interfaces were not all registered by the containing CoreContext"; +} \ No newline at end of file diff --git a/version.cmake b/version.cmake index d5cdd45a6..ee7ab369e 100644 --- a/version.cmake +++ b/version.cmake @@ -1 +1 @@ -set(autowiring_VERSION 0.2.4) +set(autowiring_VERSION 0.2.5)