de.
de.

Reputation: 8515

How to create a new object in a c function and pass a reference to it?

The essence of my question is this:

What is the best approach for creating a new object in a c function and pass some reference to that new object to the caller?

My overall setting is a bit more complex and involves mixed c and c++ code, so I am including some background information:

I am creating a c++ library for a 3D engine. The library is intended to be used from host applications on different platforms.

For easy compatibility reasons I now intent to hide all c++ code behind an API in plain c. This works pretty well so far.

The only thing I am not sure about is, which is the best way for actually creating my engine instance through my c API.

My first approach was this:

// in myEngineAPI.h
void myEngineCreate(void * myEngine);
void myEngineRelease(void * myEngine);


// in myEngineAPI.cpp
#include "myEngineAPI.h"
#include "myPrivateCppEngine.h"

void myEngineCreate(void * myEngine) {
    myEngine = new Engine;               // <- this doesn't seem to work as expected
}
void myEngineRelease(void * myEngine) {
    delete ((Engine *)myEngine);
}


// in my host application
#include "myEngineAPI.h"

void * myEngine = NULL;
myEngineCreate(myEngine);    // <- problem: myEngine is still NULL after this line.

// ...draw some fancy stuff...

myEngineRelease(myEngine);

I would expect that myEngineCreate(void * myEngine) would assign the address of my newly created object to myEngine. But after the function returns, myEngine still points to NULL. Why?

Now my second approach was this:

// in myEngineAPI.h
void * myEngineCreate();
void myEngineRelease(void * myEngine);


// in myEngineAPI.cpp
#include "myEngineAPI.h"
#include "myPrivateCppEngine.h"

void * myEngineCreate() {
    return new Engine;               // <- ok, the function returns a valid pointer
}
void myEngineRelease(void * myEngine) {
    delete ((Engine *)myEngine);
}


// in my host application
#include "myEngineAPI.h"

void * myEngine = myEngineCreate();  // <- yay, I have a void pointer to my engine

// ...draw some fancy stuff...

myEngineRelease(myEngine);

This works. myEngineCreate() gives me an opaque pointer to my engine instance, which I can use in my subsequent drawing calls and which I can also hand to my release function that cleans up memory when I'm done with it. The problem with this approach is, that my profiler complains about a memory leak in myEngineCreate(). I understand that creating an object in one place and owning it in another is a delicate business, and I seem to do something wrong here - but what?

Thanks in advance for any advice or help.

Upvotes: 2

Views: 3515

Answers (3)

Some programmer dude
Some programmer dude

Reputation: 409364

Lets take your myEngineCreate function:

void myEngineCreate(void * myEngine) {
    myEngine = new Engine;
}

This does not work because myEngine is a local variable within the scope of the myEngineCreate function. To pass a pointer "by reference" in C you have to pass it as a pointer to a pointer, and use the derefernece operator to assign to the pointer:

void myEngineCreate(void ** myEngine) {
    *reinterpret_cast<Engine**>(myEngine) = new Engine;
}

And you call it by using the address-of operator & of a pointer:

void * myEngine;
myEngineCreate(&myEngine);

As a late addendum, another possible solution is to return the new pointer:

void* myEngineCreate() {
    return reinterpret_cast<void*>(new Engine);
}

Upvotes: 9

Lundin
Lundin

Reputation: 214475

What is the best approach for creating a new object in a c function and pass some reference to that new object to the caller?

In C, the best, object-orientated way is to create an object of incomple type, also known as opaque type (a void pointer is not an opaque type, it is just a raw address) .

Example:

// engine.h

typedef struct engine_t engine_t;

engine_t* engine_create (void);

// engine.c

struct engine_t
{
  /* all variables placed here will have true private encapsulation */
};


engine_t* engine_create (void)
{
  return malloc(sizeof(engine_t));
}

// main.c

int main()
{
  engine_t* my_engine = engine_create();
}

This is real object-orientation in C, the best OO you can achieve without C++ language support. The caller will not be able to access any members of the struct.

But after the function returns, myEngine still points to NULL. Why?

Because pointers themselves are passed by value in C and C++. You either have to pass the address of a pointer or return the pointer to the caller. Multiple questions and FAQ about this topic can easily be found on SO.

Upvotes: 3

Akobold
Akobold

Reputation: 946

I did something similar using opaque pointers:

typedef struct my_c_engine *myCEngine;

extern "C" myCEngine createEngine() {
    return reinterpret_cast<myCEngine>(new myRealEngineClass());
}

extern "C" void releaseEngine(myCEngine c) {
    if(c) {
        myRealEngineClass *x = reinterpret_cast<myRealEngineClass*>(c);
        delete x;
    }
}

I did this way because I wanted the C part to be completely isolated.

Upvotes: 1

Related Questions