views:

172

answers:

3

As I'm coding for both Windows and Linux, I have a whole host of problems. Microsoft Visual C++ has no stdint header, but for that I wrote my own.

Now I found out that MS C++'s new operator does not throw an exception, so I want to fix this a quickly as possible. I know I can define a Macro with Parameters in Parenthesis, can I define a macro that replaces

MyClass x = new MyClass();

with

#ifdef MSC_VER
if(!(MyClass x = new MyClass())
{
    throw new std::bad_alloc();
}
#else
MyClass x = new MyClass();
#endif

(or something equivalent), AND works in BOTH MS C++ and G++ ?

Or alternatively if that is not possible, a batch file to run over the Code to do this? I have become rather dependent on this exception being thrown.

+2  A: 

Microsoft's C++ compiler is pretty close to 100% standards conforming, except for perhaps a few obscure things. Not throwing exceptions in new is not one of them.

That aside, you cannot write a macro that can detect that expression and handle it accordingly, unless you want to rewrite all your uses of new (defeating the purpose).

Peter Alexander
+1  A: 

Actually, there's a simple solution: replace operator new with one which does throw on allocation failure. (The linked code is standard C++ and may need tweaks for your compiler. You can use malloc for allocate_from_some_other_source and free for deallocate_from_some_other_source.)


Old answer

(These replacements do require changes in calling code, simply because the C++ grammar is structured so that's unavoidable. However, they're written to require minimal changes in order to be used as logical drop-in replacements. The real solution is upgrading to a version of MSVC from the last 7 years or so. MSVC6, which it sounds like you're using, was actually released before the C++ standard in 1998.)

Solving for the general case is hard, but here's an approximation:

struct Thrower { // needs a better name
  void* p;
  Thrower(void* p) : p(p) {
    if (!p) throw std::bad_alloc();
  }
  ~Thrower() {
    if (std::uncaught_exception()) operator delete(p);
    // this is true when the object's ctor throws
  }
  operator void*() const { return p; }
};

int main() {
  int* p = new (Thrower(operator new(sizeof(int))) int(42);
  // int is a placeholder type, but shows you can still pass ctor parameters

  // with a macro becomes:
#define MYNEW(T) new (Thrower(operator new(sizeof(T))) T
  int* p = MYNEW(int)(42);
}

For known parameters, you can avoid a macro and keep it simple:

template<class T>
T* my_new() {
  T* p = new T();
  if (!p) throw std::bad_alloc();
  return p;
}
template<class T>
T* my_new(T const& value) {
  T* p = new T(value);
  if (!p) throw std::bad_alloc();
  return p;
}

int main() {
  int* p = my_new<int>();
  int* p2 = my_new<int>(*p);
}
Roger Pate
+1  A: 

The simplest alternative is to write your own implementation of operator new, overriding the compiler's default implementation (if your version of MSVC supports it). This is what Roger also suggested in his linked answer.

The following program illustrates the attempt. operator new allocates memory from the heap using malloc and free. If you're doing conditional compilation for G++ and MSVC, you can also use HeapAlloc and HeapFree instead of malloc and free for a more WinAPI-oriented approach.

#include <iostream>
#include <cstdlib>

void *operator new(unsigned int size) throw (std::bad_alloc) {
    void *ptr = malloc(size);

    if (!ptr)
        throw std::bad_alloc();
    return ptr;
}

void operator delete(void *ptr) throw () {
    free(ptr);
}

// Sample code to test the implementation.
int main(int argc, char **argv) {
    int *values[10];

    for (int i = 0; i < 10; ++i)
        values[i] = new int(i);
    for (int i = 0; i < 10; ++i)
        std::cout << *values[i] << std::endl;
    for (int i = 0; i < 10; ++i)
        delete values[i];
    return 0;
}
Alek