Macro to switch between auto_ptr and unique_ptr
On a project that is still using pre-C ++ 11, I wanted to prepare the source for the switch by compiling it with the C ++ 11 compiler and fixing the bugs. They consisted of
- copies
std::auto_ptr<T>
replaced bystd::unique_ptr<T>
- wrapped the smart pointer where needed
std::move()
- some
0
andNULL
replaced bynullptr
Now I want to go back to the pre-C ++ compiler and write a macro that can switch changes, so when there is time for the final switch of the compiler, I just delete the macro. I tried
#ifndef HAVE_CXX11
#define nullptr NULL
namespace std {
#define unique_ptr<exvector> auto_ptr<exvector>
}
#endif
(with an exvector
example type used with a smart pointer). This and similar attempts do not work, because macros cannot change template types. I've also used typedef
no better results.
Is this even possible, and if so, how?
source to share
For only nullptr
and unique_ptr
this might work:
#ifndef HAVE_CXX11
#define nullptr NULL
#define unique_ptr auto_ptr
#endif
But I don't know how you plan to deal with the different semantics unique_ptr
and auto_ptr
.
If you're willing to live with undefined in some way for a while (the kind that is unlikely to cause real problems), you can also provide your own std::move
:
namespace std {
template <class T>
T& move(T &x) { return x; }
}
This is UB because you are not allowed to add anything to namespace std
. But if it is only a temporary measure, it should be safe (the pre-11 compiler is unlikely to have a name std::move
).
source to share
I would like to introduce very explicit macros such as
//Defines to overcome pre C++11 limitations regarding
//std::auto_ptr and absence of 'good' move (i.e. std::move) semantics.
//Strictly post C++11 code should use std::unique_ptr and std::move explicitly.
//All other code should use the macros.
//If pre-C++11 ceases to be a target the macros may be replaced...
#ifdef HAVE_CXX11 //Or whatever...
#define UNIQUE_PTR_TYPE std::unique_ptr
#define MOVE_UNIQUE_PTR(PTR) std::move(PTR)
#else
#define UNIQUE_PTR_TYPE std::auto_ptr
#define MOVE_UNIQUE_PTR(PTR) (PTR)
#endif
What for? Because even the casual reader will see some kind of replacement happening.
Yes, the code will look ugly, but the safe "is not going to disfigure anyone." We are engineers, not poets, and this is our kind of beauty!
However, I have to say that I agree with whoever thinks you should add code. This is not the only incompatibility and your code will get more bloated and you may find yourself doing more work (and contributing more bugs) trying to make one branch multipurpose than branching.
Macros are great things, but they come in the form:
#define <common symbol> <something else>
must be guaranteed to be 100% benign "you don't need to know it was replaced" before it can be indulged.
I just don't think:
#define unique_ptr auto_ptr
Or anything else that makes this replacement invisible passes this test. unique_ptr
and auto_ptr
not the same thing, and the whole point is auto_ptr
outdated that you have to be careful with it.
For lulz (sleeping in my point of view about invisible placeholders) try:
#define if(A) if(((A)&&rand()>128)||rand()>(RAND_MAX-128))
This should keep the feckers busy at noon ... It's best if you don't sow the srand()
mistakes will be repeated!
source to share
What about
#ifdef NOT_HAVING_CPP_11
namespace std{
template<typename T>
struct unique_ptr : public auto_ptr<T>{}; }
#endif
Thus, after you have replaced all your auto_ptr instances with your old code with unique_ptr, you can remove the NOT_HAVING_CPP_11 macro and it will compile without warning in a modern compiler.
source to share
#if no_Cxx11
template<class T>
struct up:auto_ptr<T> {
up(T* t):auto_ptr(t) {}
up(auto_ptr& o):auto_ptr(o) {}
};
T& my_move(T& t){return t;}
struct my_nullptr_t {
template<class T>
operator T*()const{return NULL;}
// template operator T* isn't always looked for:
operator void*()const{return NULL;}
};
static const my_nullptr_t nullptr;
#else
template<class T>
using up=std::unique_ptr<T>;
template<class T>
decltype(std::move(std::declval<T>()))
my_move(T&& t) {
return std::move(std::forward<T>(t));
}
using my_nullptr_t = std::nullptr_t;
#endif
To do this, you must replace std::unique_ptr
with up
and std::nullptr_t
with my_nullptr_t
and std::move
with my_move
in the code base. In C ++ 11 they are all C ++ 11. In C ++ 03 they are emulated.
You can even take it one step further and teach you to my_move
return lvalue_up
and do a up
copy-from-refusal up
in C ++ 03 mode (manual redirect semantics). You can even set up auto-replace on containers as you move from them to C ++ 03.
source to share