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 AutoPacket::Call #730

Merged
merged 4 commits into from
Aug 14, 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
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";
}