Add operator&() to std::unique_ptr to get internal pointer

489 views
Skip to first unread message

thesto...@gmail.com

unread,
Apr 11, 2018, 6:22:39 PM4/11/18
to ISO C++ Standard - Future Proposals
Hello,

There are many old APIs that are using double pointer as argument to return an object. Wide usage examples are WinAPI, SDL, etc.
Currently you have to use one more temporary variable to pass it and then assign it to std::unique_ptr.

// Some utility header file...
struct ComCloser
{
constexpr ComCloser() noexcept = default;
void operator()( IUnknown* comObj ) noexcept
{
comObj->Release();
}
};

template<typename T>
using ComResource = std::unique_ptr<T, ComCloser>;

// Some cpp file
IWbemLocator* pLoc = nullptr;
HRESULT hres = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc );
_com_util::CheckError( hres ); // will throw exception on error

ComResource<IWbemLocator> locator( pLoc );

By adding
pointer* operator&()
{
reset();
return &_ptr;
}

We can omit most of the cpp file code to become like
// Some cpp file
ComResource<IWbemLocator> locator;
HRESULT hres = ::CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&locator );
_com_util::CheckError( hres );

Of course when using operator&() if there is any previous pointer it will be destroyed by reset(); as it can be seen from my example.
Please provide feedback and suggestions. :)

Tony V E

unread,
Apr 11, 2018, 6:30:53 PM4/11/18
to Standard Proposals
Most of the committee hates that you can overload operator&, particularly the library implementors, who need to sprinkle std::addressof() everywhere to fight against it.


--
Be seeing you,
Tony

thesto...@gmail.com

unread,
Apr 11, 2018, 6:35:12 PM4/11/18
to ISO C++ Standard - Future Proposals
Ok, It's fine for me to be just a regular method. The general idea is more important here.

Richard Smith

unread,
Apr 12, 2018, 5:43:34 AM4/12/18
to std-pr...@isocpp.org
Why not write your own wrapper type that holds an LPVOID p and a ComResource&r, and calls r.reset(p) in its destructor, used like this:

HRESULT hres = ::CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, OutputPointerTo(locator) );

Of course when using operator&() if there is any previous pointer it will be destroyed by reset(); as it can be seen from my example.
Please provide feedback and suggestions. :)


Most of the committee hates that you can overload operator&, particularly the library implementors, who need to sprinkle std::addressof() everywhere to fight against it.


--
Be seeing you,
Tony

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/d86a203b-ca5e-43f1-8783-5d0d76f079d8%40isocpp.org.

Matt Calabrese

unread,
Apr 12, 2018, 11:24:37 AM4/12/18
to std-pr...@isocpp.org
On Wed, Apr 11, 2018 at 6:22 PM <thesto...@gmail.com> wrote:
pointer* operator&()
{
reset();
return &_ptr;
}

Microsoft tried overloading operator& for something similar to this in ATL I believe, and it has a lot of issues in my past personal experience.

First, as was mentioned, having & overloaded means that any correct code today that uses & would break and you'd now have an address of operator that does something other than take the address of the object. Many of us already consider it a mistake that we even allow the address of operator to be overloaded. Overloading that operator makes using & a "gotcha" when inside of *any* template where the operand of & would depend on a template parameter.

If we choose not to care about that, then we have two other problems: 

First, in the Microsoft approach, there is no call to reset, iirc. This causes the problems that you pointed out -- your Com object's Release() function doesn't get called, so it's only safe to use when the smart pointer is already a nullptr. Your approach attempts to go the other route to prevent this and so it calls reset() inside of the address of operator, but that fails if you genuinely are attempting to pass the address of the existing target.

The core of these problems is that the proposed change is trying to use the address of operator for something that simply is not logically taking the address of the object. A smart pointer to Foo is *not* the same thing as a pointer to Foo, even though they share some similarities in their interface.

If you want to work with these types you already can do so safely, and there are some different, solid ways to simplify it if you wanted to. For one, you can make your own version of CoCreateInstance that returns your smart pointer type and then just call that instead. You can also just pass in the address of an actual Com object, and then pass that into the unique pointer. We shouldn't try to make a semantically strange address operator for unique_ptr as an attempt to make certain, old c-ish APIs easier to use. I'd much rather we encourage Microsoft to provide more modern APIs.

-- Matt Calabrese

Richard Hodges

unread,
Apr 12, 2018, 1:31:38 PM4/12/18
to std-pr...@isocpp.org
Isn't it simpler and safer to simply wrap CoCreateInstance into a sanitised function?

namespace wrap_win32
{
  template<class Interface>
  auto createInstance(REFCLSID cls, LPUNKNOWN pUnkOuter, DWORD dwClsContextREFIID riid)
  -> ComResource<Interface>
  {
    Interface* result = nullptr;
    auto hres = ::CoCreateInstance(cls, pUnkOuter, dwClsContext, riid, &result);
    _com_util::CheckError( hres );
    return { result, ComCloser() };
  }
}

I think we're preferring to write functions that return results these days rather than functions that return results while spamming side-effects all over the place.


--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

thesto...@gmail.com

unread,
Apr 12, 2018, 2:22:17 PM4/12/18
to ISO C++ Standard - Future Proposals, thesto...@gmail.com
Hello,

Yes, now I see that overloading operator&() is very bad idea, so let's continue this discussion pretending that I proposed normal method name for doing such thing(for example fetch_internal_ptr()).
@Richard Smith Doing such thing for CoCreateInstance will cover only one case, there are much more similar API's. About Microsoft providing newer API's - well even if they do we still have other legacy libraries that we must use for some projects...
About the wrapper you proposed - yes I already use such thing

// Some header
template<typename T, typename D>
class ptrptr_type
{
using parent_type = std::unique_ptr<T, D>;
using pointer = typename parent_type::pointer;

public:
ptrptr_type( std::unique_ptr<T, D>& ptr ) noexcept : m_parent( &ptr ), m_ptr( ptr.release() )
{}

ptrptr_type( ptrptr_type&& other ) noexcept
{
*this = std::move( other );
}

ptrptr_type& operator=( ptrptr_type&& other ) noexcept
{
m_parent = other.m_parent;
m_ptr = other.m_ptr;
other.m_parent = nullptr;
return *this;
}

~ptrptr_type() noexcept
{
if( m_parent )
m_parent->reset( m_ptr );
}

operator pointer*() noexcept
{
return &m_ptr;
}

operator typename std::enable_if_t<!std::is_same_v<T, void>, void**>() noexcept
{
return (void**)&m_ptr;
}

private:
ptrptr_type( const ptrptr_type& ) = delete;
ptrptr_type& operator=( const ptrptr_type& ) = delete;

private:
parent_type* m_parent;
pointer m_ptr;
};

template<typename T, typename D>
ptrptr_type<T, D> ptrptr( std::unique_ptr<T, D>& ptr ) noexcept
{
return ptrptr_type<T, D>( ptr );
}

// Some cpp
ComResource<IWbemLocator> locator;
hres = ::CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, ptrptr( locator ) );
_com_util::CheckError( hres );

There are many workarounds for many cases which are not or were not part of the language or the std library. This doesn't mean that we shouldn't improve it in order to make less wrappers...
If the idea for additional method(not the initial overloaded proposal) for returning the internal pointer is not good perhaps the wrapper that I just published is a nice idea for new feature to the std library? It can be much more optimal if the std implementers tie it with their internal implementation specific code.

Zhihao Yuan

unread,
Apr 13, 2018, 3:17:08 AM4/13/18
to std-pr...@isocpp.org

(LPVOID*)locator.release()

 

--

Zhihao Yuan, ID lichray
The best way to predict the future is to invent it.
_______________________________________________

phdoft...@gmail.com

unread,
Apr 15, 2018, 7:42:30 PM4/15/18
to ISO C++ Standard - Future Proposals, thesto...@gmail.com
This has actually come up more times than I initially thought. There are TONS of companies who have this abstraction internally, and also tons of people who write this code externally on their own as well.

So, I put it in library form, consumable in CMake, and also enhanced it so that it is usable with `std::shared_ptr`, `boost::shared_ptr`, and (hopefully) with the upcoming `std::retain_ptr`:

https://github.com/ThePhD/ptrptr

Of note here is the benchmarks. I plan to actually write a blog post about this, because it seems there is some performance left on the table when going through the typical .release() + .reset() route with a `std::unique_ptr`, which makes this more than just "nice" to have in the standard library.

The PhD

unread,
Nov 25, 2018, 1:45:11 AM11/25/18
to ISO C++ Standard - Future Proposals, thesto...@gmail.com
I wanted to follow up to this thread.

I've been busy making sure a form of this suitable for standardization can make it into C++. It looks like it will make it for C++23, but it can be put into C++20 if I put enough effort behind it and make it to Kona (which seems unlikely right now).

Here is the current form of the proposal: https://thephd.github.io/vendor/future_cxx/papers/d1132.html

It should only need minor tweaks now. Thanks to input from tcanes and glenfe and pdimov, the wording has been significantly changed to be able to allow the optimizations discussed in the paper and employed commonly in source.

A version of this might also be available for the next Boost release cycle, putting it in user hands faster than my github implementation. It's slated to slide into Boost.SmartPtr at some point too, provided I can make sure all the design and wording decisions are solid and aid in the implementation where necessary.

Thanks for bringing this up!

The Storm

unread,
Nov 25, 2018, 4:35:13 AM11/25/18
to The PhD, ISO C++ Standard - Future Proposals
Very nice! I've been losing hope in ever seeing the final missing piece for pointers.

Wish you best of luck in getting this accepted as soon as possible!

Balog Pal

unread,
Nov 25, 2018, 5:56:38 PM11/25/18
to ISO C++ Standard - Future Proposals, thesto...@gmail.com
Using & for that purpose is an awful idea.  Even if this class was newly introduced so it would not just break/redirect user code that already uses & and cant to get the address of the unique_ptr instance.

OTOH the feature you are looking for is legit. My own suite of smart pointers have a named function that returns Ptr& to the internals -- marked with ton of warnings to not use it unless one really knows the way.  For cases similar to that. I'm pretty sure that function, that way would have snawball's chance in hell to get standardized.  

If all you look after is to deal with COM objects, ATL and MFC has great simple support.

Alberto Barbati

unread,
Nov 27, 2018, 11:10:29 AM11/27/18
to ISO C++ Standard - Future Proposals, thesto...@gmail.com
If you had read paper P1132R2 you would have noticed that it does not propose to overload operator&. It solves the problem by introducing a couple new classes without changing unique_ptr or any other existing smart pointer class. The approach is neat, it doesn't require "ton of warnings", potentially works with every smart pointer and, being a pure library extension, cannot break existing code. Although I do not currently have the need for it, I hope it gets the attention it deserves.
Reply all
Reply to author
Forward
0 new messages