Re: [boost] Hello everybody, I have something to share about the shared_ptr class
Last post was sent directly to Rob, I forgot to cc list.bost.org. Thinking of the multiple calling of the detors, which is suggested by Rob and other members, I came up with some ideas. This IS about specification. If class A has a dispose method like class A { A(){} ~A(){} //Dispose method: !A(){} }; Where !A() can be called multiple times and ~A() can be called only one time. Everything rcgc_ptr delegates will be called its dispose in !A() and therefore multiple times to just decrease the reference count and finally call ~A() when real delete happens. And this can be done on the compiler level (!A() maybe not visible at all or just visible when you use like ~A()). Saying this algorithm is a part of the C++ language itself, then C++ has no need to manually manage pointers (at least the unwanted management). Idea is inspired by all of you especially Andrey Janson and Rob. Thank you all! Best Regards, Yilin
On 2020-08-03 16:01, 逸霖 杨 via Boost wrote:
Last post was sent directly to Rob, I forgot to cc list.bost.org.
Thinking of the multiple calling of the detors, which is suggested by Rob and other members, I came up with some ideas.
This IS about specification.
If class A has a dispose method like
class A { A(){} ~A(){} //Dispose method: !A(){} };
Where !A() can be called multiple times and ~A() can be called only one time. Everything rcgc_ptr delegates will be called its dispose in !A() and therefore multiple times to just decrease the reference count and finally call ~A() when real delete happens. And this can be done on the compiler level (!A() maybe not visible at all or just visible when you use like ~A()).
1. Calling dispose method when the rcgc_shared_ptr is destroyed is still wrong. By definition, there are supposed to be multiple pointers to a shared object, and destroying a single shared pointer must not result in "cleaning up" the shared object. 2. What is the purpose of the special syntax of the dispose function? What is its semantics outside the rcgc_shared_ptr? Why do you need this special syntax and not a regular method that is used by rcgc_shared_ptr? These are the sort of questions you will be asked by the committee when you bring this proposal. In fact, I think you'll me asked these even before you bring the proposal before the committee. There needs to be a very strong reason to introduce a new syntax.
1. Calling dispose method when the rcgc_shared_ptr is destroyed is still wrong. By definition, there are supposed to be multiple pointers to a shared object, and destroying a single shared pointer must not result in "cleaning up" the shared object. -- So disposing here is not meaning “cleaning up“ but “trying to clean up”(it’s better to have another word to call it), !A() serves as ~A() the difference is it just call the disposing methods of the field’s members’ rcgc_shared_ptrs to decrease reference counts. Actually they can be almost same (!A() and ~A()) in generated assembly or machine code. Only difference is !A() only calls about the rcgc_shared_ptr, and ~A() calls about all the dtors of field members. Yes, this is upon the assumption that the memory of that object is not touched or messed up (since not ~A(), just !A(), then it should not be touched by design). 2. What is the purpose of the special syntax of the dispose function? What is its semantics outside the rcgc_shared_ptr? Why do you need this special syntax and not a regular method that is used by rcgc_shared_ptr? These are the sort of questions you will be asked by the committee when you bring this proposal. In fact, I think you'll me asked these even before you bring the proposal before the committee. There needs to be a very strong reason to introduce a new syntax. -- The purpose is only to answer Rob’s reply: if trying to make things look beautiful without changing the original design of C++ language, the only choice is to add some extra syntax. Yes, this needs a very strong reason. So what do you think, if reference counting algorithm can be tuned up into “universal” mode and achieve the goal that the smart pointers are designed to achieve? (the one and only smart pointer to manage memories without coders’ attention) The reply to Rob was not cc to the list and this reply is following that one. I already said in that one, it’s not my purpose to bring a proposal to the committee. Reason: things evolve, and the good ones continue and being accepted, and the users are the judges, so let the users judge. If good enough, got accepted; not good enough, got forgotten. So this would not be a problem. I think or hope you already read and debug the code and already be aware of how it works. This understanding of the code is the basic ground we talk about further. Because the code answered the reason already. (We’re coders, aren’t we? The code speaks better then my vocabulary-limited English.)
On 2020-08-03 18:41, 逸霖 杨 via Boost wrote:
1. Calling dispose method when the rcgc_shared_ptr is destroyed is still wrong. By definition, there are supposed to be multiple pointers to a shared object, and destroying a single shared pointer must not result in "cleaning up" the shared object. -- So disposing here is not meaning “cleaning up“ but “trying to clean up”(it’s better to have another word to call it), !A() serves as ~A() the difference is it just call the disposing methods of the field’s members’ rcgc_shared_ptrs to decrease reference counts. Actually they can be almost same (!A() and ~A()) in generated assembly or machine code. Only difference is !A() only calls about the rcgc_shared_ptr, and ~A() calls about all the dtors of field members. Yes, this is upon the assumption that the memory of that object is not touched or messed up (since not ~A(), just !A(), then it should not be touched by design).
This doesn't answer my point. Namely, that destroying the shared pointer should not "clean up"/"try to clean up"/"dispose" the referenced object, unless this is the last shared pointer to that object.
2. What is the purpose of the special syntax of the dispose function? What is its semantics outside the rcgc_shared_ptr? Why do you need this special syntax and not a regular method that is used by rcgc_shared_ptr? These are the sort of questions you will be asked by the committee when you bring this proposal. In fact, I think you'll me asked these even before you bring the proposal before the committee. There needs to be a very strong reason to introduce a new syntax.
-- The purpose is only to answer Rob’s reply: if trying to make things look beautiful without changing the original design of C++ language, the only choice is to add some extra syntax.
This doesn't require a special syntax. You can have a regular function to clear the object, and have that function called from rcgc_shared_ptr.
Yes, this needs a very strong reason. So what do you think, if reference counting algorithm can be tuned up into “universal” mode and achieve the goal that the smart pointers are designed to achieve? (the one and only smart pointer to manage memories without coders’ attention)
I don't think "the universal smart pointer" is practically possible or even desirable. shared_ptr is close to that ideal, but obviously that kind of flexibility has an associated cost that not everyone is willing to pay. This is why there are other kinds of smart pointers, some of which overlap with shared_ptr in functionality. And even if we do want to implement a universal smart pointer, it still doesn't explain the need for a new special function.
The reply to Rob was not cc to the list and this reply is following that one. I already said in that one, it’s not my purpose to bring a proposal to the committee.
If you're not intending to change the language then both changing the destructor special semantics and the new syntax for dispose function are moot. (To be clear, I don't like either of these ideas.)
Reason: things evolve, and the good ones continue and being accepted, and the users are the judges, so let the users judge. If good enough, got accepted; not good enough, got forgotten.
That's not how C++ standardization works, as what went into the standard is there to stay for many years ahead. You don't put stuff in the standard just to test the idea. But since you're not planning to propose a change in the language, you are mostly limited to a library-only solution. Which means, your code has to play by the current C++ language rules.
I think or hope you already read and debug the code and already be aware of how it works. This understanding of the code is the basic ground we talk about further.
Because the code answered the reason already. (We’re coders, aren’t we? The code speaks better then my vocabulary-limited English.)
I did not run the code, as I can see how it works without running it. I don't see it solving any of the problems with shared_ptr, and I have pointed out problems with the code, which you seem to have disregarded so far.
This doesn't answer my point. Namely, that destroying the shared pointer should not "clean up"/"try to clean up"/"dispose" the referenced object, unless this is the last shared pointer to that object. -- Shared_pointer is a part of the object most of time or local variable which will be freed at the end of lexical scope. So only protect it as a field member of object will do, no need to worry about the lexical scope. And if memory is not messed-up, it��s already being protected �C this gets back to how you define destroying object. You already see this object as destroyed, then no further answer is valid for you. If you see it��s not destroyed, no question is needed to be asked. *** If you don��t see the code, whatever I say you can not understand for sure! --- This doesn't require a special syntax. You can have a regular function to clear the object, and have that function called from rcgc_shared_ptr. -- True, that��s why this is just the answer to Rob��s question and the reason for the following answers. -- I don't think "the universal smart pointer" is practically possible or even desirable. shared_ptr is close to that ideal, but obviously that kind of flexibility has an associated cost that not everyone is willing to pay. This is why there are other kinds of smart pointers, some of which overlap with shared_ptr in functionality. -- Why just don��t read the code? I can not force you. But if we talk about something, we have to Base on something, not thinking or imagination or desire, Aren��t we? -- And even if we do want to implement a universal smart pointer, it still doesn't explain the need for a new special function. -- So just don��t. -- If you're not intending to change the language then both changing the destructor special semantics and the new syntax for dispose function are moot. (To be clear, I don't like either of these ideas.) -- Then ignore this as never happened (told you that it��s the reply to Rob) -- That's not how C++ standardization works, as what went into the standard is there to stay for many years ahead. You don't put stuff in the standard just to test the idea. But since you're not planning to propose a change in the language, you are mostly limited to a library-only solution. Which means, your code has to play by the current C++ language rules. -- Dispose pattern does not violate anything. -- I did not run the code, as I can see how it works without running it. I don't see it solving any of the problems with shared_ptr, and I have pointed out problems with the code, which you seem to have disregarded so far. -- The problem is the detor��s abusing, I accepted and provided solution which means not disregarded at all. However the abusing is not even the part of the idea or algorithm: just to make it beautiful in C++. Changing back to common function is OK. And do you want me to rewrite this to the C++��s standard? Don��t you think it��s going a bit far as a conversation? Is that so necessary?
which overlap with shared_ptr in functionality. -- Why just don’t read the code? I can not force you. But if we talk about something, we have to Base on something, not thinking or imagination or desire, Aren’t we?
-- Just stepping in for a second here. It looks here as if you are saying
On ma, 03. aug 17:24, 逸霖 杨 via Boost wrote: the people you're discussing your code with didn't read it. It is obvious to me that they did: they have raised excellent points and examples which could only ever be made with a good understanding of the code. It's natural that you would be feeling a bit of frustration since you came up with something you think is cool, but as you said, you cannot make others agree unless you address the problems they see. A completely off-topic note: I think c++-cli already has syntax extensions that make it possible to harness the CLR from the language. This includes COM types through interop. And you can mix/match with C++ standard memory model types as much as you wish. Regards, Seth
which overlap with shared_ptr in functionality. -- Why just don��t read the code? I can not force you. But if we talk about something, we have to Base on something, not thinking or imagination or desire, Aren��t we?
-- Just stepping in for a second here. It looks here as if you are saying
the people you're discussing your code with didn't read it. It is obvious to me that they did: they have raised excellent points and examples which could only ever be made with a good understanding of the code. -- Yes, read, some read. The code is very short, 150+ lines. But the calling sequence is not as obvious as short. Since they call each other in the indirect-recursion way. This makes it complicated and you almost have to debug or trace the calling sequence then you can see how it works clearly �C there are many callings are invisible because they��re part of the compiler-generated code. That��s the reason why I insisted on reading (if can be done only by reading) or debugging or tracing. Actually, this is the strategy to find someone who is interested in this area. And seems that I got very few interested. This is the reason, not forcing others to read, just seeking for interested programmers, and if not interested, you can always ignore me. -- It's natural that you would be feeling a bit of frustration since you came up with something you think is cool, but as you said, you cannot make others agree unless you address the problems they see. -- I did address the problem (showing the alternative implementation), just in other posts. (my email program crashed and I lost the topic, When pressing ctrl+c, win10��s email program crashed, for a fast reply, I started a new post with different name). A completely off-topic note: I think c++-cli already has syntax extensions that make it possible to harness the CLR from the language. This includes COM types through interop. And you can mix/match with C++ standard memory model types as much as you wish. -- Thanks for informing. Maybe we just get back to the standard c++ topic as permitted (if my understanding of permission is correct). -- Anyway, thanks for stepping in and your understanding! Regards, Seth _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Seems that everything we talk about is around C++ standard/specification, or accepted by the committee etc. As I know that the boost is found of the coders who like C++ and want to boost it. Any reason I have to focus on things that I really don’t care about? Isn’t talking about technology and possibilities plain and simple a good thing? Please don’t answer me.
Accepting the kindly advices from dear members (Rob, Andrey, Seth and others), I just updated the code with disposing version of rcgc_ptr (shared is removed to avoid misunderstandings) New added class is called rcgc_d_ptr. rcgc_ptr uses the dtor and would call the dtor multiple times of the reference object. rcgc_d_ptr needs the class to have a function named disposing() where all rcgc_d_ptr wrapped fields should call their .dispose() method (not ->dispose() method). class D { public: D() :_ptr_outC(){} ~D() { std::cout << "dtor for object of D:" << std::hex << this << std::endl; } public: void disposing() { //Should NOT be this->_ptr_outC->disposing() //which will lead to this function itself (direct/indirect-recursion). //NOTICE:Don't call self directly or indirectly! this->_ptr_outC.disposing(); } public: rcgc_d_ptr<C> _ptr_outC; }; And here is the GitHub address again in case you don’t get the last posts. https://github.com/yyl-20020115/RCGC
Greetings, everyone, Please, don’t get me wrong … although I’ve made lots of mistakes. The story I posted seems one night of work. However, I worked on the field of a general parser framework for nearly two decades since as a teenager. Started coding in BASIC from 12 years old (later ASM/Pascal/C/C++/Java/C#/ PHP/Python/…) and now 38, Two decades as a programmer. Most of the my programming experience was focus on the language parsing area, or some kind of AI based parser (not the AI we used to know, or saying, not connectionism, but symbolism), building a parser which can recognize all computer languages or even all human languages was always my dream. (please don’t think current NLP technology has solved this problem already, it has not yet) For quite a long time, experiments on the parser I mentioned before all failed. Until in late 2018, I found a way to solve the BIG problem with a new idea borrowed from the AI that we all know (Deep Neuron Network etc). Here is the easy way to understand what I’m talking about: How can you cut a three-layers birthday cake? Of course, you can cut it into fans vertically. However, if you realized that its three-layers birthday cake already, you don’t have to cut, you can just split it horizontally into three layers directly without a knife. So the key is, changing point of view can bring up solutions: From Control Flow based to Data Flow based programming. Freeing or deleting or destroying linked objects is just another Graph Theory problem in Computer Science. Actually we do have method to deal with graphs efficiently(DFS,BFS) in control flow based programming. But there is limit in languages, such as we know that the detors can have no parameters (this is important if you traverse the graph). And the binding of detors and free() is another obstacle of making things right, but the Data Flow based programming can help solving this problem. (Of course, it is realized after the implementation, not before, not planed, nothing is foreseen.) And If we can do this on common graph, we can do this on the freeing of object graph either in C++ or in other OO languages. This is the thing, not a trick or simple idea, but implemented in C++ (other choice is C, as you can not choose Java or C#). However, to explain it correctly, I may be limited by my English vocabulary. So if you’re interested, please read and debug the code to see what happens. And we can talk about details or failure situations accordingly. I’m still on trail-and-error stage. And don’t have full confidence that in every situation things will work correctly. Feed-back is always welcomed. And if you don’t like the “abuse of the detors”, please just change it to dispose(or disposing?), Because they’re logically equivalent (maybe semantically not). Best Regards, Yilin
Let me try to explain how it works (in case you don��t want to read the code or think, 150+ lines of code can do nothing). Objects get linked together into a network. Most of time, no need to be so complicated. However, language related or knowledge related projects Are exceptions, which most of time I worked on them. Circular-Reference is a tough problem even in Java or C# although they have GC, And GC has bad performance as we known. Reference Counting is the most close to the final solution. It��s good enough to free trees, and near good to free graphs. When we solve circular reference in graphs, we use DFS or BFS. DFS requires implicit stack and BFS requires explicit stack. Moving these concepts to C++��s deleting objects, we can only use DFS. Because the compiler did the work to free objects inside of objects in cascade. But this is not enough. You can not delete your object in it��s detor again if one object has its link Back to itself. Or if in the case of shared_ptr, the shared_ptr object itself is in the content Of the object it referenced. Therefore if you free the memory of the shared_ptr��s containing Object, the shared_ptr will be gone(so unable to decrease 1), if you free the shared_ptr��s memory (so the shared_ptr��s content is gone) and the containing object will not be freed (refence count has 1 to be decreased). So in either case, you can not decrease 1. And therefore one object referencing itself with shared_ptr can not be deleted At correct time or even to the end(Same thing happens when other longer circular reference with objects�� graph). But if you let shared_ptr call it��s referenced object��s detor(can be dispose method) and the detor calls back to the shared_ptr��s detor (or dispose) The detor of shared_ptr can understand and just decrease count then return to the caller which is the referenced object��s detor(or dispose). And if we don��t free memory instantly here, we get the count decreased correctly and content of either object or the reference intact. And when every related object within this object as fields get reference decreased and if any decreased to zero, They (objects) can be all put into the _wild (to be free) list which we can free them all together after this round. This is the simplest situation that one object use shared_ptr to reference itself. Things can go much complicated. However, you just don��t free memory, and call every detor or dispose of the referenced objects And according detors or dispose methods of shared_ptrs on one stage and free the object which has a reference count decreased to 0 on next stage, you just fix the conflict and solve the problem. 1. DFS (compiler generated code enforces this method) works in circle results in stack overflow can be solved by the control provided by detor or dispose method of shared_ptrs. 2. Detor calling stage and freeing stage are separated can make memory intact and the detors can be called multiple times to decrease count correctly. With the correct decreased count, one object can be freed at the very right time, no need to do manual work, or use GC method to build object linkage graph on the fly or try invalid pointers like GO��s runtime does. Yes, this method violates C++ specification if calling detors of shared_ptr pointed objects. But this is optional. You can still use common method To call, maybe named dispose or disposing. Because the algorithm is not based on C++ undefined behavior. One defect is that you have to Call the field��s shared_ptr��s dispose method manually inside of the dispose method of the container object which means, write more code. This is why I don��t like this solution. However if you insist in C++ specification, and don��t want to add any syntax, this is the final solution I can provide. The other defect is that something like std::string can not be put as field directly in some container wrapped with rcgc_shared_ptr, Because they can not ensure correct freeing or destroying when detor��s called for the second time. The solution for this is that we always Use rcgc_shared_ptr on fields in side of the classes which would be wrapped by rcgc_shared_ptrs, or use simple pointers and protect The pointer will be NULL checked. Either shared_ptr<A> or A*, just not A as field member of a class B (which will be wrapped with rcgc_shared_ptr<B>). What I��m talking about here is, reference counting aided method of automatic memory management is practical and the code of 150+ lines already made it true. And the above is how it works �C I tried my best, but still feel limited in the vocabulary of English. So, is this OK? Again, thanks for pointing out the detor��s abusing problem. As Andrey suggested, if put in a library, has to be the disposing way. I accept the kindly suggestion. And please see the point. BR, Yilin
participants (3)
-
Andrey Semashev
-
sehe
-
逸霖 杨