Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement auto_out #648

Merged
merged 1 commit into from
Jul 13, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion autowiring/AutoFilterArgument.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@ struct AutoFilterArgumentT:
auto_arg<T>::tshift
)
{}
};
};
9 changes: 6 additions & 3 deletions autowiring/AutoPacket.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#pragma once
#include "AnySharedPointer.h"
#include "at_exit.h"
#include "auto_arg.h"
#include "auto_id.h"
#include "AutoFilterArgument.h"
#include "DecorationDisposition.h"
Expand Down Expand Up @@ -167,12 +168,14 @@ class AutoPacket:

public:
/// <returns>
/// The number of distinct decoration types on this packet
/// The number of distinct decoration types on this packet (this is really an implementation-detail-based count
/// of the parameters of all relevant filters, including lambdas appended to this packet).
/// </returns>
size_t GetDecorationTypeCount(void) const;

/// <returns>
/// A copy of the decoration dispositions collection
/// A copy of the decoration dispositions collection (this is really an implementation-detail-based description
/// of the parameters of all relevant filters, including lambdas appended to this packet).
/// </returns>
/// <remarks>
/// This is a diagnostic method, users are recommended to avoid the use of this routine where possible
Expand Down Expand Up @@ -524,7 +527,7 @@ class AutoPacket:
static_assert(
!Decompose<decltype(&Fx::operator())>::template any<arg_is_out>::value,
"Cannot add an AutoFilter to a const AutoPacket if any of its arguments are output types"
);
);
*const_cast<AutoPacket*>(this) += std::forward<Fx&&>(fx);
return *this;
}
Expand Down
2 changes: 1 addition & 1 deletion autowiring/AutoTimeStamp.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ class AutoTimeStamp {
/// AutoPacketFactory.NewPacket()->Has<AutoTimeStamp::time>() == true.
/// </remarks>
void AutoFilter(auto_out<std::chrono::high_resolution_clock::time_point> t) {
t = std::chrono::high_resolution_clock::now();
*t = std::chrono::high_resolution_clock::now();
}
};
3 changes: 2 additions & 1 deletion autowiring/CallExtractor.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved.
#pragma once
#include "auto_arg.h"
#include "auto_in.h"
#include "auto_tuple.h"
#include "AutoPacket.h"
#include "CurrentContextPusher.h"
Expand Down Expand Up @@ -40,7 +41,7 @@ struct CallExtractorSetup
auto_arg<typename autowiring::nth_type<N, Args...>::type>::is_output,
bool
>::type Commit(bool) {
packet.Decorate(autowiring::get<N>(args));
auto_arg<typename autowiring::nth_type<N, Args...>::type>::Commit(packet, autowiring::get<N>(args));
return true;
}

Expand Down
25 changes: 11 additions & 14 deletions autowiring/auto_arg.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved.
#pragma once
#include "auto_id.h"
#include "auto_in.h"
#include "auto_out.h"
#include "SharedPointerSlot.h"

class AutoPacket;
template <class T> class auto_in;
class CoreContext;

/*
Expand Down Expand Up @@ -108,6 +107,8 @@ class auto_arg<T*>
}
};

namespace detail {

/// <summary>
/// Construction helper for output-by-reference decoration types
/// </summary>
Expand Down Expand Up @@ -149,6 +150,8 @@ struct auto_arg_ctor_helper<T, has_default, false> {
}
};

} // end of namespace detail

/// <summary>
/// Specialization for "T&" ~ auto_out<T>
/// </summary>
Expand Down Expand Up @@ -176,7 +179,12 @@ class auto_arg<T&>
static const int tshift = 0;

static std::shared_ptr<T> arg(AutoPacket& packet) {
return auto_arg_ctor_helper<T>::arg(packet);
return detail::auto_arg_ctor_helper<T>::arg(packet);
}

template<class C>
static void Commit (C& packet, type val) {
packet.template Decorate<T>(val);
}
};

Expand Down Expand Up @@ -217,17 +225,6 @@ class auto_arg<std::shared_ptr<T>> {
);
};

/// <summary>
/// Specialization for equivalent T auto_out<T>
/// </summary>
template<class T>
class auto_arg<auto_out<T>>:
public auto_arg<T&>
{
public:
typedef auto_out<T> arg_type;
};

/// <summary>
/// CoreContext specialization
/// </summary>
Expand Down
148 changes: 107 additions & 41 deletions autowiring/auto_out.h
Original file line number Diff line number Diff line change
@@ -1,71 +1,137 @@
// Copyright (C) 2012-2015 Leap Motion, Inc. All rights reserved.
#pragma once
#include "AutoPacket.h"
#include "autowiring_error.h"
#include MEMORY_HEADER

class AutoPacket;

template<class T>
class auto_arg;

/// <summary>
/// Declares an output of an AutoFilter method via an argument.
/// The auto_out<T> class provides a way to implement deferred-output autofilters.
/// </summary>
/// <remarks>
/// Lazy allocation is implemented, and is triggered on destruction.
/// Any attempt to access the referenced data will trigger
/// instantiation and a commitment to output the data.
/// Moved construction and assignment removes the option to output
/// from the r-value operand, but does *not* force an output.
/// An `auto_out<T>` parameter in an autofilter is an output parameter equivalent to type T. It stores a
/// std::shared_ptr to the AutoPacket from which the autofilter call was generated, and essentially just
/// waits to be assigned to, at which point it decorates the packet with the assigned value. If the auto_out<T>
/// instance (and all copies of it) is destroyed without being assigned to, then it marks T as unsatisfiable,
/// which has particular consequences (see AutoPacket::MarkUnsatisfiable).
///
/// In particular, auto_out<T> allows one to define asynchronously processed autofilters by deferring output
/// decoration until much later than the filter itself was called. For example, one could store the auto_out<T>,
/// begin an asynchronous process on a different thread, and assign to the auto_out<T> once that process completes.
///
/// The auto_out<T> class has shared_ptr style semantics (pointer dereferencing), and additionally can be assigned
/// to from various types, primary including T and its relatives (e.g. `const T&` and `T&&` and `std::shared_ptr<T>`.
/// When assigned to from a T-like typed value, the packet is decorated with that value. Assignment can only happen
/// once.
///
/// Note that `auto_out<T>` as an output parameter of an autofilter is conceptually less strict than `T&`. The former
/// does not guarantee that T will be decorated onto the packet by the time the autofilter method returns, and the
/// determination of which may require reasoning about the run-time state of the program. The latter guarantees that
/// T will be decorated onto the packet by the time the autofilter method returns. Use `T&` if possible, and only
/// use auto_out<T> when the deferred output behavior is required.
/// </remarks>
template<class T>
class auto_out
{
public:
typedef T id_type;

auto_out(const auto_out<T>& rhs):
m_value(rhs.m_value)
auto_out(void) = default;
auto_out(const auto_out& ao) = default;
auto_out(AutoPacket& packet) :
m_auto_out_impl(std::make_shared<auto_out_impl>(packet))
{}

auto_out(std::shared_ptr<T>& value) :
m_value(value)
auto_out(auto_out&& rhs) :
m_auto_out_impl(std::move(rhs.m_auto_out_impl))
{}

protected:
std::shared_ptr<T>& m_value;
struct auto_out_impl {
public:
auto_out_impl(void) = delete;
auto_out_impl(const auto_out_impl &aoi) = default;

auto_out_impl(auto_out_impl&& rhs) :
m_packet(std::move(rhs.m_packet)),
m_decoration(std::move(rhs.m_decoration))
{}

auto_out_impl(AutoPacket& packet) :
m_packet(packet.shared_from_this())
{}

~auto_out_impl(void) {
if (m_decoration)
m_packet->Decorate<T>(std::move(m_decoration));
else
m_packet->Unsatisfiable<T>();
}

private:
std::shared_ptr<AutoPacket> m_packet;
std::shared_ptr<T> m_decoration;

public:
T &operator*(void) const {
return *m_decoration;
}
T *operator->(void) const {
return m_decoration.get();
}
void operator=(const T& t) {
m_decoration = std::make_shared<T>(t);
}
void operator=(T&& t) {
m_decoration = std::make_shared<T>(std::forward<T&&>(t));
}
void operator=(const std::shared_ptr<T>& t) {
m_decoration = t;
}
};

private:
std::shared_ptr<auto_out_impl> m_auto_out_impl;

public:
/// <summary>
/// Returns the dumb pointer to the underlying output value
/// </summary>
T* get(void) const { return m_value.get(); }
// Returns true iff this auto_out object is nonempty (i.e. refers to a packet).
explicit operator bool(void) const { return bool(m_auto_out_impl); }

/// <summary>
/// Releases the internal shared pointer, preventing it from being decorated on the packet
/// Releases the internal implementatoin object
/// </summary>
/// <remarks>
/// If this auto_out is the last handle to this output instance, this method will cause
/// the attached decoration to be assigned
/// </remarks>
void reset(void) {
m_value.reset();
m_auto_out_impl.reset();
}

// Convenience operator overloads
T& operator* () { return *m_value; }
T* operator-> () { return m_value.get(); }
explicit operator bool() const { return m_value; }
operator T&(void) { return *m_value; }
operator std::shared_ptr<T>&(void) { return m_value; }
auto_out_impl& operator*(void) const { return *m_auto_out_impl; }
void operator=(auto_out&& rhs) { m_auto_out_impl = std::move(rhs.m_auto_out_impl); }
};

/// <summary>Output will be shared data provided by rhs</summary>
/// <remarks>
/// This method faciliates output of shared ObjectPool data, with
/// destructors defined accordingly.
/// </remarks>
auto_out& operator=(std::shared_ptr<T> rhs) {
std::swap(m_value, rhs);
return *this;
}
template<class T>
class auto_arg<auto_out<T>>
{
public:

typedef auto_out<T> type;
typedef auto_out<T> arg_type;
typedef auto_id<T> id_type;
static const bool is_input = false;
static const bool is_output = true;
static const bool is_shared = false;
static const bool is_multi = false;
static const int tshift = 0;

auto_out& operator=(T&& rhs) {
m_value = std::make_shared<T>(std::forward<T&&>(rhs));
return *this;
static auto_out<T> arg(AutoPacket& packet) {
return auto_out<T>(packet);
}

auto_out& operator=(const T& rhs) {
m_value = std::make_shared<T>(rhs);
return *this;
static void Commit(AutoPacket&, const type&) {
// Do nothing -- auto_out does its own deferred decoration.
}
};
5 changes: 4 additions & 1 deletion src/autowiring/AutoPacket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ void AutoPacket::AddSatCounterUnsafe(SatCounter& satCounter) {
});
switch (entry.m_state) {
case DispositionState::Complete:
satCounter.Decrement();
// Either decorations must be present, or the decoration type must be a shared_ptr.
if (!entry.m_decorations.empty() || pCur->is_shared) {
satCounter.Decrement();
}
break;
default:
break;
Expand Down
13 changes: 10 additions & 3 deletions src/autowiring/test/AutoFilterFunctionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ class AutoFilterFunctionalTest:
};

void FilterFunction(const Decoration<0>& typeIn, auto_out<Decoration<1>> typeOut) {
typeOut->i += 1 + typeIn.i;
*typeOut = Decoration<1>(1 + 1 + typeIn.i);
}

// void FilterFunction(const Decoration<0>& typeIn, Decoration<1> &typeOut) {
// typeOut = Decoration<1>(1 + 1 + typeIn.i);
// }

TEST_F(AutoFilterFunctionalTest, FunctionDecorationTest) {
// AddRecipient that is an instance of std::function f : a -> b
// This must be satisfied by decoration of type a,
Expand All @@ -27,9 +31,11 @@ TEST_F(AutoFilterFunctionalTest, FunctionDecorationTest) {
{
auto packet = factory->NewPacket();
packet->Decorate(Decoration<0>());
ASSERT_EQ(1, packet->GetDecorationTypeCount()) << "Unexpected number of decorations";
packet->AddRecipient(AutoFilterDescriptor(&FilterFunction));
const Decoration<1>* getdec;
ASSERT_TRUE(packet->Get(getdec)) << "Decoration function was not called";
ASSERT_EQ(2, packet->GetDecorationTypeCount()) << "Unexpected number of decorations";
}

//Decoration with function first
Expand All @@ -40,6 +46,7 @@ TEST_F(AutoFilterFunctionalTest, FunctionDecorationTest) {
packet->Decorate(Decoration<0>());
const Decoration<1>* getdec;
ASSERT_TRUE(packet->Get(getdec)) << "Decoration function was not called";
ASSERT_EQ(2, packet->GetDecorationTypeCount()) << "Unexpected number of decorations";
}
}

Expand All @@ -53,7 +60,7 @@ TEST_F(AutoFilterFunctionalTest, FunctionDecorationLambdaTest) {
auto sentry = std::make_shared<bool>(true);
*packet +=
[addType, sentry](const Decoration<0>& typeIn, auto_out<Decoration<1>> typeOut) {
typeOut = Decoration<1>(1 + 1 + typeIn.i);
*typeOut = Decoration<1>(1 + 1 + typeIn.i);
};

// Sentry's use count should be precisely two at this point
Expand All @@ -72,7 +79,7 @@ TEST_F(AutoFilterFunctionalTest, FunctionInjectorTest) {
auto packet = factory->NewPacket();
int addType = 1;
packet->AddRecipient([addType](auto_out<Decoration<0>> typeOut) {
typeOut = Decoration<0>(addType);
*typeOut = Decoration<0>(addType);
});
const Decoration<0>* getdec;
ASSERT_TRUE(packet->Get(getdec)) << "Decoration function was not called";
Expand Down
Loading