如何通过unsigned long传递std :: shared_ptr回调?

时间:2016-10-14 12:16:00

标签: c++ shared-ptr

我有一个旧的C风格的库,它使用带有unsigned long的回调作为用户参数,我想将我的shared_ptr传递给回调,以便引用计数递增。

void callback( unsigned long arg ) {
  std::shared_ptr<my_class> ptr = ??? arg ???
}

void starter_function() {
  std::shared_ptr<my_class> ptr = std::make_shared<my_class>();

  unsigned long arg = ??? ptr ???
  // pass to library so it may be used by callback
}

目前我在shared_ptr上使用get()然后使用C样式转换,但是当start_function超出范围时会产生问题。

3 个答案:

答案 0 :(得分:9)

创建一个静态存储(可以基于std::map<unsigned long, std::shared_ptr<T>>)。提供以下功能:

  • 在商店中注册共享指针(将其保存在地图中,并返回唯一的长整数)。
  • 检索与特定无符号长
  • 对应的shared_ptr
  • 删除特定的unsigned long

(最后两个功能可以合并)。

这有吸引力,不需要在指针和unsigned long之间进行任何狡猾的转换,并且(如果返回值基于您测试唯一性的递增计数器),使得更容易找到问题创建,删除对象,然后在同一地址创建另一个对象。

这是这个想法的草图:(特别注意,它不是线程安全的!)

template typename<T>
class Store
{
    static std::map<unsigned long, std::shared_ptr<T>> store;
    unsigned long h;
    bool contains(unsigned long i)
    {
        return store.find(i) != store.end();
    }
 public:
    unsigned long save(const std::shared_ptr& ptr)
    {
        if (store.size() >= std::numeric_limits<unsigned long>::max())
        {
            // Handle error.  Only possible if 64 bit program with
            // 32 bit unsigned long.
        }
        // find an unused handle.
        do
        {
            ++h;
        } 
        while(contains(h));  // Not a good approach if there are main long-lived objects, and h might wrap.
        // Store and return handle.
        store[h] = ptr;
        return h;
    }

    std::shared_ptr<T> retrieve(unsigned long handle)
    {
       if (!contains(handle))
       {
           // handle error
       }
       const auto result = store[handle];
       store.erase(handle);
       return result;
    }
 };

答案 1 :(得分:1)

可悲的是,没有&#34;转到&#34;这个问题的解决方案。我有几次,并根据情况不同地解决这个问题。

&#34; easy&#34;解决方案是(如果你有一些类)将指针存储在容器中作为类成员,因此确保它们保持活着状态。然后在函数内部使用原始指针。您必须确保在销毁对象时不会调用您的回调(因此将回调与对象一起存储)。只有当您不从容器中移除任何对象时,这才能顺利运行。添加很好。

使用线程,您可以通过引用传递共享ptr,然后将其复制

{
    auto sP = make_shared<...>(...);
    thread_start(&sp);
    wait_for_thread_init();
}

void thread_start(std::shared_ptr<...>* ref ) 
    //or unsigned long but you can cast this to a shared_ptr<...>* later.
{
    std::shared_ptr otherPointer = *ref; //here your ref count gets increased
    send_init_complete(); 
    //never use ref from here on. only otherPointer.
}

但是C ++ 11有一个自己的线程库可以更好地处理这个问题,因此你应该在使用线程时使用它。

根据您的环境,您必须在此处发挥创意。最后,当你没有实际的解决方案时,你需要使用原始指针。只需添加足够的内容来评论回调应该删除那些对象等。

答案 2 :(得分:1)

您有两个基本选项。这两个选项都假设在您的平台上,unsigned long足以容纳指针:

  1. 将指针转换为std::shared_ptrunsigned long,并将其传递。

  2. 使用get()检索基础指针,然后传递它。

  3. 对于第二个选项,假设您的回调不需要构建另一个shared_ptr;否则你需要使用enable_shared_from_this

    此外,这两个选项都假定在调用回调时shared_ptr和底层对象将保留在作用域中。如果不是:

    1. 使用shared_ptr制作new的副本。在不再需要后,您需要delete,并且不再使用回调。
    2. 如果在您的平台上unsigned long不足以容纳指针,它会变得更加笨拙。最干净的解决方案是保持std::map<unsigned long, std::shared_ptr<...>>左右,以及为每个unsigned long分配唯一shared_ptr值的计数器,将shared_ptr插入地图,然后通过映射回调的关键。