2

I want to declare a "safe" push() function for use with auto_ptr like this:

template<class StackType,typename T>
inline void push( StackType &s, auto_ptr<T> p ) {
  s.push( p.get() );
  p.release();
}

I also want it to work for null pointers, e.g.:

push( my_stack, 0 ); // push a null pointer

Hence, a specialization:

template<class StackType>
inline void push( StackType &s, int p ) {
  s.push( reinterpret_cast<typename StackType::value_type>( p ) );
} 

While it works, it's both ugly and allows erroneous code like:

push( my_stack, 1 ); // ???

to compile.

How can I write a specialization of push() such that it accepts only 0 as a valid int value (for the null pointer)?

Requirements

  • StackType is some stack-like container class that I must use and whose source code I can not change (just like std::stack). I can assume it has a push() member function.

  • I can not use nullptr since I can not require a C++0x compiler.

Paul J. Lucas
  • 6,895
  • 6
  • 44
  • 88
  • Will you know at compile-time that the pointer is null? i.e. will you have code like this?: `char * ptr = 0; push( my_stack, ptr );` – Aaron McDaid May 18 '11 at 00:17
  • @Aaron: I will know at compile-time that the pointer is null, i.e., is the literal 0. – Paul J. Lucas May 18 '11 at 00:28
  • I don't have a good answer yet, but it's useful to have that clarified that it's a literal `0`. Would you be allowed to make your own clone of the `nullptr` type, and require users to pass in an instance of that type? `push (my_stack, my_null)` And one more question: assuming it must be a literal `0`, would you be allowed to define `push` as a macro? – Aaron McDaid May 18 '11 at 13:14
  • @Aaron: yes, I can define push as a macro, e.g., PUSH(stack,arg). – Paul J. Lucas May 18 '11 at 15:55
  • I've given up. But I'll tell you what I was thinking about. You might be able to use template specialization to distinguish a literal `0`from other literal ints. `template struct MustBeZero; template<> struct MustBeZero<0> { }; int main() { MustBeZero<0> m0; /* compiles OK */ MustBeZero<1> m1; /* error: aggregate ‘MustBeZero<1> m1’ has incomplete type and cannot be defined */ } ` – Aaron McDaid May 20 '11 at 14:21

3 Answers3

3

You could overload the function as follows:

template<class StackType,typename T>
inline void push( StackType &s, auto_ptr<T> p ) {
  s.push( p.get() );
  p.release();
}

template<class StackType>
inline void push( StackType &s )
{
  s.push( reinterpret_cast<typename StackType::value_type>( 0 ) );
}

then use it:

push( my_stack );
push( my_stack, auto_ptr_var );
Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212
  • We were not given details on `s.push`, but I think `s.push(0)` is what was intended rather than the `reinterpret_cast` stuff. (e.g. the null pointer is mentioned in the question). – Luc Danton May 17 '11 at 19:56
1

EDIT: second iteration. (The first assumed that the push method of some particular stack class was to be overloaded.)

This iteration instead seeks to provide push as a function template for any class providing a push member that takes can store T*. The intent is to allow auto_ptr<T> and 0 as arguments, but to disallow other integral values and pointers.

The basic trick is still the same: provide an overload such that push(s, 0) actually interprets 0 as a null pointer to member.

class X;

template<typename StackType, typename T>
void push(StackType& s, std::auto_ptr<T> p);

template<typename ST>
void push(ST&, int (X::*));

class X {
  private:
    int m;
    X(); // unimplemented
    X(const X&); // unimplemented
    X& operator=(const X&); // unimplemented
    ~X(); // unimplemented

   template<typename ST>
   friend
   void push(ST&, int (X::*));
};

template<typename StackType, typename T>
void push(StackType& s, std::auto_ptr<T> p) {
    s.push(p.get());
    p.release();
}

template<typename StackType>
void push(StackType& s, int (X::*)) {
    s.push(0);
}

Test:

std::stack<int*> s;
push(s, std::auto_ptr(new int(1))); // works
push(s, 0);  // works
push(s, 1);  // errors out: no matching function
Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
Lambdageek
  • 12,465
  • 1
  • 25
  • 33
  • Aside form being overkill, your implementation doesn't solve the problem. Your line "stk.push_back(p.release());" is not exception safe: if the push_back() throws, p will leak. The whole point is not to release until after push succeeds. – Paul J. Lucas May 17 '11 at 21:23
  • I wasn't trying to make it exception safe. I was trying to show you how to overload a function so that it accepts NULL as the only bare pointer. The only overkill is that I wanted a fully standalone example. – Lambdageek May 17 '11 at 21:25
  • Okay, to be fair, I should say "accepts NULL as the only bare pointer **from non-`friend`s of `S`**" – Lambdageek May 17 '11 at 21:28
  • But point taken. Let me change it so that `push` doesn't leak if `push_back` throws... – Lambdageek May 17 '11 at 21:29
  • Although not explicit in my original question, the assumption was that StackType is some other class whose source I can not modify. All I know about it is that it has a push() member function. – Paul J. Lucas May 18 '11 at 00:07
  • Ah okay, sorry, didn't understand what the restrictions were. Edited my solution to provide a non-member push that works with `auto_ptr` and `0` and nothing else. (Obviously the intent here is to prevent accidental misuse, not a determined abuser) – Lambdageek May 18 '11 at 01:16
0

You need a specific null pointer type to handle this case. This is why C++11 brings in nullptr.

Puppy
  • 144,682
  • 38
  • 256
  • 465