Permalink
Browse files

Implementing unique/shared_ptr for custom allocators (like Arena) in …

…folly

Summary:
- moving simple allocator *_ptr and convenience functions to folly
- getting rid of arena_new - it encourages manually constructing smart pointers when folly::allocate_* should be used instead
- using std::allocate_shared to construct shared_ptrs, thus properly using the allocator for the ref-counter too
- fixing forwarding of parameters in the convenience functions
- uniform allocation of smart pointers using both stl and non stl-like allocators

Test Plan:
- build + tests of existing client code
- basic unit tests for arena smart pointers

Reviewed By: delong.j@fb.com

FB internal diff: D672818
  • Loading branch information...
1 parent fe26f5b commit cc86cd35792f320d4111fac91ca88f1733266a8a @juchem juchem committed with jdelong Jan 8, 2013
Showing with 502 additions and 5 deletions.
  1. +133 −4 folly/StlAllocator.h
  2. +69 −1 folly/Traits.h
  3. +178 −0 folly/test/ArenaSmartPtrTest.cpp
  4. +122 −0 folly/test/HasMemberFnTraitsTest.cpp
View
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 Facebook, Inc.
+ * Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,14 +17,22 @@
#ifndef FOLLY_STLALLOCATOR_H_
#define FOLLY_STLALLOCATOR_H_
+#include "folly/Traits.h"
+
#include <memory>
+#include <limits>
+#include <utility>
+#include <exception>
+#include <stdexcept>
+
+#include <cstddef>
namespace folly {
/**
- * Wrap a simple allocator into a STL-compliant allocator.
+ * Wrap a SimpleAllocator into a STL-compliant allocator.
*
- * The simple allocator must provide two methods:
+ * The SimpleAllocator must provide two methods:
* void* allocate(size_t size);
* void deallocate(void* ptr, size_t size);
* which, respectively, allocate a block of size bytes (aligned to the maximum
@@ -45,6 +53,8 @@ namespace folly {
* free(p);
* }
* };
+ *
+ * author: Tudor Bosman <tudorb@fb.com>
*/
// This would be so much simpler with std::allocator_traits, but gcc 4.6.2
@@ -128,7 +138,126 @@ class StlAllocator {
Alloc* alloc_;
};
+/*
+ * Helper classes/functions for creating a unique_ptr using a custom allocator
+ *
+ * @author: Marcelo Juchem <marcelo@fb.com>
+ */
+
+// A deleter implementation based on std::default_delete,
+// which uses a custom allocator to free memory
+template <typename Allocator>
+class allocator_delete {
+ typedef typename std::remove_reference<Allocator>::type allocator_type;
+
+public:
+ allocator_delete() = default;
+
+ explicit allocator_delete(const allocator_type& allocator):
+ allocator_(allocator)
+ {}
+
+ explicit allocator_delete(allocator_type&& allocator):
+ allocator_(std::move(allocator))
+ {}
+
+ template <typename U>
+ allocator_delete(const allocator_delete<U>& other):
+ allocator_(other.get_allocator())
+ {}
+
+ allocator_type& get_allocator() const {
+ return allocator_;
+ }
+
+ void operator()(typename allocator_type::pointer p) const {
+ if (!p) {
+ return;
+ }
+
+ allocator_.destroy(p);
+ allocator_.deallocate(p, 1);
+ }
+
+private:
+ mutable allocator_type allocator_;
+};
+
+template <typename T, typename Allocator>
+class is_simple_allocator {
+ FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_destroy, destroy);
+
+ typedef typename std::remove_const<
+ typename std::remove_reference<Allocator>::type
+ >::type allocator;
+ typedef T value_type;
+ typedef value_type* pointer;
+
+public:
+ constexpr static bool value = !has_destroy<allocator, void(pointer)>::value
+ && !has_destroy<allocator, void(void*)>::value;
+};
+
+template <typename T, typename Allocator>
+typename std::enable_if<
+ is_simple_allocator<T, Allocator>::value,
+ folly::StlAllocator<typename std::remove_reference<Allocator>::type, T>
+>::type make_stl_allocator(Allocator&& allocator) {
+ return folly::StlAllocator<
+ typename std::remove_reference<Allocator>::type, T
+ >(&allocator);
+}
+
+template <typename T, typename Allocator>
+typename std::enable_if<
+ !is_simple_allocator<T, Allocator>::value,
+ typename std::remove_reference<Allocator>::type
+>::type make_stl_allocator(Allocator&& allocator) {
+ return std::move(allocator);
+}
+
+template <typename T, typename Allocator>
+struct AllocatorUniquePtr {
+ typedef std::unique_ptr<T,
+ folly::allocator_delete<
+ typename std::conditional<
+ is_simple_allocator<T, Allocator>::value,
+ folly::StlAllocator<typename std::remove_reference<Allocator>::type, T>,
+ typename std::remove_reference<Allocator>::type
+ >::type
+ >
+ > type;
+};
+
+template <typename T, typename Allocator, typename ...Args>
+typename AllocatorUniquePtr<T, Allocator>::type allocate_unique(
+ Allocator&& allocator, Args&&... args
+) {
+ auto stlAllocator = folly::make_stl_allocator<T>(
+ std::forward<Allocator>(allocator)
+ );
+ auto p = stlAllocator.allocate(1);
+
+ try {
+ stlAllocator.construct(p, std::forward<Args>(args)...);
+
+ return {p,
+ folly::allocator_delete<decltype(stlAllocator)>(std::move(stlAllocator))
+ };
+ } catch (...) {
+ stlAllocator.deallocate(p, 1);
+ throw;
+ }
+}
+
+template <typename T, typename Allocator, typename ...Args>
+std::shared_ptr<T> allocate_shared(Allocator&& allocator, Args&&... args) {
+ return std::allocate_shared<T>(
+ folly::make_stl_allocator<T>(std::forward<Allocator>(allocator)),
+ std::forward<Args>(args)...
+ );
+}
+
} // namespace folly
#endif /* FOLLY_STLALLOCATOR_H_ */
-
View
@@ -1,5 +1,5 @@
/*
- * Copyright 2012 Facebook, Inc.
+ * Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -292,4 +292,72 @@ FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(std::function);
// Boost
FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(boost::shared_ptr);
+#define FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL(classname, func_name, cv_qual) \
+ template <typename TTheClass_, typename RTheReturn_, typename... TTheArgs_> \
+ class classname<TTheClass_, RTheReturn_(TTheArgs_...) cv_qual> { \
+ template < \
+ typename UTheClass_, RTheReturn_ (UTheClass_::*)(TTheArgs_...) cv_qual \
+ > struct sfinae {}; \
+ template <typename UTheClass_> \
+ constexpr static bool test(sfinae<UTheClass_, &UTheClass_::func_name>*) \
+ { return true; } \
+ template <typename> \
+ constexpr static bool test(...) { return false; } \
+ public: \
+ constexpr static bool value = test<TTheClass_>(nullptr); \
+ }
+
+/*
+ * The FOLLY_CREATE_HAS_MEMBER_FN_TRAITS is used to create traits
+ * classes that check for the existence of a member function with
+ * a given name and signature. It currently does not support
+ * checking for inherited members.
+ *
+ * Such classes receive two template parameters: the class to be checked
+ * and the signature of the member function. A static boolean field
+ * named `value` (which is also constexpr) tells whether such member
+ * function exists.
+ *
+ * Each traits class created is bound only to the member name, not to
+ * its signature nor to the type of the class containing it.
+ *
+ * Say you need to know if a given class has a member function named
+ * `test` with the following signature:
+ *
+ * int test() const;
+ *
+ * You'd need this macro to create a traits class to check for a member
+ * named `test`, and then use this traits class to check for the signature:
+ *
+ * namespace {
+ *
+ * FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_test_traits, test);
+ *
+ * } // unnamed-namespace
+ *
+ * void some_func() {
+ * cout << "Does class Foo have a member int test() const? "
+ * << boolalpha << has_test_traits<Foo, int() const>::value;
+ * }
+ *
+ * You can use the same traits class to test for a completely different
+ * signature, on a completely different class, as long as the member name
+ * is the same:
+ *
+ * void some_func() {
+ * cout << "Does class Foo have a member int test()? "
+ * << boolalpha << has_test_traits<Foo, int()>::value;
+ * cout << "Does class Foo have a member int test() const? "
+ * << boolalpha << has_test_traits<Foo, int() const>::value;
+ * cout << "Does class Bar have a member double test(const string&, long)? "
+ * << boolalpha << has_test_traits<Bar, double(const string&, long)>::value;
+ * }
+ */
+#define FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(classname, func_name) \
+ template <typename, typename> class classname; \
+ FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL(classname, func_name, ); \
+ FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL(classname, func_name, const); \
+ FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL(classname, func_name, volatile); \
+ FOLLY_CREATE_HAS_MEMBER_FN_TRAITS_IMPL(classname, func_name, volatile const)
+
#endif //FOLLY_BASE_TRAITS_H_
Oops, something went wrong.

0 comments on commit cc86cd3

Please sign in to comment.