1

What is wrong with the following "trick" for hiding unique_ptr ugliness?

class Drawable;
typedef unique_ptr<Drawable> pDrawable;
#define newDrawable(...) pDrawable(new Drawable (##__VA_ARGS__))

The first two are fine. But the third one is causing an error in VS2012:

 23 IntelliSense: "std::unique_ptr<_Ty, _Dx>::unique_ptr(const std::unique_ptr<_Ty, _Dx>::
_Myt &) [with _Ty=Drawable, _Dx=std::default_delete<Drawable>]" (declared at line 1447 of
"C:\vs2012\VC\include\memory") is inaccessible  file.h  36  26  

I don't see why this wouldn't work, unless I am misunderstanding how C++ define macros work. I thought that it would simply replace this code:

newDrawable(a, b, c)

with

unique_ptr<Drawable>(new Drawable(a, b, c));

I understand that unique_ptr cannot be copied, but I am not copying it, here. Am I?

edit:

I have received some requests for "use" of the macro in question:

If I were to use it, it would be used as follows:

pDrawable myDraw = newDrawable();

which I would want to translate to:

unique_ptr<Drawable> myDraw = unique_ptr<Drawable>(new Drawable());

However, I cannot even compile the macro without visual studio giving the below error. It's as if something in the #define is impermissible, per se. The error is returned on the line where I make the define, not where I call the define.

See here for why make_unique doesn't work: make_unique does not compile

edit2

I have answered the question below. The code above does compile, and work.

Community
  • 1
  • 1
Dan
  • 1,692
  • 14
  • 16

2 Answers2

14

Well, #define is, IMHO, a huge problem because it doesn't obey scoping rules and they do simple textual substitution that can sometimes have surprising results. I consider preprocessor macros to be the last resort when I need to get something done.

It would be much better to define a template like make_shared that returned a unique_ptr<T>. This is allowable because you could then move it into place.

auto a_drawable = make_unique<Drawable>(a, b, c);

template <typename T, typename... Args>
::std::unique_ptr<T> make_unique(Args&&... args)
{
    return ::std::unique_ptr<T>{new T(::std::forward<Args>(args)...)};
}

That's much cleaner than the macro, and it works for any non-array type. Which is a problem. It can be made to work for array types as well, but that code is a bit more complex, and rather than repeat it, I'll just point at the answer a helpful commenter referred me to:

As for why you're getting the error you do when you use your macro, I'm not sure. It looks like you're using your macro in some context where the result gets turned into a const ::std::unique_ptr<Drawable> &, and then tries to move construct it into a different pDrawable. This won't work. Why that's happening, I don't know. It depends crucially on the context in which you're using the macro, and you haven't provided that.

And that just highlights my first point. One reason macros are really ugly because they are simple textual substitution and their meaning can change depending on context.

Community
  • 1
  • 1
Omnifarious
  • 50,447
  • 15
  • 117
  • 181
  • Oh, I'm amused that I came up with a version of `make_unique` almost identical to Herb Sutter's without even knowing about his. It's such a simple and obvious function though that that's not much of a surprise. – Omnifarious Feb 07 '13 at 00:43
  • 1
    Amusingly, you both forgot to handle `char[]`. http://stackoverflow.com/a/13512344/845092 – Mooing Duck Feb 07 '13 at 01:18
  • @MooingDuck - Oops! _laugh_ Yes, we both did. People allocate arrays and have a `unique_ptr` point at them?! Why, who does that?! _chuckle_ – Omnifarious Feb 07 '13 at 01:27
  • I can't think of a reason that would _also_ use a standard `make_unique`. (One might need C interop with malloc/free, but can't use `make_unique` for that. – Mooing Duck Feb 07 '13 at 01:36
  • @MooingDuck - I can't think of a reason either. `unique_ptr` doesn't support `operator []` at all. So really the only useful unique array handle type is `::std::vector`. And yes, interop with malloc/free might be nice, and you could accomplish that with a custom deleter, but you can't use `make_unique` then. – Omnifarious Feb 07 '13 at 01:47
  • @MooingDuck - Oh my. _raises eyebrows_ – Omnifarious Feb 07 '13 at 03:03
  • Does your implementation work with VS2012? See here: http://stackoverflow.com/questions/12547983/is-there-a-way-to-write-make-unique-in-vs2012 – Dan Feb 07 '13 at 07:07
  • @Dan: Apparently not! Wow! VS2012 implements varargs macros, but not varargs templates. What a very strange choice. I consider varargs templates to be second only to rvalue references in their usefulness. I do believe it would be possible to use a varargs macro to simulate a varargs template though. But that would be ugly. :-) – Omnifarious Feb 07 '13 at 17:33
2

Answer:

Make sure you actually use the macro, and everything will be ok.

Apparently, in an attempt to validate #define macros, VC++ checks your actual use of the macro. If there is no actual use of the macro compiled, VC++ tries to "understand" it, and can throw compiler errors.

Magically, if I add:

pDrawable myDraw = newDrawable();

After my code above, the error disappears.

Sorry for wasting everyone's time. At least this inspired me to read the following article:

http://blogs.msdn.com/b/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx

Dan
  • 1,692
  • 14
  • 16