Re: [GSoC] Patches for shared_ptr array and polymorphic_allocator

2015-07-20 Thread Jonathan Wakely

On 18/07/15 00:02 -0700, Tim Shen wrote:

On Fri, Jul 17, 2015 at 7:16 PM, Fan You youfan.n...@gmail.com wrote:

Hi,

According to 
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4335.html#memory.smartptr

Here is my implementation of

[8.2] Extend shared_ptr to support arrays


Please don't resend the shared_ptr patch, since it's already tracked
in another thread.


Right, we can keep the two pieces of work entirely separate, I think.


[8.3] Type-Erased allocator


Please send a working patch with tests and (probably with Makefile.am changes).


Yes, the code looks really good but we need tests to know it works.

Tim's review is great, I will just add a few more comments.


#ifndef _GLIBCXX_MEMORY_RESOURCE
#define _GLIBCXX_MEMORY_RESOURCE 1


Please make this _GLIBCXX_EXPERIMENTAL_MEMORY_RESOURCE so that it
doesn't need to be renamed if the header is added to a later standard
and we have both memory_resource and experimental/memory_resource.


// Decleartion
 class memory_resource;


I don't think we need this comment, especially spelled wrong :-)


L70:
   static std::atomicmemory_resource* s_default_resource;

naming: _S_default_resource.


Similarly, dont_care_type is not a reserved name, so the header could
not be used in this legal program:

#define dont_care_type oops this isn't going to compile
#include experimental/memory_resource

However, I think the dont_care_type and __constructor_helper should go
away completely, see below.

Also, the TS says The name resource_adaptor_imp is for exposition
only and is not normative, which means we cannot use that name,
because users could define it as a macro. I suggest changing it to
__resource_adaptor_impl or similar.



L43:
   virtual ~memory_resource() { }

Please break the line after virtual/return type. This also applies for
other places in the patch.


Not essential for 'virtual' or generally for inline functions that fit
on a single line, so this is OK to leave as it is.


Consider use a more readable helper name, like
__uses_allocator_construction_helper and document it.


I don't think the helper type is needed at all. It is performing the
same job as the helper types in bits/uses_alloc.h, which I see get
used, but not correctly.

The point of the __uses_alloc0, __uses_alloc1 and __uses_alloc2 tag
types is that you overload on them to perform the correct type of
initialization according to the tag type.

The second construct() overload:

 // Specializations for pair using piecewise construction
 template typename _Tp1, typename _Tp2,
  typename... _Args1, typename... _Args2
   void construct(pair_Tp1, _Tp2* __p, piecewise_construct_t,
  tuple_Args1... __x,
  tuple_Args2... __y)

should not use __constructor_helper at all, because you never need to
pass an allocator to std::pair, instead you (conditionally) pass an
allocator to its members, but that's done by _M_construct_p(). So you
should not be using __constructor_helper for this overload. Just
construct a std::pair directly.

And the first construct() overload:

 template typename _Tp1, typename... _Args //used here
   void construct(_Tp1* __p, _Args... __args)
   {
 using _Ctor_imp = __constructor_helper_Tp1;
 ::new ((void*)__p) _Ctor_imp(allocator_arg,
  this-resource(),
  std::forward_Args(__args)...);
   }

also doesn't need __constructor_helper if you follow the same design
as used in scoped_allocator and dispatch to another function
(called _M_construct in std::scoped_allocator_adaptor) using the
appropriate __use_alloc tag type.

Implementing uses-allocator construction that way is consistent with
our existing code and will work for this type, which I think would
fail with your __constructor_helper because only polymorphic_allocator
can call its constructor:

 class Unfriendly
 {
 private:
   Unfriendly() = default;

   templatetypename T friend class polymorphic_allocator;
 };

Also in regard to the __uses_allocX tag types, these functions:

 templatetypename... _Args
   decltype(auto)
   _M_construct_p(__uses_alloc1_, tuple_Args... __t)
   { return tuple_cat(make_tuple(allocator_arg, this-resources()),
  std::move(__t)); }

 templatetypename... _Args
   decltype(auto)
   _M_construct_p(__uses_alloc2_, tuple_Args... __t)
   { return tuple_cat(std::move(__t), make_tuple(this-resources())); }

could use the _M_a member of the tag type to get the resource, instead
of this-resources(). That will actually compile, because the member
function is called resource() not resources() (so I assume these
functions have never been tested :-)

All the calls to tuple_cat and make_tuple must be qualified with std::
so that they do not use ADL.



L73:
 bool operator==(const memory_resource __a,
 const memory_resource __b) noexcept
 { return __a == __b || 

Re: [GSoC] Patches for shared_ptr array and polymorphic_allocator

2015-07-18 Thread Tim Shen
On Fri, Jul 17, 2015 at 7:16 PM, Fan You youfan.n...@gmail.com wrote:
 Hi,

 According to 
 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4335.html#memory.smartptr

 Here is my implementation of

 [8.2] Extend shared_ptr to support arrays

Please don't resend the shared_ptr patch, since it's already tracked
in another thread.

 [8.3] Type-Erased allocator

Please send a working patch with tests and (probably with Makefile.am changes).

Format: please replace leading consecutive spaces with tabs.

L70:
static std::atomicmemory_resource* s_default_resource;

naming: _S_default_resource.

L43:
virtual ~memory_resource() { }

Please break the line after virtual/return type. This also applies for
other places in the patch.

L81:
  template typename _Tp
class __constructor_helper_imp

This doesn't work correctly for at least following case:
std::__constructor_helper_impint*(std::allocator_arg,
std::allocatorint(),
std::true_type(),
std::true_type(),
std::true_type());

Based on [allocator.uses.construction], this falls into the second
rule, because there exists priorities in those rules; but overloading
resolution thinks it's ambiguous.

To enforce the order, you can do this:

templateint __rule_num
struct __uses_allocator_construction_helper;

...
{
constexpr bool __uses_alloc = uses_allocator...::value;
constexpr bool __normally_constructible = is_constructible_Tp,
_Args...::value;
constexpr bool __constructible_alloc_before =
is_constructible_Tp, allocator_tag_t, _Alloc, _Args...::value;
constexpr bool __constructible_alloc_after =
is_constructible_Args..., _Alloc::value;

constexpr bool __uses_rule1 = !__uses_alloc  __normally_constructible;
constexpr bool __uses_rule2 = __uses_alloc  __constructible_alloc_before;
constexpr bool __uses_rule3 = __uses_alloc  __constructible_alloc_after;
__uses_allocator_construction_helper__uses_rule1 ? 1 :
(__uses_rule2 ? 2 : (__uses_rule3 ? 3 : 0))::_S_apply(...);
}

Consider use a more readable helper name, like
__uses_allocator_construction_helper and document it.

L73:
  bool operator==(const memory_resource __a,
  const memory_resource __b) noexcept
  { return __a == __b || __a.is_equal(__b); }

Make all non-template functions inlined.

L178:
  { } // used here

What does this mean?

L180:
  polymorphic_allocator(memory_resource* __r)
  : _M_resource(__r ? __r : get_default_resource())
  { }

[8.6.2.3] describes __r != nullptr as a precondition, which is
guaranteed by the caller, so we don't have to check here.
Alternatively you can use _GLIBCXX_ASSERT.

L262:
  memory_resource* _M_resource;

private member variables should be defined after private member functions.

L286:
  template class _Tp1, class _Tp2
bool operator!=(const polymorphic_allocator_Tp1 __a,
const polymorphic_allocator_Tp2 __b) noexcept
{ return ! (__a == __b); }

Remove extra space after !.

L340:
auto __p = dynamic_castconst resource_adaptor_imp*(__other);

What if the user turns off RTTI?

L345:
  // Calculate Aligned Size
  size_t _Aligned_size(size_t __size, size_t __alignment)
  { return ((__size - 1)|(__alignment - 1)) + 1; }

  bool _M_supported (size_t __x)
  { return ((__x != 0)  (__x != 0)  !(__x  (__x - 1))); }

Document these two functions' behaviors, e.g.:
Returns a size that is larger than or equal to __size and divided by
__alignment, where __alignment is required to be the power of 2.

L355:
  // Global memory resources
  atomicmemory_resource* memory_resource::s_default_resource;
L386:
  // The default memory resource
  memory_resource* get_default_resource() noexcept
  {
memory_resource *__ret
  = memory_resource::s_default_resource.load();

if (__ret == nullptr) { __ret = new_delete_resource(); }
return __ret;
  }

According to [8.8.5], memory resource pointer should be intialized
with new_delete_resource(), not nullptr; and get_default_resource
should only return the pointer.

L396:
  memory_resource* set_default_resource(memory_resource* __r) noexcept
  {
if ( __r == nullptr)
{ __r = new_delete_resource(); }

memory_resource* __prev = get_default_resource();
memory_resource::s_default_resource.store(__r);
return __prev;
  }

We shouldn't care if it's nullptr or not.
Your get-then-set may cause a data race. I think
std::atomic::exchange will work, but we should confirm with Jon.


-- 
Regards,
Tim Shen