1

I have a (modifiable) C API that calls my C++ code back from a POSIX thread. Because directly calling non-static member functions from plain C is impossible, I have set up a static wrapper which uses a pointer provided by the callback caller to refer to the instance of the class as described in two other questions and very verbosely here.

The major pita (as a Java-spoiled C++ newbie :) is that this does not seem to be very stable at all. For example, if I store the objects I just registered to the callback in a Vector with push_back it seems as if the old object's life is over after going out of scope and only a copy is stored in the Vector. This produces fancy explosions but that's not what I am looking for ;)

What options do I have to mitigate this problem?

Since it was requested let me show the current state/problem:

std::vector<A> v;
{
    A cur(...);
    cur.set_handler(); /* to avoid leaking 'this' in the constructor */
    v.push_back(cur); /* creates a copy of cur */
} /* I presume cur is destroyed here */

Important parts of class A:

handler_t handler;

void A::set_handler() {
    handler.handle = handle_stuff_static;
    handler.user_data = this;
    add_handler(&handler); /* C function which stores the pointer for further reference (no pun intended) */
}

void A::handle_stuff_static(void *user_data) {
    if (user_data != NULL)
        static_cast<A *>(user_data)->handle_stuff_instance();
}

void A::handle_stuff_instance() { /* non-static member function */
    // hurray
}
Community
  • 1
  • 1
stefanct
  • 1,772
  • 1
  • 23
  • 25
  • Consider adding a bit of code, how exactly does the C++, non-static method (which works with the vector) look like. – Yirkha Apr 17 '14 at 02:04
  • 2
    How about declaring a vector of pointers to your object rather than vector of objects? Then there would be no copies made when you `push_back` a pointer. – anonymous Apr 17 '14 at 02:08
  • @anonymous yes, I was thinking about this option but I am unsure how this changes the lifetime of the actual objects. I did not grasp the concepts of the different smart pointers and references (or probably the whole lifetime concept of C++) yet, but for normal pointers I'd guess that they become tangling when the scope where the objects were created ends? Of course I could use `new` but I presume there are better options? – stefanct Apr 17 '14 at 02:18
  • 1
    Lifetime management (or rather how to avoid it) is one of the core concepts in C++, you should learn to embrace it, to understand when an object is alive and when it is dead, when a pointer is still valid and when it will be invalidated. For this particular purpose, you should follow @anonymous's advice and use a vector of *smart* pointers (`shared_ptr`/`unique_ptr`) the smart pointer managing the lifetime of the object. – David Rodríguez - dribeas Apr 17 '14 at 02:22
  • Of course I am aware of the importance of lifetime hence the whole question :) – stefanct Apr 17 '14 at 02:35

1 Answers1

0

My own solution for C++03 with boost (I actually can not use C++11) based on the comments of anonymous and David Rodríguez - dribeas (thanks!).

#include <boost/shared_ptr.hpp>

{
    std::vector<boost::shared_ptr<A> > v;
    {
        A *cur = new A(...);
        cur.set_handler(); /* to avoid leaking 'this' in the constructor */
        v.push_back(boost::shared_ptr<A>(cur)); /* creates a shared pointer pointing at cur */
    } /* the object is no longer destroyed here because of the smart pointer still in scope */
} /* the object previously pointed to by cur gets deleted when the vector gets out of scope here */
Community
  • 1
  • 1
stefanct
  • 1,772
  • 1
  • 23
  • 25
  • 1
    As a side comment, if you are going to use shared pointers, you can (and should whenever possible) create them with `std::make_shared` (`boost::make_shared`), for two reasons. The main one: it ensures that it will *not* leak; the other one is that you reduce the cost of the operation. The `shared_ptr` needs to hold additional state (shared/weak counts, deleter…), if you use the code in the answer, that shared state must be allocated independently of the object, and you need two dynamic allocations (new). Using `make_shared` reduces that to a single allocation for both the object and counts. – David Rodríguez - dribeas Apr 18 '14 at 02:31