Skip to content
This repository has been archived by the owner on Mar 4, 2021. It is now read-only.

Document and improve pointer #159

Merged
merged 1 commit into from
Sep 3, 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
59 changes: 59 additions & 0 deletions doc/common/pointer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# NAME
SharedPointer -- Smart shared pointer with nullptr checks

# LIBRARY
MeasurementKit (libmeasurement-kit, -lmeasurement-kit).

# SYNOPSIS
```C++
#include <measurement_kit/common.hpp>

using namespace measurement_kit::common;

// Construct as a shared_ptr<T>

SharedPointer<T> p; // pointer is nullptr
SharedPointer<T> p(std::make_shared<T>()); // construct from shared_ptr
SharedPointer<T> p(new T()); // construct from raw pointer
p = std::make_shared<T>(); // assign from shared_ptr

// The three overriden operations

T *rawptr = p.get(); // Get pointer value or raise
p->foo(); // Call T::foo() or raise
T value = *p; // Return pointed value or raise
```

# DESCRIPTION

`SharedPointer<T>` template class is a drop-in replacement for the
standard library `std::shared_ptr<T>` template. It reimplements common
`std::shared_ptr<T>` operations by checking that the pointee is not
`nullptr`. Otherwise, a runtime exception is raised.

Whenever possible RAII should be used within MeasurementKit to guarantee
resources deallocation. In the few specific cases in which a pointer is
needed instead, a `SharedPointer<T>` should be used to guarantee that the
impact of programming errors (i.e. null pointers) is low.

It is safe to construct (or to assign) a `SharedPointer<T>` from an
`std::shared_ptr<T>`. In fact `SharedPointer<T>` is implemented overriding
the three basic operations of `std::shared_ptr<T>`.

# BUGS

Since `SharedPointer<T>` overrides `std::shared_ptr<T>` and since it is
meant to be used as a class (not as a pointer or as a reference), one should
remember not to add attributes to `SharedPointer<T>` implementation. This
guarantees that the following:

```C++
SharedPointer<T> p = std::make_shared<T>();
```

does not result in object slicing (i.e. in the construction of a
`SharedPointer<T>` with possibly uninitialized attributes).

# HISTORY

The `SharedPointer` class appeared in MeasurementKit 0.1.
55 changes: 15 additions & 40 deletions include/measurement_kit/common/pointer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,60 +11,35 @@
namespace measurement_kit {
namespace common {

/*!
* \brief Improved std::shared_ptr<T> with null pointer checks.
*
* This template class is a drop-in replacemente for the standard library's
* shared_ptr<>. It extends shared_ptr<>'s ->() and *() operators to check
* whether the pointee is a nullptr. In such case, unlike shared_ptr<>, the
* pointee is not accessed and a runtime exception is raised.
*
* Use this class as follows:
*
* using namespace measurement_kit::common;
* ...
* SharedPointer<Foo> ptr;
* ...
* ptr = std::make_shared<Foo>(...);
*
* That is, declare ptr as SharedPointer<Foo> and the behave like ptr was
* a shared_ptr<> variable instead.
*
* It is safe to assign the return value of std::make_shared<Foo> to
* SharedPointer<Foo> because SharedPointer have exactly the same fields
* as std::shared_pointer and because it inherits the copy and move
* constructors from std::shared_pointer.
*/
template<typename T> class SharedPointer : public std::shared_ptr<T> {
/// Improved std::shared_ptr<T> with null pointer checks
template <typename T> class SharedPointer : public std::shared_ptr<T> {
using std::shared_ptr<T>::shared_ptr;

public:
public:
T *get() const { return operator->(); } ///< Get the raw pointer

/*!
* \brief Access the pointee to get one of its fields.
* \returns A pointer to the pointee that allows one to access
* the requested pointee field.
* \throws std::runtime_error if the pointee is nullptr.
*/
/// Syntactic sugar to get the raw pointer
T *operator->() const {
if (this->get() == nullptr) {
if (std::shared_ptr<T>::get() == nullptr) {
throw std::runtime_error("null pointer");
}
return std::shared_ptr<T>::operator->();
}

/*!
* \brief Get the value of the pointee.
* \returns The value of the pointee.
* \throws std::runtime_error if the pointee is nullptr.
*/
/// Dereference the raw pointer
typename std::add_lvalue_reference<T>::type operator*() const {
if (this->get() == nullptr) {
if (std::shared_ptr<T>::get() == nullptr) {
throw std::runtime_error("null pointer");
}
return std::shared_ptr<T>::operator*();
}

protected:
private:
// NO ATTRIBUTES HERE BY DESIGN. DO NOT ADD ATTRIBUTES HERE BECAUSE
// DOING THAT CREATES THE RISK OF OBJECT SLICING.
};

}}
} // namespace common
} // namespace measurement_kit
#endif
27 changes: 27 additions & 0 deletions test/common/pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,22 @@ struct Foo {
double elem = 3.14;
Foo() {}
Foo(double x) : elem(x) {}
void mascetti() {}
};

TEST_CASE("SharedPointer raises an exception when the pointee is nullptr") {
SharedPointer<Foo> foo;
REQUIRE_THROWS(auto k = foo->elem);
REQUIRE_THROWS(*foo);
REQUIRE_THROWS(foo.get());
}

TEST_CASE("We can safely assign to SharedPointer an empty shared_ptr") {
std::shared_ptr<Foo> antani;
SharedPointer<Foo> necchi = antani;
REQUIRE_THROWS(auto k = necchi->elem);
REQUIRE_THROWS(*necchi);
REQUIRE_THROWS(necchi.get());
}

TEST_CASE("We can assign to SharedPointer the result of make_shared") {
Expand All @@ -38,3 +41,27 @@ TEST_CASE("We can assign to SharedPointer the result of make_shared") {
auto foo = *necchi;
REQUIRE(foo.elem == 6.28);
}

TEST_CASE("The smart pointer works as expected") {
auto pnecchi = new Foo(6.28);
SharedPointer<Foo> necchi(pnecchi);
REQUIRE(necchi->elem == 6.28);
REQUIRE((*necchi).elem == 6.28);
REQUIRE(necchi.get() == pnecchi);
REQUIRE(necchi.operator->() == pnecchi);
}

TEST_CASE("Operator->() throws when nullptr") {
SharedPointer<Foo> necchi;
REQUIRE_THROWS(necchi->mascetti());
}

TEST_CASE("Operator->* throws when nullptr") {
SharedPointer<Foo> sassaroli;
REQUIRE_THROWS(*sassaroli);
}

TEST_CASE("Get() throws when nullptr") {
SharedPointer<Foo> il_melandri;
REQUIRE_THROWS(il_melandri.get());
}