Skip to content

Commit

Permalink
Clean up auto_tuple, make it slightly more efficient
Browse files Browse the repository at this point in the history
By avoiding the use of a base type and switching to a templated ctor, we make it possible to initialize tuple members with single arguments directly in place rather than constructing a temporary that will requiring moving.  Assure this new efficiency going forward with a new test in TupleTest.
  • Loading branch information
codemercenary committed Sep 30, 2015
1 parent 2c4ad8d commit 3e59579
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 41 deletions.
136 changes: 97 additions & 39 deletions autowiring/auto_tuple.h
Expand Up @@ -11,6 +11,16 @@ namespace autowiring {
template<class... Args>
struct tuple {};

template<typename T>
struct is_tuple {
static const bool value = false;
};

template<typename... Args>
struct is_tuple<tuple<Args...>> {
static const bool value = true;
};

/// <summary>
/// Finds the specified type T in the argument pack
/// </remarks>
Expand Down Expand Up @@ -46,39 +56,16 @@ namespace autowiring {
nth_type<N - 1, Tail...>
{};

template<int N, class T>
struct tuple_value {
tuple_value(void) = default;

tuple_value(T&& value) :
value(std::forward<T&&>(value))
{}

T value;
};

template<int N, class... Args>
typename nth_type<N, Args...>::type& get(tuple<Args...>& val) {
static_assert(N < sizeof...(Args), "Requested tuple index is out of bounds");
return
static_cast<
tuple_value<
sizeof...(Args) - N - 1,
typename nth_type<N, Args...>::type
>&
>(val).value;
return val.get(std::integral_constant<int, sizeof...(Args) - N - 1>{});
}

template<int N, class... Args>
const typename nth_type<N, Args...>::type& get(const tuple<Args...>& val) {
static_assert(N < sizeof...(Args), "Requested tuple index is out of bounds");
return
static_cast<
tuple_value<
sizeof...(Args) - N - 1,
typename nth_type<N, Args...>::type
>&
>(val).value;
return val.get(std::integral_constant<int, sizeof...(Args) - N - 1>{});
}

template<class Arg, class... Args>
Expand All @@ -96,30 +83,101 @@ namespace autowiring {
return get<index - 1>(val);
}

template<class Arg, class... Args>
struct tuple<Arg, Args...>:
tuple<Args...>,
tuple_value<sizeof...(Args), Arg>
template<typename T>
T&& transfer(tuple<T>&& rhs) {
return std::move(rhs.val);
}

template<typename T>
typename std::enable_if<!is_tuple<T>::value, T&&>::type
transfer(typename std::remove_reference<T>::type& rhs)
{
return static_cast<T&&>(rhs);
}

template<class Arg>
struct tuple<Arg>
{
public:
tuple(void) = default;
tuple(const tuple& rhs) :
val(rhs.val)
{}
tuple(tuple&& rhs) :
val(std::forward<Arg&&>(rhs.val))
{}

template<typename T>
explicit tuple(T&& rhs) :
val(transfer<T&&>(rhs))
{}

Arg val;

Arg& get(std::integral_constant<int, 0>&&) { return val; }
const Arg& get(std::integral_constant<int, 0>&&) const { return val; }

template<class OtherT>
tuple& operator=(const tuple<OtherT>& rhs) {
val = rhs.val;
return *this;
}

template<class OtherT>
tuple& operator=(tuple<OtherT>&& rhs) {
std::swap(val, rhs.val);
return *this;
}
};

template<class Arg, class ArgNext, class... Args>
struct tuple<Arg, ArgNext, Args...> :
tuple<ArgNext, Args...>
{
typedef tuple_value<sizeof...(Args), Arg> t_value;
typedef tuple<ArgNext, Args...> t_base;

tuple(void) = default;
tuple(const tuple&) = default;
tuple(const tuple& rhs) :
t_base{ static_cast<const t_base&>(rhs) },
val(rhs.val)
{}

tuple(tuple&& rhs) :
t_base{ std::forward<t_base&&>(rhs) },
val(std::forward<Arg&&>(rhs.val))
{}

tuple(Arg&& arg, Args&&... args) :
tuple<Args...>(std::forward<Args>(args)...),
tuple_value<sizeof...(Args), Arg>(std::forward<Arg&&>(arg))
template<typename FnArg1, typename FnArg2, typename... FnArgs>
tuple(
FnArg1&& arg1,
FnArg2&& arg2,
FnArgs&&... args
) :
t_base{ std::forward<FnArg2&&>(arg2), std::forward<FnArgs&&>(args)... },
val(std::forward<FnArg1&&>(arg1))
{}

template<class OtherT, class... OtherTs>
tuple& operator=(const tuple<OtherT, OtherTs...>& rhs) {
// Base type copy
static_cast<tuple<Args...>&>(*this) = static_cast<const tuple<OtherTs...>&>(rhs);
// Copy base then ourselves
static_cast<t_base&>(*this) = static_cast<const tuple<OtherTs...>&>(rhs);
val = rhs.val;
return *this;
}

// Interior copy:
t_value::value = static_cast<const typename tuple<OtherT, OtherTs...>::t_value&>(rhs).value;
template<class OtherT, class... OtherTs>
tuple& operator=(tuple<OtherT, OtherTs...>&& rhs) {
// Move base then ourselves
static_cast<t_base&>(*this) = static_cast<const tuple<OtherTs...>&&>(rhs);
val = std::move(rhs.val);
return *this;
}

using t_base::get;
Arg& get(std::integral_constant<int, 1 + sizeof...(Args)>&&) { return val; }
const Arg& get(std::integral_constant<int, 1 + sizeof...(Args)>&&) const { return val; }

Arg val;
};

template<class... Args>
Expand All @@ -131,4 +189,4 @@ namespace autowiring {
tuple<Args...> make_tuple(Args&&... args) {
return tuple<Args...>(std::forward<Args&&>(args)...);
}
}
}
38 changes: 36 additions & 2 deletions src/autowiring/test/TupleTest.cpp
Expand Up @@ -4,6 +4,7 @@
#include <string>

static_assert(10 == autowiring::sum<1, 2, 7>::value, "Sum template function did not evaluate a sum correctly");
static_assert(autowiring::is_tuple<autowiring::tuple<int, int>>::value, "Autowiring tuple not correctly recognized as a tuple");

class TupleTest:
public testing::Test
Expand Down Expand Up @@ -47,7 +48,7 @@ TEST_F(TupleTest, AddressTest) {
autowiring::tuple<std::unique_ptr<int>> tup;
std::unique_ptr<int>& v = autowiring::get<0>(tup);

ASSERT_EQ(&tup.value, &v) << "Get operation did not provide a correct reference to the underlying tuple value";
ASSERT_EQ(&tup.val, &v) << "Get operation did not provide a correct reference to the underlying tuple value";
}

TEST_F(TupleTest, GetByTypeTest) {
Expand All @@ -59,4 +60,37 @@ TEST_F(TupleTest, GetByTypeTest) {
ASSERT_EQ(1, iVal) << "Integer value type mismatch";
ASSERT_EQ(2, lVal) << "Long value type mismatch";
ASSERT_EQ(1.9f, fVal) << "Float value type mismatch";
}
}

namespace {
class CountsCopies {
public:
CountsCopies(void) = default;
CountsCopies(const CountsCopies& rhs) { nCopies++; }
void operator=(const CountsCopies& rhs) { nCopies++; }
static int nCopies;
};

int CountsCopies::nCopies = 0;
}

TEST_F(TupleTest, NoUnneededCopies) {
CountsCopies isCopied;
autowiring::tuple<CountsCopies> nCopies(isCopied);

ASSERT_EQ(1, CountsCopies::nCopies) << "Too many copies made during in-place tuple construction";
}

TEST_F(TupleTest, CanHoldMoveOnly) {
auto up = std::unique_ptr<int>{ new int {999} };
int* pVal = up.get();

autowiring::tuple<std::unique_ptr<int>, bool> pp{ std::move(up), false };
autowiring::tuple<std::unique_ptr<int>, bool> rhs = std::move(pp);

ASSERT_EQ(nullptr, autowiring::get<0>(pp).get()) << "Tuple move did not wipe out an r-value properly";
ASSERT_EQ(pVal, autowiring::get<0>(rhs).get()) << "Tuple move did not transfer an r-value properly";

rhs = std::move(rhs);
ASSERT_EQ(pVal, autowiring::get<0>(rhs).get()) << "Reflexive transfer invalidated incorrectly";
}

0 comments on commit 3e59579

Please sign in to comment.