Hi All, As we are creating a user API, unless there is a significant performance benefit, I think we should try to take the simpler route.
I see benefit to the approach being proposed, but I don’t see a significant benefit. Someone please school me if I am wrong. I am still in the shared pointer camp for cache and a raw pointer to cache in cacheImpl. Sorry, and thanks, Mark > On Sep 18, 2017, at 10:14 AM, David Kimura <dkim...@pivotal.io> wrote: > > +1 value approach (via some implementation from this thread) > > I think I like this. > > In all BAD cases, it's the user who shot themselves in the foot by using > std::move unsafely. I expect this behavior is the same behavior as for any > other object. And if we're ever able to get rid of the Cache/CacheImpl > circular dependency then we can add a copy constructor. > > I also like the idea of passing in a CacheConfig. My concern though is > that it's piling on another big change. > > Thanks, > David > > On Sun, Sep 17, 2017 at 12:02 AM, Jacob Barrett <jbarr...@pivotal.io> wrote: > >> Ok, one more idea. >> https://gist.github.com/pivotal-jbarrett/beed5f70c1f3a238cef94832b13dab7a >> >> The biggest issue with the value model is that we have been using a factory >> to build the Cache object. We really don't need one and if we get rid of it >> things look much better. They aren't perfect since we still need the back >> pointer from the impl to the cache for later use. If we didn't need that >> then we could allow copy construction. As it stands right now this version >> allows value, shared_prt, unique_ptr, etc. without any real overhead or RVO >> issues. >> >> The big change is that, rather than have a factory that we set a bunch of >> values on and then ask it to create the Cache, we create a CacheConfig >> object and pass that in to the Cache's constructor. Cache passes it to >> CacheImpl and CacheImpl sets itself up based on the config. If you look at >> what the current factory model does it isn't that different. For clarity I >> added an XmlCacheConfig object to that builds up the CacheConfig via Xml. >> You could imagine a YamlCacheConfig object *shiver*. The point is we don't >> care as long as we get a CacheConfig with all the things we support at >> "init" time. >> >> I know it is a more radical change but I feel it is more C++ and more >> testable than the factory model. I also like that it solves some of the >> issues with the value model we were looking at. >> >> -Jake >> >> On Thu, Sep 14, 2017 at 5:16 PM Jacob Barrett <jbarr...@pivotal.io> wrote: >> >>> Y'all here is an attempt to get the best of both worlds. >>> https://gist.github.com/pivotal-jbarrett/52ba9ec5de0b494368d1c5282ef188 >> ef >>> >>> I thought I would try posting to Gist but so far not impressed, sorry. >>> >>> The Gist of it is that we can overcome the thirdpary or transient >>> reference back to the public Cache instance by keeping a reference to it >> in >>> the implementation instance and updating it whenever the move constructor >>> is called. >>> >>> The downside is if your run this test it doesn't show RVO kicking in on >>> the second test where we move the value into a shared pointer. There are >> a >>> couple of pitfalls you can stumble into as well by trying to used the >>> previous instance to access the cache after the move operation, as >>> illustrated by the "BAD" commented lines. >>> >>> The upside is the choices this gives the use for ownership of their >>> factory constructed Cache instance. They can keep it a value or move it >> to >>> unique or shared pointer. >>> >>> Overhead wise I think we better off in value as long as there are no >>> moves, rare I would thing, but the moves are pretty cheap at the point >>> since we only maintain a unique_ptr. After moving into a shared_ptr it >> acts >>> identical to the shared_ptr model proposed earlier. >>> >>> -Jake >>> >>> >>> On Thu, Sep 14, 2017 at 3:36 PM Michael Martell <mmart...@pivotal.io> >>> wrote: >>> >>>> Late to this party. >>>> >>>> Confession 1: I had to look up both RVO and copy-elision. >>>> Confession 2: I don't like using pointers at all. I used to, but I've >>>> evolved to just use C# and Java :) >>>> >>>> Without investing a lot more time, I don't have strong feelings about >> raw >>>> vs shared pointers. My only question is: Can we return ptr to abstract >>>> class everywhere we return objects? Just thinking of mocking, which >> always >>>> wants to mock interfaces. >>>> >>>> On Thu, Sep 14, 2017 at 2:25 PM, Michael William Dodge < >> mdo...@pivotal.io >>>>> >>>> wrote: >>>> >>>>> +0 shared pointer >>>>> >>>>>> On 14 Sep, 2017, at 14:09, Ernest Burghardt <eburgha...@pivotal.io> >>>>> wrote: >>>>>> >>>>>> Calling a vote for: >>>>>> >>>>>> - Raw pointer >>>>>> - shard pointer >>>>>> >>>>>> +1 raw Pointer, I had to look up RVO and am new to std::move(s) >>>>>> >>>>>> On Thu, Sep 14, 2017 at 3:02 PM, Michael William Dodge < >>>>> mdo...@pivotal.io> >>>>>> wrote: >>>>>> >>>>>>> I generally dig reference-counted pointers for avoiding lifetime >>>> issues >>>>>>> with objects allocated off the heap but I can live with bare >>>> pointers, >>>>> too. >>>>>>> >>>>>>> Sarge >>>>>>> >>>>>>>> On 13 Sep, 2017, at 16:25, Mark Hanson <mhan...@pivotal.io> >> wrote: >>>>>>>> >>>>>>>> Hi All, >>>>>>>> >>>>>>>> I favor the “pointer" approach that is identified in the code >>>> sample. >>>>>>> There is greater clarity and less bytes seemingly created and >>>> written. >>>>> We >>>>>>> do sacrifice the potential ease of using an object, but in all, I >>>> think >>>>> the >>>>>>> way our code is structured. It is not conducive to do a value >>>> approach, >>>>>>> from an efficiency standpoint, because of our use of the pimpl >>>> model. >>>>>>>> >>>>>>>> Thanks, >>>>>>>> Mark >>>>>>>> >>>>>>>>> On Sep 12, 2017, at 11:09 AM, Jacob Barrett <jbarr...@pivotal.io >>> >>>>>>> wrote: >>>>>>>>> >>>>>>>>> My biggest concern with this model is that access to the public >>>> Cache >>>>>>>>> object from other public objects results in additional >> allocations >>>> of >>>>> a >>>>>>>>> Cache value. Think about when we are inside a Serializable object >>>> and >>>>> we >>>>>>>>> access the Cache from DataOutput. >>>>>>>>> >>>>>>>>> As value: >>>>>>>>> Serializable* MyClass::fromData(DataInput& dataInput) { >>>>>>>>> auto cache = dataOutput.getCache(); >>>>>>>>> ... >>>>>>>>> } >>>>>>>>> In this the value of cache will RVO the allocation of Cache in >> the >>>>>>> getCache >>>>>>>>> call into the stack of this method, great. The problem is that >>>> Cache >>>>>>> must >>>>>>>>> contain a std::shared_ptr<CacheImpl> which means that each >>>> allocation >>>>>>> is 8 >>>>>>>>> bytes (pointer to control block and pointer to CacheImpl) as well >>>> as >>>>>>> having >>>>>>>>> to increment the strong counter in the control block. On >>>> exit/descope, >>>>>>> the >>>>>>>>> Cache will have to decrement the control block as well. >>>>>>>>> >>>>>>>>> Using current shared_ptr pimple model: >>>>>>>>> Serializable* MyClass::fromData(DataInput& dataInput) { >>>>>>>>> auto& cache = dataOutput.getCache(); >>>>>>>>> ... >>>>>>>>> } >>>>>>>>> We only suffer the ref allocation of 4 bytes and no ref count. >>>> Since >>>>>>> this >>>>>>>>> function call can't survive past the lifespan of Cache/CacheImpl >>>> they >>>>>>> don't >>>>>>>>> need to have shared_ptr and refcounting. >>>>>>>>> >>>>>>>>> Given that this method could be called numerous times is the >>>> overhead >>>>> of >>>>>>>>> the value version going to be a significant performance issue? >>>>>>>>> >>>>>>>>> I worry that moves and RVO is just beyond most developers. Heck I >>>>> didn't >>>>>>>>> know anything about it until we started exploring it. >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> -Jake >>>>>>>>> >>>>>>>>> >>>>>>>>> On Tue, Sep 12, 2017 at 8:06 AM David Kimura <dkim...@pivotal.io >>> >>>>>>> wrote: >>>>>>>>> >>>>>>>>>> Follow up of attached discussion after more investigation. I >>>> created >>>>>>> an >>>>>>>>>> example of returning Cache as shared pointer versus raw value: >>>>>>>>>> >>>>>>>>>> https://github.com/dgkimura/geode-native-sandbox >>>>>>>>>> >>>>>>>>>> I still like returning by value as it lets the user do what they >>>> want >>>>>>> with >>>>>>>>>> their object. >>>>>>>>>> >>>>>>>>>> // Here user creates object on their stack. >>>>>>>>>> auto c = CacheFactory::createFactory().create(); >>>>>>>>>> >>>>>>>>>> // Here user creates smart pointer in their heap. >>>>>>>>>> auto cptr = >>>>>>>>>> std::make_shared<Cache>(CacheFactory::createFactory(). >> create()); >>>>>>>>>> >>>>>>>>>> Difficulty of implementing this is high due to circular >>>> dependencies >>>>> of >>>>>>>>>> Cache/CacheImpl as well as objects hanging off CacheImpl that >>>> return >>>>>>>>>> Cache. We must be extra careful when dealing with move/copy >>>>> semantics >>>>>>> of >>>>>>>>>> Cache/CacheImpl. >>>>>>>>>> >>>>>>>>>> Alternative, is to keep as is and only permit heap allocations >>>> from >>>>>>>>>> factory using shared pointers. >>>>>>>>>> >>>>>>>>>> Thanks, >>>>>>>>>> David >>>>>>>>>> >>>>>>>> >>>>>>> >>>>>>> >>>>> >>>>> >>>> >>> >>