Skip to content

Turning boost dependence optional. - #3

Closed
p-brz wants to merge 2 commits into
AndreyG:masterfrom
p-brz:master
Closed

Turning boost dependence optional.#3
p-brz wants to merge 2 commits into
AndreyG:masterfrom
p-brz:master

Conversation

@p-brz

@p-brz p-brz commented Oct 9, 2016

Copy link
Copy Markdown

Hello @AndreyG,
i have found your library, while searching to some c++ wrapper for libgit2.
Btw, congratulations, it looks very nice.

Well, i saw (when trying to compile it) that there was some dependencies with boost.
To avoid requiring boost (i plan to build a library that uses libgit2cpp), i am submitting these changes.
As you can see, is still possible to use boost with a build option (-DUSE_BOOST=ON). But, now is an optional requirement.

I hope that you find useful.
Below the detailed changes.


  • 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.

* 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.
@AndreyG

AndreyG commented Oct 10, 2016

Copy link
Copy Markdown
Owner

Thank you for the contribution!
I used to use boost in the every C++ project since pre C++11 epoch, but you are right, the dependency can be avoided on such a big framework as boost for such a small library as libgit2cpp.
Is it acceptable for you to use std::experimental::optional instead of boost::optional? Which compliers and versions support is required?
I found a few drawbacks in your git::internal::optional implementation. First of all it has semantics of shared ownership, but optional shouldn't. Secondly, I'm afraid about perfomance penalties: optional doesn't make memory allocation. Thirdly, if you choose to allocate memory in the heap and use smart pointer why is it shared_ptr but not unique_ptr?
I'm also not sure about boost::format replacement by std::stringstream, may be something simple using C++11 std::snprintf will be be appropriate.
If boost::container::flat_map will be the only dependency on boost, no doubts, it can be replaced by std::unordered_map.

@p-brz

p-brz commented Oct 10, 2016

Copy link
Copy Markdown
Author

Thanks for the feedback.

About using std::experimental::optional, the problem is that i don't know how well it is supported. Testing on my machine (with gcc 4.8.4), for example, looks like it is not supported. But is possible to use conditional compilation to allow using it (however, i can not do it, since i can not test it).

Thanks for the tips about my handcrafted optional. I have not used this class before, so i was not sure about the semantics behind it. I don't think that it could impact so much the performance. But, if this is a concern, i think i can make an implementation that uses local memory (inside the object).

In relation to snprintf vs stringstream, there is an annoying thing: the need to preallocate the buffer. So, i would need to allocate a big buffer and then convert it to string, or something like that. An alternative would be use a string::append. Ex.:

...
      explicit commit_tree_error(git_oid const & id)
         : error_t(std::string("Could not get tree for commit ").append(id_to_str(id)))
      {}

This is simpler. However i think is more ugly and i don't know how much faster.
Considering that this will be called only in exceptional instances (when an error is thrown), i would not worry about it.

One last thing. Do you wanna remove the boost dependence completely? Or do you prefer keeping it as optional dependence?

@AndreyG

AndreyG commented Oct 10, 2016

Copy link
Copy Markdown
Owner

Yes, I'd like to see implementation of the class optional without memory allocation, it should be rather simple using std::aligned_storage.
It's possible to implement format using std::snprintf without preallocation:

template<typename... Args>
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;
}

I prefer to keep optional dependency on Boost, and I imagine that libgit2cpp will contain some code like the following

namespace git {
namespace internal
{
#if [std implementation available]
    using std::optional;
#elif [std::experimental implementation available]
    using std::experimental::optional;
#elif [boost available]
    using boost::optional;
#else
    own optional implementation
#endif

#if [boost available]
    namespace details
    {
          template<typename... Args>
          boost::format eat_args(boost::format fmt, Args&& ... args);

          boost::format eat_args(boost::format fmt)
          {
                return std::move(fmt);
          }

          template<typename Arg, typename... Rest>
          boost::format eat_args(boost::format fmt, Arg && arg, Rest&& ... rest)
          {
                return eat_args(fmt % arg, rest...);
          }
    }
#endif

    template<typename... Args>
    std::string format(const char * fmt, Args&& ... args)
    {
#if [boost available]
         return str(details::eat_args(boost::format(fmt), args...));
#else
         own implementation
#endif
   }
}}

* 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.
@p-brz

p-brz commented Oct 18, 2016

Copy link
Copy Markdown
Author

I finished the changes we discussed.
I think the only thing to note is that i moved the headers that contain internal details
(format.h and optional.h) to include/internal.
That way, becomes explicit what are internal details.

@AndreyG

AndreyG commented Oct 19, 2016

Copy link
Copy Markdown
Owner

I squashed your commits simplified optional implementation a little, slightly reformated code and merged into master.

@AndreyG AndreyG closed this Oct 19, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants