Manually incrementing and decrementing a boost::shared_ptr?

前端 未结 7 1444
后悔当初
后悔当初 2021-01-05 22:57

Is there a way to manually increment and decrement the count of a shared_ptr in C++?

The problem that I am trying to solve is as follows. I am writing a library in C

7条回答
  •  無奈伤痛
    2021-01-05 23:45

    1. A handle?

    If you want maximum security, gives the user a handle, not the pointer. This way, there's no way he will try to free it and half-succeed.

    I'll assume below that, for simplicity's sake, you'll give the user the object pointer.

    2. acquire and unacquire ?

    You should create a manager class, as described by Matthieu M. in his answer, to memorize what was acquired/unacquired by the user.

    As the inferface is C, you can't expect him to use delete or whatever. So, a header like:

    #ifndef MY_STRUCT_H
    #define MY_STRUCT_H
    
    #ifdef __cplusplus
    extern "C"
    {
    #endif // __cplusplus
    
    typedef struct MyStructDef{} MyStruct ; // dummy declaration, to help
                                            // the compiler not mix types
    
    MyStruct * MyStruct_new() ;
    size_t     MyStruct_getSomeValue(MyStruct * p) ;
    void       MyStruct_delete(MyStruct * p) ;
    
    #ifdef __cplusplus
    }
    #endif // __cplusplus
    
    #endif // MY_STRUCT_H
    

    Will enable the user to use your class. I used a declaration of a dummy struct because I want to help the C user by not imposing him the use of the generic void * pointer. But using void * is still a good thing.

    The C++ source implementing the feature would be:

    #include "MyClass.hpp"
    #include "MyStruct.h"
    
    MyManager g_oManager ; // object managing the shared instances
                           // of your class
    
    extern "C"
    {
    
    MyStruct * MyStruct_new()
    {
       MyClass * pMyClass = g_oManager.createMyClass() ;
       MyStruct * pMyStruct = reinterpret_cast(pMyClass) ;
       return pMyStruct ;
    }
    
    size_t MyStruct_getSomeValue(MyStruct * p)
    {
       MyClass * pMyClass = reinterpret_cast(p) ;
    
       if(g_oManager.isMyClassExisting(pMyClass))
       {
          return pMyClass->getSomeValue() ;
       }
       else
       {
          // Oops... the user made a mistake
          // Handle it the way you want...
       }
    
       return 0 ;
    }
    
    void MyStruct_delete(MyStruct * p)
    {
       MyClass * pMyClass = reinterpret_cast(p) ;
       g_oManager.destroyMyClass(pMyClass) ;
    }
    
    }
    

    Note that the pointer to MyStruct is plain invalid. You should not use it for whatever reason without reinterpret_cast-ing it into its original MyClass type (see Jaif's answer for more info on that. The C user will use it only with the associated MyStruct_* functions.

    Note too that this code verify the class does exist. This could be overkill, but it is a possible use of a manager (see below)

    3. About the manager

    The manager will hold, as suggested by Matthieu M., a map containing the shared pointer as a value (and the pointer itself, or the handle, as the key). Or a multimap, if it is possible for the user to somehow acquire the same object multiple times.

    The good thing about the use of a manager will be that your C++ code will be able to trace which objects were not "unacquired" correctly by the user (adding info in the acquire/unacquire methods like __FILE__ and __LINE__ could help narrow the bug search).

    Thus the manager will be able to:

    1. NOT free a non-existing object (how did the C user managed to acquire one, by the way ?)
    2. KNOW at the end of execution which objects were not unaquired
    3. In case of unacquired objets, destroy them anyway (which is good from a RAII viewpoint) This is somewhat evil, but you could offer this
    4. As shown in the code above, it could even help detect a pointer does not point to a valid class

提交回复
热议问题