domlao
domlao

Reputation: 16029

Convert shared_ptr to void* in callbacks

I want to pass a shared_ptr in a C function that accept a void*. Is it safe that the created object in shared_ptr will be destroyed and when can it be destroyed? Like,

class CBar
{
 public:
    CBar(){}
    ~CBar(){}
    void lock(){}
    void unlock(){}
};

//----------------------------------------------------

typedef struct element
{
   std::string filename;
   void (*cb)(std::string filename, void* data);
   void* data;
};

std::list<element> elm;

void reg(std::string filename, void (*cb)(std::string filename, void*data), void* data)
{
    element item;
    item.filename = filename;
    item.cb = cb;
    item.data = data;
}

void unreg(std::string filename)
{
   std::list<elements>::iter;
   for(iter=elm.begin();iter!=elm.end();++iter)
   {
      if((*iter).filename == filename)
      {
         elm.erase(iter);
         break;
      }
   }
}

void exec()
{
    std::list<element>::iterator iter;
    for(iter=elm.begin();iter!=elm.end();++iter)
    {
        (*iter).cb((*iter).filename,(*iter).data);
    }
}

//---------------------------------------------------
// in another source file

void cb(std::string filename, void* data)
{
   boost::shared_ptr<CBar> cbar = *(boost::shared_ptr<CBar>*)(data);
   cbar->lock();
}

void foo(std::string filename)
{
    //create the shared_ptr here.
    boost::shared_ptr<CBar> ffoo(new CBar);

    //then pass the shared_ptr object
    reg(filename, &cb, &ffoo);

}

int main()
{
   foo("file");
   exec();

   return 0;
}

Upvotes: 2

Views: 2599

Answers (1)

WhozCraig
WhozCraig

Reputation: 66194

The shared pointer will be destroyed when your function leaves scope. If it is the last one wrapping that data, so goes the data with it (and thus storage in the list becomes bogus as well). Your sample is doubly worse, as you're sending the address of a local variable, which will be outright undefined behavior the moment the reg() function exits and the local variable falls out of scope

I would suggest you put a shared pointer in your element struct as well. It is, after all, shared, and thus it will extend the life of that data until such time as the element is removed. If this is a true C-type callback you may also want to make the first callback parameter a const char* and send it the c_str() method accordingly.

Note: I don't have boost installed, so the code below uses std::shared_ptr<>, but the semantics should be the same.

#include <iostream>
#include <vector>
#include <string>
#include <list>
#include <memory>
#include <cstdlib>

class CBar
{
public:
    CBar(){}
    ~CBar(){}
    void lock(){}
    void unlock(){}
};

struct element
{
    std::string filename;
    void (*cb)(std::string, void* data);
    std::shared_ptr<CBar> data;
};


std::list<element> elm;

void reg(std::string filename,
         void (*cb)(std::string, void*),
         std::shared_ptr<CBar>& data)
{
    element item;
    item.filename = filename;
    item.cb = cb;
    item.data = data;
    elm.push_back(item);
}

void exec()
{
    std::list<element>::iterator iter;
    for(iter=elm.begin();iter!=elm.end();++iter)
    {
        iter->cb(iter->filename, iter->data.get());
    }
}

// in another source file
void cb(std::string filename, void* data)
{
    CBar *cbar = static_cast<CBar*>(data);
    cbar->lock();
}

void foo(std::string filename)
{
    //create the shared_ptr here.
    std::shared_ptr<CBar> ffoo(new CBar);

    //then pass the shared_ptr object
    reg(filename, &cb, ffoo);

}

int main()
{
    foo("file");
    exec();
    return 0;
}

Upvotes: 1

Related Questions