From 8dd4cf7f72da4ec82379dd34d7a99781f3834cbb Mon Sep 17 00:00:00 2001 From: Mattia Basaglia Date: Sat, 26 Sep 2015 21:22:28 +0200 Subject: [PATCH] GSL implementation --- CMakeLists.txt | 16 +++ include/misclib/gsl.hpp | 302 +++++++++++++++++++++++++++++++++++++++ include/misclib/math.hpp | 110 ++++++++++++++ include/misclib/util.hpp | 77 ++++++++++ test/CMakeLists.txt | 64 +++++++++ test/gsl.cpp | 136 ++++++++++++++++++ 6 files changed, 705 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 include/misclib/gsl.hpp create mode 100644 include/misclib/math.hpp create mode 100644 include/misclib/util.hpp create mode 100644 test/CMakeLists.txt create mode 100644 test/gsl.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..65b6ae8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright (C) 2015 Mattia Basaglia +# +# This program 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 program 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 . + +add_subdirectory(test) diff --git a/include/misclib/gsl.hpp b/include/misclib/gsl.hpp new file mode 100644 index 0000000..5dd4071 --- /dev/null +++ b/include/misclib/gsl.hpp @@ -0,0 +1,302 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2015 Mattia Basaglia + * + * This program 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 program 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 . + */ +#ifndef MISCLIB_GSL_HPP +#define MISCLIB_GSL_HPP + +#include +#include +#include +#include +#include +#include +#include + +#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304 +# define CONSTEXPR14 constexpr +#else +# define CONSTEXPR14 +#endif + +/** + * \brief A simple GSL implementation for C++11 (and C++14) + * \see https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#gsl-guideline-support-library + */ +namespace gsl { + +template + using owner = T; + +template + constexpr owner move_owner(owner&& owner) + { + return std::move(owner); + } + +using zstring = char*; +using czstring = const char*; + +using std::unique_ptr; +using std::shared_ptr; + +template + using stack_array = std::array; + +/// \todo +template + using dyn_array = std::vector; + + +/// \todo Macros to disable expression evaluation +inline CONSTEXPR14 void Expects(bool expression, czstring message = nullptr) +{ + if ( !expression ) + throw std::logic_error(message ? message : ""); +} + +inline CONSTEXPR14 void Ensures(bool expression, czstring message = nullptr) +{ + if ( !expression ) + throw std::logic_error(message ? message : ""); +} + +template + class not_null +{ +public: + CONSTEXPR14 explicit not_null(const T& ptr) + : ptr(ptr) + { + Expects(this->ptr != nullptr); + } + CONSTEXPR14 explicit not_null(T&& ptr) + : ptr(std::move(ptr)) + { + Expects(this->ptr != nullptr); + } + not_null(const std::nullptr_t&) = delete; + not_null(int) = delete; + not_null(const not_null &other) = default; + not_null(not_null &&other) = default; + + CONSTEXPR14 not_null& operator=(const T& ptr) + { + Expects(ptr != nullptr); + this->ptr = ptr; + return *this; + } + CONSTEXPR14 not_null& operator=(T&& ptr) + { + Expects(ptr != nullptr); + this->ptr = std::move(ptr); + return *this; + } + not_null& operator=(const std::nullptr_t&) = delete; + not_null& operator=(int) = delete; + CONSTEXPR14 not_null& operator=(const not_null &other) = default; + CONSTEXPR14 not_null& operator=(not_null &&other) = default; + + CONSTEXPR14 operator T() const { return ptr; } + CONSTEXPR14 T operator->() const { return ptr; } + CONSTEXPR14 decltype(*std::declval()) operator*() const { return *ptr; } + + CONSTEXPR14 bool operator==(const T& rhs) const { return ptr == rhs; } + CONSTEXPR14 bool operator!=(const T& rhs) const { return !(*this == rhs); } + +private: + T ptr; +}; + +template + class array_view +{ +public: + using value_type = T; + using reference = value_type&; + using pointer = value_type*; + using iterator = T*; + using const_iterator = const T*; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using difference_type = typename std::iterator_traits::difference_type; + using size_type = std::size_t; + + CONSTEXPR14 array_view(pointer begin, pointer end) + : begin_(begin), end_(end) + { + Expects(begin <= end); + } + + CONSTEXPR14 array_view(pointer begin, size_type size) + : begin_(begin), end_(begin+size) + { + Expects(size == 0 || (size > 0 && begin != nullptr)); + } + + constexpr array_view() + : begin_(nullptr), end_(nullptr) + { + } + + template + CONSTEXPR14 array_view(T (&array)[size]) + : array_view(array, size) + {} + + template::value + >::type> + array_view(const Iterator& begin, const Iterator& end) + : array_view(&*begin, &*end) + { + Expects(std::distance(begin, end) == &*end - &*begin); + } + + template::value + >::type> + array_view(Container& container) + : array_view(container.data(), container.size()) + { + } + + constexpr size_type size() const + { + return end_ - begin_; + } + + constexpr bool empty() const + { + return end_ == begin_; + } + + CONSTEXPR14 reference operator[] (size_type i) const + { + Expects(i >= 0 && i < size()); + return begin_[i]; + } + + constexpr explicit operator bool() const + { + return begin_; + } + + constexpr iterator begin() const + { + return begin_; + } + + constexpr iterator end() const + { + return end_; + } + + constexpr const_iterator cbegin() const + { + return begin_; + } + + constexpr const_iterator cend() const + { + return end_; + } + + constexpr const_iterator rbegin() const + { + return reverse_iterator(end_); + } + + constexpr const_iterator rend() const + { + return reverse_iterator(begin_); + } + + constexpr const_reverse_iterator crbegin() const + { + return reverse_iterator(cbegin()); + } + + constexpr const_reverse_iterator crend() const + { + return reverse_iterator(cbegin()); + } + + constexpr bool operator== (const array_view& rhs) const + { + return begin_ == rhs.begin_ && end_ == rhs.end_; + } + + constexpr bool operator!= (const array_view& rhs) const + { + return !(*this == rhs); + } + +private: + T* begin_; + T* end_; +}; + +/// \todo +template + class array_view_p; + +using string_view = array_view; +using cstring_view = array_view; + +template + class Final_act +{ +public: + explicit Final_act(const Functor& func) : func(func) {} + ~Final_act() { func(); } + +private: + Functor func; +}; + +template + inline Final_act finally(const Functor& func) + { + return Final_act(func); + } + +template + T narrow_cast(U value) + { + return static_cast(value); + } + +class narrowing_error : public std::exception {}; + +template + T narrow(U value) + { + auto narrowed = narrow_cast(value); + if ( !(narrowed == value) ) + throw narrowing_error{}; + return narrowed; + } + +#define implicit + +} // namespace gsl +#endif // MISCLIB_GSL_HPP diff --git a/include/misclib/math.hpp b/include/misclib/math.hpp new file mode 100644 index 0000000..c959d33 --- /dev/null +++ b/include/misclib/math.hpp @@ -0,0 +1,110 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2015 Mattia Basaglia + * + * This program 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 program 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 . + */ +#ifndef MISCLIB_MATH_HPP +#define MISCLIB_MATH_HPP + +#include + +namespace math { + +/** + * \brief Maximum between two values + */ +template + inline constexpr T max(T&& a, T&& b) + { + return a < b ? b : a; + } + +/** + * \brief Maximum among several values + */ +template + inline constexpr T max(T&& a, Ts&&... b) + { + return max(std::forward(a), max(std::forward(b)...)); + } + +/** + * \brief Minimum between two values + */ +template + inline constexpr T min(T&& a, T&& b) + { + return !(a < b) ? b : a; + } + +/** + * \brief Minimum among several values + */ +template + inline constexpr T min(T&& a, Ts&&... b) + { + return min(std::forward(a), min(std::forward(b)...)); + } + +/** + * \brief Absolute value + */ +template + inline constexpr T abs(T&& x) + { + return x < 0 ? -x : x; + } + +/** + * \brief Limit \p value to be in [min_value, max_value] + * \pre min_value < max_value + * \post value in [min_value, max_value] + */ +template + inline constexpr T bound(T&& min_value, T&& value, T&& max_value) + { + return max(std::forward(min_value), + min(std::forward(value), std::forward(max_value)) + ); + } + +/** + * \brief Normalize a value + * \pre value in [min, max] && min < max + * \post value in [0, 1] + */ +template + inline constexpr Real normalize(Real value, Real min, Real max) +{ + return (value - min) / (max - min); +} + +/** + * \brief Denormalize a value + * \pre value in [0, 1] && min < max + * \post value in [min, max] + */ +template + inline constexpr Real denormalize(Real value, Real min, Real max) +{ + return value * (max - min) + min; +} + + +} // namespace math +#endif // MISCLIB_MATH_HPP diff --git a/include/misclib/util.hpp b/include/misclib/util.hpp new file mode 100644 index 0000000..a0fe7e1 --- /dev/null +++ b/include/misclib/util.hpp @@ -0,0 +1,77 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2015 Mattia Basaglia + * + * This program 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 program 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 . + */ +#ifndef MISCLIB_UTIL_HPP +#define MISCLIB_UTIL_HPP + +#include +#include + +namespace util { + +/** + * \brief Utility to get a cleaner syntax when disambiguating function pointers + */ +template + auto overload(R (C::*f)(Args...)) -> R (C::*)(Args...) + { + return f; + } + +/** + * \brief Determine the distance between a data member and its object + */ +template + std::size_t offset_of(const Class* object, Type Class::* member) + { + return reinterpret_cast(&(object->*member)) - + reinterpret_cast(object); + } + +/** + * \brief Template to retrieve information about a function signature + * + * Use as FunctionSignature or FunctionSignature + */ +template + struct FunctionSignature; + +template + struct FunctionSignature + { + using pointer_type = Ret (*) (Args...); + using return_type = Ret; + using arguments_types = std::tuple; + }; + +template + struct FunctionSignature + : public FunctionSignature + { + }; + +/** + * \brief Clean syntax to get a function pointer type + */ +template + using FunctionPointer = typename FunctionSignature::pointer_type; + +} // namespace util +#endif // MISCLIB_UTIL_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..e2dc8b8 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,64 @@ +# Copyright (C) 2015 Mattia Basaglia +# +# This program 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 program 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(CTest) + +set(Boost_FOUND ON) + +# Boost +find_package (Boost COMPONENTS unit_test_framework) + +#Tests +if (Boost_FOUND) + +include_directories (${Boost_INCLUDE_DIRS}) +add_definitions (-DBOOST_TEST_DYN_LINK) + +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address --coverage -O0 -fno-inline") +set(MISCLIB_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/../include/misclib") +include_directories(${MISCLIB_SOURCES}/..) + +message(STATUS "Test targets for Misclib enabled") +enable_testing() + +set(TEST_TARGETS "") +function(test test_name) + add_executable(${test_name} EXCLUDE_FROM_ALL ${ARGN}) + target_link_libraries(${test_name} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + add_test (NAME ${test_name} COMMAND ${test_name}) + set(TEST_TARGETS ${TEST_TARGETS} ${test_name} PARENT_SCOPE) +endfunction(test) + +test(misclib_test_gsl ${MISCLIB_SOURCES}/gsl.hpp gsl.cpp) + +# Coverage +add_custom_target(misclib_test_all + DEPENDS ${TEST_TARGETS} + COMMENT "Building all tests" +) + +set(TRACEFILE "${CMAKE_CURRENT_BINARY_DIR}/coverage.info") +add_custom_target(misclib_coverage + COMMAND cd "${CMAKE_CURRENT_BINARY_DIR}" + COMMAND ctest -V + COMMAND lcov -c -d "${CMAKE_CURRENT_BINARY_DIR}" -b "${MISCLIB_SOURCES}" -o ${TRACEFILE} --no-external + COMMAND genhtml ${TRACEFILE} -o "${CMAKE_CURRENT_BINARY_DIR}/lconv" -p "${MISCLIB_SOURCES}" --demangle-cpp + DEPENDS ${TEST_TARGETS} +) + +add_custom_target(misclib_coverage_view + COMMAND xdg-open "${CMAKE_CURRENT_BINARY_DIR}/lconv/index.html" +) + +endif() diff --git a/test/gsl.cpp b/test/gsl.cpp new file mode 100644 index 0000000..b91c5f9 --- /dev/null +++ b/test/gsl.cpp @@ -0,0 +1,136 @@ +/** + * \file + * + * \author Mattia Basaglia + * + * \copyright Copyright (C) 2015 Mattia Basaglia + * + * This program 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 program 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 . + * + */ + +#define BOOST_TEST_MODULE Test_Formatter + +#include + +#include "misclib/gsl.hpp" +#include +#include +#include +#include + +BOOST_AUTO_TEST_CASE( test_owner ) +{ + gsl::owner owner = new int{42}; + BOOST_CHECK(owner); + BOOST_CHECK(*owner == 42); + int* not_owner = owner; + BOOST_CHECK(*not_owner == 42); + BOOST_CHECK(not_owner == owner); + delete owner; +} + +unsigned subtract(unsigned a, unsigned b) +{ + gsl::Expects(b > 0); + auto r = a - b; + gsl::Ensures(r < a); + return r; +} + +BOOST_AUTO_TEST_CASE( test_pre_post ) +{ + BOOST_CHECK(subtract(5, 4) == 1); + BOOST_CHECK_THROW(subtract(5, 0), std::exception); + BOOST_CHECK_THROW(subtract(5, 6), std::exception); +} + +BOOST_AUTO_TEST_CASE( test_not_null ) +{ + int* null = nullptr; + int val = 5; + int* not_null = &val; + gsl::not_null ptr(&val); + int* maybe_null; + BOOST_CHECK_THROW( gsl::not_null foo(null), std::exception); + BOOST_CHECK( gsl::not_null(not_null) == not_null ); + BOOST_CHECK( ptr ); + BOOST_CHECK( *ptr == val ); + BOOST_CHECK( ptr == &val ); + BOOST_CHECK( ptr != null ); + BOOST_CHECK_THROW( ptr = null, std::exception); + BOOST_CHECK( ptr = not_null ); + BOOST_CHECK( ptr == &val ); + BOOST_CHECK( maybe_null = ptr ); + BOOST_CHECK( *maybe_null = val ); + /// \todo Test with smart pointers +} + +int fifth(const gsl::array_view& array) +{ + return array[5]; +} + +BOOST_AUTO_TEST_CASE( test_array_view ) +{ + using array_view = gsl::array_view; + std::vector intv = { 1, 2, 3, 4, 5, 6, 7, 8 }; + std::array inta = { 1, 2, 3, 4, 5, 6, 7, 8 }; + std::list intl = { 1, 2, 3, 4, 5, 6, 7, 8 }; + int intca[8] = { 1, 2, 3, 4, 5, 6, 7, 8 }; + + BOOST_CHECK(fifth(intv) == 6); + BOOST_CHECK(fifth(inta) == 6); + BOOST_CHECK(fifth(intca) == 6); + BOOST_CHECK(fifth({intca, 8}) == 6); + BOOST_CHECK_THROW(fifth({intl.begin(), intl.end()}), std::exception); + BOOST_CHECK_THROW(fifth({intca, 4}), std::exception); + + array_view view = intv; + array_view view2{intv.begin(), intv.end()}; + BOOST_CHECK(view); + BOOST_CHECK(view.begin() == &intv.front()); + BOOST_CHECK(view.end() == &intv.back()+1); + BOOST_CHECK(view.size() == intv.size()); + BOOST_CHECK(view == view2); + view = inta; + BOOST_CHECK(view.begin() == &inta.front()); + BOOST_CHECK(view.end() == &inta.back()+1); + BOOST_CHECK(view.size() == inta.size());; + BOOST_CHECK(view != view2); + view = {intca, std::size_t(0)}; + BOOST_CHECK(view.begin() == intca); + BOOST_CHECK(view.size() == 0); +} + +BOOST_AUTO_TEST_CASE( test_finally ) +{ + int i = 0; + auto lambda = [&i]{++i;}; + + { + auto f = gsl::finally(lambda); + BOOST_CHECK(i == 0); + } + + BOOST_CHECK(i == 1); +} + +BOOST_AUTO_TEST_CASE( test_narrow ) +{ + long big = std::numeric_limits::max(); + big *= 2; + BOOST_CHECK_THROW(gsl::narrow(big), gsl::narrowing_error); + BOOST_CHECK(gsl::narrow(big) == big); +}