Skip to content

Instantly share code, notes, and snippets.

@fenbf
Last active February 24, 2021 06:23
Show Gist options
  • Select an option

  • Save fenbf/0f3d404a57fe78278a2605d45b2ff2b8 to your computer and use it in GitHub Desktop.

Select an option

Save fenbf/0f3d404a57fe78278a2605d45b2ff2b8 to your computer and use it in GitHub Desktop.

Revisions

  1. fenbf revised this gist Apr 26, 2016. 1 changed file with 61 additions and 24 deletions.
    85 changes: 61 additions & 24 deletions smart_ptr_deleters.cpp
    Original file line number Diff line number Diff line change
    @@ -5,17 +5,29 @@
    #include <iostream>
    #include <memory>
    #include <functional>
    #include <cassert>

    // This class cannot be changed, might come from 3rd party library
    // to delete it completely you need to call ReleaseElements first!
    class LegacyList {
    public:
    LegacyList() { s_refCounter++; }
    ~LegacyList() { s_refCounter--; }
    LegacyList(const LegacyList&) { s_refCounter++; }
    LegacyList & operator= ( const LegacyList & ) { s_refCounter++; return *this; }

    void Add(const std::string& str ) { }
    void Remove(const std::string& str ) { }

    void ReleaseElements() { }

    static int GetGlobalObjectRefCount() { return s_refCounter; }
    private:
    static int s_refCounter;
    };

    int LegacyList::s_refCounter = 0;

    // old style Class, with explicit new/delete/raw pointers
    class WordCache {
    public:
    @@ -87,30 +99,19 @@ class ModernSharedWordCache {
    std::shared_ptr<LegacyList> m_pList;
    };

    int main()
    void TestOldWay()
    {
    // size of unique_ptr will be 4 or 8 bytes
    std::cout << "sizeof(std::unique_ptr<LegacyList>) = " << sizeof(std::unique_ptr<LegacyList>) << '\n';
    // std::function is a bit heavy, around 32 bytes! (on x64)
    std::cout << "sizeof(std::function<void (LegacyList*)>) = " << sizeof(std::function<void (LegacyList*)>) << '\n';
    std::cout << "sizeof(unique_legacylist_ptr_stdfunc) = " << sizeof(ModernWordCache::unique_legacylist_ptr_stdfunc) << '\n';
    // with normal function pointer we have two pointers to store, so 8 or 16 bytes
    std::cout << "sizeof(unique_legacylist_ptr_function) = " << sizeof(ModernWordCache::unique_legacylist_ptr_function) << '\n';
    // if we use stateless functor then the compiler will probably use Empty Base Class optimization and thus we use only 8 bytes(like normal unique_ptr) !!
    std::cout << "sizeof(unique_legacylist_ptr_functor) = " << sizeof(ModernWordCache::unique_legacylist_ptr_functor) << '\n';
    // shared_ptr does not include deleter in type definition, size is always the same: two pointers: the actual data and ptr to control block
    std::cout << "sizeof(std::shared_ptr<LegacyList>) = " << sizeof(std::shared_ptr<LegacyList>) << '\n';

    //
    // Old way
    //
    WordCache myTestClass;
    LegacyList* pList = new LegacyList();
    // fill the list...
    myTestClass.UpdateCache(pList);

    //
    // Modern Way
    //
    LegacyList* pList2 = new LegacyList();
    // fill the list
    myTestClass.UpdateCache(pList2);
    }

    void TestUniqueModernWay()
    {
    ModernWordCache myModernClass;
    // whe nunique_legacylist_ptr uses a function, then we have to pass it in constructor
    // ModernWordCache::unique_legacylist_ptr pUniqueList(new LegacyList(), &DeleteLegacyList);
    @@ -121,10 +122,46 @@ int main()
    //ModernWordCache::unique_legacylist_ptr pUniqueList2(new LegacyList(), &DeleteLegacyList);
    ModernWordCache::unique_legacylist_ptr pUniqueList2(new LegacyList());
    myModernClass.UpdateCache(std::move(pUniqueList2));
    //
    // Modern Waywith shared_ptr
    //
    }

    void TestSharedModernWay()
    {
    ModernSharedWordCache mySharedClass;
    mySharedClass.UpdateCache(std::shared_ptr<LegacyList>(new LegacyList(), LegacyListDeleterFunctor()));

    mySharedClass.UpdateCache(std::shared_ptr<LegacyList>(new LegacyList(), DeleteLegacyList));

    // simple example with custom deleter for <int>
    std::shared_ptr<int> pIntPtr(new int(10), [](int *pi) { delete pi; });
    }

    int main()
    {
    std::cout << "sizeof(int*) = " << sizeof(int*) << '\n';
    // size of unique_ptr will be 4 or 8 bytes
    std::cout << "sizeof(std::unique_ptr<LegacyList>) = " << sizeof(std::unique_ptr<LegacyList>) << '\n';
    // std::function is a bit heavy, around 32 bytes! (on x64)
    std::cout << "sizeof(std::function<void (LegacyList*)>) = " << sizeof(std::function<void (LegacyList*)>) << '\n';
    std::cout << "sizeof(unique_legacylist_ptr_stdfunc) = " << sizeof(ModernWordCache::unique_legacylist_ptr_stdfunc) << '\n';
    // with normal function pointer we have two pointers to store, so 8 or 16 bytes
    std::cout << "sizeof(unique_legacylist_ptr_function) = " << sizeof(ModernWordCache::unique_legacylist_ptr_function) << '\n';
    // if we use stateless functor then the compiler will probably use Empty Base Class optimization and thus we use only 8 bytes(like normal unique_ptr) !!
    std::cout << "sizeof(unique_legacylist_ptr_functor) = " << sizeof(ModernWordCache::unique_legacylist_ptr_functor) << '\n';
    // shared_ptr does not include deleter in type definition, size is always the same
    std::cout << "sizeof(std::shared_ptr<LegacyList>) = " << sizeof(std::shared_ptr<LegacyList>) << '\n';

    auto IntDel = [](int *p) { delete p; };
    std::cout << "sizeof(std::unique_ptr<int, decltype(IntDel)>) = " << sizeof(std::unique_ptr<int, decltype(IntDel)>) << '\n';

    assert(LegacyList::GetGlobalObjectRefCount() == 0);
    TestOldWay();
    assert(LegacyList::GetGlobalObjectRefCount() == 0);

    assert(LegacyList::GetGlobalObjectRefCount() == 0);
    TestUniqueModernWay();
    assert(LegacyList::GetGlobalObjectRefCount() == 0);

    assert(LegacyList::GetGlobalObjectRefCount() == 0);
    TestSharedModernWay();
    assert(LegacyList::GetGlobalObjectRefCount() == 0);
    }
  2. fenbf revised this gist Apr 19, 2016. 1 changed file with 25 additions and 5 deletions.
    30 changes: 25 additions & 5 deletions smart_ptr_deleters.cpp
    Original file line number Diff line number Diff line change
    @@ -75,17 +75,31 @@ class ModernWordCache {
    unique_legacylist_ptr m_pList;
    };

    // Modern version that uses shared_ptr
    class ModernSharedWordCache {
    public:
    void UpdateCache(std::shared_ptr<LegacyList> pInputList) {
    m_pList = pInputList;
    // do something with the list...
    }

    private:
    std::shared_ptr<LegacyList> m_pList;
    };

    int main()
    {
    // size of unique_ptr will be 4 or 8 bytes
    std::cout << "sizeof(std::unique_ptr<LegacyList>) = " << sizeof(std::unique_ptr<LegacyList>) << std::endl;
    std::cout << "sizeof(std::unique_ptr<LegacyList>) = " << sizeof(std::unique_ptr<LegacyList>) << '\n';
    // std::function is a bit heavy, around 32 bytes! (on x64)
    std::cout << "sizeof(std::function<void (LegacyList*)>) = " << sizeof(std::function<void (LegacyList*)>) << std::endl;
    std::cout << "sizeof(unique_legacylist_ptr_stdfunc) = " << sizeof(ModernWordCache::unique_legacylist_ptr_stdfunc) << std::endl;
    std::cout << "sizeof(std::function<void (LegacyList*)>) = " << sizeof(std::function<void (LegacyList*)>) << '\n';
    std::cout << "sizeof(unique_legacylist_ptr_stdfunc) = " << sizeof(ModernWordCache::unique_legacylist_ptr_stdfunc) << '\n';
    // with normal function pointer we have two pointers to store, so 8 or 16 bytes
    std::cout << "sizeof(unique_legacylist_ptr_function) = " << sizeof(ModernWordCache::unique_legacylist_ptr_function) << std::endl;
    std::cout << "sizeof(unique_legacylist_ptr_function) = " << sizeof(ModernWordCache::unique_legacylist_ptr_function) << '\n';
    // if we use stateless functor then the compiler will probably use Empty Base Class optimization and thus we use only 8 bytes(like normal unique_ptr) !!
    std::cout << "sizeof(unique_legacylist_ptr_functor) = " << sizeof(ModernWordCache::unique_legacylist_ptr_functor) << std::endl;
    std::cout << "sizeof(unique_legacylist_ptr_functor) = " << sizeof(ModernWordCache::unique_legacylist_ptr_functor) << '\n';
    // shared_ptr does not include deleter in type definition, size is always the same: two pointers: the actual data and ptr to control block
    std::cout << "sizeof(std::shared_ptr<LegacyList>) = " << sizeof(std::shared_ptr<LegacyList>) << '\n';

    //
    // Old way
    @@ -107,4 +121,10 @@ int main()
    //ModernWordCache::unique_legacylist_ptr pUniqueList2(new LegacyList(), &DeleteLegacyList);
    ModernWordCache::unique_legacylist_ptr pUniqueList2(new LegacyList());
    myModernClass.UpdateCache(std::move(pUniqueList2));

    //
    // Modern Waywith shared_ptr
    //
    ModernSharedWordCache mySharedClass;
    mySharedClass.UpdateCache(std::shared_ptr<LegacyList>(new LegacyList(), LegacyListDeleterFunctor()));
    }
  3. fenbf created this gist Apr 17, 2016.
    110 changes: 110 additions & 0 deletions smart_ptr_deleters.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,110 @@
    // smart_ptr_deleters.cpp
    // shows how to use custom deleters with unique_ptr and shared_ptr
    // Bartlomiej Filipek, April 2016, bfilipek.com

    #include <iostream>
    #include <memory>
    #include <functional>

    // This class cannot be changed, might come from 3rd party library
    // to delete it completely you need to call ReleaseElements first!
    class LegacyList {
    public:
    void Add(const std::string& str ) { }
    void Remove(const std::string& str ) { }

    void ReleaseElements() { }
    };

    // old style Class, with explicit new/delete/raw pointers
    class WordCache {
    public:
    WordCache() { m_pList = nullptr; }
    ~WordCache() { ClearCache(); }

    void UpdateCache(LegacyList *pInputList) {
    ClearCache();
    m_pList = pInputList;
    if (m_pList)
    {
    // do something with the list...
    }
    }

    private:
    void ClearCache() { if (m_pList) { m_pList->ReleaseElements(); delete m_pList; m_pList = nullptr; } }

    LegacyList *m_pList; // owned by the object
    };

    // stateless functor
    struct LegacyListDeleterFunctor {
    void operator()(LegacyList* p) {
    p->ReleaseElements();
    delete p;
    std::cout << "LegacyListDeleterFunctor..." << std::endl;
    }
    };

    // normal free function used to delete LegacyList
    void DeleteLegacyList(LegacyList* p) {
    p->ReleaseElements();
    delete p;
    std::cout << "DeleteLegacyList function..." << std::endl;
    }

    // Modern version of the cache, we use std::unique_ptr with custom deleter to handle deletion of LegacyList
    // we can remove default constructor and destructor, since unique_ptr will do this for us
    // also we could remove ClearCache method (since it was only for conveniance)
    class ModernWordCache {
    public:
    // different options for custom deleters
    using unique_legacylist_ptr_stdfunc = std::unique_ptr<LegacyList, std::function<void (LegacyList*)>>;
    using unique_legacylist_ptr_functor = std::unique_ptr<LegacyList, LegacyListDeleterFunctor>;
    using unique_legacylist_ptr_function = std::unique_ptr<LegacyList, void (*)(LegacyList *)>;

    using unique_legacylist_ptr = unique_legacylist_ptr_functor;
    public:
    void UpdateCache(unique_legacylist_ptr pInputList) {
    m_pList = std::move(pInputList);
    // do something with the list...
    }

    private:
    //unique_legacylist_ptr m_pList { nullptr, &DeleteLegacyList };
    unique_legacylist_ptr m_pList;
    };

    int main()
    {
    // size of unique_ptr will be 4 or 8 bytes
    std::cout << "sizeof(std::unique_ptr<LegacyList>) = " << sizeof(std::unique_ptr<LegacyList>) << std::endl;
    // std::function is a bit heavy, around 32 bytes! (on x64)
    std::cout << "sizeof(std::function<void (LegacyList*)>) = " << sizeof(std::function<void (LegacyList*)>) << std::endl;
    std::cout << "sizeof(unique_legacylist_ptr_stdfunc) = " << sizeof(ModernWordCache::unique_legacylist_ptr_stdfunc) << std::endl;
    // with normal function pointer we have two pointers to store, so 8 or 16 bytes
    std::cout << "sizeof(unique_legacylist_ptr_function) = " << sizeof(ModernWordCache::unique_legacylist_ptr_function) << std::endl;
    // if we use stateless functor then the compiler will probably use Empty Base Class optimization and thus we use only 8 bytes(like normal unique_ptr) !!
    std::cout << "sizeof(unique_legacylist_ptr_functor) = " << sizeof(ModernWordCache::unique_legacylist_ptr_functor) << std::endl;

    //
    // Old way
    //
    WordCache myTestClass;
    LegacyList* pList = new LegacyList();
    myTestClass.UpdateCache(pList);

    //
    // Modern Way
    //
    ModernWordCache myModernClass;
    // whe nunique_legacylist_ptr uses a function, then we have to pass it in constructor
    // ModernWordCache::unique_legacylist_ptr pUniqueList(new LegacyList(), &DeleteLegacyList);
    // when nunique_legacylist_ptr uses a functor, functor object is created by default...
    ModernWordCache::unique_legacylist_ptr pUniqueList(new LegacyList());

    myModernClass.UpdateCache(std::move(pUniqueList));
    //ModernWordCache::unique_legacylist_ptr pUniqueList2(new LegacyList(), &DeleteLegacyList);
    ModernWordCache::unique_legacylist_ptr pUniqueList2(new LegacyList());
    myModernClass.UpdateCache(std::move(pUniqueList2));
    }