From 8863fac02234040207512cde61ca8e8fd68e05a9 Mon Sep 17 00:00:00 2001 From: Paulo Brizolara Date: Sun, 9 Oct 2016 18:53:35 -0300 Subject: [PATCH 1/2] Turning boost dependence optional. * Included in CMakeLists option to enable/disable use of boost (and check if it is found on system). * Modified 'error.h' to use a custom 'format' (using variadic template) instead of boost::format. * Created 'optional.h' to wrap 'boost/optional' (when using boost) or using custom optional (without boost). * Changed 'repo.cpp' to use it * Modified 'diff.cpp' to allow alternate between 'boost::flatmap'(when available) or 'std::unordered_map' * Modified examples (log.cpp and rev-parse.cpp) to align with the changes. --- CMakeLists.txt | 12 +++++- examples/log.cpp | 14 +++---- examples/rev-parse.cpp | 11 +++--- include/git2cpp/error.h | 39 ++++++++++++------- include/git2cpp/optional.h | 76 ++++++++++++++++++++++++++++++++++++++ src/diff.cpp | 34 +++++++++++++---- src/repo.cpp | 13 ++++--- src/tree.cpp | 2 +- 8 files changed, 160 insertions(+), 41 deletions(-) create mode 100644 include/git2cpp/optional.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c2b1653..f25643d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,10 @@ project (libgit2cpp) cmake_minimum_required(VERSION 2.8) +# Build options +OPTION( USE_BOOST "Enable use of boost header libraries" OFF ) + + set(CMAKE_CXX_FLAGS -std=c++11) file(GLOB lib_sources @@ -8,6 +12,12 @@ file(GLOB lib_sources include/git2cpp/*.h ) +IF(USE_BOOST) + add_definitions(-DUSE_BOOST=1) + find_package(Boost REQUIRED) + include_directories(${BOOST_INCLUDEDIR}) +ENDIF() + add_library(git2cpp ${lib_sources}) target_include_directories(git2cpp @@ -41,4 +51,4 @@ foreach (example ${examples}) target_link_libraries(${example} git2cpp) endforeach(example) -file(COPY test.sh DESTINATION . FILE_PERMISSIONS ${EXE_PERM}) \ No newline at end of file +file(COPY test.sh DESTINATION . FILE_PERMISSIONS ${EXE_PERM}) diff --git a/examples/log.cpp b/examples/log.cpp index 76a5084..0fd2aaf 100644 --- a/examples/log.cpp +++ b/examples/log.cpp @@ -3,8 +3,6 @@ #include #include -#include -#include #include @@ -16,6 +14,8 @@ #include "git2cpp/revwalker.h" #include "git2cpp/diff.h" +#include "git2cpp/optional.h" + static void usage(const char *message, const char *arg) { if (message && arg) @@ -26,11 +26,11 @@ static void usage(const char *message, const char *arg) exit(1); } -struct log_state +struct log_state { std::string repodir = "."; - boost::optional repo; - boost::optional walker; + git::internal::optional repo; + git::internal::optional walker; int hide = 0; git::revwalker::sorting::type sorting = git::revwalker::sorting::time; }; @@ -38,7 +38,7 @@ struct log_state static void set_sorting(struct log_state *s, git::revwalker::sorting::type sort_mode) { if (!s->repo) { - s->repo = boost::in_place(s->repodir); + s->repo = git::internal::in_place(s->repodir); } if (!s->walker) @@ -91,7 +91,7 @@ void add_revision(struct log_state *s, const char *revstr) int hide = 0; if (!s->repo) { - s->repo = boost::in_place(s->repodir); + s->repo = git::internal::in_place(s->repodir); } if (!revstr) { diff --git a/examples/rev-parse.cpp b/examples/rev-parse.cpp index 257f040..556d230 100644 --- a/examples/rev-parse.cpp +++ b/examples/rev-parse.cpp @@ -3,8 +3,7 @@ #include #include -#include -#include +#include #include "git2cpp/initializer.h" #include "git2cpp/repo.h" @@ -22,14 +21,14 @@ static void usage(const char *message, const char *arg) } struct parse_state { - boost::optional repo; - std::string repodir = "."; + std::shared_ptr repo; + std::string repodir = "."; }; void parse_revision(parse_state & ps, const char *revstr) { - if (!ps.repo) - ps.repo = boost::in_place(ps.repodir); + if (!ps.repo) + ps.repo = std::make_shared(ps.repodir); Revspec rs = ps.repo->revparse(revstr); diff --git a/include/git2cpp/error.h b/include/git2cpp/error.h index 623b3f5..12d3083 100644 --- a/include/git2cpp/error.h +++ b/include/git2cpp/error.h @@ -1,17 +1,30 @@ #pragma once #include - -#include +#include #include "id_to_str.h" namespace git { + namespace internal{ + inline void _format(std::stringstream & ss){ + } + template + inline void _format(std::stringstream & ss, T&&value, Args&&... args){ + ss << value; + _format(ss, std::forward(args)...); + } + template + inline std::string format(Args&&... args){ + std::stringstream ss; + _format(ss, std::forward(args)...); + return ss.str(); + } + } struct error_t : std::runtime_error { explicit error_t(std::string const & message) : std::runtime_error(message) {} - explicit error_t(boost::format const & message) : error_t(str(message)) {} }; struct repository_open_error : error_t @@ -88,7 +101,7 @@ namespace git struct revparse_error : error_t { explicit revparse_error(const char * spec) - : error_t(boost::format("Could not resolve %1%") % spec) + : error_t(internal::format("Could not resolve ", spec)) {} }; @@ -107,21 +120,21 @@ namespace git struct file_not_found_error : error_t { explicit file_not_found_error(const char * filepath) - : error_t(boost::format("file path \"%1%\" not found") % filepath) + : error_t(internal::format("file path \"",filepath,"\" not found")) {} }; struct ambiguous_path_error : error_t { explicit ambiguous_path_error(const char * filepath) - : error_t(boost::format("file path \"%1%\" is ambiguous") % filepath) + : error_t(internal::format("file path \"",filepath,"\" is ambiguous")) {} }; struct unknown_file_status_error : error_t { explicit unknown_file_status_error(const char * filepath) - : error_t(boost::format("unknown error during getting status for file \"%1%\"") % filepath) + : error_t(internal::format("unknown error during getting status for file \"",filepath,"\"")) {} }; @@ -143,7 +156,7 @@ namespace git struct non_commit_object_error : error_t { explicit non_commit_object_error(git_oid const & id) - : error_t(str(boost::format("object %1% is not a commit") % id_to_str(id))) + : error_t(internal::format("object ",id_to_str(id)," is not a commit")) {} }; @@ -182,11 +195,11 @@ namespace git struct merge_base_error : error_t { - merge_base_error(git_oid const & c1, git_oid const & c2) - : error_t(boost::format("Could not find merge base for commits %1% and % 2%") - % id_to_str(c1, 8) - % id_to_str(c2, 8)) - {} + merge_base_error(git_oid const & c1, git_oid const & c2) + : error_t(internal::format( + "Could not find merge base for commits ", id_to_str(c1, 8)," and ", id_to_str(c2, 8) + )) + {} }; struct config_open_error : error_t diff --git a/include/git2cpp/optional.h b/include/git2cpp/optional.h new file mode 100644 index 0000000..0e02ad8 --- /dev/null +++ b/include/git2cpp/optional.h @@ -0,0 +1,76 @@ +#ifndef OPTIONAL_H +#define OPTIONAL_H + +#ifdef USE_BOOST + #include + #include +#else + #include +#endif + +namespace git +{ + namespace internal{ + +#ifdef USE_BOOST + using namespace boost; +#else + using none_t = nullptr_t; + const auto none = nullptr; + + //Simple optional implementations + template + class optional{ + public: + using Pointer = std::shared_ptr; + + enum invalid_type1{}; + enum invalid_type2{}; + + typedef typename std::conditional::value, T, invalid_type1>::type MovableT; + typedef typename std::conditional::value, T, invalid_type2>::type CopyableT; + + template + static Pointer make(U&& u) { return Pointer();} //default case + static Pointer make(MovableT && u) { return std::make_shared(std::move(u));} + static Pointer make(CopyableT && u) { return std::make_shared(std::move(u));} + + public: + optional(none_t t = none){} + + optional(std::shared_ptr ptr) : ptr(ptr) + {} + + optional(T && v) + : ptr(make(std::forward(v))) + {} + optional(const T & v) + : ptr(make(std::forward(v))) + {} + + + T const* get_ptr() const {return ptr.get();} + T* get_ptr() {return ptr.get();}; + + T& operator*() const {return *ptr.get();} + T* operator->() const {return ptr.get();} + + bool is_initialized() const {return (bool)ptr;} + + operator bool() const { return (bool)ptr;} + private: + std::shared_ptr ptr; + }; + + + template + std::shared_ptr in_place(Args&&... args){ + return std::make_shared(std::forward(args)...); + } + +#endif + + }//namespace internal +}//namespace git + +#endif // OPTIONAL_H diff --git a/src/diff.cpp b/src/diff.cpp index e731422..3f9cc08 100644 --- a/src/diff.cpp +++ b/src/diff.cpp @@ -3,8 +3,12 @@ #include +#ifdef USE_BOOST #include #include +#else +#include +#endif namespace git { @@ -20,15 +24,31 @@ namespace git const type include_summary (GIT_DIFF_STATS_INCLUDE_SUMMARY); }} +#ifdef USE_BOOST + template + using map_container = boost::container::flat_map; +#else + struct EnumHash{ + template + std::size_t operator()(T t) const{ + return static_cast(t); + } + }; + + template + using map_container = std::unordered_map; +#endif + git_diff_format_t convert(format f) { - static const boost::container::flat_map converter - = boost::assign::list_of> - ( format::patch, GIT_DIFF_FORMAT_PATCH ) - ( format::patch_header, GIT_DIFF_FORMAT_PATCH_HEADER ) - ( format::raw, GIT_DIFF_FORMAT_RAW ) - ( format::name_only, GIT_DIFF_FORMAT_NAME_ONLY ) - ( format::name_status, GIT_DIFF_FORMAT_NAME_STATUS ) + static const map_container converter + = { + { format::patch, GIT_DIFF_FORMAT_PATCH }, + { format::patch_header, GIT_DIFF_FORMAT_PATCH_HEADER }, + { format::raw, GIT_DIFF_FORMAT_RAW }, + { format::name_only, GIT_DIFF_FORMAT_NAME_ONLY }, + { format::name_status, GIT_DIFF_FORMAT_NAME_STATUS } + } ; return converter.at(f); } diff --git a/src/repo.cpp b/src/repo.cpp index 7a5b7c2..77ae08b 100644 --- a/src/repo.cpp +++ b/src/repo.cpp @@ -8,12 +8,13 @@ #include #include -#include -#include +#include #include "git2cpp/repo.h" #include "git2cpp/error.h" +#include "git2cpp/optional.h" + namespace git { namespace @@ -162,13 +163,13 @@ namespace git { case GIT_OK: assert(type == type_); - ref_ = boost::in_place(ref); + ref_ = internal::in_place(ref); break; case GIT_ITEROVER: - ref_ = boost::none; + ref_ = internal::none; break; default: - ref_ = boost::none; + ref_ = internal::none; throw std::logic_error("unknown git_branch_next error"); } } @@ -194,7 +195,7 @@ namespace git private: git_branch_t type_; git_branch_iterator * base_; - boost::optional ref_; + internal::optional ref_; }; std::vector Repository::branches(branch_type type) const diff --git a/src/tree.cpp b/src/tree.cpp index 4eee6f0..a7bcacf 100644 --- a/src/tree.cpp +++ b/src/tree.cpp @@ -72,7 +72,7 @@ namespace git case GIT_ENOTFOUND: throw file_not_found_error(path); default: - throw error_t(boost::format("unknown error inside function: 'git_tree_entry_bypath': %1%") % status); + throw error_t(internal::format("unknown error inside function: 'git_tree_entry_bypath': ", status)); } } From 291b8ac34edb7f17e02dd2ad0baca6319bb6fd5e Mon Sep 17 00:00:00 2001 From: Paulo Brizolara Date: Tue, 18 Oct 2016 18:47:44 -0300 Subject: [PATCH 2/2] Enhancements to turn boost optional * Changed custom `optional` implementation to avoid dynamic allocation * Changed `format` implementation to use `std::snprintf` instead of stream * Using `boost::format` if using boost * Moved 'internal' headers to specific folder * Minor changes (in `examples/log.cpp` and `src/repo.cpp`) to adapt to new implementations. --- examples/log.cpp | 6 +- include/git2cpp/error.h | 30 ++--- include/git2cpp/internal/format.h | 39 +++++++ include/git2cpp/internal/optional.h | 172 ++++++++++++++++++++++++++++ include/git2cpp/optional.h | 76 ------------ src/repo.cpp | 4 +- 6 files changed, 224 insertions(+), 103 deletions(-) create mode 100644 include/git2cpp/internal/format.h create mode 100644 include/git2cpp/internal/optional.h delete mode 100644 include/git2cpp/optional.h diff --git a/examples/log.cpp b/examples/log.cpp index 0fd2aaf..3b814c3 100644 --- a/examples/log.cpp +++ b/examples/log.cpp @@ -14,7 +14,7 @@ #include "git2cpp/revwalker.h" #include "git2cpp/diff.h" -#include "git2cpp/optional.h" +#include "git2cpp/internal/optional.h" static void usage(const char *message, const char *arg) { @@ -38,7 +38,7 @@ struct log_state static void set_sorting(struct log_state *s, git::revwalker::sorting::type sort_mode) { if (!s->repo) { - s->repo = git::internal::in_place(s->repodir); + git::internal::emplace(s->repo, s->repodir); } if (!s->walker) @@ -91,7 +91,7 @@ void add_revision(struct log_state *s, const char *revstr) int hide = 0; if (!s->repo) { - s->repo = git::internal::in_place(s->repodir); + git::internal::emplace(s->repo, s->repodir); } if (!revstr) { diff --git a/include/git2cpp/error.h b/include/git2cpp/error.h index 12d3083..4ecda46 100644 --- a/include/git2cpp/error.h +++ b/include/git2cpp/error.h @@ -1,27 +1,13 @@ #pragma once #include -#include #include "id_to_str.h" +#include "internal/format.h" namespace git { - namespace internal{ - inline void _format(std::stringstream & ss){ - } - template - inline void _format(std::stringstream & ss, T&&value, Args&&... args){ - ss << value; - _format(ss, std::forward(args)...); - } - template - inline std::string format(Args&&... args){ - std::stringstream ss; - _format(ss, std::forward(args)...); - return ss.str(); - } - } + struct error_t : std::runtime_error { explicit error_t(std::string const & message) : std::runtime_error(message) {} @@ -101,7 +87,7 @@ namespace git struct revparse_error : error_t { explicit revparse_error(const char * spec) - : error_t(internal::format("Could not resolve ", spec)) + : error_t(internal::format("Could not resolve %s", spec)) {} }; @@ -120,21 +106,21 @@ namespace git struct file_not_found_error : error_t { explicit file_not_found_error(const char * filepath) - : error_t(internal::format("file path \"",filepath,"\" not found")) + : error_t(internal::format("file path \"%s\" not found", filepath)) {} }; struct ambiguous_path_error : error_t { explicit ambiguous_path_error(const char * filepath) - : error_t(internal::format("file path \"",filepath,"\" is ambiguous")) + : error_t(internal::format("file path \"%s\" is ambiguous", filepath)) {} }; struct unknown_file_status_error : error_t { explicit unknown_file_status_error(const char * filepath) - : error_t(internal::format("unknown error during getting status for file \"",filepath,"\"")) + : error_t(internal::format("unknown error during getting status for file \"%s\"", filepath)) {} }; @@ -156,7 +142,7 @@ namespace git struct non_commit_object_error : error_t { explicit non_commit_object_error(git_oid const & id) - : error_t(internal::format("object ",id_to_str(id)," is not a commit")) + : error_t(internal::format("object %s is not a commit", id_to_str(id).c_str())) {} }; @@ -197,7 +183,7 @@ namespace git { merge_base_error(git_oid const & c1, git_oid const & c2) : error_t(internal::format( - "Could not find merge base for commits ", id_to_str(c1, 8)," and ", id_to_str(c2, 8) + "Could not find merge base for commits %s and %s", id_to_str(c1, 8).c_str(), id_to_str(c2, 8).c_str() )) {} }; diff --git a/include/git2cpp/internal/format.h b/include/git2cpp/internal/format.h new file mode 100644 index 0000000..a49d18f --- /dev/null +++ b/include/git2cpp/internal/format.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#ifdef USE_BOOST +#include +#endif + +namespace git { +namespace internal { + +#ifndef USE_BOOST + template + inline std::string format(const char * fmt, Args&& ... args) + { + auto size = std::snprintf(nullptr, 0, fmt, args...); + std::string result(size, 0); + std::snprintf(&result[0], size + 1, fmt, args...); + return result; + } +#else + inline boost::format & format_apply(boost::format & fmt){ + return fmt; + } + + template + inline boost::format & format_apply(Format && fmt, T && data, Args&& ... args){ + return format_apply(fmt % data, std::forward(args)...); + } + + template + inline std::string format(const char * fmt, Args&& ... args){ + return boost::str(format_apply(boost::format(fmt), std::forward(args)...)); + } +#endif + +}//namespace +}//namespace diff --git a/include/git2cpp/internal/optional.h b/include/git2cpp/internal/optional.h new file mode 100644 index 0000000..e337f75 --- /dev/null +++ b/include/git2cpp/internal/optional.h @@ -0,0 +1,172 @@ +#pragma once + +#ifdef USE_BOOST + #include + #include +#else + #include + #include +#endif + +namespace git +{ + namespace internal{ + +#ifdef USE_BOOST + using boost::optional; + using boost::in_place; + using boost::none_t; + using boost::none; + + template + optional & emplace(optional & opt, Args&&... args){ + opt = in_place(std::forward(args)...); + return opt; + } +#else + typedef nullptr_t none_t; + const constexpr none_t none = nullptr; + + //Simple (and incomplete) optional implementations + template + class optional{ + public: //type alias + typedef typename std::aligned_storage::type + StorageT; + using optionalT = optional; + + public: //type traits + enum non_movable_t{}; + enum non_copyable_t{}; + + static const constexpr bool movable = std::is_move_constructible::value && std::is_move_assignable::value; + static const constexpr bool copyable = std::is_copy_constructible::value && std::is_copy_assignable::value; + typedef typename std::conditional::type MovableT; + typedef typename std::conditional::type CopyableT; + + public: //initializing and attribution + constexpr + optional(none_t t = none){} + optional(const T & v) { assign(v);} + optional(T && v) { assign(std::forward(v));} + + optional(const optionalT & other){ assign(other);} + optional( optionalT && other){ assign(std::move(other));} + + optionalT & operator=(none_t none) { reset(); return *this;} + optionalT & operator=(T && v) { return assign(std::forward(v));} + optionalT & operator=(const T & v) { return assign(v);} + + optionalT & operator=(const optionalT & other){ return assign(other);} + optionalT & operator=( optionalT && other){ return assign(std::move(other));} + + ~optional() { reset(); } + + public: //getting value + T const* get_ptr() const {return reinterpret_cast(&storage);} + T* get_ptr() {return reinterpret_cast(&storage);} + + const + T& value() const {return *get_ptr();} + T& value() {return *get_ptr();} + + const + T& operator*() const {return value();} + T& operator*() {return value();} + + const + T* operator->() const {return get_ptr();} + T* operator->() {return get_ptr();} + + public: //querying state + bool is_initialized() const {return initialized;} + + operator bool() const { return is_initialized();} + + public: //change state + void reset(){ + if(is_initialized()){ + get_ptr()->~T(); + initialized = false; + } + } + + template + void emplace(Args&&... args){ + if(is_initialized()){ + reset(); + } + init(std::forward(args)...); + } + + protected: //assign + + template + optionalT & assign(U&& u) { + static constexpr const bool same_class = std::is_same, T&>::value; + static_assert(!same_class , "Invalid type"); + static_assert(same_class && !copyable, "Type is non copyable"); + static_assert(same_class && !movable , "Type is non movable"); + + return *this; + } //default case + + optionalT & assign(MovableT && value) { + return set_from_value(std::move(value)); + } + optionalT & assign(const CopyableT & value) { + return set_from_value(value); + } + optionalT & assign(const optionalT & other) { + return set_or_reset(other.value(), other.is_initialized()); + } + optionalT & assign(optionalT && other) { + return set_or_reset(std::move(other.value()), other.is_initialized()); + } + + protected: + template + optionalT & set_or_reset(U&& u, bool isValid) { + if(isValid){ + set_from_value(std::forward(u)); + } + else{ + reset(); + } + + return *this; + } + + template + optionalT & set_from_value(U&& u) { + if(is_initialized()){ + reinterpret_cast(storage) = std::forward(u); + } + else{ + init(std::forward(u)); + } + + return *this; + } + + template + void init(Args&& ... args){ + new (&storage) T(std::forward(args)...); + initialized = true; + } + + protected: + StorageT storage; + bool initialized=false; + }; + + template + optional & emplace(optional & opt, Args&&... args){ + opt.emplace(std::forward(args)...); + return opt; + } + +#endif + + }//namespace internal +}//namespace git diff --git a/include/git2cpp/optional.h b/include/git2cpp/optional.h deleted file mode 100644 index 0e02ad8..0000000 --- a/include/git2cpp/optional.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef OPTIONAL_H -#define OPTIONAL_H - -#ifdef USE_BOOST - #include - #include -#else - #include -#endif - -namespace git -{ - namespace internal{ - -#ifdef USE_BOOST - using namespace boost; -#else - using none_t = nullptr_t; - const auto none = nullptr; - - //Simple optional implementations - template - class optional{ - public: - using Pointer = std::shared_ptr; - - enum invalid_type1{}; - enum invalid_type2{}; - - typedef typename std::conditional::value, T, invalid_type1>::type MovableT; - typedef typename std::conditional::value, T, invalid_type2>::type CopyableT; - - template - static Pointer make(U&& u) { return Pointer();} //default case - static Pointer make(MovableT && u) { return std::make_shared(std::move(u));} - static Pointer make(CopyableT && u) { return std::make_shared(std::move(u));} - - public: - optional(none_t t = none){} - - optional(std::shared_ptr ptr) : ptr(ptr) - {} - - optional(T && v) - : ptr(make(std::forward(v))) - {} - optional(const T & v) - : ptr(make(std::forward(v))) - {} - - - T const* get_ptr() const {return ptr.get();} - T* get_ptr() {return ptr.get();}; - - T& operator*() const {return *ptr.get();} - T* operator->() const {return ptr.get();} - - bool is_initialized() const {return (bool)ptr;} - - operator bool() const { return (bool)ptr;} - private: - std::shared_ptr ptr; - }; - - - template - std::shared_ptr in_place(Args&&... args){ - return std::make_shared(std::forward(args)...); - } - -#endif - - }//namespace internal -}//namespace git - -#endif // OPTIONAL_H diff --git a/src/repo.cpp b/src/repo.cpp index 77ae08b..945a11c 100644 --- a/src/repo.cpp +++ b/src/repo.cpp @@ -13,7 +13,7 @@ #include "git2cpp/repo.h" #include "git2cpp/error.h" -#include "git2cpp/optional.h" +#include "git2cpp/internal/optional.h" namespace git { @@ -163,7 +163,7 @@ namespace git { case GIT_OK: assert(type == type_); - ref_ = internal::in_place(ref); + internal::emplace(ref_, ref); break; case GIT_ITEROVER: ref_ = internal::none;