10

Hi!

I've used the following C macro, But in C++ it can't automatically cast void* to type*.

#define MALLOC_SAFE(var, size) { \
    var = malloc(size); \
    if (!var) goto error; \
}

I know, I can do something like this:

#define MALLOC_SAFE_CPP(var, type, size) { \
    var = (type)malloc(size); \
    if (!var) goto error; \
}

But I don't want to rewrite a big portion of code, where MALLOC_SAFE was used.

Is there any way to do this without giving the type to the macro? Maybe some MSVC 2005 #pragma/__declspec/other ?

p.s.: I can't use C compiler, because my code is part (one of hundreds modules) of the large project. And now it's on C++. I know, I can build my code separately. But it's old code and I just want to port it fast.

The question is about void* casting ;) If it's not possible, I'll just replace MACRO_SAFE with MACRO_SAFE_CPP

Thank You!

zxcat
  • 2,054
  • 3
  • 26
  • 40
  • How is your memory freed? using `free` directly, or a macro? – Steve Townsend Oct 26 '10 at 20:18
  • Why can't you compile the code as a C program? Visual C++ will compile C. – James McNellis Oct 26 '10 at 20:20
  • So wait, you're programming C and cannot use a C compiler? What? – GManNickG Oct 26 '10 at 20:21
  • 1
    @zxcat: Which version of MSVC? – kennytm Oct 26 '10 at 20:23
  • 3
    The C++ compilers I'm familiar with compile C as well, if the file is specified as C. In the meantime, if you're replacing the macro, you might consider changing to `new`/`delete` instead of `malloc`/`free`. – David Thornley Oct 26 '10 at 20:53
  • 2
    I know you said you don't want to do this, that's why I'm not providing it as an answer, just a consideration. Would it really be that bad to rewrite the code? I mean, it's terrible even as C code. It certainly doesn't belong in a C++ program. If it were me, even if it took a few full working days to rewrite it, I would do it. In my time off if I had to. – Benjamin Lindley Oct 26 '10 at 20:54
  • @PigBen: yes, I know, it's terrible. And of course I'll rewrite it – zxcat Oct 26 '10 at 21:04
  • @zxcat: You will? You know I was referring to the "big portion of code, where MALLOC_SAFE was used.", which you said you didn't want to rewrite, not just the macro itself, right? – Benjamin Lindley Oct 26 '10 at 21:22
  • @PigBen: yes, I don't want to rewrite it now. Now I want to compile it and use as fast as possible. But I see the code is bad. Not only with this macro. Later the whole module will be rewritten. – zxcat Oct 26 '10 at 22:43

4 Answers4

37

To makes James' answer even dirtier, if you don't have decltype support you can also do this:

template <typename T>
class auto_cast_wrapper
{
public:
    template <typename R>
    friend auto_cast_wrapper<R> auto_cast(const R& x);

    template <typename U>
    operator U()
    {
        return static_cast<U>(mX);
    }

private:
    auto_cast_wrapper(const T& x) :
    mX(x)
    {}

    auto_cast_wrapper(const auto_cast_wrapper& other) :
    mX(other.mX)
    {}

    // non-assignable
    auto_cast_wrapper& operator=(const auto_cast_wrapper&);

    const T& mX;
};

template <typename R>
auto_cast_wrapper<R> auto_cast(const R& x)
{
    return auto_cast_wrapper<R>(x);
}

Then:

#define MALLOC_SAFE(var, size)                      \
{                                                   \
    var = auto_cast(malloc(size));                  \
    if (!var) goto error;                           \
}

Don't use it for anything but evil.

GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • 3
    +1 for the first real solution I've seen to C++'s broken `void *` behavior. – R.. GitHub STOP HELPING ICE Oct 26 '10 at 20:41
  • 8
    @R.: What broken `void *` behavior in C++? Given that it's a statically typed language, I have to approve any movement toward type safety, and you rarely need a `void *` in C++ anyway. – David Thornley Oct 26 '10 at 20:51
  • I upvoted it, it's just too perverted to not upvote. If you downvote we can split the difference and say we both gave it half a vote each way ;) – jalf Oct 26 '10 at 20:56
  • 5
    Also as David says, I don't see what's "broken" about a statically typed language requiring you to respect the static type system. I know some people don't like to hear this, but it is possible for languages to be different than C **and at the same time not be broken**. – jalf Oct 26 '10 at 20:57
  • Heh, It's works (at least compiles). And with `inline` added produces good (without any overhead) asm code :) – zxcat Oct 26 '10 at 20:58
  • 1
    As a personal rule, I never follow a link named "how perverted." This is the Internet; I don't even want to think of what might be at that link... – James McNellis Oct 26 '10 at 21:26
  • @GMan: Also will this work if friend functions 'T' is a built-in type (void * always?). In that case ADL will fail isn't it and hence friend function (auto_cast) won't be found? Sorry, but I am having trouble understanding this. – Chubsdad Oct 27 '10 at 03:13
  • I <3 GMan. Is there a reason you used `const R&` instead of forcing it to only work with void*? –  May 11 '11 at 20:00
  • @acid: Just generality, for the fun of it. I prefer generic utilities being used in specific cases over specific utilities for specific cases; the former allows re-usability. – GManNickG May 11 '11 at 20:01
  • I can't imagine where ELSE this SHOULD be used. Hence my question >: -). But then again I never use typecasting (except mallocs) –  May 11 '11 at 20:43
  • @acid: Oh I don't know where to use it either, lol, I just do it anyway. – GManNickG May 11 '11 at 20:45
  • To bad it doesnt work with single parameter constructors http://www.pastie.org/1890862 I dont think there is a way to get that to work –  May 11 '11 at 22:29
  • @acid: The reason for that is that it's not sure what overload to pick: the user-defined constructor or the copy-constructor, even though with the normal value overload resolution works. (You might think to use `enable_if` and `is_convertible` on the conversion operator, but the template argument isn't known until *after* the overload is picked.) I can't think of a way either. – GManNickG May 11 '11 at 22:57
16

I do not recommend doing this; this is terrible code and if you are using C you should compile it with a C compiler (or, in Visual C++, as a C file)

If you are using Visual C++, you can use decltype:

#define MALLOC_SAFE(var, size)                      \
{                                                   \
    var = static_cast<decltype(var)>(malloc(size)); \
    if (!var) goto error;                           \
}
James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • Thank you, @James. This is the answer I've searched. Bad, that vs2010 only – zxcat Oct 26 '10 at 20:38
  • 5
    On another note, a macro should never be `{ ... }`, it's much safer to use `do { ... } while(0)` instead. It will behave much better with any trailing `;` you might have and `if else` blocks. – Aaron McDaid Jan 09 '15 at 14:16
5

For example, like this:

template <class T>
void malloc_safe_impl(T** p, size_t size)
{
    *p = static_cast<T*>(malloc(size));
}

#define MALLOC_SAFE(var, size) { \
    malloc_safe_impl(&var, size); \
    if (!var) goto error; \
}
UncleBens
  • 40,819
  • 6
  • 57
  • 90
4

Is there a reason nobody just casts var, your argument to SAFE_MALOC()? I mean, malloc() returns a pointer. You're storing it somewhere that accepts a pointer... There are all sorts of neat type-safe things that other folks have already pointed out... I'm just wondering why this didn't work:

#define MALLOC_SAFE(var,size)  {  \
    (* (void **) & (var)) = malloc(size); \
    if ( ! (var) ) goto error;    \
    }

Yeah... I know. It's sick, and throws type-safety right out the window. But a straight ((void *)(var))= cast wouldn't always work.

Mr.Ree
  • 8,320
  • 27
  • 30