Add macros.h, pointer.h and optional_ownership_pointer.h

This commit is contained in:
Sebastian Messmer 2015-02-16 21:31:07 +01:00
parent 60bed8635e
commit a01a6f64d1
8 changed files with 410 additions and 0 deletions

97
CMakeLists.txt Normal file
View File

@ -0,0 +1,97 @@
INCLUDE(messmer/cmake/tools)
# Initializes block variables
INIT_BIICODE_BLOCK()
SETUP_GOOGLETEST()
# Actually create targets: EXEcutables and libraries.
ADD_BIICODE_TARGETS()
ACTIVATE_CPP14()
# You can safely delete lines from here...
###############################################################################
# REFERENCE #
###############################################################################
#
# This CMakeLists.txt file helps defining your block building and compiling
# To learn more about the CMake use with biicode, visit http://docs.biicode.com/c++.html
#
# ----------------------------------------------------
# NEW FEATURE! Include cmake files from remote blocks:
# -----------------------------------------------------
# Now you can handle cmake dependencies alike you do with c/c++:
#
# INCLUDE(user/block/myrecipe) # include myrecipe.cmake from remote user/block
#
# > EXAMPLE: Include our recipes and activate C++11 in your block (http://www.biicode.com/biicode/cmake)
#
# INCLUDE(biicode/cmake/tools) # Include tools.cmake file from "cmake" block from the "biicode" user
# ACTIVATE_CPP11(INTERFACE ${BII_BLOCK_TARGET})
#
# Remember to run "bii find" to download out cmake tools file
#
# ---------------------
# INIT_BIICODE_BLOCK()
# ---------------------
# This function creates several helper variables as ${BII_BLOCK_NAME} and ${BII_BLOCK_USER}
# Also it loads variables from the cmake/bii_user_block_vars.cmake
# ${BII_LIB_SRC} File list to create the library
# ${BII_LIB_TYPE} Empty (default, STATIC most casess) STATIC or SHARED
# ${BII_LIB_DEPS} Dependencies to other libraries (user2_block2, user3_blockX)
# ${BII_LIB_SYSTEM_HEADERS} System linking requirements as windows.h, pthread.h, etc
#
# You can use or modify them here, for example, to add or remove files from targets based on OS
# Or use typical cmake configurations done BEFORE defining targets. Examples:
# ADD_DEFINITIONS(-DFOO)
# FIND_PACKAGE(OpenGL QUIET)
# You can add INCLUDE_DIRECTORIES here too
#
# ---------------------
# ADD_BIICODE_TARGETS()
# ---------------------
#
# This function creates the following variables:
# ${BII_BLOCK_TARGET} Interface (no files) target for convenient configuration of all
# targets in this block, as the rest of targets always depend on it
# has name in the form "user_block_interface"
# ${BII_LIB_TARGET} Target library name, usually in the form "user_block". May not exist
# if BII_LIB_SRC is empty
# ${BII_BLOCK_TARGETS} List of all targets defined in this block
# ${BII_BLOCK_EXES} List of executables targets defined in this block
# ${BII_exe_name_TARGET}: Executable target (e.g. ${BII_main_TARGET}. You can also use
# directly the name of the executable target (e.g. user_block_main)
#
# > EXAMPLE: Add include directories to all targets of this block
#
# TARGET_INCLUDE_DIRECTORIES(${BII_BLOCK_TARGET} INTERFACE myincludedir)
#
# You can add private include directories to the Lib (if existing)
#
# > EXAMPLE: Link with pthread:
#
# TARGET_LINK_LIBRARIES(${BII_BLOCK_TARGET} INTERFACE pthread)
# or link against library:
# TARGET_LINK_LIBRARIES(${BII_LIB_TARGET} PUBLIC pthread)
# or directly use the library target name:
# TARGET_LINK_LIBRARIES(user_block PUBLIC pthread)
#
# NOTE: This can be also done adding pthread to ${BII_LIB_DEPS}
# BEFORE calling ADD_BIICODE_TARGETS()
#
# > EXAMPLE: how to activate C++11
#
# IF(APPLE)
# TARGET_COMPILE_OPTIONS(${BII_BLOCK_TARGET} INTERFACE "-std=c++11 -stdlib=libc++")
# ELSEIF (WIN32 OR UNIX)
# TARGET_COMPILE_OPTIONS(${BII_BLOCK_TARGET} INTERFACE "-std=c++11")
# ENDIF(APPLE)
#
# > EXAMPLE: Set properties to target
#
# SET_TARGET_PROPERTIES(${BII_BLOCK_TARGET} PROPERTIES COMPILE_DEFINITIONS "IOV_MAX=255")
#

44
biicode.conf Normal file
View File

@ -0,0 +1,44 @@
# Biicode configuration file
[requirements]
google/gtest: 10
messmer/cmake: 0
[parent]
# The parent version of this block. Must match folder name. E.g.
# user/block # No version number means not published yet
# You can change it to publish to a different track, and change version, e.g.
# user/block(track): 7
[paths]
# Local directories to look for headers (within block)
# /
# include
[dependencies]
# Manual adjust file implicit dependencies, add (+), remove (-), or overwrite (=)
# hello.h + hello_imp.cpp hello_imp2.cpp
# *.h + *.cpp
test/main.cpp + test/*.cpp
[mains]
# Manual adjust of files that define an executable
# !main.cpp # Do not build executable from this file
# main2.cpp # Build it (it doesnt have a main() function, but maybe it includes it)
[hooks]
# These are defined equal to [dependencies],files names matching bii*stage*hook.py
# will be launched as python scripts at stage = {post_process, clean}
# CMakeLists.txt + bii/my_post_process1_hook.py bii_clean_hook.py
[includes]
# Mapping of include patterns to external blocks
# hello*.h: user3/depblock # includes will be processed as user3/depblock/hello*.h
[data]
# Manually define data files dependencies, that will be copied to bin for execution
# By default they are copied to bin/user/block/... which should be taken into account
# when loading from disk such data
# image.cpp + image.jpg # code should write open("user/block/image.jpg")

17
macros.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#ifndef MESSMER_CPP_UTILS_MACROS_H_
#define MESSMER_CPP_UTILS_MACROS_H_
/**
* Disallow the copy and assignment constructors of a class
*/
#define DISALLOW_COPY_AND_ASSIGN(Class) \
Class(const Class &rhs) = delete; \
Class &operator=(const Class &rhs) = delete;
/**
* Declare a function parameter as intentionally unused to get rid of the compiler warning
*/
#define UNUSED(expr) (void)(expr)
#endif

43
optional_ownership_ptr.h Normal file
View File

@ -0,0 +1,43 @@
#pragma once
#ifndef MESSMER_CPP_UTILS_OPTIONALOWNERSHIPPOINTER_H_
#define MESSMER_CPP_UTILS_OPTIONALOWNERSHIPPOINTER_H_
#include <memory>
/**
* optional_ownership_ptr can be used to hold a pointer to an instance of an object.
* The pointer might or might not have ownership of the object.
*
* If it has ownership, it will delete the stored object in its destructor.
* If it doesn't have ownership, it won't.
*
* You can create such pointers with
* - WithOwnership(ptr)
* - WithoutOwnership(ptr)
* - null()
*/
namespace cpputils {
template<typename T>
using optional_ownership_ptr = std::unique_ptr<T, std::function<void(T*)>>;
template<typename T>
optional_ownership_ptr<T> WithOwnership(std::unique_ptr<T> obj) {
auto deleter = obj.get_deleter();
return optional_ownership_ptr<T>(obj.release(), [deleter](T* obj){deleter(obj);});
}
template<typename T>
optional_ownership_ptr<T> WithoutOwnership(T *obj) {
return optional_ownership_ptr<T>(obj, [](T*){});
}
template<typename T>
optional_ownership_ptr<T> null() {
return WithoutOwnership<T>(nullptr);
}
}
#endif

24
pointer.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#ifndef MESSMER_CPP_UTILS_POINTER_H_
#define MESSMER_CPP_UTILS_POINTER_H_
#include <memory>
namespace cpputils {
/**
* dynamic_cast implementation for unique_ptr (moving unique_ptr into a unique_ptr of different type)
*/
template<typename DST, typename SRC>
inline std::unique_ptr<DST> dynamic_pointer_move(std::unique_ptr<SRC> &source) {
//TODO Deleter
DST *casted = dynamic_cast<DST*>(source.get());
if (casted != nullptr) {
source.release();
}
return std::unique_ptr<DST>(casted);
}
}
#endif

6
test/main.cpp Normal file
View File

@ -0,0 +1,6 @@
#include "google/gtest/gtest.h"
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,112 @@
#include "google/gtest/gtest.h"
#include "../OptionalOwnershipPointer.h"
using namespace fspp;
using std::unique_ptr;
using std::function;
using ::testing::Test;
using fspp::ptr::optional_ownership_ptr;
#define UNUSED(x) ((void)x)
class TestObject {
public:
TestObject(function<void()> destructorListener) : _destructorListener(destructorListener) {}
virtual ~TestObject() {
_destructorListener();
}
private:
function<void()> _destructorListener;
};
class TestObjectHolder {
public:
TestObjectHolder()
: _isDestructed(false),
_testObject(new TestObject([this]() {_isDestructed = true;})) {
}
~TestObjectHolder() {
if (!_isDestructed) {
delete _testObject;
_isDestructed = true;
}
}
TestObject *get() {
return _testObject;
}
bool isDestructed() {
return _isDestructed;
}
private:
bool _isDestructed;
TestObject *_testObject;
};
class OptionalOwnershipPointerTest: public Test {
public:
TestObjectHolder obj;
TestObjectHolder obj2;
};
TEST_F(OptionalOwnershipPointerTest, TestIsInitializedCorrectly) {
EXPECT_FALSE(obj.isDestructed());
}
TEST_F(OptionalOwnershipPointerTest, DestructsWhenItHasOwnership) {
{
optional_ownership_ptr<TestObject> ptr = fspp::ptr::WithOwnership(unique_ptr<TestObject>(obj.get()));
UNUSED(ptr);
}
EXPECT_TRUE(obj.isDestructed());
}
TEST_F(OptionalOwnershipPointerTest, DestructsWhenItHasOwnershipAfterAssignment) {
{
optional_ownership_ptr<TestObject> ptr = fspp::ptr::WithoutOwnership(obj.get());
ptr = fspp::ptr::WithOwnership(unique_ptr<TestObject>(obj2.get()));
}
EXPECT_FALSE(obj.isDestructed());
EXPECT_TRUE(obj2.isDestructed());
}
TEST_F(OptionalOwnershipPointerTest, DoesntDestructWhenItDoesntHaveOwnership) {
{
optional_ownership_ptr<TestObject> ptr = fspp::ptr::WithoutOwnership(obj.get());
UNUSED(ptr);
}
EXPECT_FALSE(obj.isDestructed());
}
TEST_F(OptionalOwnershipPointerTest, DoesntDestructWhenItDoesntHaveOwnershipAfterAssignment) {
{
optional_ownership_ptr<TestObject> ptr = fspp::ptr::WithOwnership(unique_ptr<TestObject>(obj.get()));
ptr = fspp::ptr::WithoutOwnership(obj2.get());
EXPECT_TRUE(obj.isDestructed());
}
EXPECT_FALSE(obj2.isDestructed());
}
TEST_F(OptionalOwnershipPointerTest, DestructsOnReassignmentWithNull) {
optional_ownership_ptr<TestObject> ptr = fspp::ptr::WithOwnership(unique_ptr<TestObject>(obj.get()));
ptr = fspp::ptr::null<TestObject>();
EXPECT_TRUE(obj.isDestructed());
}
TEST_F(OptionalOwnershipPointerTest, DoesntCrashWhenDestructingNullptr1) {
optional_ownership_ptr<TestObject> ptr = fspp::ptr::null<TestObject>();
UNUSED(ptr);
}
TEST_F(OptionalOwnershipPointerTest, DoesntCrashWhenDestructingNullptrWithoutOwnership) {
optional_ownership_ptr<TestObject> ptr = fspp::ptr::WithoutOwnership((TestObject*)nullptr);
UNUSED(ptr);
}
TEST_F(OptionalOwnershipPointerTest, DoesntCrashWhenDestructingNullptrWithOwnership) {
optional_ownership_ptr<TestObject> ptr = fspp::ptr::WithOwnership(unique_ptr<TestObject>(nullptr));
UNUSED(ptr);
}

67
test/pointer_test.cpp Normal file
View File

@ -0,0 +1,67 @@
#include "google/gtest/gtest.h"
#include "../pointer.h"
using namespace fspp;
using std::unique_ptr;
using std::make_unique;
class Parent {
public:
virtual ~Parent() {}
};
class Child: public Parent {};
class Child2: public Parent {};
TEST(DynamicPointerMoveTest, NullPtrParentToChildCast) {
unique_ptr<Parent> source(nullptr);
unique_ptr<Child> casted = dynamic_pointer_move<Child>(source);
EXPECT_EQ(nullptr, source.get());
EXPECT_EQ(nullptr, casted.get());
}
TEST(DynamicPointerMoveTest, NullPtrChildToParentCast) {
unique_ptr<Child> source(nullptr);
unique_ptr<Parent> casted = dynamic_pointer_move<Parent>(source);
EXPECT_EQ(nullptr, source.get());
EXPECT_EQ(nullptr, casted.get());
}
TEST(DynamicPointerMoveTest, NullPtrSelfCast) {
unique_ptr<Parent> source(nullptr);
unique_ptr<Parent> casted = dynamic_pointer_move<Parent>(source);
EXPECT_EQ(nullptr, source.get());
EXPECT_EQ(nullptr, casted.get());
}
TEST(DynamicPointerMoveTest, ValidParentToChildCast) {
Child *obj = new Child();
unique_ptr<Parent> source(obj);
unique_ptr<Child> casted = dynamic_pointer_move<Child>(source);
EXPECT_EQ(nullptr, source.get()); // source lost ownership
EXPECT_EQ(obj, casted.get());
}
TEST(DynamicPointerMoveTest, InvalidParentToChildCast1) {
Parent *obj = new Parent();
unique_ptr<Parent> source(obj);
unique_ptr<Child> casted = dynamic_pointer_move<Child>(source);
EXPECT_EQ(obj, source.get()); // source still has ownership
EXPECT_EQ(nullptr, casted.get());
}
TEST(DynamicPointerMoveTest, InvalidParentToChildCast2) {
Child2 *obj = new Child2();
unique_ptr<Parent> source(obj);
unique_ptr<Child> casted = dynamic_pointer_move<Child>(source);
EXPECT_EQ(obj, source.get()); // source still has ownership
EXPECT_EQ(nullptr, casted.get());
}
TEST(DynamicPointerMoveTest, ChildToParentCast) {
Child *obj = new Child();
unique_ptr<Child> source(obj);
unique_ptr<Parent> casted = dynamic_pointer_move<Parent>(source);
EXPECT_EQ(nullptr, source.get()); // source lost ownership
EXPECT_EQ(obj, casted.get());
}