0

Given a function declaration:

void foo(void *ptr);

I am NOT able to change signature of the function. Now I need to allocate memory in foo, starting with ptr. If I write:

void foo(void *ptr) {
  ptr = malloc(NUM * sizeof(TYPE));
}

It does not actually change contents of pointer when I call it with:

void *myPtr = NULL;
foo(myPtr);

And I know the reason since I am passing a copy of myPtr into foo().

What I need to do is declare input parameter as reference of pointer:

foo(void *& ptr) {
  ptr = malloc(...);
}

However, I can not change input type from (void *) to (void *&). How can I solve this problem?


I forgot that return value is not part of signature, and yes, I can not change return.

I think it's true that I can not do so in C++. I have to allocate memory outside this function.

3 Answers3

1

What you want is not possible, without either changing the function signature or the call site. i.e. one very dirty hack would be:

void foo(void *ptr) {
  *((void **)ptr) = malloc(NUM * sizeof(TYPE));
}

int main() {
    void *myPtr = NULL;
    foo(&myPtr);
}

However, for most situation (especially, if you don't want to change existing call sites), you'd probably fare better if you write a new function that does what you want and make foo a wrapper for it (or vice versa, depending on what exactly foo was supposed to do previously):

void new_foo (void*& ptr ) {
     // ... whatever came before in the original foo
     ptr = malloc(NUM * sizeof(TYPE));
     // ... whatever came after in the original foo
}

void foo (void* ptr) {//<-- called on old call sites
     void * tptr=ptr;
     new_foo(tptr);
     // free(tptr) ?

}

int main() {
   void* ptr2=NULL;
   foo(ptr1) //<- old call site)

   //...

   void* ptr2=NULL;
   new_foo(ptr2); //<- new call site       
}
MikeMB
  • 20,029
  • 9
  • 57
  • 102
  • You can do this without touching any of those two (see my answer). – 3442 Mar 23 '16 at 06:51
  • 1
    @KerryLand As far as I can see, you changed the call site (you are now calling trap_foo instead of foo) – MikeMB Mar 23 '16 at 06:55
  • That's just a convenience. If you read my answer, you'll notice that's not actually the case. You call the same function, but surround calls to it with trap hooks. `trap_foo` just makes this into a helper function. – 3442 Mar 23 '16 at 07:01
  • 1
    @KerryLAnd: You still call have to call some function (whether it is `foo_trap` or `trap_malloc_set` with the address of` myPtr` (`some_ptr`) as an argument. And the fact, that you have to call the helper functions defintively means, you have to controll the call site. – MikeMB Mar 23 '16 at 07:08
  • No. It means you have to control the call site *for those that require it*. I mean, you don't have to change the "call syntax", as you call it. You just call wrapper functions if and only if your code needs to retrieve the pointer. Your answer will make any old code, that relies on the old interface, to crash, and so *all* code has to be changed. Mine has no such requirement. If the OP can't change the signature, it's most likely because the function has already been used considerably, but now needs to be used by new code, yet it's might not be feasible to change old code just for this. – 3442 Mar 23 '16 at 07:09
-1

If you can move code to/and from foo, without changing its signature, and this is feasible for your use case, you may do something like...

void underlying_foo(void *ptr)
{
    // Do the stuff.
}

void foo(void *ptr)
{
    ptr = malloc(...);
    underlying_foo(ptr);
}

void new_foo(void **ptr)
{
    *ptr = malloc(...);
    underlying_foo(*ptr);
}

If you don't want to/can't twist too much around with foo, then you can use an ugly workaround like this...

#include <cstddef>

static void **trap_malloc_trap = nullptr;

void trap_malloc_set(void **trap)
{
    trap_malloc_trap = trap;
}

void trap_malloc_unset()
{
    trap_malloc_trap = nullptr;
}

void *trap_malloc(size_t size)
{
    void *ptr = malloc(size);
    if(trap_malloc_trap != nullptr)
        *trap_malloc_trap = ptr;

    return ptr;
}

void foo(void *ptr)
{
    ptr = trap_malloc(NUM * sizeof(TYPE));
    // Whatever else goes here...
}

Then...

#include "foo_header.h"

int main()
{
    void *some_pointer;
    trap_malloc_set(&some_pointer):
    foo(some_pointer);
    trap_malloc_unset();

    return 0;
}

This way, any code that called the original function will still work, and new code that needs to retrieve the pointer can use trap_foo() for that.

You can use this other one. It's faster/tighter, yet it'll break any existing code that relies on the existing API...

void foo(void *trap_ptr)
{
    void **ptr = (void**)trap_ptr;
    *ptr = malloc(NUM * sizeof(TYPE));
    // Et cetera...
}

Then...

void hacky_foo(void **ptr)
{
    foo((void*)ptr);
}

int main()
{
    void *the_pointer;
    hacky_foo(&the_pointer);

    return 0;
}
3442
  • 8,248
  • 2
  • 19
  • 41
  • The "workaround" involves calling a different function. Might as well not use `foo` at all and get rid of all the complexity. – juanchopanza Mar 23 '16 at 06:57
  • Can you explain, what makes the first version less hacky than the second? – MikeMB Mar 23 '16 at 06:58
  • @juanchopanza: Read the code more carefully. You don't actually need to call a different function, but to surround code calling `foo` with trap hooks. – 3442 Mar 23 '16 at 06:59
  • @MikeMB: Nothing in particular, but the first version allows old code to work the way it does, and "new" (whether it's new or not) code that needs to retrieve the pointer's value to get it, using both of them the same underlying function. – 3442 Mar 23 '16 at 07:00
  • @KemyLand in `main()` you have this: `trap_foo(&some_pointer);`. So, just to the simple thing in `trap_foo` and do away with `foo()`. – juanchopanza Mar 23 '16 at 07:00
  • @juanchopanza: The OP explicitly said that he can't change the function signature. I won't expect him to remove the function then... – 3442 Mar 23 '16 at 07:02
  • @KerryLand: By your logic, ýou could also just do all the work in `trap_malloc_set` and do nothing in `foo` at all. (you wouldn't even require `trap_unset` or `trap_malloc`) – MikeMB Mar 23 '16 at 07:10
  • 1
    Essentially, what your code says is *"foo can't chagne the value of your pointer with the given interface, so simply ignore it and call a different function, that does what you want"* - just in an unnecessarily complex way. – MikeMB Mar 23 '16 at 07:14
  • @MikeMB: That makes no sense. Please understand that *both* old code, that relies on the existing `foo` function, and new code, that needs to gather the pointer used in `foo`, may be needed. Thus, you need a way to only change `foo` internally, in a way that doesn't conflicts with existing code, yet can be used by new code in the way the OP wants. This is the only way to do that. – 3442 Mar 23 '16 at 07:15
  • @MikeMB: BTW, `foo` does (almost) everything. No function is replacing it. – 3442 Mar 23 '16 at 07:16
  • @KerrLand: What do you think the old code expects `foo` to do? – MikeMB Mar 23 '16 at 07:16
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/107096/discussion-between-kemyland-and-mikemb). – 3442 Mar 23 '16 at 07:16
-1

You should really ask this tagged as a C question -- this kind void* pointer tomfoolery is frowned on in C++. Although it is part of the language, and occasionally has its uses, you may have more luck getting the expertise you need from someone looking for C questions to answer.

Moving on to your question, I think what you want to do (at least the example you are posting) is simply impossible -- specifically:

void *myPtr = NULL;
foo(myPtr);

Consider what you are actually doing here. You're declaring a variable myPtr, with a void* type, and assigning it a placeholder value -- an invalid area of memory C programmers call NULL, which it typically 0 (C++ programmers prefer nullptr which is a specific type, providing some safety benefits).

Now you pass that to foo:

void foo(void *ptr) {
  ptr = malloc(NUM * sizeof(TYPE));
}

foo receives a NULL pointer -- an invalid memory address which is typically 0. When you call the ptr = malloc(...) statement your variable ptr gets the value of the memory address returned by malloc -- which has allocated some memory for you and returned an address pointing to that memory.

You have not modified the contents of the variable myPtr, which contains an invalid memory adress NULL.

You need to change the signature of the function so that it can fill myPtr with a valid address -- the address of the memory you just allocated. You could do this with:

 1:  void* foo(void* ptr) { return malloc(...);} // return the address.
 2:  void foo(void** ptr) { *ptr = malloc(...);} // ptr to ptr.
 3:  void foo(void*& ptr) { ptr = malloc(...);} // C++:  reference to ptr.

I don't see any other way around it. Incidentally I would prefer option 1, since at point of reading it's clearest what's happening:

myPtr = foo(myPtr);

And unless you're doing anything with the contents of myPtr in foo (e.g. you're checking if it's null and allocating if not), you should lose the parameter.

Spacemoose
  • 3,856
  • 1
  • 27
  • 48