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

    and NULL

    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?

+4


source to share


4 answers


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

).

+2


source


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!

+3


source


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.

0


source


#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.

0


source







All Articles