A free-standing weak_ptr equivalent to handle lifetime of non-shared callback objects

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
3
down vote

favorite












I've been finding myself wanting the following pattern recently:



void perform_async_operation(std::function<void(int)>);

class some_class
public:
void foo()
perform_async_operation([this](int result)
data_ = result;
);


private:
int data_;
;


Now, the issue here is that the lifetime of some_class if fully disconnected from when the callback will be invoked, and I do not want to move to shared ownership to put a shared_ptr in the lambda (like how the boost::ASIO examples handle that situation).



I do, however, have a saving grace: I know for certain that the callback will be invoked in the same thread that manages the lifetime of some_class.



In order to solve this, I've created a bastardized version of weak_ptr (with a matching equivalent of enabled_shared_from_this) that does not require the object to be managed by a shared_ptr in the first place.



#ifndef SLT_WEAK_REF_INCLUDED_H
#define SLT_WEAK_REF_INCLUDED_H

#include <memory>

template <typename T>
class weak_ref
public:
weak_ref(std::shared_ptr<T*> weak_data) : weak_data_(std::move(weak_data))

operator bool() const return *weak_data_ != nullptr;
T* operator->() const return *weak_data_;

private:
// Can be safely assumed to always be set.
std::shared_ptr<T*> weak_data_;
;

template <typename CRTP>
class enable_weak_ref
public:
enable_weak_ref() = default;
enable_weak_ref(enable_weak_ref&& rhs)
: weak_data_(std::move(rhs.weak_data_))
if (weak_data_)
*weak_data_ = this;



enable_weak_ref& operator=(enable_weak_ref&& rhs)
weak_data_ = std::move(rhs.weak_data_);
if (weak_data_)
*weak_data_ = static_cast<CRTP*>(this);



// explicitely do nothing
enable_weak_ref(enable_weak_ref const& rhs)
enable_weak_ref& operator=(enable_weak_ref const&)

~enable_weak_ref()
if (weak_data_)
*weak_data_ = nullptr;



weak_ref<CRTP> get_weak_ref()
if (!weak_data_)
weak_data_ = std::make_shared<CRTP*>(static_cast<CRTP*>(this));


return weak_ref<CRTP>(weak_data_);


private:
// TODO: perhaps switch to a simple manually ref-counted type here.
// instead of the overkill shared_ptr<>
std::shared_ptr<CRTP*> weak_data_;
;

#endif


sample usage:



#include "weak_ref.h"

#include <queue>
#include <iostream>
#include <functional>

std::queue<std::function<void(int)>> queue_;

void queue_delayed(std::function<void(int)> cb)
queue_.emplace(std::move(cb));


class my_class : public enable_weak_ref<my_class>
public:
my_class()
queue_delayed([ref = get_weak_ref()](int v)
if(ref)
std::cout << "passed " << v << std::endl;
ref->value = v;

else
std::cout << "saved " << v << std::endl;

);


int value = 0;
;

int main()
my_class a;
queue_.front()(12);
queue_.pop();

my_class b;

queue_.front()(5);
queue_.pop();

return 0;



Notes:



  • Moving an object in a lambda is a C++14 feature, without which, a lot of back-and-forth ref-counting happens.

  • As mentioned, this is intentionally not thread-safe. Thread safety can be added if anyone needs it eventually.

Issues:



  • Is there a nicer way to tackle this I'm overlooking here?


  • weak_ref is very weak name, I'd gladly welcome better suggestions.

  • As always, comments on general code quality are most desired.






share|improve this question



























    up vote
    3
    down vote

    favorite












    I've been finding myself wanting the following pattern recently:



    void perform_async_operation(std::function<void(int)>);

    class some_class
    public:
    void foo()
    perform_async_operation([this](int result)
    data_ = result;
    );


    private:
    int data_;
    ;


    Now, the issue here is that the lifetime of some_class if fully disconnected from when the callback will be invoked, and I do not want to move to shared ownership to put a shared_ptr in the lambda (like how the boost::ASIO examples handle that situation).



    I do, however, have a saving grace: I know for certain that the callback will be invoked in the same thread that manages the lifetime of some_class.



    In order to solve this, I've created a bastardized version of weak_ptr (with a matching equivalent of enabled_shared_from_this) that does not require the object to be managed by a shared_ptr in the first place.



    #ifndef SLT_WEAK_REF_INCLUDED_H
    #define SLT_WEAK_REF_INCLUDED_H

    #include <memory>

    template <typename T>
    class weak_ref
    public:
    weak_ref(std::shared_ptr<T*> weak_data) : weak_data_(std::move(weak_data))

    operator bool() const return *weak_data_ != nullptr;
    T* operator->() const return *weak_data_;

    private:
    // Can be safely assumed to always be set.
    std::shared_ptr<T*> weak_data_;
    ;

    template <typename CRTP>
    class enable_weak_ref
    public:
    enable_weak_ref() = default;
    enable_weak_ref(enable_weak_ref&& rhs)
    : weak_data_(std::move(rhs.weak_data_))
    if (weak_data_)
    *weak_data_ = this;



    enable_weak_ref& operator=(enable_weak_ref&& rhs)
    weak_data_ = std::move(rhs.weak_data_);
    if (weak_data_)
    *weak_data_ = static_cast<CRTP*>(this);



    // explicitely do nothing
    enable_weak_ref(enable_weak_ref const& rhs)
    enable_weak_ref& operator=(enable_weak_ref const&)

    ~enable_weak_ref()
    if (weak_data_)
    *weak_data_ = nullptr;



    weak_ref<CRTP> get_weak_ref()
    if (!weak_data_)
    weak_data_ = std::make_shared<CRTP*>(static_cast<CRTP*>(this));


    return weak_ref<CRTP>(weak_data_);


    private:
    // TODO: perhaps switch to a simple manually ref-counted type here.
    // instead of the overkill shared_ptr<>
    std::shared_ptr<CRTP*> weak_data_;
    ;

    #endif


    sample usage:



    #include "weak_ref.h"

    #include <queue>
    #include <iostream>
    #include <functional>

    std::queue<std::function<void(int)>> queue_;

    void queue_delayed(std::function<void(int)> cb)
    queue_.emplace(std::move(cb));


    class my_class : public enable_weak_ref<my_class>
    public:
    my_class()
    queue_delayed([ref = get_weak_ref()](int v)
    if(ref)
    std::cout << "passed " << v << std::endl;
    ref->value = v;

    else
    std::cout << "saved " << v << std::endl;

    );


    int value = 0;
    ;

    int main()
    my_class a;
    queue_.front()(12);
    queue_.pop();

    my_class b;

    queue_.front()(5);
    queue_.pop();

    return 0;



    Notes:



    • Moving an object in a lambda is a C++14 feature, without which, a lot of back-and-forth ref-counting happens.

    • As mentioned, this is intentionally not thread-safe. Thread safety can be added if anyone needs it eventually.

    Issues:



    • Is there a nicer way to tackle this I'm overlooking here?


    • weak_ref is very weak name, I'd gladly welcome better suggestions.

    • As always, comments on general code quality are most desired.






    share|improve this question























      up vote
      3
      down vote

      favorite









      up vote
      3
      down vote

      favorite











      I've been finding myself wanting the following pattern recently:



      void perform_async_operation(std::function<void(int)>);

      class some_class
      public:
      void foo()
      perform_async_operation([this](int result)
      data_ = result;
      );


      private:
      int data_;
      ;


      Now, the issue here is that the lifetime of some_class if fully disconnected from when the callback will be invoked, and I do not want to move to shared ownership to put a shared_ptr in the lambda (like how the boost::ASIO examples handle that situation).



      I do, however, have a saving grace: I know for certain that the callback will be invoked in the same thread that manages the lifetime of some_class.



      In order to solve this, I've created a bastardized version of weak_ptr (with a matching equivalent of enabled_shared_from_this) that does not require the object to be managed by a shared_ptr in the first place.



      #ifndef SLT_WEAK_REF_INCLUDED_H
      #define SLT_WEAK_REF_INCLUDED_H

      #include <memory>

      template <typename T>
      class weak_ref
      public:
      weak_ref(std::shared_ptr<T*> weak_data) : weak_data_(std::move(weak_data))

      operator bool() const return *weak_data_ != nullptr;
      T* operator->() const return *weak_data_;

      private:
      // Can be safely assumed to always be set.
      std::shared_ptr<T*> weak_data_;
      ;

      template <typename CRTP>
      class enable_weak_ref
      public:
      enable_weak_ref() = default;
      enable_weak_ref(enable_weak_ref&& rhs)
      : weak_data_(std::move(rhs.weak_data_))
      if (weak_data_)
      *weak_data_ = this;



      enable_weak_ref& operator=(enable_weak_ref&& rhs)
      weak_data_ = std::move(rhs.weak_data_);
      if (weak_data_)
      *weak_data_ = static_cast<CRTP*>(this);



      // explicitely do nothing
      enable_weak_ref(enable_weak_ref const& rhs)
      enable_weak_ref& operator=(enable_weak_ref const&)

      ~enable_weak_ref()
      if (weak_data_)
      *weak_data_ = nullptr;



      weak_ref<CRTP> get_weak_ref()
      if (!weak_data_)
      weak_data_ = std::make_shared<CRTP*>(static_cast<CRTP*>(this));


      return weak_ref<CRTP>(weak_data_);


      private:
      // TODO: perhaps switch to a simple manually ref-counted type here.
      // instead of the overkill shared_ptr<>
      std::shared_ptr<CRTP*> weak_data_;
      ;

      #endif


      sample usage:



      #include "weak_ref.h"

      #include <queue>
      #include <iostream>
      #include <functional>

      std::queue<std::function<void(int)>> queue_;

      void queue_delayed(std::function<void(int)> cb)
      queue_.emplace(std::move(cb));


      class my_class : public enable_weak_ref<my_class>
      public:
      my_class()
      queue_delayed([ref = get_weak_ref()](int v)
      if(ref)
      std::cout << "passed " << v << std::endl;
      ref->value = v;

      else
      std::cout << "saved " << v << std::endl;

      );


      int value = 0;
      ;

      int main()
      my_class a;
      queue_.front()(12);
      queue_.pop();

      my_class b;

      queue_.front()(5);
      queue_.pop();

      return 0;



      Notes:



      • Moving an object in a lambda is a C++14 feature, without which, a lot of back-and-forth ref-counting happens.

      • As mentioned, this is intentionally not thread-safe. Thread safety can be added if anyone needs it eventually.

      Issues:



      • Is there a nicer way to tackle this I'm overlooking here?


      • weak_ref is very weak name, I'd gladly welcome better suggestions.

      • As always, comments on general code quality are most desired.






      share|improve this question













      I've been finding myself wanting the following pattern recently:



      void perform_async_operation(std::function<void(int)>);

      class some_class
      public:
      void foo()
      perform_async_operation([this](int result)
      data_ = result;
      );


      private:
      int data_;
      ;


      Now, the issue here is that the lifetime of some_class if fully disconnected from when the callback will be invoked, and I do not want to move to shared ownership to put a shared_ptr in the lambda (like how the boost::ASIO examples handle that situation).



      I do, however, have a saving grace: I know for certain that the callback will be invoked in the same thread that manages the lifetime of some_class.



      In order to solve this, I've created a bastardized version of weak_ptr (with a matching equivalent of enabled_shared_from_this) that does not require the object to be managed by a shared_ptr in the first place.



      #ifndef SLT_WEAK_REF_INCLUDED_H
      #define SLT_WEAK_REF_INCLUDED_H

      #include <memory>

      template <typename T>
      class weak_ref
      public:
      weak_ref(std::shared_ptr<T*> weak_data) : weak_data_(std::move(weak_data))

      operator bool() const return *weak_data_ != nullptr;
      T* operator->() const return *weak_data_;

      private:
      // Can be safely assumed to always be set.
      std::shared_ptr<T*> weak_data_;
      ;

      template <typename CRTP>
      class enable_weak_ref
      public:
      enable_weak_ref() = default;
      enable_weak_ref(enable_weak_ref&& rhs)
      : weak_data_(std::move(rhs.weak_data_))
      if (weak_data_)
      *weak_data_ = this;



      enable_weak_ref& operator=(enable_weak_ref&& rhs)
      weak_data_ = std::move(rhs.weak_data_);
      if (weak_data_)
      *weak_data_ = static_cast<CRTP*>(this);



      // explicitely do nothing
      enable_weak_ref(enable_weak_ref const& rhs)
      enable_weak_ref& operator=(enable_weak_ref const&)

      ~enable_weak_ref()
      if (weak_data_)
      *weak_data_ = nullptr;



      weak_ref<CRTP> get_weak_ref()
      if (!weak_data_)
      weak_data_ = std::make_shared<CRTP*>(static_cast<CRTP*>(this));


      return weak_ref<CRTP>(weak_data_);


      private:
      // TODO: perhaps switch to a simple manually ref-counted type here.
      // instead of the overkill shared_ptr<>
      std::shared_ptr<CRTP*> weak_data_;
      ;

      #endif


      sample usage:



      #include "weak_ref.h"

      #include <queue>
      #include <iostream>
      #include <functional>

      std::queue<std::function<void(int)>> queue_;

      void queue_delayed(std::function<void(int)> cb)
      queue_.emplace(std::move(cb));


      class my_class : public enable_weak_ref<my_class>
      public:
      my_class()
      queue_delayed([ref = get_weak_ref()](int v)
      if(ref)
      std::cout << "passed " << v << std::endl;
      ref->value = v;

      else
      std::cout << "saved " << v << std::endl;

      );


      int value = 0;
      ;

      int main()
      my_class a;
      queue_.front()(12);
      queue_.pop();

      my_class b;

      queue_.front()(5);
      queue_.pop();

      return 0;



      Notes:



      • Moving an object in a lambda is a C++14 feature, without which, a lot of back-and-forth ref-counting happens.

      • As mentioned, this is intentionally not thread-safe. Thread safety can be added if anyone needs it eventually.

      Issues:



      • Is there a nicer way to tackle this I'm overlooking here?


      • weak_ref is very weak name, I'd gladly welcome better suggestions.

      • As always, comments on general code quality are most desired.








      share|improve this question












      share|improve this question




      share|improve this question








      edited Jan 4 at 6:44
























      asked Jan 4 at 2:45









      Frank

      2,947319




      2,947319




















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          1
          down vote













          I never saw a similar pattern so it was really interesting to understand your code !



          First, about the name weak_ref, it implies that it has the syntax of a reference, but it is not the case (for instance, operator-> is for pointers). I personnaly named it something like ptr_to_instance.



          Then, it seems the weak_ref should not be used on its own, without calling enable_weak_ref::get_weak_ref(). So I will make the weak_ref constructor private, making enable_weak_ref a friend. I will put the weak_ref class inside enable_weak_ref :



          template<typename CRTP>
          class enable_weak_ref
          public:
          class weak_ref /**/ ;
          /**/
          ;


          So instead of weak_ref<T> we have enable_weak_ref<T>::weak_ref. I think it makes more understandable the use of these classes.



          Also, because weak_ref should not modify the pointer in weak_data_, I would use std::shared_ptr<T* const> so it is clear that this pointer is for read-only use.



          If you do not want to use std::shared_ptr, you should create a int* (counting references), a CRTP** (which will hold this or nullptr) in enable_weak_ref, and weak_ref should have a int* const and a CRTP const* const (const specifier shows that weak_ref is linked to only one enable_weak_ref and can not be changed).






          share|improve this answer























          • Thanks for the review! A few notes: both my problem statement and sample code showcase mutation, I'm not sure where you see the constness requirement. Namespacing weak_ref would just be super-rough api-wise, it's not always going to be inferred. Your alternative to shared_ptr leaves a lot to be desired, it would be much better to pack the ref count and data pointer in a struct. It's also not "that" simple, there's quite a few edge cases to handle.
            – Frank
            Jan 4 at 14:27










          • About alternative to shared_ptr, yes it was just "you may need" not a full implementation. I don't know very much about mutation, but constness as I depicted is not a requirement but just to explicit the uses of the variable. I saw I made a mistake, I didn't want to say shared_ptr<T const*> but shared_ptr<T* const>, because your weak_ptr should not modify which instance the pointer points.
            – Julien Vernay
            Jan 4 at 14:50










          • But the variable is not const in any meaning of the word. The pointer can be changed (in enable_weak_ref's move constructor), and the content pointed to by the pointer can change (as seen in the examples)
            – Frank
            Jan 4 at 14:51










          • enable_weak_ref can change where the pointer points, so indeed it has a shared_ptr<T*>, but weak_ref needs only to know the value of the pointer itself, eventually modifying the dereference. but the pointer itself should not be modified in weak_ref, so it holds shared_ptr<T* const>. It is what i meant.
            – Julien Vernay
            Jan 4 at 14:55










          Your Answer




          StackExchange.ifUsing("editor", function ()
          return StackExchange.using("mathjaxEditing", function ()
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          );
          );
          , "mathjax-editing");

          StackExchange.ifUsing("editor", function ()
          StackExchange.using("externalEditor", function ()
          StackExchange.using("snippets", function ()
          StackExchange.snippets.init();
          );
          );
          , "code-snippets");

          StackExchange.ready(function()
          var channelOptions =
          tags: "".split(" "),
          id: "196"
          ;
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function()
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled)
          StackExchange.using("snippets", function()
          createEditor();
          );

          else
          createEditor();

          );

          function createEditor()
          StackExchange.prepareEditor(
          heartbeatType: 'answer',
          convertImagesToLinks: false,
          noModals: false,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          );



          );








           

          draft saved


          draft discarded


















          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f184242%2fa-free-standing-weak-ptr-equivalent-to-handle-lifetime-of-non-shared-callback-ob%23new-answer', 'question_page');

          );

          Post as a guest






























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          1
          down vote













          I never saw a similar pattern so it was really interesting to understand your code !



          First, about the name weak_ref, it implies that it has the syntax of a reference, but it is not the case (for instance, operator-> is for pointers). I personnaly named it something like ptr_to_instance.



          Then, it seems the weak_ref should not be used on its own, without calling enable_weak_ref::get_weak_ref(). So I will make the weak_ref constructor private, making enable_weak_ref a friend. I will put the weak_ref class inside enable_weak_ref :



          template<typename CRTP>
          class enable_weak_ref
          public:
          class weak_ref /**/ ;
          /**/
          ;


          So instead of weak_ref<T> we have enable_weak_ref<T>::weak_ref. I think it makes more understandable the use of these classes.



          Also, because weak_ref should not modify the pointer in weak_data_, I would use std::shared_ptr<T* const> so it is clear that this pointer is for read-only use.



          If you do not want to use std::shared_ptr, you should create a int* (counting references), a CRTP** (which will hold this or nullptr) in enable_weak_ref, and weak_ref should have a int* const and a CRTP const* const (const specifier shows that weak_ref is linked to only one enable_weak_ref and can not be changed).






          share|improve this answer























          • Thanks for the review! A few notes: both my problem statement and sample code showcase mutation, I'm not sure where you see the constness requirement. Namespacing weak_ref would just be super-rough api-wise, it's not always going to be inferred. Your alternative to shared_ptr leaves a lot to be desired, it would be much better to pack the ref count and data pointer in a struct. It's also not "that" simple, there's quite a few edge cases to handle.
            – Frank
            Jan 4 at 14:27










          • About alternative to shared_ptr, yes it was just "you may need" not a full implementation. I don't know very much about mutation, but constness as I depicted is not a requirement but just to explicit the uses of the variable. I saw I made a mistake, I didn't want to say shared_ptr<T const*> but shared_ptr<T* const>, because your weak_ptr should not modify which instance the pointer points.
            – Julien Vernay
            Jan 4 at 14:50










          • But the variable is not const in any meaning of the word. The pointer can be changed (in enable_weak_ref's move constructor), and the content pointed to by the pointer can change (as seen in the examples)
            – Frank
            Jan 4 at 14:51










          • enable_weak_ref can change where the pointer points, so indeed it has a shared_ptr<T*>, but weak_ref needs only to know the value of the pointer itself, eventually modifying the dereference. but the pointer itself should not be modified in weak_ref, so it holds shared_ptr<T* const>. It is what i meant.
            – Julien Vernay
            Jan 4 at 14:55














          up vote
          1
          down vote













          I never saw a similar pattern so it was really interesting to understand your code !



          First, about the name weak_ref, it implies that it has the syntax of a reference, but it is not the case (for instance, operator-> is for pointers). I personnaly named it something like ptr_to_instance.



          Then, it seems the weak_ref should not be used on its own, without calling enable_weak_ref::get_weak_ref(). So I will make the weak_ref constructor private, making enable_weak_ref a friend. I will put the weak_ref class inside enable_weak_ref :



          template<typename CRTP>
          class enable_weak_ref
          public:
          class weak_ref /**/ ;
          /**/
          ;


          So instead of weak_ref<T> we have enable_weak_ref<T>::weak_ref. I think it makes more understandable the use of these classes.



          Also, because weak_ref should not modify the pointer in weak_data_, I would use std::shared_ptr<T* const> so it is clear that this pointer is for read-only use.



          If you do not want to use std::shared_ptr, you should create a int* (counting references), a CRTP** (which will hold this or nullptr) in enable_weak_ref, and weak_ref should have a int* const and a CRTP const* const (const specifier shows that weak_ref is linked to only one enable_weak_ref and can not be changed).






          share|improve this answer























          • Thanks for the review! A few notes: both my problem statement and sample code showcase mutation, I'm not sure where you see the constness requirement. Namespacing weak_ref would just be super-rough api-wise, it's not always going to be inferred. Your alternative to shared_ptr leaves a lot to be desired, it would be much better to pack the ref count and data pointer in a struct. It's also not "that" simple, there's quite a few edge cases to handle.
            – Frank
            Jan 4 at 14:27










          • About alternative to shared_ptr, yes it was just "you may need" not a full implementation. I don't know very much about mutation, but constness as I depicted is not a requirement but just to explicit the uses of the variable. I saw I made a mistake, I didn't want to say shared_ptr<T const*> but shared_ptr<T* const>, because your weak_ptr should not modify which instance the pointer points.
            – Julien Vernay
            Jan 4 at 14:50










          • But the variable is not const in any meaning of the word. The pointer can be changed (in enable_weak_ref's move constructor), and the content pointed to by the pointer can change (as seen in the examples)
            – Frank
            Jan 4 at 14:51










          • enable_weak_ref can change where the pointer points, so indeed it has a shared_ptr<T*>, but weak_ref needs only to know the value of the pointer itself, eventually modifying the dereference. but the pointer itself should not be modified in weak_ref, so it holds shared_ptr<T* const>. It is what i meant.
            – Julien Vernay
            Jan 4 at 14:55












          up vote
          1
          down vote










          up vote
          1
          down vote









          I never saw a similar pattern so it was really interesting to understand your code !



          First, about the name weak_ref, it implies that it has the syntax of a reference, but it is not the case (for instance, operator-> is for pointers). I personnaly named it something like ptr_to_instance.



          Then, it seems the weak_ref should not be used on its own, without calling enable_weak_ref::get_weak_ref(). So I will make the weak_ref constructor private, making enable_weak_ref a friend. I will put the weak_ref class inside enable_weak_ref :



          template<typename CRTP>
          class enable_weak_ref
          public:
          class weak_ref /**/ ;
          /**/
          ;


          So instead of weak_ref<T> we have enable_weak_ref<T>::weak_ref. I think it makes more understandable the use of these classes.



          Also, because weak_ref should not modify the pointer in weak_data_, I would use std::shared_ptr<T* const> so it is clear that this pointer is for read-only use.



          If you do not want to use std::shared_ptr, you should create a int* (counting references), a CRTP** (which will hold this or nullptr) in enable_weak_ref, and weak_ref should have a int* const and a CRTP const* const (const specifier shows that weak_ref is linked to only one enable_weak_ref and can not be changed).






          share|improve this answer















          I never saw a similar pattern so it was really interesting to understand your code !



          First, about the name weak_ref, it implies that it has the syntax of a reference, but it is not the case (for instance, operator-> is for pointers). I personnaly named it something like ptr_to_instance.



          Then, it seems the weak_ref should not be used on its own, without calling enable_weak_ref::get_weak_ref(). So I will make the weak_ref constructor private, making enable_weak_ref a friend. I will put the weak_ref class inside enable_weak_ref :



          template<typename CRTP>
          class enable_weak_ref
          public:
          class weak_ref /**/ ;
          /**/
          ;


          So instead of weak_ref<T> we have enable_weak_ref<T>::weak_ref. I think it makes more understandable the use of these classes.



          Also, because weak_ref should not modify the pointer in weak_data_, I would use std::shared_ptr<T* const> so it is clear that this pointer is for read-only use.



          If you do not want to use std::shared_ptr, you should create a int* (counting references), a CRTP** (which will hold this or nullptr) in enable_weak_ref, and weak_ref should have a int* const and a CRTP const* const (const specifier shows that weak_ref is linked to only one enable_weak_ref and can not be changed).







          share|improve this answer















          share|improve this answer



          share|improve this answer








          edited Jan 4 at 14:52


























          answered Jan 4 at 13:58









          Julien Vernay

          3138




          3138











          • Thanks for the review! A few notes: both my problem statement and sample code showcase mutation, I'm not sure where you see the constness requirement. Namespacing weak_ref would just be super-rough api-wise, it's not always going to be inferred. Your alternative to shared_ptr leaves a lot to be desired, it would be much better to pack the ref count and data pointer in a struct. It's also not "that" simple, there's quite a few edge cases to handle.
            – Frank
            Jan 4 at 14:27










          • About alternative to shared_ptr, yes it was just "you may need" not a full implementation. I don't know very much about mutation, but constness as I depicted is not a requirement but just to explicit the uses of the variable. I saw I made a mistake, I didn't want to say shared_ptr<T const*> but shared_ptr<T* const>, because your weak_ptr should not modify which instance the pointer points.
            – Julien Vernay
            Jan 4 at 14:50










          • But the variable is not const in any meaning of the word. The pointer can be changed (in enable_weak_ref's move constructor), and the content pointed to by the pointer can change (as seen in the examples)
            – Frank
            Jan 4 at 14:51










          • enable_weak_ref can change where the pointer points, so indeed it has a shared_ptr<T*>, but weak_ref needs only to know the value of the pointer itself, eventually modifying the dereference. but the pointer itself should not be modified in weak_ref, so it holds shared_ptr<T* const>. It is what i meant.
            – Julien Vernay
            Jan 4 at 14:55
















          • Thanks for the review! A few notes: both my problem statement and sample code showcase mutation, I'm not sure where you see the constness requirement. Namespacing weak_ref would just be super-rough api-wise, it's not always going to be inferred. Your alternative to shared_ptr leaves a lot to be desired, it would be much better to pack the ref count and data pointer in a struct. It's also not "that" simple, there's quite a few edge cases to handle.
            – Frank
            Jan 4 at 14:27










          • About alternative to shared_ptr, yes it was just "you may need" not a full implementation. I don't know very much about mutation, but constness as I depicted is not a requirement but just to explicit the uses of the variable. I saw I made a mistake, I didn't want to say shared_ptr<T const*> but shared_ptr<T* const>, because your weak_ptr should not modify which instance the pointer points.
            – Julien Vernay
            Jan 4 at 14:50










          • But the variable is not const in any meaning of the word. The pointer can be changed (in enable_weak_ref's move constructor), and the content pointed to by the pointer can change (as seen in the examples)
            – Frank
            Jan 4 at 14:51










          • enable_weak_ref can change where the pointer points, so indeed it has a shared_ptr<T*>, but weak_ref needs only to know the value of the pointer itself, eventually modifying the dereference. but the pointer itself should not be modified in weak_ref, so it holds shared_ptr<T* const>. It is what i meant.
            – Julien Vernay
            Jan 4 at 14:55















          Thanks for the review! A few notes: both my problem statement and sample code showcase mutation, I'm not sure where you see the constness requirement. Namespacing weak_ref would just be super-rough api-wise, it's not always going to be inferred. Your alternative to shared_ptr leaves a lot to be desired, it would be much better to pack the ref count and data pointer in a struct. It's also not "that" simple, there's quite a few edge cases to handle.
          – Frank
          Jan 4 at 14:27




          Thanks for the review! A few notes: both my problem statement and sample code showcase mutation, I'm not sure where you see the constness requirement. Namespacing weak_ref would just be super-rough api-wise, it's not always going to be inferred. Your alternative to shared_ptr leaves a lot to be desired, it would be much better to pack the ref count and data pointer in a struct. It's also not "that" simple, there's quite a few edge cases to handle.
          – Frank
          Jan 4 at 14:27












          About alternative to shared_ptr, yes it was just "you may need" not a full implementation. I don't know very much about mutation, but constness as I depicted is not a requirement but just to explicit the uses of the variable. I saw I made a mistake, I didn't want to say shared_ptr<T const*> but shared_ptr<T* const>, because your weak_ptr should not modify which instance the pointer points.
          – Julien Vernay
          Jan 4 at 14:50




          About alternative to shared_ptr, yes it was just "you may need" not a full implementation. I don't know very much about mutation, but constness as I depicted is not a requirement but just to explicit the uses of the variable. I saw I made a mistake, I didn't want to say shared_ptr<T const*> but shared_ptr<T* const>, because your weak_ptr should not modify which instance the pointer points.
          – Julien Vernay
          Jan 4 at 14:50












          But the variable is not const in any meaning of the word. The pointer can be changed (in enable_weak_ref's move constructor), and the content pointed to by the pointer can change (as seen in the examples)
          – Frank
          Jan 4 at 14:51




          But the variable is not const in any meaning of the word. The pointer can be changed (in enable_weak_ref's move constructor), and the content pointed to by the pointer can change (as seen in the examples)
          – Frank
          Jan 4 at 14:51












          enable_weak_ref can change where the pointer points, so indeed it has a shared_ptr<T*>, but weak_ref needs only to know the value of the pointer itself, eventually modifying the dereference. but the pointer itself should not be modified in weak_ref, so it holds shared_ptr<T* const>. It is what i meant.
          – Julien Vernay
          Jan 4 at 14:55




          enable_weak_ref can change where the pointer points, so indeed it has a shared_ptr<T*>, but weak_ref needs only to know the value of the pointer itself, eventually modifying the dereference. but the pointer itself should not be modified in weak_ref, so it holds shared_ptr<T* const>. It is what i meant.
          – Julien Vernay
          Jan 4 at 14:55












           

          draft saved


          draft discarded


























           


          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f184242%2fa-free-standing-weak-ptr-equivalent-to-handle-lifetime-of-non-shared-callback-ob%23new-answer', 'question_page');

          );

          Post as a guest













































































          Popular posts from this blog

          Greedy Best First Search implementation in Rust

          Function to Return a JSON Like Objects Using VBA Collections and Arrays

          C++11 CLH Lock Implementation