Skip to content

Commit

Permalink
Merge pull request #82 from leapmotion/feature-autoinit
Browse files Browse the repository at this point in the history
AutoInit post-construction callback
  • Loading branch information
gtremper committed Aug 25, 2014
2 parents 98a81c2 + 674a600 commit aad15dd
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 1 deletion.
4 changes: 4 additions & 0 deletions autowiring/CoreContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "CreationRules.h"
#include "CurrentContextPusher.h"
#include "fast_pointer_cast.h"
#include "has_autoinit.h"
#include "InvokeRelay.h"
#include "result_or_default.h"
#include "JunctionBoxManager.h"
Expand Down Expand Up @@ -556,6 +557,9 @@ class CoreContext:
// Cannot safely inject while holding the lock, so we have to unlock and then inject
retVal.reset(CreationRules::New<TActual>(std::forward<Args>(args)...));

// AutoInit if sensible to do so:
CallAutoInit(*retVal, has_autoinit<T>());

try {
// Pass control to the insertion routine, which will handle injection from this point:
AddInternal(AddInternalTraits(retVal, (T*)nullptr));
Expand Down
45 changes: 45 additions & 0 deletions autowiring/has_autoinit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (C) 2012-2014 Leap Motion, Inc. All rights reserved.
#pragma once
#include "Decompose.h"

template<class T>
struct has_valid_autoinit {
template<class Fn, Fn>
struct unnamed_constant;

template<class U>
static std::false_type select(...);

template<class U>
static std::true_type select(unnamed_constant<decltype(&U::AutoInit), &U::AutoInit>*);

// Conveninece typedef used externally:
typedef decltype(select<T>(nullptr)) has_valid;

// Evaluates to true only if T includes a unique AutoFilter method with at least one argument.
static const bool value = has_valid::value;
};

/// <summary>
/// Detects whether the specified type T has a method with the name AutoInit
/// </summary>
/// <remarks>
/// 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<T> from AutoInit will necessarily fail.
/// </remarks>
template<class T>
struct has_autoinit : has_valid_autoinit<T>::has_valid {};

template<class T>
static void CallAutoInit(T& obj, std::true_type) {
obj.AutoInit();
}

template<class T>
static void CallAutoInit(T&, std::false_type) {}

1 change: 1 addition & 0 deletions src/autowiring/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ set(Autowiring_SRCS
ExceptionFilter.h
GlobalCoreContext.cpp
GlobalCoreContext.h
has_autoinit.h
has_autofilter.h
has_simple_constructor.h
has_static_new.h
Expand Down
2 changes: 1 addition & 1 deletion src/autowiring/test/AutoConstructTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@ TEST_F(AutoConstructTest, CanConstructRvalueCtor) {
}

ASSERT_TRUE(originalPtr.unique()) << "Memory leak detected due to incorrect forwarding of a unique pointer";
}
}
40 changes: 40 additions & 0 deletions src/autowiring/test/PostConstructTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,3 +354,43 @@ TEST_F(PostConstructTest, RecursiveNotificationPostConstruction) {
ASSERT_TRUE(called.unique()) << "Autowiring notification lambda was not properly cleaned up";
}

struct ClassWithAutoInit:
std::enable_shared_from_this<ClassWithAutoInit>
{
ClassWithAutoInit(void) :
m_constructed(true),
m_postConstructed(false)
{}

void AutoInit(void) {
ASSERT_TRUE(m_constructed) << "A postconstruct routine was called BEFORE the corresponding constructor";
m_postConstructed = true;

auto myself = shared_from_this();
ASSERT_EQ(this, myself.get()) << "Reflexive shared_from_this did not return a shared pointer to this as expected";
}

bool m_constructed;
bool m_postConstructed;
};

static_assert(has_autoinit<ClassWithAutoInit>::value, "AutoInit-bearing class did not pass a static type check");

TEST_F(PostConstructTest, PostConstructGetsCalled) {
AutoRequired<ClassWithAutoInit> cwai;
ASSERT_TRUE(cwai->m_constructed) << "Trivial constructor call was not made as expected";
ASSERT_TRUE(cwai->m_postConstructed) << "Auto-initialization routine was not called on an initializable type";
}

struct PostConstructThrowsException {
void AutoInit(void) const {
throw std::runtime_error("Autoinit crashing for no reason");
}
};

TEST_F(PostConstructTest, PostConstructCanSafelyThrow) {
ASSERT_ANY_THROW(AutoRequired<PostConstructThrowsException>()) << "AutoInit call threw an exception, but it was incorrectly eaten by Autowiring";

Autowired<PostConstructThrowsException> pcte;
ASSERT_FALSE(pcte.IsAutowired()) << "A context member which threw an exception post-construction was incorrectly introduced into a context";
}

0 comments on commit aad15dd

Please sign in to comment.