Topic: make_shared and friends.
Author: Jason McKesson <jmckesson@gmail.com>
Date: Fri, 27 Apr 2012 11:12:10 -0700 (PDT)
Raw View
Does the C++11 specification allow you to say that the `make_shared`
template is a friend, and thus guarantee that it can access the
appropriate private constructors of a class? Yes, there is syntax that
would make `make_shared` a friend. But does the spec *require* that
the constructors are called directly from `make_shared` itself, since
it cannot transfer friendship to any subsidiary object types?
--
[ 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 ]
Author: =?ISO-8859-1?Q?Daniel_Kr=FCgler?=<daniel.kruegler@googlemail.com>
Date: Fri, 27 Apr 2012 11:47:05 -0700 (PDT)
Raw View
Am 27.04.2012 20:12, schrieb Jason McKesson:
> Does the C++11 specification allow you to say that the `make_shared`
> template is a friend, and thus guarantee that it can access the
> appropriate private constructors of a class? Yes, there is syntax that
> would make `make_shared` a friend. But does the spec *require* that
> the constructors are called directly from `make_shared` itself, since
> it cannot transfer friendship to any subsidiary object types?
I cannot read anything from this from the specification. To the
contrary, there is a general requirement:
"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"
and there is nothing in the wording that allows to conclude that this
requirement is context-based. Requirements in the standard are general
context-free (The standard does not say so explicitly, but the lack of
such guarantee gives evidence for this interpretation). The wording form
used in [structure.requirements] p4
"Requirements are stated in terms of well-defined expressions that
define valid terms of the types that satisfy the requirements."
and p6:
"In some cases the semantic requirements are presented as C++ code. Such
code is intended as a specification of equivalence of a construct to
another construct, not necessarily as the way the construct must be
implemented."
clearly indicates that implementations can use indirection techniques,
so I would strongly discourage any assumption that friendship dedication
to library components could be done portably.
Further, there is some rewording intended due to
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2070
This rewording effectively makes the semantics more general and more
indirect again.
The only chance I see for you is to use std::allocate_shared with a
user-provided allocator where you have assigned friendship to the
construct function (I guess that one is of interest for you?) of the
allocator.
If that does not work for you: What kind of problem are you trying to solve?
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 ]
Author: "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>
Date: Sat, 28 Apr 2012 17:00:32 -0700 (PDT)
Raw View
On 27.04.2012 20:12, Jason McKesson wrote:
>
> Does the C++11 specification allow you to say that the `make_shared`
> template is a friend, and thus guarantee that it can access the
> appropriate private constructors of a class? Yes, there is syntax that
> would make `make_shared` a friend. But does the spec *require* that
> the constructors are called directly from `make_shared` itself, since
> it cannot transfer friendship to any subsidiary object types?
I don't think so.
But as a practical matter make your destructor protected (or private),
instead of the constructors.
If the private constructors imply factory functions, then by switching
to making the destructor non-public for the general case you reduce
the number of construction/destruction support functions from M class
specific factory functions, where M is the average number of
constructors, to 1 common destroy function, which you can make a
friend. And if you have make_shared as a single common factory
function (note that this can be a bit unsafe since it or its wrapper
will then have access to /all/ the constructors), then by doing the
switch you introduce just 1 more function. No matter the number of
such classes you create.
Cheers & hth.,
- Alf
--
[ 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 ]
Author: Jason McKesson <jmckesson@gmail.com>
Date: Sat, 28 Apr 2012 17:02:09 -0700 (PDT)
Raw View
On Friday, April 27, 2012 11:47:05 AM UTC-7, Daniel Kr gler wrote:
> Am 27.04.2012 20:12, schrieb Jason McKesson:
> > Does the C++11 specification allow you to say that the `make_shared`
> > template is a friend, and thus guarantee that it can access the
> > appropriate private constructors of a class? Yes, there is syntax that
> > would make `make_shared` a friend. But does the spec *require* that
> > the constructors are called directly from `make_shared` itself, since
> > it cannot transfer friendship to any subsidiary object types?
>
> I cannot read anything from this from the specification. To the
> contrary, there is a general requirement:
>
> "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"
>
> and there is nothing in the wording that allows to conclude that this
> requirement is context-based. Requirements in the standard are general
> context-free (The standard does not say so explicitly, but the lack of
> such guarantee gives evidence for this interpretation). The wording form
> used in [structure.requirements] p4
>
> "Requirements are stated in terms of well-defined expressions that
> define valid terms of the types that satisfy the requirements."
>
> and p6:
>
> "In some cases the semantic requirements are presented as C++ code. Such
> code is intended as a specification of equivalence of a construct to
> another construct, not necessarily as the way the construct must be
> implemented."
>
> clearly indicates that implementations can use indirection techniques,
> so I would strongly discourage any assumption that friendship dedication
> to library components could be done portably.
>
> Further, there is some rewording intended due to
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2070
>
> This rewording effectively makes the semantics more general and more
> indirect again.
>
> The only chance I see for you is to use std::allocate_shared with a
> user-provided allocator where you have assigned friendship to the
> construct function (I guess that one is of interest for you?) of the
> allocator.
>
> If that does not work for you: What kind of problem are you trying to solve?
>
> HTH& Greetings from Bremen,
>
> Daniel Kr gler
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`.
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.
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.
Also, allocate_shared won't help, because that's about the allocation
of the block of memory, not the calling of the constructor.
--
[ 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 ]
Author: =?ISO-8859-1?Q?Daniel_Kr=FCgler?= <daniel.kruegler@googlemail.com>
Date: Sun, 29 Apr 2012 23:59:51 -0700 (PDT)
Raw View
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.
> 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.
> 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. 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.
Don't feel attempted to hope that assigning friendship to types or
functions other than those under your control is a feasible approach.
In fact, you really should *know* the one, to whom you become friend
with ;-)
> 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.
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 ]
Author: Jason McKesson <jmckesson@gmail.com>
Date: Tue, 1 May 2012 11:29:57 -0700 (PDT)
Raw View
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.
> > 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.
> > 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.
> 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.
> Don't feel attempted to hope that assigning friendship to types or
> functions other than those under your control is a feasible approach.
> In fact, you really should *know* the one, to whom you become friend
> with ;-)
>
> > 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.
--
[ 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 ]
Author: "Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>
Date: Wed, 2 May 2012 11:11:55 -0700 (PDT)
Raw View
On 01.05.2012 20:29, Jason McKesson wrote:
>
> 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.
No, happily[1] that's incorrect: as illustrated below, forcing the
user to use a factory function does not require access-restricted
constructors. We can just be a little smart about it. I described a
more convoluted technique for C++03 (which didn't support perfect
forwarding) in 2005 in section 1.3.5 of my "pointers tutorial"[2],
which for a while was linked to by Wikipedia's article on pointers.
However, regarding `make_shared` and `allocate_shared`, you're
apparently right that they can't be easily forced in any other way.
Their design is IMHO flawed by not supporting a deleter, and so we'll
just have to hope that functionality like shown below will appear in
some library Technical Report?
And then with similar optimization (namely, just one dynamic
allocation) recommended for create_shared as today with make_shared,
plus the important wording lacking for make_shared, enabling you to
be-friend the function or class that does the allocation.
Hopefully.
<code>
#include <memory>
#include <utility>
using namespace std;
//-------------------------------- Machinery:
template< class Type, class Deleter = std::default_delete<Type> >
class create_unique
: public unique_ptr< Type, Deleter >
{
public:
template< class... Args >
create_unique( Args&&... args )
: unique_ptr< Type, Deleter >(
new Type( forward<Args>( args )... ), Deleter()
)
{}
};
template< class Type, class Deleter = std::default_delete<Type> >
class create_shared
: public shared_ptr< Type >
{
public:
template< class... Args >
create_shared( Args&&... args )
: shared_ptr< Type >(
// Can very much be optimized by a std lib implementation:
new Type( forward<Args>( args )... ), Deleter()
)
{}
};
class WithProtectedSharedCreation
{
template< class Type, class Deleter > friend class create_shared;
protected:
static void* operator new( size_t const size )
{ return ::operator new( size ); }
static void* operator new( size_t const size, void* ) noexcept
{ return ::operator new( size ); }
static void operator delete( void* p )
{ ::operator delete( p ); }
static void operator delete( void* p, void* ) noexcept
{ ::operator delete( p ); }
};
//-------------------------------- Example usage:
class Blah
: public WithProtectedSharedCreation
, public enable_shared_from_this< Blah >
{
template< class Type> friend struct std::default_delete;
private:
Blah& operator=( Blah const& ) = delete;
protected:
Blah( Blah const& other ) = default;
virtual ~Blah(){} // Ensure dynamic allocation only.
Blah( double ) {} // Construction from `double` is protected.
public:
Blah() {}
Blah( int ) {}
Blah( char const* ) {}
};
int main()
{
//Blah o; &o; // !Nix.
//shared_ptr<Blah> pA( new Blah( "blah" ) ); // !Nyet.
//shared_ptr<Blah> pB = create_shared<Blah>( 3.14 ); // !Nah.
shared_ptr<Blah> p = create_shared<Blah>( "blah" );
shared_ptr<Blah> q = p->shared_from_this();
}
</code>
One advantage of the above is that the access levels of the
constructors then really say where they're available, as intended by
the language design.
E.g., the constructor taking a `double` above is protected, and
therefore can't be used via `create_shared` (you get a compilation
error).
Cheers & hth.,
- Alf
Notes:
[1] "Happily" because restricting access to the constructors is more
code, less clear code, less maintainable code, and in general it's
IMHO just very ugly and very kludgy; a spit, gum and rubber-string
solution.
[2] Google docs does not display the table of contents, but it's there
when the PDF is downloaded. <url:
https://docs.google.com/fileview?id=0B2oiI2reHOh4M2MzNzYwYzQtMGZkNC00NTljLWJiM2UtOGI0MmRkMTMyZGY4>
--
[ 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 ]
Author: =?ISO-8859-1?Q?Daniel_Kr=FCgler?= <daniel.kruegler@googlemail.com>
Date: Wed, 2 May 2012 11:12:39 -0700 (PDT)
Raw View
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 ]