From -1810615379171842926
X-Google-Thread: f78e5,6f5c74c3d7959916
X-Google-NewGroupId: yes
X-Google-Attributes: gid7894ca11fe,domainid0,public,usenet
X-Google-Language: ENGLISH,ASCII
Received: by 10.68.234.38 with SMTP id ub6mr121662pbc.2.1335982362146;
        Wed, 02 May 2012 11:12:42 -0700 (PDT)
MIME-Version: 1.0
Path: r9ni124329pbh.0!nntp.google.com!news1.google.com!goblin1!goblin.stu.neva.ru!news.albasani.net!.POSTED!not-for-mail
From: =?ISO-8859-1?Q?Daniel_Kr=FCgler?= <daniel.kruegler@googlemail.com>
Newsgroups: comp.std.c++
Subject: Re: make_shared and friends.
Date: Wed,  2 May 2012 11:12:39 -0700 (PDT)
Organization: A noiseless patient Spider
Lines: 181
Sender: std-cpp-request@vandevoorde.com
Approved: james.dennett@gmail.com
Message-ID: <jnpo78$ig4$1@dont-email.me>
References: <5583145.28.1335476739188.JavaMail.geo-discussion-forums@ynnn35>
 <jnep30$d2s$1@dont-email.me>
 <17423064.271.1335555821298.JavaMail.geo-discussion-forums@vbq5>
 <jnj0aq$b2b$1@dont-email.me>
 <14621972.860.1335819954688.JavaMail.geo-discussion-forums@ynce8>
NNTP-Posting-Host: OjiqvXE+Vg1wJBM1LDawMF3ZJQiS6f5x3o1wUc/oIg8=
X-Trace: news.albasani.net i8jgKq88WQd5XWwa4c3B0sYaoidrevgBeZCEK0Mv63hfldTu8MMpI9i+Bma9WPOeCOPem+wzsxLoTIIeugc/zw==
X-Complaints-To: abuse@albasani.net
NNTP-Posting-Date: Wed, 2 May 2012 18:12:41 +0000 (UTC)
X-Mailer: Perl5 Mail::Internet v2.05
X-Submission-Address: std-cpp-submit@vandevoorde.com
Cancel-Lock: sha1:pIcLHwOeFD5PfNcKtJEJbgqeSCA=
X-Original-Date: Wed, 02 May 2012 00:31:07 +0200
Content-Type: text/plain; charset=ISO-8859-1; format=flowed

Am 01.05.2012 20:29, schrieb Jason McKesson:
>
> On Sunday, April 29, 2012 11:59:51 PM UTC-7, Daniel Kr�gler wrote:
>>
>> Am 29.04.2012 02:02, schrieb Jason McKesson:
>> [..]
>>>
>>>
>>> It's not so much a problem as wondering how this is supposed to work.
>>> Without the ability to have `make_shared` be a friend (in a useful
>>> way), it is impossible to *force* users to use `make_shared`.
>>
>>
>>
>> I don't understand your question: Surely it cannot be the
>> responsibility of a library implementation to enforce user code to use
>> some particular component.
>
>
> A good API makes it difficult if not impossible to do things
> incorrectly. And if a particular object has `enable_shared_from_this`
> as a base class, then it is incorrect to create an instance of that
> object *without* a shared_ptr.
>
> So you have to either force the user to use `make_shared` or force the
> user to use factory functions. Enforcing either requires the use of
> private constructors.


I disagree with your conclusion. There exist well-known strategies to
handle this situation without making the constructor itself private.
As often the case, one further indirection can solve the problem. A
simple one is add an "access-point" parameter to any constructor. This
access-point can only be created by your own factory function which
again invokes make_shared. Something along the lines of [pseudo-code]

class YourPrivacy : public std::enable_shared_from_this<YourPrivacy> {
public:
 class AccessPoint {
   AccessPoint(){}
   friend std::shared_ptr<YourPrivacy> make_ptr(Args...);
 };

 YourPrivacy(AccessPoint, Args...);
};

inline std::shared_ptr<YourPrivacy> make_ptr(Args... args) {
 return std::make_shared<YourPrivacy>(AccessPoint(), args...);
}

>>> You also can't effectively combine factory functions with
>>> `make_shared`. So you lose a lot of the benefits of them when dealing
>>> with factories.
>>
>>
>>
>> I don't understand what you are trying to say here.
>
>
> See above. You usually enforce the use of factory functions by making
> constructors private.


Or introduce an access-point parameter as shown above.

>>> It shouldn't be too much of a burden on implementations to force them
>>> to do the final construction of the type within `make_shared` itself,
>>> rather than in some object they create.
>>
>>
>>
>> Even if some particular implementations decides to do so, this won't be
>> portable code anymore. Standardizing this for this special function
>> template would also seem very odd, a more general policy should be
>> considered, if needed.
>
>
> Extending it to `emplace` member functions wouldn't be a bad idea.


This can be solved by the same kind of access-point technique.

>> In regard to the "burden" I think you are
>> mislead. The guarantee that the actual code is called in a particular
>> location is *not* sufficient. Todays high-quality implementations of
>> the Standard Library usually provide a lot of static concept-checking
>> to ensure that user-types satisfy the requirements of the standard.
>> One requirement is that
>>
>> "The expression ::new (pv) T(std::forward<Args>(args)...), where pv
>> has type void* and points to storage suitable to hold an object of
>> type T, shall be well formed."
>>
>> If a static concept-checking tool attempts to validate this it won't
>> help that you have assigned friendship to the function: The
>> static-tool machinery would also require this friendship.
>
>
> The wording would have to be changed. That expression would be valid,
> but *only* within the make_shared function itself. So any concept
> checking (if it's still possible) would have to be done within the
> function, not in extra classes and so forth.


I don't think that such wording extensions will help you. Let me give
one further example why I think that asking of "befriendable" library
components is a bad idea:

Real-life libraries are not immediately C++11 ready, so they may have
incomplete support for variadic templates. They could still provide a
generally usable library by emulating the variadics by a series of
overloaded templates with increasing argument length.

Your friend-ship technique requires *exact* knowledge of the signature
of the library-component. This is a very bad dependency on your
concrete library implementation.

>>> Also, allocate_shared won't help, because that's about the allocation
>>> of the block of memory, not the calling of the constructor.
>>
>>
>>
>> Yes, the current wording is defective in this regard, but the proposed
>> resolution of
>>
>> http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2070
>>
>> would ensure that the allocator's construct and destroy function will
>> be used. gcc 4.8 has already implemented the P/R for evaluation
>> purposes.
>
>
> True, but that requires a *lot* of boilerplate to do something that
> ought to be very, very simple. You have to make a special allocator to
> be used for instances of the class, and most of its methods will just
> call the std::allocator function. It requires the user to use the
> less-well-known `allocate_shared` instead of `make_shared`. It's less
> idiomatic.


My previous example shows that it is possible to realize your needs
also with make_shared. Nonetheless I disagree that allocate_shared
should be considered as less idiomatic. In fact, allocators in C++11
are a real key-point for access-control *and* construction-destruction
control in many places in the library. In fact, the allocator-based
containers defer all construction issues to the allocator, therefore
new requirement names have been introduced like Copy/MoveInsertable,
and EmplaceConstructible, and most recently DefaultInsertable and
Erasable. Even if you *can* solve your problem with make_shared, it
might be worth to look at an allocator-based approach, because it
provides other advantages. E.g. it would allow you more easily to get
a statistics of allocated objects or you can insert dedicated
pre/post-construction/destruction code parts (aspect-oriented).
Further, the needed boiler-plate to create a fully functional
allocator has been dramatically reduced in C++11. The following
interface is sufficient (more or less stolen from the standard):

template <class T>
struct SimpleAllocator {
 typedef T value_type;
 SimpleAllocator(/some args/);
 template <class U> SimpleAllocator(const SimpleAllocator<U>& other);
 T* allocate(std::size_t n);
 void deallocate(T* p, std::size_t n);
};

template <class T, class U>
bool operator==(const SimpleAllocator<T>&, const SimpleAllocator<U>&);
template <class T, class U>
bool operator!=(const SimpleAllocator<T>&, const SimpleAllocator<U>&);

HTH & Greetings from Bremen,

Daniel Kr�gler


--
[ comp.std.c++ is moderated.  To submit articles, try posting with your ]
[ newsreader.  If that fails, use mailto:std-cpp-submit@vandevoorde.com ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]


