diff --git a/libraries/AP_HAL/utility/OwnPtr.h b/libraries/AP_HAL/utility/OwnPtr.h new file mode 100644 index 0000000000..5ca9c54682 --- /dev/null +++ b/libraries/AP_HAL/utility/OwnPtr.h @@ -0,0 +1,127 @@ +/// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- +/* + * Copyright (C) 2015-2016 Intel Corporation. All rights reserved. + * + * This file is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ +#pragma once + +#include + +namespace AP_HAL { + +/* Poor's man std::unique_ptr + * + * OwnPtr is a container for a pointer declaring ownership. + * + * The goal is to allow to own object, pass it around and automatically delete + * the pointer when the container goes out of scope. + * + * In order to pass it around the move constructor or move assignment operator + * must be used. operator*, operator-> and the get() method can be used to get + * the pointer contained in the OwnPtr container. + * + * The OwnPtr name comes from similar class in WebKit, before they switched to + * std::unique_ptr. The implementation is different/simpler. We need our own + * implementation since the header (and thus std::unique_ptr) is not + * compatible with the PX4 toolchain and/or nuttx headers. + */ +template +class OwnPtr { +public: + OwnPtr() : _ptr(nullptr) { } + OwnPtr(std::nullptr_t) : _ptr(nullptr) { } + + /* non-copyable */ + OwnPtr(const OwnPtr &other) = delete; + + /* Allow construction from a derived class U */ + template + OwnPtr(OwnPtr&& other) : _ptr(other.leak()) { } + + OwnPtr(T *ptr) : _ptr(ptr) { } + + OwnPtr& operator=(std::nullptr_t) { clear(); return *this; } + + template + OwnPtr& operator=(OwnPtr&& other) + { + T *old = _ptr; + _ptr = other.leak(); + delete old; + return *this; + } + + template + OwnPtr& operator=(U *other) + { + T *old = _ptr; + _ptr = other; + delete old; + return *this; + } + + ~OwnPtr() { delete _ptr; } + + void clear() + { + delete leak(); + } + + T *leak() + { + T *old = _ptr; + _ptr = nullptr; + return old; + } + + T *get() const + { + return _ptr; + } + + T& operator*() const { return *_ptr; } + T *operator->() const { return _ptr; } + bool operator !() const { return !_ptr; } + explicit operator bool() const { return _ptr != nullptr; } + +private: + T *_ptr; +}; + +template +inline bool operator==(T* a, const OwnPtr& b) +{ + return a == b.get(); +} + +template +inline bool operator==(const OwnPtr& a, T* b) +{ + return a.get() == b; +} + +template +inline bool operator!=(T* a, const OwnPtr& b) +{ + return a != b.get(); +} + +template +inline bool operator!=(const OwnPtr& a, T* b) +{ + return a.get() != b; +} + +} diff --git a/libraries/AP_HAL/utility/tests/test_own_ptr.cpp b/libraries/AP_HAL/utility/tests/test_own_ptr.cpp new file mode 100644 index 0000000000..655ef6722e --- /dev/null +++ b/libraries/AP_HAL/utility/tests/test_own_ptr.cpp @@ -0,0 +1,199 @@ +/// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- +/* + * Copyright (C) 2015-2016 Intel Corporation. All rights reserved. + * + * This file is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This file is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ +#include + +#include +#include + +TEST(OwnPtrTest, SamePointer) +{ + int *a = new int{42}; + AP_HAL::OwnPtr own(a); + + EXPECT_TRUE(own == a); + EXPECT_TRUE(a == own); + + EXPECT_FALSE(own != a); + EXPECT_FALSE(a != own); + + EXPECT_EQ(a, own.get()); +} + +TEST(OwnPtrTest, MoveOwnership) +{ + int *a = new int{42}; + AP_HAL::OwnPtr own(a); + AP_HAL::OwnPtr own2 = std::move(own); + + EXPECT_EQ(own2.get(), a); + EXPECT_EQ(own.get(), nullptr); +} + +class TestDeleted { +public: + TestDeleted(unsigned int &d) : deleted(d) { } + ~TestDeleted() { deleted++; } + + unsigned int &deleted; +}; + +TEST(OwnPtrTest, DeleteOutOfScope) +{ + unsigned int deleted = 0; + + { + AP_HAL::OwnPtr own(new TestDeleted{deleted}); + } + + EXPECT_EQ(deleted, 1); +} + +TEST(OwnPtrTest, DeleteOutOfScopeAfterMove) +{ + unsigned int deleted = 0; + AP_HAL::OwnPtr own; + + { + AP_HAL::OwnPtr own2(new TestDeleted{deleted}); + own = std::move(own2); + } + + // own2 is now out of scope, but it has been moved already + EXPECT_EQ(deleted, 0); + + // now remove also 'own' + own.clear(); + + EXPECT_EQ(deleted, 1); +} + +class TestCall { +public: + int foo() { return 42; } +}; + +TEST(OwnPtrTest, CallMethod) +{ + AP_HAL::OwnPtr own(new TestCall{}); + EXPECT_EQ(own->foo(), 42); + EXPECT_EQ((*own).foo(), 42); +} + +class TestDestructor { +public: + TestDestructor(AP_HAL::OwnPtr v) : _v(std::move(v)) { } + + AP_HAL::OwnPtr _v; +}; + +TEST(OwnPtrTest, MoveToConstructor) +{ + unsigned int deleted = 0; + + AP_HAL::OwnPtr own(new TestDeleted{deleted}); + { + EXPECT_EQ(0, deleted); + TestDestructor destructor{std::move(own)}; + EXPECT_EQ(0, deleted); + } + EXPECT_EQ(1, deleted); +} + +static AP_HAL::OwnPtr create_test_deleted(unsigned int &deleted) +{ + return AP_HAL::OwnPtr(new TestDeleted(deleted)); +} + +TEST(OwnPtrTest, ReturnType) +{ + unsigned int deleted = 0; + + auto own = create_test_deleted(deleted); + EXPECT_EQ(0, deleted); + { + auto own2 = create_test_deleted(deleted); + EXPECT_EQ(0, deleted); + } + EXPECT_EQ(1, deleted); + own.clear(); + EXPECT_EQ(2, deleted); +} + +TEST(OwnPtrTest, ReplacePointer) +{ + unsigned int deleted1 = 0; + unsigned int deleted2 = 0; + + auto own = create_test_deleted(deleted1); + EXPECT_EQ(0, deleted1); + { + own = create_test_deleted(deleted2); + EXPECT_EQ(1, deleted1); + } + EXPECT_EQ(0, deleted2); + own = nullptr; + EXPECT_EQ(1, deleted2); +} + +TEST(OwnPtrTest, ReplaceWithRawPointer) +{ + unsigned int deleted1 = 0; + + auto own = create_test_deleted(deleted1); + EXPECT_EQ(0, deleted1); + { + own = new TestDeleted{deleted1}; + EXPECT_EQ(1, deleted1); + } +} + +TEST(OwnPtrTest, Empty) +{ + int *a = new int{42}; + + AP_HAL::OwnPtr own1; + EXPECT_FALSE(own1); + + own1 = a; + EXPECT_TRUE((bool) own1); +} + +class A { +public: + A(int a) : _a(a) { } + int _a; +}; + +class B : public A { +public: + B() : A(42) { } +}; + +TEST(OwnPtrTest, Inheritance) +{ + A *a = new A(21); + B *b = new B(); + AP_HAL::OwnPtr own_a(a); + AP_HAL::OwnPtr own_b(b); + + own_a = std::move(own_b); + EXPECT_EQ(b, own_a.get()); + EXPECT_EQ(42, own_a->_a); +} + +AP_GTEST_MAIN() diff --git a/libraries/AP_HAL/utility/tests/wscript b/libraries/AP_HAL/utility/tests/wscript new file mode 100644 index 0000000000..cd3e5e3ce7 --- /dev/null +++ b/libraries/AP_HAL/utility/tests/wscript @@ -0,0 +1,7 @@ +#!/usr/bin/env python +# encoding: utf-8 + +def build(bld): + bld.ap_find_tests( + use='ap', + )