Creating C++ objects from C wrappers

Viewed 72

I have a question about returning pointers to C++ objects from C wrappers.

Here are two C++ classes A and B. I essentially want to create an instance of A from B.


class A {
private:
    int a;

public:
    explicit A(int a) : a(a) {};

    int getA() const {
        return a;
    }

    void setA(int a) {
        A::a = a;
    }
};


class B {
private:
    int b;

public:
    explicit B(int b) : b(b) {};

    int getB() const {
        return b;
    }

    void setB(int b) {
        B::b = b;
    }

    A createA() {
        return A(getB()); // A will be a complicated object that must be created from B
    }
};

And the C wrapper

extern "C" {
int A_get_a(A *a) {
    return a->getA();
}
A *B_getA_1(B *b) {
    A a = b->createA();
    A *a_ptr = &a;
    return a_ptr;
}
void B_getA_2(B *b, A* a_ptr) {
    A a = b->createA();
    a_ptr = &a;
}
}

In B_getA_1 the static analyzer is complaining that

Address of local variable may escape the function.

Presumably because of the scoping issue. In B_getA_2 however, when using the function via:

int main() {

    B b(4);
    A* a;
    B_getA_2(&b, a); // <--

    return 0;
};

the static analyzer complains

Variable 'a' is uninitialized when used here

So, what would be a good way to create an instance of A from B?

3 Answers

The warning you get for this function:

A *B_getA_1(B *b) {
    A a = b->createA();
    A *a_ptr = &a;
    return a_ptr;
}

Is because you're returning the address of a local variable. That variable's lifetime ends when the function exits, so the returned pointer is invalid. Attempting to dereference that pointer invokes undefined beahavior.

You have a related problem with this function:

void B_getA_2(B *b, A* a_ptr) {
    A a = b->createA();
    a_ptr = &a;
}

Here, you're assigning a value to a_ptr which is a parameter and therefore local to the function, which means it doesn't affect a in main. You would need to pass the address of a and assign to the dereferenced pointer. But again, you would be exporting the address of a local variable whose lifetime ends when the function exits.

You need to change createA to return a pointer to a dynamically allocated object:

A *createA() {
    return new A(getB());
}

Then change your wrapper functions to return that:

A *B_getA_1(B *b) {
    return b->createA();
}
void B_getA_2(B *b, A **a_ptr) {
    *a_ptr = b->createA();
}

And change how you call the second function:

B_getA_2(&b, &a); 

You should also expose a cleanup function for this returned pointer:

void free_A(A *a)
{
    delete a;
}

In any case, this:

A a = b->createA();
a_ptr = &a;
return;

...will allocate a on the stack, take its address, return it to the caller and then destroy a because it's a local variable. So the pointer will point to invalid memory anyway.

You can return the object itself (as opposed to returning a pointer to it), or allocate memory dynamically, copy that object there and return a pointer to that dynamically allocated region.

Second problem:

void B_getA_2(B *b, A* a_ptr) {
    A a = b->createA();
    a_ptr = &a;   // this will have no effect, a_ptr is a local variable
}

You probably want this:

 void B_getA_2(B *b, A **a_ptr) {
        A a = b->createA();
        *a_ptr = &a;
    }

 ...
 B_getA_2(&b, &a);

But this is wrong too because now you return a pointer to a local variable, see this answer.

Related