Skip to content

Commit

Permalink
Merge pull request #730 from leapmotion/feature-apcall
Browse files Browse the repository at this point in the history
Implement AutoPacket::Call
  • Loading branch information
hham committed Aug 14, 2015
2 parents cd69545 + 09eb745 commit 7479857
Show file tree
Hide file tree
Showing 4 changed files with 243 additions and 7 deletions.
124 changes: 124 additions & 0 deletions autowiring/AutoPacket.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
#include "at_exit.h"
#include "auto_arg.h"
#include "auto_id.h"
#include "auto_tuple.h"
#include "AutoFilterArgument.h"
#include "Decompose.h"
#include "DecorationDisposition.h"
#include "demangle.h"
#include "is_any.h"
#include "index_tuple.h"
#include "is_shared_ptr.h"
#include "noop.h"
#include "TeardownNotifier.h"
Expand All @@ -29,6 +32,9 @@ template<class MemFn>
struct Decompose;

namespace autowiring {
template<class MemFn, class Index>
struct CE;

template<typename T>
struct is_shared_ptr:
std::false_type
Expand All @@ -53,6 +59,9 @@ namespace autowiring {
struct is_shared_ptr<T&> :
is_shared_ptr<T>
{};

template<typename Arg, typename Pack, typename = void>
struct choice;
}

/// <summary>
Expand Down Expand Up @@ -701,6 +710,75 @@ class AutoPacket:
/// </remarks>
static AutoPacket& CurrentPacket(void);
template<typename Fn, typename Args, int... N>
static void CallWithChoiceTuple(Fn& fn, Args&& args, index_tuple<N...>) {
fn(autowiring::get<N>(args).value...);
}
/// <summary>
/// Invokes the specified function as though it were an AutoFilter on this packet
/// </summary>
template<typename... InArgs, typename... Outputs>
void Call(void(*pfn)(InArgs...), Outputs&... outputs) {
typedef typename make_index_tuple<sizeof...(InArgs)>::type t_index;
typedef autowiring::CE<decltype(pfn), t_index> t_call;
typedef typename t_call::t_ceSetup t_ceSetup;
// Completely unnecessary. Call will avoid making unneeded copies, and this is guaranteed
// by a unit test. Dereference your shared pointers before passing them in.
static_assert(
!is_any<is_shared_ptr<Outputs>::value...>::value,
"Do not use shared pointer arguments as output arguments with Call"
);
auto outputTuple = autowiring::tie(outputs...);
CallWithChoiceTuple(
pfn,
autowiring::make_tuple(
autowiring::choice<InArgs, decltype(outputTuple)>{
*this, outputTuple
}...
),
t_index{}
);
}
/// <summary>
/// Invokes a particular member function on the specified function object
/// </summary>
template<typename Fx, typename... InArgs, typename... Outputs>
void Call(Fx&& fx, void (Fx::*memfn)(InArgs...) const, Outputs&... outputs) {
typedef typename make_index_tuple<Decompose<decltype(&Fx::operator())>::N>::type t_index;
typedef autowiring::CE<decltype(&Fx::operator()), t_index> t_call;
typedef typename t_call::t_ceSetup t_ceSetup;
// Completely unnecessary. Call will avoid making unneeded copies, and this is guaranteed
// by a unit test. Dereference your shared pointers before passing them in.
static_assert(
!is_any<is_shared_ptr<Outputs>::value...>::value,
"Do not use shared pointer arguments as output arguments with Call"
);
auto outputTuple = autowiring::tie(outputs...);
CallWithChoiceTuple(
fx,
autowiring::make_tuple(
autowiring::choice<InArgs, decltype(outputTuple)>{
*this, outputTuple
}...
),
t_index{}
);
}
/// <summary>
/// Invokes the specified function as though it were an AutoFilter on this packet
/// </summary>
template<typename Fx, typename... Outputs>
void Call(Fx&& fx, Outputs&... outputs) {
Call(std::forward<Fx&&>(fx), &Fx::operator(), outputs...);
}
/// <summary>
/// Sets the current AutoPacket pointer
/// </summary>
Expand All @@ -718,6 +796,52 @@ class AutoPacket:
std::shared_ptr<CoreContext> GetContext(void) const;
};
namespace autowiring {
template<typename Arg, typename Pack, typename>
struct choice {
choice(AutoPacket&, Pack&) {}
typename std::decay<Arg>::type value;
};
template<typename Arg, typename... Outputs>
struct choice<
Arg,
autowiring::tuple<Outputs...>,
typename std::enable_if<
auto_arg<Arg>::is_output &&
find<
typename std::decay<Arg>::type,
typename std::decay<Outputs>::type...
>::value
>::type
>
{
static const bool is_matched = true;
choice(AutoPacket&, autowiring::tuple<Outputs...>& outputs):
value(get<Arg>(outputs))
{}
Arg& value;
};
template<typename Arg, typename Pack>
struct choice<
Arg,
Pack,
typename std::enable_if<
!auto_arg<Arg>::is_output
>::type
>
{
static const bool is_matched = true;
choice(AutoPacket& packet, Pack&) :
value(packet.Get<typename std::decay<Arg>::type>())
{}
const Arg& value;
};
}
/// <summary>
/// AutoPacket specialization
/// </summary>
Expand Down
4 changes: 4 additions & 0 deletions autowiring/CallExtractor.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ namespace autowiring {
// The type of the call centralizer
typedef void(*t_extractedCall)(const void* obj, AutoPacket&);

/// <summary>
/// An argument pack that holds all of the inputs and outputs to an AutoFilter during its invocation
/// </summary>
/// <param name="shared_outputs">Holds true if output types should be declared as shared pointers, false otherwise</param>
template<class... Args>
struct CESetup {
CESetup(AutoPacket& packet) :
Expand Down
17 changes: 10 additions & 7 deletions autowiring/auto_tuple.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ namespace autowiring {
/// Finds the specified type T in the argument pack
/// </remarks>
template<typename T, typename... Args>
struct find;
struct find {
static const bool value = false;
static const size_t index = 0;
};

template<typename T, typename Arg, typename... Args>
struct find<T, Arg, Args...> {
Expand All @@ -30,12 +33,6 @@ namespace autowiring {
(value ? 1 + find<T, Args...>::index : 0);
};

template<typename T>
struct find<T> {
static const bool value = false;
static const size_t index = 0;
};

template<int N, class... Args>
struct nth_type;

Expand Down Expand Up @@ -107,6 +104,7 @@ namespace autowiring {
typedef tuple_value<sizeof...(Args), Arg> t_value;

tuple(void) = default;
tuple(const tuple&) = default;

tuple(Arg&& arg, Args&&... args) :
tuple<Args...>(std::forward<Args>(args)...),
Expand All @@ -128,4 +126,9 @@ namespace autowiring {
tuple<Args&...> tie(Args&... args) {
return tuple<Args&...>(args...);
}

template<class... Args>
tuple<Args...> make_tuple(Args&&... args) {
return tuple<Args...>(std::forward<Args&&>(args)...);
}
}
105 changes: 105 additions & 0 deletions src/autowiring/test/AutoPacketTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,108 @@ TEST_F(AutoPacketTest, ComplexNetwork) {
ASSERT_EQ(pPacketB, packet.get()) << "Current packet in second second-level filter call was not equal as expected";
ASSERT_EQ(pPacketAA, packet.get()) << "Current packet in third-level filter call was not equal as expected";
}

TEST_F(AutoPacketTest, CallTest) {
AutoRequired<AutoPacketFactory> factory;
auto packet = factory->NewPacket();

packet->Decorate(Decoration<0>{101});
packet->Decorate(Decoration<1>{102});

bool called = false;
Decoration<0> rd0;
Decoration<1> rd1;

Decoration<2> rd2;

packet->Call(
[&](const Decoration<0>& d0, Decoration<1> d1, Decoration<2>& d2) {
called = true;
rd0 = d0;
rd1 = d1;
d2 = Decoration<2>{ 299 };
},
rd2
);

ASSERT_TRUE(called) << "Call-by lambda was not invoked as expected";
ASSERT_EQ(101, rd0.i) << "Decoration<0> was not properly copied into a call";
ASSERT_EQ(102, rd1.i) << "Decoration<1> was not properly copied into a call";
ASSERT_EQ(299, rd2.i) << "Decoration<2> was not extracted from the call filter properly";
}

static void SimpleCall(std::shared_ptr<const Decoration<4>> d4, const Decoration<0>& d0, Decoration<1>& d1, Decoration<2>& d2, Decoration<3>& unused) {
d1.i = d4->i;
d2.i = d0.i;
unused.i = 9999;
}

static_assert(auto_arg<Decoration<2>&>::is_output, "Output type not correctly detected");

static_assert(
autowiring::choice<Decoration<2>&, autowiring::tuple<const Decoration<0>&, Decoration<1>&, Decoration<2>&>>::is_matched,
"Failed to match an obvious choice output"
);

TEST_F(AutoPacketTest, ObjectCallTest) {
AutoRequired<AutoPacketFactory> factory;
auto packet = factory->NewPacket();
packet->Decorate(Decoration<0>{1001});
packet->Decorate(Decoration<4>{9001});

Decoration<1> d1;
Decoration<2> d2;
packet->Call(SimpleCall, d1, d2);

ASSERT_EQ(9001, d1.i) << "Moore value not assigned correctly";
ASSERT_EQ(1001, d2.i) << "Mealy value not assigned correctly";
}

namespace {
class CountsCopies {
public:
CountsCopies(void) {
s_nConstructions++;
}

CountsCopies(CountsCopies& rhs) :
nCopies(rhs.nCopies + 1),
value(rhs.value)
{}

void operator=(const CountsCopies& rhs) {
nCopies = rhs.nCopies + 1;
value = rhs.value;
}

static size_t s_nConstructions;

int nCopies = 0;
int value = 2;
};

size_t CountsCopies::s_nConstructions = 0;
}

TEST_F(AutoPacketTest, NoUnneededOutputCopy) {
AutoRequired<AutoPacketFactory> factory;
auto packet = factory->NewPacket();

packet->Decorate(Decoration<0>{101});

CountsCopies rcc;
CountsCopies* pRcc = nullptr;

packet->Call(
[&](const Decoration<0>& d0, CountsCopies& cc) {
cc.value = 109;
pRcc = &cc;
},
rcc
);

ASSERT_EQ(1UL, CountsCopies::s_nConstructions) << "An unexpected number of default constructed entities was created";
ASSERT_EQ(&rcc, pRcc) << "Destination value was not found at the correct address";
ASSERT_EQ(109, rcc.value) << "Copy-counting output value was not copied correctly";
ASSERT_EQ(0UL, rcc.nCopies) << "An unnecessary number of copies was made during an extracting call";
}

0 comments on commit 7479857

Please sign in to comment.