Skip to content

Commit

Permalink
iox-eclipse-iceoryx#391 test function copy and move behavior
Browse files Browse the repository at this point in the history
Signed-off-by: Matthias Killat <matthias.killat@apex.ai>
  • Loading branch information
MatthiasKillat committed Dec 27, 2020
1 parent a4773ac commit 77a97c7
Show file tree
Hide file tree
Showing 2 changed files with 215 additions and 11 deletions.
14 changes: 9 additions & 5 deletions iceoryx_utils/include/iceoryx_utils/internal/cxx/function.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,10 @@ class storable_function<StorageType, signature<ReturnType, Args...>>
{
if (&rhs != this)
{
m_vtable = rhs.m_vtable;
// note: src vtable is needed for destroy, then changed to src vtable
m_vtable.destroy(*this);
m_function = nullptr; // only needed when the src has no object
m_vtable = rhs.m_vtable;
m_vtable.copy(rhs, *this);
}

Expand All @@ -170,8 +172,10 @@ class storable_function<StorageType, signature<ReturnType, Args...>>
{
if (&rhs != this)
{
m_vtable = rhs.m_vtable;
// note: src vtable is needed for destroy, then changed to src vtable
m_vtable.destroy(*this);
m_function = nullptr; // only needed when the src has no object
m_vtable = rhs.m_vtable;
m_vtable.move(rhs, *this);
}

Expand All @@ -188,9 +192,7 @@ class storable_function<StorageType, signature<ReturnType, Args...>>
// todo: think about constness, calling it may change the stored object
ReturnType operator()(Args... args)
{
std::cout << "invoke" << std::endl;
auto r = m_function(std::forward<Args>(args)...);
std::cout << "invoked" << std::endl;
return r;
}

Expand Down Expand Up @@ -246,6 +248,7 @@ class storable_function<StorageType, signature<ReturnType, Args...>>
template <typename T>
static void copy(const storable_function& src, storable_function& dest) noexcept
{
std::cout << "###cpy" << std::endl;
if (!src.m_storedObj)
{
dest.m_storedObj = nullptr;
Expand Down Expand Up @@ -282,6 +285,8 @@ class storable_function<StorageType, signature<ReturnType, Args...>>
p = new (p) T(std::move(*obj));
dest.m_function = *p;
dest.m_storedObj = p;
src.m_function = nullptr;
src.m_storedObj = nullptr;
}
}

Expand All @@ -293,7 +298,6 @@ class storable_function<StorageType, signature<ReturnType, Args...>>
auto p = static_cast<T*>(f.m_storedObj);
p->~T();
f.m_storage.deallocate();
f.m_storedObj = nullptr;
}
}
};
Expand Down
212 changes: 206 additions & 6 deletions iceoryx_utils/test/moduletests/test_cxx_function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,78 @@ template <typename T>
using fixed_size_function = iox::cxx::function<T, 128>;
using test_function = fixed_size_function<signature>;

class Functor

// helper template to count construction and copy statistics,
// for our purpose (test_function) we do not need to distinguish
// between copy (move) ctor and copy(move) assignment
template <typename T>
class Counter
{
public:
static uint64_t numCreated;
static uint64_t numCopied;
static uint64_t numMoved;
static uint64_t numDestroyed;

Counter()
{
++numCreated;
}

Counter(const Counter&)
{
++numCreated;
++numCopied;
}

Counter(Counter&&)
{
++numMoved;
}

~Counter()
{
++numDestroyed;
}

Counter& operator=(const Counter&)
{
++numCopied;
}

Counter& operator=(Counter&&)
{
++numMoved;
}

static void resetCounts()
{
numCreated = 0U;
numCopied = 0U;
numMoved = 0U;
numDestroyed = 0U;
}
};

template <typename T>
uint64_t Counter<T>::numCreated = 0U;

template <typename T>
uint64_t Counter<T>::numCopied = 0U;

template <typename T>
uint64_t Counter<T>::numMoved = 0U;

template <typename T>
uint64_t Counter<T>::numDestroyed = 0U;


class Functor : public Counter<Functor>
{
public:
Functor(int32_t state)
: m_state(state)
: Counter<Functor>()
, m_state(state)
{
}

Expand Down Expand Up @@ -80,8 +147,10 @@ TEST_F(function_test, DefaultConstructionCreatesNoCallable)
TEST_F(function_test, ConstructionFromFunctorIsCallable)
{
Functor f(73);
Functor::resetCounts();
test_function sut(f);

EXPECT_EQ(Functor::numCreated, 1U);
ASSERT_TRUE(sut.operator bool());
EXPECT_EQ(sut(1), f(1));
}
Expand Down Expand Up @@ -116,10 +185,10 @@ TEST_F(function_test, ConstructionFromStaticFunctionIsCallable)
TEST_F(function_test, FunctionStateIsIndependentOfSource)
{
constexpr uint32_t INITIAL_STATE = 73U;
static_storage<1024> storage;
static_storage<1024U> storage;
auto p = storage.allocate<Functor>();
auto f = new (p) Functor(INITIAL_STATE);
auto& functor = *f;
p = new (p) Functor(INITIAL_STATE);
auto& functor = *p;

// test whether the function really owns the functor
// (no dependency or side effects)
Expand All @@ -131,10 +200,141 @@ TEST_F(function_test, FunctionStateIsIndependentOfSource)
EXPECT_EQ(sut(1U), functor(1U));

// clear original (set to 0)
f->~Functor();
p->~Functor();
storage.clear();

EXPECT_EQ(sut(1U), INITIAL_STATE + 2U);
}

// The implementation uses type erasure and we need to verify that the corresponding
// constructors and operators of the underlying object (functor) are called.

TEST_F(function_test, DestructorCallsDestructorOfStoredFunctor)
{
Functor f(73);
Functor::resetCounts();

{
test_function sut(f);
}

EXPECT_EQ(Functor::numDestroyed, 1U);
}

TEST_F(function_test, CopyCtorCopiesStoredFunctor)
{
Functor functor(73);
test_function f(functor);
Functor::resetCounts();

test_function sut(f);

EXPECT_EQ(Functor::numCopied, 1U);
ASSERT_TRUE(sut.operator bool());
EXPECT_EQ(sut(1), f(1));
}

TEST_F(function_test, MoveCtorMovesStoredFunctor)
{
Functor functor(73);
test_function f(functor);
Functor::resetCounts();

test_function sut(std::move(f));

EXPECT_EQ(Functor::numMoved, 1U);
ASSERT_TRUE(sut.operator bool());
EXPECT_EQ(sut(1), functor(1));
EXPECT_FALSE(f.operator bool());
}

TEST_F(function_test, CopyAssignmentCopiesStoredFunctor)
{
Functor functor1(73);
Functor functor2(73);
test_function f(functor1);
test_function sut(functor2);

Functor::resetCounts();
sut = f;

EXPECT_EQ(Functor::numDestroyed, 1U);
EXPECT_EQ(Functor::numCopied, 1U);
ASSERT_TRUE(sut.operator bool());
EXPECT_EQ(sut(1), f(1));
}

TEST_F(function_test, MoveAssignmentMovesStoredFunctor)
{
Functor functor1(73);
Functor functor2(73);
test_function f(functor1);
test_function sut(functor1);

Functor::resetCounts();
sut = std::move(f);

EXPECT_EQ(Functor::numDestroyed, 1U);
EXPECT_EQ(Functor::numMoved, 1U);
ASSERT_TRUE(sut.operator bool());
EXPECT_EQ(sut(1), functor1(1));
EXPECT_FALSE(f.operator bool());
}

TEST_F(function_test, CopiedNonCallableFunctionIsNotCallable)
{
test_function f;
Functor::resetCounts();

test_function sut(f);

EXPECT_EQ(Functor::numCopied, 0U);
EXPECT_EQ(Functor::numMoved, 0U);
EXPECT_FALSE(sut.operator bool());
}

TEST_F(function_test, MovedNonCallableFunctionIsNotCallable)
{
test_function f;
Functor::resetCounts();

test_function sut(std::move(f));

EXPECT_EQ(Functor::numCopied, 0U);
EXPECT_EQ(Functor::numMoved, 0U);
EXPECT_FALSE(sut.operator bool());
EXPECT_FALSE(f.operator bool());
}

TEST_F(function_test, CopyAssignedNonCallableFunctionIsNotCallable)
{
Functor functor(73);
test_function f;
test_function sut(functor);

Functor::resetCounts();
sut = f;

EXPECT_EQ(Functor::numCopied, 0U);
EXPECT_EQ(Functor::numMoved, 0U);
EXPECT_FALSE(sut.operator bool());
EXPECT_FALSE(f.operator bool());
}

TEST_F(function_test, MoveAssignedNonCallableFunctionIsNotCallable)
{
Functor functor(73);
test_function f;
test_function sut(functor);

Functor::resetCounts();
sut = std::move(f);

EXPECT_EQ(Functor::numCopied, 0U);
EXPECT_EQ(Functor::numMoved, 0U);
EXPECT_FALSE(sut.operator bool());
EXPECT_FALSE(f.operator bool());
}


} // namespace

0 comments on commit 77a97c7

Please sign in to comment.