Click here to Skip to main content
Click here to Skip to main content
 

new(local) for safer and simpler code with no need for smart-pointer template wrappers

By , 3 Oct 2012
 
This is an old version of the currently published article.
  •  

Introduction    

     
Pointers always envied automatic cleanup that is in c++ provided only for big fat slow Static Object or their arrays stored by value. In this article we will introduce concept of scopes also for dynamic objects and scoped version of new/malloc to take advantage of those scopes. Ie associated objects/memory will get destroyed with the scope. Plus your code will now be smaller and simpler. I got this idea about pointers deserving automatic cleanup love too in my other article about new && operator
struct Object : Scope::Obj {
};

Scope global;
// entering scope
void proc() { 
    Scope local;
    char*  text = strdup(local,"text"): // malloc based memory have automatic cleanup too
    
    Object* obj = new(global) Object(); // obj will get destructor called on program exit 

    Object* array[1000]={0};            //sorting pointers is fast compared to array of objects
    for(int i=0;i<1000;i++) {
        array[i] = new(local) Object(); // creates object associated with "local" scope 
    }
}
// we left scope so "Scope local" and all associated object /memory 
// gets destructors called/freed in proper order    

Why manual cleanup is source of bugs.     

Automatic cleanup provided for static objects saves time lines of code and a lot and I mean really lot of bugs. Bugs by forgetting to match all allocations with deallocations. Bugs by memory being leaked when exception or error handler exits something somewhere prematurely and not all control paths contain correct number of release statements.   
Bugs by releasing objects twice or more and corrupting memory by confused condition statements.

Performance point of view    

So we definitely want automatic cleanup that is provided for static objects. But pretty much any program serious about performance will store objects by pointers and not by value because price for reallocation insert or sort is horrendous as you can see in benchmark here

So can't we have both? Performance and efficiency of pointers and safety and simplicity of static objects by automatic cleanup when leaving scope? Yes we can if we implement concept of scopes also for dynamic objects.

Simpler alternative to smart pointers   

Big advantage of scopes is that you don't need kinda heavy dependency on stl/boost just for simple thing like pointer. Code simplicity and clarity keeps bugs at bay. Why ?

in the bigger struct A sample code bellow it's clear what is going on and you are not limited in array storage either. 
 

                 // by keeping simple pointers it's always obvious what is going on 
                 // and how efficiently.
   ptr1 =  ptr2; // just pointers are assigned ie MOVE semantics
  *ptr1 = *ptr2; // objects are copied ie COPY semantics 
                 // ie you decide  when you copy or move by clear syntax. 

With smart pointers the fact when they actually copy or move is one big jungle.

Most of them simply don't work in containers. some require c++11 compiller or big template libs resulting to big executables and all make code unusable by others since libs/dlls  with stl/boost are incompatible on binary level not having any concept of stable interface .  

auto_ptr is very bad choice. It's pointer with copy and with move semantics and ownership (=auto-delete) can't be stored in containers

scoped_ptr is a auto_ptr without copy and without move semanticscan be stored in containers.and requires boost It adds a level of safety. memory created will exist only for that scope and no more, thus you get compile-time protection against attempting to move it or transfer it.  

unique_ptr
 is a auto_ptr without copy but with  move semantics.can be stored in containers requires c++ 11 compillerOwnership is movable,  

shared_ptr. requires c++ 11 compillerIf you want to create something and share that pointer and cleanup when all referencers are gone,  

weak_ptr ,intrusive_ptr ,... whatever new ones they will attempt to create  in future just making the whole mess and confusion even deeper

The real solution is to have smart pointers build in c++ language and not create zillion of weird templates supporting this and not that. For example let Object *~ be declaration of smart pointer. Will have destructor called going out of scope and all of the copy move is already part of low level pointer arithmetics. no zillion of template wrapers/libs/operators. period. I am right now attempting to implement it in CLang compiller as an funny experiment.
 

But back to smart-pointers. Take for example this guy having trouble sorting simple array of pointers and obviously having noidea what state machine he actually created due to geting responses like  

"...The IS specifies that a predicate should not assume that the dereferenced iterator yields a non-const  object. Arguments should therefore be taken by reference-to-const or by value.  The IS also specifies that

algorithms that take function objects as arguments are permitted to copy those function objects. So in most cases, the overloaded function call operator would be a const member function ..."                                                                                                                                            
 
Hmm.. 

It is unbelivable what complexity we vere able introduce to ehm... "simplify our life" 

With smart pointers you need to wrap every pointer reference ie change all the code to alien lenghty syntax always worying about state machine you just created. With scopes you just derive your objects from Scope::Obj in one line and that's all. 

Keeping simple easy to debug code and libs/dlls usable by others. Just insert scope to your class and use scoped de/allocators. All your data will be properly deallocated exactly like with smart pointers even when constructor fails in the middle during exception etc. 

struct A {
    Scope local; 
    B* ptr1;
    B* ptr2;
    A() {
        ptr1=new(local) B();
        ptr2=new(local) B();// will not allocate and will leave prematurely due to memory 
                            // exhaustion exception. But no memory leak will happen since scope
                            // deallocates ptr1 on scope destruction as part of object
                            // destruction that follows this constructor exception
    }
} 

So if you are like me preferring things small and simple so you know by simple look at source what is going on.
Let's try simpler but I hope equally functional alternative.  

Let's Scopes for dynamic heap objects be born  

    So how does the automatic cleanup of static objects actually work? Well. It works by simply adding pointer of every static object within scope to internal invisible linked list and when leaving calling destructors for each of them in reverse order.

Can we make something similar for dynamic objects?
Why not. We can have array of void pointers and store pointers to various object types in one single array.
The only issue are calling proper object specific destructors. Unfortunately as far as I know c++ doesnt allow to get destructor adresses. So the only sollution I come up so far is to use virtual destructor in all stored objects so polymorphysm selects proper one for us.  

Implementation of Scope.h                                                   

This code snippet bellow is kept simple with compressed formating just for purpose of article so it is clear what is going on. Complete implementation along with scoped versions of new delete strdup malloc calloc free and threadsafe version of scope called TScope (usefull for using for example global scope from multiple threads etc) is in Scope.h that is attached in zip file with Example.cpp on top of the article. I did not attached project files since most of you will not be able to open vs2012 anyway. 

struct Scope { // This is Just simple linked list
       struct Obj { virtual ~Obj() {} };
       Scope*    prev;  Obj* ptr;
       Scope() : prev(0) ,   ptr(0) {}	
      ~Scope() { ptr->~Obj(); free(ptr); delete prev; } // deleting all childs in reverse order on exit
};

inline void * __cdecl operator new(unsigned int size,Scope& scope)
{
    void *ptr = (void *)malloc(size); // we add all new objects to this linked list
    if(scope.ptr) { Scope* prev=0; prev=(Scope*)calloc(sizeof(Scope),1); *prev=scope;
    scope.prev=prev; }
    scope.ptr=(Scope::Obj*)ptr; 
    return(ptr);
};

Limitations of this solution 

To support automatic cleanup your objects need to be derived from Scope::Obj or have virtual destructor as first virtual method. That being said adding one derive statement in class declaration is on changed line of code ie less work and changes than going about replacing every ocurence of pointer of that class as you need with smart-pointers . 

Example code  

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "Scope.h"   

Scope global; // Deallocates/Destroys all associated objects on program exit no matter 
              // what exception or error in program happens

struct A : Scope:Obj {// As for supported objects. They must be derived
              // from Scope::Obj or have virtual destructor as the first vitual method 
              // so it is first in vtable and correct call is constructed on cleanup
              // In a sense we are keeping pointers to destructors as known this way
       A(){ printf("\n  A %x",this); }
      ~A(){ printf("\n ~A %x",this); } 
};

void Test() {
    Scope local; // all objects associated with scope will be deallocated by leaving procedure
    A* array[3]={0};
    for(int i=0;i<3;i++) {
        array[i]=new(local) A();
    }
    char* text=strdup(global,"this will get deallocated on program exit");
    A* a=new(global) A();  // this will get destructor called on progam exit
}

void main() {
    Test();
}                      

Output: 

   A 689718   A 68b110   A 68b198   A 68b220 ~A 68b198 ~A 68b110 ~A 689718 ~A 68b220

Points of Interest 

The fact that derive from Scope::Obj or with virtual destructor as first virtual method kinda is slight inconvenience considering what you will gain.  

Also if you want to autodeallocate memory from malloc/calloc/strdup just use scoped versions from scope.h in zip file. Off coarse for explicit dealocation you must use  scoped versions of free/delete too so they are removed from scope and no another attempt is made to free them when leaving scope 

  

 Where this leads and future work

As I said before right now I am playing with the clang c++ compiller sources and implementing smart pointers as build in to c++. Feature that should had been there long time ago (calling destructor when not null and going out of scope) so low level os /driver developers can produce smaller and safer code too without big template monsters like boost. I am open to interesting new ideas or suggestions 

I have no idea how to mark them yet. But so far I am testing with variable*~ syntax
 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Ladislav Nevery
Software Developer (Senior)
Slovakia Slovakia
Member
Past Projects:
[Siemens.sk]Mobile network software: HLR-Inovation for telering.at (Corba)
Medical software: CorRea module for CT scanner
[cauldron.sk]Computer Games:XboxLive/net code for Conan, Knights of the temple II, GeneTroopers, CivilWar, Soldier of fortune II
[www.elveon.com]Computer Games:XboxLive/net code for Elveon game based on Unreal Engine 3
ESET Reasearch.
Looking for job

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions


Discussions posted for the Published version of this article. Posting a message here will take you to the publicly available article in order to continue your conversation in public.
 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionWhy not std::unique_ptr<T>? [modified]memberC++ Pirate Programmer18 Oct '12 - 5:46 
It isn't clear to me why this is better than using std::unique_ptr<T>. It seems worse. Your solution requires a second dynamic memory allocation just to manage the object lifetime, while std::unique_ptr<T> does not. Your solution requires virtual destructors, while std::unique_ptr<T> does not. Your solution does not properly handle a destructor that throws, std::unique_ptr<T> does. Your solution does not allow me to move responsibility for pointer destruction to someone else, std::unique_ptr<T> does. Your solution does not allow for different scoping levels within a single function, std::unique_ptr<T> does. Your solution does not allow for destructing a pointer explicitly, std::unique_ptr<T> does. I could probably go on, but I think I've made my point.
 
The only disadvantage to std::unique_ptr<T> that you claim is that it is complicated, but I disagree. std::unique_ptr<T>'s implementation for g++ 4.7.0 is 560 lines, but that includes all the custom deleter junk. If you want a simple implementation, there is boost::scoped_ptr<T>, which is 130 lines, is ridiculously straightforward, and has none of the shortcomings of your solution. Or you can make your own to do whatever you want.
 
Your example of "this guy having trouble sorting simple array of pointers which he normally wouldnt have problem" is disingenuous. His problem was const-correctness. Had SortFunctor::operator() been written as operator()(Test*& object1, Test*& object2) (the moral equivalent to operator()(std::shared_ptr<Test>& object1, std::shared_ptr<Test>& object2) with naked pointers), he would have had the exact same issue. shared_ptr made the compile error worse, but was not the source of the problem.
 
As far as doing this in a language switch goes: You really need to consider the prospective audience. If somebody cares enough about memory usage to be concerned with the possible code bloat that comes from a templated smart pointer, they're going to be more concerned with the memory bloat coming from the shortcomings of your solution: double-allocation, requiring a virtual destructor, etc. In embedded systems, code bloat is far less significant than per-object or per-allocation bloat, since code bloat is at least fixed.

modified 18 Oct '12 - 11:57.

AnswerRe: Why not std::unique_ptr? [modified]memberLadislav Nevery18 Oct '12 - 16:02 
thank you for fact based discussion. I value it.
 

C++ Pirate Programmer wrote:
It isn't clear to me why this is better than using std::unique_ptr<T>. It seems worse.

 
This article is an attempt to offer an alternative to c (no templates there) and c++ programmers that can't or don't want use slow and wastefull template wrappers.
 
I bet you know about price for copy semantics in template based containers.
Yes one day when everybody will have c++ 11 compiller at least heap part of this waste could be reduced (not that it all will and only if objects have move versions of every operator constructor etc.).Since most of the objects are small that means most of the waste is static object mem that even && rvalue operator can't solve.
 
I also bet you know the price for using wrappers is zillion methods invoked unnecessary and methods invoked in places where there was simple assembly instruction.
 
Take for example what is going on when you have common compiller and store 1000 shard_ptr's
(very common case since let's say sorting or reallocating 1000 objects stored by value is bad idea)
that is default constructor/constructor/operator=/destructor + 1 wasted copy = around 4000 noninlinable methods invoked. all doing stack push/pops local var reserve/cleanups exception handler de/registrations.
 
now compare that to storing 2000 numbers (if we use preallocated arrays instead of linked list in scope(which I plan))
 
C++ Pirate Programmer wrote:
Your solution requires virtual destructors,

 
No problem in C and in most code I can think of in c++ either.
But it's on the readers of article to weight pros and cons and maybe gain another alternative. Which is not entirelly a bad thing I guess.
 

C++ Pirate Programmer wrote:
Your solution does not properly handle a destructor that throws

 
Yes it does.
 
proc() {
    Scope local;
    try {
        Object* array[1000]={0};            //sorting pointers is fast compared to array of objects
        for(int i=0;i<1000;i++) {
            array[i] = new(local) Object(); // creates object associated with "local" scope 
        } 
    } catch(...) { 
        printf("no manual cleanup needed anymore");
    } 
} 
C++ Pirate Programmer wrote:
Your solution does not allow me to move responsibility for pointer destruction to someone else

 
the need for shared ptr is rare thing and reference counting is 2 lines of code to object where it is relevant. I actually wana see what is going on.
 
Side effect of copy/paste programming is that many people use shared_ptr instead of scoped_ptr justbecause everybody proposes them not having idea about unnecessary reference counting going on.
 
Are you actually happy with zillion of design patterns mixed with automatic cleanup ?
I mean the whole mess with this smart_ptr does this but doesn't do that being inefficient with this but not that. This uses move semantics for this but not that. This uses just copy semantics etc.
 

C++ Pirate Programmer wrote:
Your solution does not allow for different scoping levels within a single function

 
Yes it does.
 
   Scope local;
   Object* object = new (local) Object();
   {
      Scope local;
      Object* object = new (local) Object();
   }
 

C++ Pirate Programmer wrote:
Your solution does not allow for destructing a pointer explicitly

 
Yes it does.
 
   Scope local;
   Object* object = new (local) Object();
   delete(local) object;
 
C++ Pirate Programmer wrote:
As far as doing this in a language switch goes: You really need to consider the prospective audience. If somebody cares enough about memory usage to be concerned with the possible code bloat that comes from a templated smart pointer, they're going to be more concerned with the memory bloat coming from the shortcomings of your solution: double-allocation,

 
Switch sollution doesn't involve any more overhead that we gladly acceptin c++ for automatic objects. And that is perfectly fine with me.
 
The switch is first and foremost about removing manual cleanup thus reducing memory leaks double cleanups or memory overwrites(stability) by deallocating wrong object(security) etc and generating smaller code.
No in compiller you have access to all internal info so you don't need any virtual destructors just as you don't need it with automatic objects
"There is always a better way"


modified 18 Oct '12 - 22:36.

QuestionThanksmemberYannAchard18 Oct '12 - 1:29 
I find it nice to see new approaches that are simple (well simpler than heavy duty templated code anyway) and that don't come at a big performance cost or require a lot of code re-write to adopt.
 
Sure, it may still be preferable to use other constructs in many cases, (frankly I can't follow half of the debate) but it doesn't change the fact that you've just expanded our tool-set, or at least out idea-set.
 
Thank you for that
Keep trying new stuff Smile | :)
AnswerRe: ThanksmemberLadislav Nevery18 Oct '12 - 13:00 
Thx I appreciate it Wink | ;)
"There is always a better way"

GeneralMy vote of 2memberSeattleC++15 Oct '12 - 9:53 
Far too limited to be of use. Does not allow to put pointed-to objects in containers, doesn't solve problem of passing a pointer out of scope (even if you're careful) which shared_ptr, for instance, solves.
GeneralRe: My vote of 2memberLadislav Nevery16 Oct '12 - 9:56 
SeattleC++ wrote:
Far too limited to be of use.

 
In what way you feel limited?
Please elaborate in concrete example.
 
SeattleC++ wrote:
Does not allow to put pointed-to objects in containers

 
Why do you think objects used in example can't be stored to containers ?
Please elaborate in concrete example.
 
SeattleC++ wrote:
doesn't solve problem of passing a pointer out of scope

 
That's because this article is about automatic cleanup when going out of scope.
 
You are confusing two independent design patterns.
One is concept of temporary lifetime = Scope.
And second is concept of shared resource and Reference counting.
 
While all of us needs and benefits from first the same can't be said about second.
In other words not every thime you need automatic cleanup you need also overhead of reference counting.
 
But lot of people actually use shared_ptr instead of scoped_ptr just because it's more agressively pushed in forums in sentences "hey... use shared_ptr it can finally be stored to an array".
 
This is why I prefer Scope functionality to do Scope functionality.
And when I would need reference counting I would simply write 2 lines of code?
"There is always a better way"

GeneralRe: My vote of 2memberSeattleC++16 Oct '12 - 11:24 
Let me be clear. Your method is far too limited to be of general interest because it is unsafe. The storage is reclaimed at a scope boundary, but plain-old-pointers still point to it. You could too-easily pass your plain-old-pointer to a variable or data structure with longer lifetime, resulting in dangling references to reclaimed storage. By contrast, reference counted smart pointers have dynamic lifetime. Other kinds of smart pointers control the ownership and lifetime of the pointed-to storage as well and better limit leaking plain-old-pointers. The RAII idiom scopes objects without releasing pointers to them.
 
It might be tempting to use this mechanism in a project where you were sure you wouldn't leak pointers out of the managed lexical scope. If the code was sufficiently well documented, it might even make it past a code review. But it would have to be clear to everyone on the project that you were doing something unsafe. If an inexperienced coder tried to use this mechanism, the chance of disaster would be great. That is why I would not recommend this mechanism.
 
Your basic smart pointer template is only 30-50 lines of C++ You don't have to load all of boost to use smart pointers. If you don't understand the smart pointer idiom well enough to read an existing implementation or write one with semantics you like, you are probably not experienced enough to use your proposed mechanism safely. In this case you should stick with reference-counted smart pointers, or switch to a language like Java where you don't have to think about storage management.
GeneralRe: My vote of 2 [modified]memberLadislav Nevery17 Oct '12 - 5:44 
SeattleC++ wrote:
Let me be clear. Your method is far too limited to be of general interest because it is unsafe. The storage is reclaimed at a scope boundary, but plain-old-pointers still point to it. You could too-easily pass your plain-old-pointer to a variable or data structure with longer lifetime, resulting in dangling references to reclaimed storage.

 

Unsafe? Concept of scopes for dynamic objects is no more safe or less safe then let's say
concept of scopes for objects allocated using static(fixed and known at compiletime) stack memory. How come ?
 
void * proc() {
    Object   object;  // Typical bug
    return   &object;
}
You see ? you are in exactly the same risk of dangling pointer with static data as with dynamic data.
 
Does the fact that we are allowed to pass around pointer to static object mean that static objects are equally unsafe and shall not be used because they allow dangling pointers ?
 
Definitely not. Exactly the concept of scope and automatic cleanup was big improvement in c++ and stopping their usage would be big step back.
 
You will still need to use your brain and be aware what you are doing.
 

SeattleC++ wrote:
By contrast, reference counted smart pointers have dynamic lifetime. Other kinds of smart pointers control the ownership and lifetime of the pointed-to storage as well and better limit leaking plain-old-pointers.

 
Reference counting is pretty much 2 lines of code.
I have no problem adding them anytime just for rare cases that need them.
 
So what exactly makes them better? Other than the fact that you make code less readable more complex( templates are miniature statemachines added to each variable they wrap around ) with hard to read simple codelogic (did you read some template sources using template deductions?) spread over zillion files.
Then there is fact that for pretty much any operation +=() you name it tons of calls pushes on stack are made instead of using the tool that does it most efficiently. the cpu instructions and registers.
 
Go ahead and ask any programmer using so called smart pointers whether his data are being uselessly copied over and over or moved? I bet most of them will have no idea due to way templates are written.
 
Ask him how much effort it will take him to make it to use the new move semantics in c++11 instead of old and wastefull copy.
Yes sir it means pretty much every copy constructor and assign(along with others) operator need to have duplicite move rvalue reference && variant that still allows only moving dynamic not static part of objects. That is the performance price for wrappers. All that effort and code just attempting to simulate data transfer efficiency you already have with low level pointer.
 

SeattleC++ wrote:
If you don't understand the smart pointer idiom well enough to read an existing implementation or write one with semantics you like, you are probably not experienced enough to use your proposed mechanism safely.

 
D'Oh! | :doh:
"There is always a better way"


modified 18 Oct '12 - 3:36.

GeneralRe: My vote of 2memberSeattleC++17 Oct '12 - 10:29 
Ladislav Nevery wrote:
Unsafe? Concept of scopes for dynamic objects is no more safe or less safe then let's say
concept of scopes for static objects. How come ?
void * proc() {
    Object   object;  // Typical bug
    return   &object;
}
You see ? you are in exactly the same risk of dangling pointer with static data as with dynamic data.

Well, you would be, except this is not static data, it's automatic data. Static data is directly visible within the lexical scope where it's defined, but its lifetime persists until main() returns or exit() is called. If you add the "static" storage class specifier to your declaration of object, you can pass a pointer out of the lexical scope, safe from fear of a dangling reference.
Ladislav Nevery wrote:
So what exactly makes [reference counted smart pointers] better?

What makes them better is that the storage is managed correctly no matter the lifetimes of the pointers *and* dangling smart pointer references cannot happen. This is my definition of "safe".
Ladislav Nevery wrote:
did you read some template sources using template deductions?) spread over zillion files.

Yeah, template code can be pretty hairy. Its definitely not for beginners, just like inventing your own storage management mechanism is not for beginners. The longer this conversation goes on, the more it proves this point.
 
Thanks for an interesting discussion.
GeneralRe: My vote of 2 [modified]memberLadislav Nevery17 Oct '12 - 23:06 
SeattleC++ wrote:
Ladislav Nevery wrote:
Unsafe? Concept of scopes for dynamic objects is no more safe or less safe then let's say

concept of scopes for static objects. How come ?

void * proc() {
    Object   object;  // Typical bug
    return   &object;
}
You see ? you are in exactly the same risk of dangling pointer with static data as with dynamic data.



Well, you would be, except this is not static data, it's automatic data.

 
(I guess word automatic alloc is better wording choice then static allocated memory. ) But. Yes we can shift to wording instead of topic
 
I gave you concrete performance and code clarity explanations why exactly smart pointers are not better not just generic statements like "better"
 
SeattleC++ wrote:
*and* dangling smart pointer references cannot happen

 
Really?
    std::unique_ptr<Object> ptr(object);
    delete object;  // somewhere in other thread in old code and different source burriedin some base class destructor
or
std::unique_ptr<Object>  proc() {
    Object   object;  // Typical bug
    std::unique_ptr<Object> ptr(&object);
    return    ptr;
}
 
What do I see ? dangling smart pointer? You see it doesn't matter wether object was dynamic automatic or smart.
 
Because programmers doing Manual Cleanup repeat their own bad habits misteakes and bad design practices. And the ones doing dangling pointers before are just as likely to make them again.
 
It's automatic cleanup idiom that reduces the error landscape by reducing the manual cleanup. And it doesn't matter wether you use templates or other means result will be significantly more robust code.
 

SeattleC++ wrote:
Yeah, template code can be pretty hairy. Its definitely not for beginners

 
You think people not using templates like operating system /kernel/driver hackers or doing any c/c++ static library useable by others (forget stl/boost) are begginers?
 
Keeping code state machine simple is important design philosophy tested by time.
 
When things get complex the last thing you wana have is simple code logic spread over many sources/headers.
 
You wana have as much relevent codelogic in one place as possible and have as compact and robust error handling as possible.
 
I use "Occam's razor" whenever I can.
 
Because projects grow like trees. People with varying degree of experience start working in paralell on the same code. Belive me. You will pay for complex code dearly in maintenance and bugfixing time since you are never alone on the project.
"There is always a better way"


modified 18 Oct '12 - 8:02.

QuestionWhat's about an exception handling?memberkonik1015 Oct '12 - 6:05 
Hi Ladislav,
 
At first, nice article to show power of C/C++ and how can these two languages can be either used or misused.
 
I have only one question: Did you consider in your design that the constructor of any class (derived from your Scope::Obj; or any class which has a virtual destructor and is instantiated by calling your scope functionality) can throw exception?
 
struct A {  // objects must have virtual destructor
    A()
    {
        throw std::bad_alloc();
        printf("\n  A %x",this);
    }
 
    virtual ~A()
    {
        printf("\n ~A %x",this);
    }
};
 
void Test() {
    try
    {
        Scope local; // all objects associated with scope will be deallocated by leaving procedure
        A* array[3]={0};
        for(int i=0;i<3;i++) {
            array[i]=new(local) A();
            //array[i]=new A();
        }
        char* text=strdup(global,"this will get deallocated on program exit"); //associate with global scope
        A* a=new(global) A();  // this will get destructor called on progam exit
    }
    catch (...)
    {
 
    }
}
 

 
Because C++ standard says that any class constructor or assignment operator can throw exception. So, when the class A constructor (from your example code) throws any exception (that means, the instance of A is not instantiated and the allocated memory should be just simply deallocated without calling of class A destructor) you are going to call the virtual destructor of class A; the is_obj is unfortunately 1 and the line if(is_obj) ptr->~Obj() from your ~Scope() is executed . This behaviour leads to another exception!!!
 
Please, try to solve this problem or just write a comment to your article: "The proposed solution is not exception safe!"
AnswerRe: What's about an exception handling? [modified]memberLadislav Nevery16 Oct '12 - 1:47 
Thx for noticing this.
You should keep scope declaration above (outside) of try catch otherwise it can't serve it's role. Ie do cleanup whatever happens.
I added this important info to article.
 
Objects are automatically deregistered from scope thank's to automatic call of placement delete(defined in scope.h) called from within _unwind. And for deregistered objects obviously no destructor is called when leaving scope. I uploaded updated code and article
 
I like constructive criticism that makes article better. Thx again.
"There is always a better way"


modified 16 Oct '12 - 15:06.

GeneralRe: What's about an exception handling? [modified]memberkonik1023 Oct '12 - 21:24 
When I was saying in my original comment that your article shows the strength of C++ and how it can be used or misused. I asked how you solve the exception handling. My purpose was to bring you to think a little bit more about your solution. I'm afraid, that your modification inside your Scope class destructor is not enough to consider your solution to be a generic one.
I hope that you agree that the behaviour of your allocation handler and generic template "Smart pointers" shall be the same in the case of exception handling (when the exception is raised in the constructor).
 
~Scope()
      {
         if(ptr) {   //your new check
             if(is_obj) ptr->~Obj(); // if we stored object call it's destructor first
             free(ptr);
         }
         delete prev;
         if(lock) delete lock;
      }
 

I will try to explain that in detail and it's up to you to consider my comment or not. So, I will show you the problem with a little bit modified code of your example.
 
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <exception>
#include <memory>
#include "Scope.h"   
 
TScope global; // this one is threadsafe and deallocates all associated objects on program exit no matter 
               // what exception or error in program happens

struct A1 {  // objects must have virtual destructor
    A1()
    {
        printf("\n  A1 %x",this);
    }
 
    virtual ~A1()
    {
        printf("\n ~A1 %x",this);
    }
};
 
struct A2 {  // objects must have virtual destructor
    A2()
    {
        printf("\n  A2 %x",this);
    }
 
    virtual ~A2()
    {
        printf("\n ~A2 %x",this);
    }
};
 
struct A3 {  // objects must have virtual destructor
    A3()
    {
        throw std::bad_alloc();
        printf("\n  A3 %x",this);
    }
 
    virtual ~A3()
    {
        printf("\n ~A3 %x",this);
    }
};
 

void Test() {
    try
    {
        Scope local; // all objects associated with scope will be deallocated by leaving procedure
        /*
        A1* pA1 = new (local) A1;
        A2* pA2 = new (local) A2;
        A3* pA3 = new (local) A3;
        */
        std::tr1::shared_ptr<A1> spA1(new A1);
        std::tr1::shared_ptr<A2> spA2(new A2);
        std::tr1::shared_ptr<A3> spA3(new A3);
        
    }
    catch (...)
    {
        printf("\n exception");
    }
}
 
void main() {
    Test();
}
 
At first let me comment out your allocation handlers and use tr1 version of shared_ptr and just simply run the test. The output is following
 
A1 635f68
A2 6322d0
~A2 6322d0
~A1 635f68
exception
 
what exactly corresponds to the standard (when an exception happens during a object construction - in our case the struct A3 - the memory is deallocated and no destructor of the corresponding class is called). I hope, that this simple explanation is clear (not only for you, but also to other members of codeproject).
 
Now I come to your allocation handler. Let me comment out shared pointers and use your allocation handlers.
 
void Test() {
    try
    {
        Scope local; // all objects associated with scope will be deallocated by leaving procedure
 
        A1* pA1 = new (local) A1;
        A2* pA2 = new (local) A2;
        A3* pA3 = new (local) A3;
        /*
        std::tr1::shared_ptr<A1> spA1(new A1);
        std::tr1::shared_ptr<A2> spA2(new A2);
        std::tr1::shared_ptr<A3> spA3(new A3);
        */
 
    }
    catch (...)
    {
        printf("\n exception");
    }
}
 
The output after this modification is unfortunately different:
 
A1 625f68
A2 625fa8
~A3 622320 //ops, why this is here?
~A2 625fa8
~A1 625f68
exception
 
You can see that the destructor of A3 structure is called, which is against the standard. The reason of this behaviour can be found out in your operator delete, which is called immediately after a throwing inside A3 constructor.
 
inline void   __cdecl operator delete(void* ptr,Scope& scope)
{
    scope.del(ptr);
    ((Scope::Obj*)ptr)->~Obj();  //here you call the destructor on object which has not been constructed; unfortunately ptr is not null to indicate that A3 object was not constructed.  
    free(ptr);
}
 
You can argue, that my example code has a scope object inside the try block. Ok, let me modify the code of the test function in the way how you suggested in your article.
 
void Test() {
    Scope local; // all objects associated with scope will be deallocated by leaving procedure
    try
    {
        A1* pA1 = new (local) A1;
        A2* pA2 = new (local) A2;
        A3* pA3 = new (local) A3;
        /*        
        std::tr1::shared_ptr<A1> spA1(new A1);
        std::tr1::shared_ptr<A2> spA2(new A2);
        std::tr1::shared_ptr<A3> spA3(new A3);
        */
        
    }
    catch (...)
    {
        printf("\n exception");
    }
}
 
Unfortunately the output after this modification is almost the same.
 
A1 2d5f68
A2 2d5fa8
~A3 2d2320 //ops, the A3 destructor is still called
exception
~A2 2d5fa8
~A1 2d5f68
 

Let me make a conclusion. If you want community to accept your solution, you have to provide the same exception handling in all aspects like generic smart pointers do. Anyway I can say, that your approach is an interesting study case, and personally I'm curious how far you can get with this simple design. I always like when someone brings an unconventional ideas in C++, because it can be a way to improve our skills. Good luck and surprise me.

modified 24 Oct '12 - 8:01.

GeneralRe: What's about an exception handling?memberLadislav Nevery24 Oct '12 - 22:58 
Well the solution is simple. If you wanna have standard behavior (ie no destructor called) Use derive statement as advised in article.
 
A1 2d5f68
A2 2d5fa8
exception
~A2 2d5fa8
~A1 2d5f68 
 
The example in zip was without it to show that cleanup works also without derive.
 
This part of the article needs to be improved. I will update samples and article right after this post
 
I guess I should remove the no derive alternative altogether and don't even mention it.
It will lead only to problems. Ie some newcomer on project can easily reorder virtual methods etc.
 
So. Thx for this important observation.
You are helping to make article better and code more usefull to people.
And I highly appreciate it.
Wink | ;)
"There is always a better way"

GeneralRe: What's about an exception handling?memberkonik1025 Oct '12 - 1:20 
When you restrict that a type has to be derived from Scope::Obj class, the following text from your article can be modified:
 
"Since we want Scope to cleanup everything for us no matter what exception happens we must keep scope declaration outside try catch statement (not within)."
 
There is no limitation where the Scope is placed in function. It may be placed also inside a try block. In such case we can get even a better order of objects' destructor. The same like for smart pointers.
 
I have tested only two cases:
- exception occurs after creation of A2
- exception occurs during construction of A3
For both I got the same output.
 
void Test() {
    try
    {
        Scope local;
        
        A1* pA1 = new (local) A1;  //A1 derives (public/private) from Scope::Obj
        A2* pA2 = new (local) A2;  //A2 derives (public/private) from Scope::Obj 
        //throw std::exception();
        A3* pA3 = new (local) A3; //A3 derives (public/private) from Scope::Obj
    }
    catch (...)
    {
        printf("\n exception");
    }
}
 
Output:
A1 785f68
A2 785fa8
~A2 785fa8
~A1 785f68
exception
GeneralRe: What's about an exception handling?memberLadislav Nevery25 Oct '12 - 4:41 
Yup.
postpone cleanup of objects that didn't fail after exception handler can be useful.
Store valid results. etc.
 
I changed mentioned article text and added your observation as preffered way ( Scope within try )while explaining alternatives.
 
Hope it's better
"There is always a better way"

GeneralRe: What's about an exception handling? [modified]memberkonik1025 Oct '12 - 5:43 
Just only one interesting tip, one question and provocative case:
 
If you want to little bit improve the performance of locked version (no guaranty, it has to be tested) of Scope, have a look at this page (Benaphore version for Win32):
 
http://preshing.com/20120226/roll-your-own-lightweight-mutex[^]
 
Did you already think about the array version of new and delete operators? To fulfil for example following case:
 
{
Scope local;
A1* aA1 = new A1[10];
delete[] aA1;
 
A1* slA1 = new (local) A1[10];
}
 
And some devil case from theory:
The new operator cannot be used to allocate a function, but it can be used to allocate pointers to functions. The following example allocates and then frees an array of seven pointers to functions that return integers.
 
int (**p) () = new (int (*[7]) ());
delete [] p;
 
Of course, this case is really out of scope of your article and limits of your solution (the required inheritance from Scope::Obj class). However, someone can replace a pointer to function with the pointer to a function object derived from your Scope::Obj class. Afterwards the only missing part is new implementation of operator new[] and operator delete[].

modified 25 Oct '12 - 14:03.

GeneralRe: What's about an exception handling?memberLadislav Nevery25 Oct '12 - 12:50 
Wow very interesting lock optimization. I love how this skips expensive trip to kernel in singlethreaded usage cases Smile | :) I definitely plan some benchmarks comparing to smart pointers so this can get interesting.
 
Yes I am planning adding subscript versions and realloc that i forgot before soon too. But I need to replace linked list from c version first to remove those allocs and add preallocate
"There is always a better way"

QuestionException / Thread safety?memberPetro Protsyk8 Oct '12 - 1:20 
What about exception / thread safety of your Scope implementation?
AnswerRe: Exception / Thread safety?memberLadislav Nevery8 Oct '12 - 7:54 
There is thread safe version TScope in Scope.h if you need it.
"There is always a better way"

GeneralMy vote of 2memberPatrick Niedzielski7 Oct '12 - 5:38 
The biggest killer for me is that you are imposing inheritance on all classes that I want to use this. In many cases, I don't have the ability to modify the classes I'm going to use (i.e., external library classes). A smart pointer is a truly generic solution that isn't alien syntax, because it's so commonly used. shared_ptr has been in use for a decade in production code. It is a much cleaner solution.
GeneralRe: My vote of 2 [modified]memberLadislav Nevery7 Oct '12 - 10:26 
Everytime I get downvote I try to findout what was the reason so article can be improved.
 
The term "generic" in your claim of boost shared_ptr being generic solution for decade.
 
In C land smart-pointers simply doesn't offer any (nor "generic") solution at all and pretty much all lover level code like os/drivers are writen in it.
But C variant of scopes.h offers solution without need to inherit anything.
 
And in c++ If you can and wana rewrite any project by wrapping all ocurences of pointers and code using them to use stl/boost/whatever templates then yes you can call it "generic" solution.
 
If you can't than it is not "generic" solution for c++ as you said for decade.

There is large amount of source code around you that you decided to not see. Mainly os sources/drivers/embeded/or any c/c++ lib /c code.
 

What I don't understand looking in your profile thou is
how come that your account was created in year 2010
and zero everything just number of messages 1.
just to post this one downvote message.
 
Is this some new trend to pick one time use accounts to downvote ?
"There is always a better way"


modified 7 Oct '12 - 17:33.

GeneralRe: My vote of 2memberPatrick Niedzielski7 Oct '12 - 10:30 
You're funny. You're taking every criticism of your code as an attack against you. Oh well. Peace out dude. Also, before you decide to argue with someone over what generic code is, please do the research to find out that it has a very precising definition when used in relation to C++ code that your response shows you don't really understand.
GeneralMy vote of 5membernv33 Oct '12 - 22:03 
Your idea would gain some more charm if you took the storage allocation from the stack instead of the heap. Microsoft uses the _alloca function to do that. This would combine the performance advantages of allocating from the stack with your principal idea.
 
Although I have to agree with most of the arguments in the "vote 2" thread, I voted 5 as kind of a compensation.
GeneralMy vote of 2memberPhilippe Mori2 Oct '12 - 12:24 
A good exercise but no real value as smart pointers are a better alternative starting with the fact that they don't require a virtual desctructor and also because they don't bring any undefined behavior.
GeneralRe: My vote of 2 [modified]memberLadislav Nevery2 Oct '12 - 13:26 
Philippe Mori wrote:
no real value

 
Hmm let's see .
 
Stl in current standard don't have any alternative to code I provided since auto_ptr can't be stored to containers. Ie you can't have automatically cleaned up array of dynamic objects as in article. This situation will take a looots of time until new versions of ide along with new versions of compillers become more widespread since only the new standard in top of the notch compillers supports unique_ptr that still don't work in containers (custom deleter). Penetration of boost with shared_ptr is even smaller then stl.
 
Requiring virtual destructor or deriving from Scope::Obj is no problem. Objects are yours afterall.
 
requiring boost/stl for simple pointer and not being able to do usable libs or dll's anymore problem is.
 
Plus what is your solution for situations where you can't use stl/boost? embedded/games/legacy/libs? Still no value ?
 
Now what you call undefined behavior is actually defined behavior known as preserving order of methods in vtable that's why you either derive from Scope::Obj or have first virtual method destructor the result is the same.
 
To sumarise deriving from Scope::Obj is very small price for what you gain and there is nothing undefined or questionable about it. And as for value it clearly has value in situations where you can't or don't wana use boost.
"There is always a better way"


modified 3 Oct '12 - 5:36.

GeneralRe: My vote of 2 [modified]memberPhilippe Mori2 Oct '12 - 13:56 
If you cannot uses shared_ptr, then you can define your own simplified scoped_ptr as this:
 
template <typename T> class scoped_ptr {
public:
  scoped_ptr(T *p_) : p(p_) { }
  ~scoped_ptr() { delete static_cast<T *>(p); /* Ensure T fully known */ }
private:
  scoped_ptr(const scoped_ptr &); // Prevent copy
  scoped_ptr &operator=(const scoped_ptr &); // Prevent copy
  T *p;
};
  
 
and use like that:
 
{
  A *a = new A;
  scoped_ptr<A> xa(a);
  a->do_something();
} // a automatically deleted here by xa object.
 
By the way,
Philippe Mori


modified 2 Oct '12 - 20:48.

GeneralRe: My vote of 2 [modified]memberLadislav Nevery2 Oct '12 - 14:15 
Yes thats one solution too. Althou scoped_ptr is not very good choice since you will later actually use the objects even within scope.
 
A auto_ptr is a pointer with copy and with move semantics and ownership (=auto-delete).
A unique_ptr is a auto_ptr without copy but with move semantics.
A scoped_ptr is a auto_ptr without copy and without move semantics.
 

Whenever you want to explicitely have move semantics, use a unique_ptr.
Whenever you want to explicitely disallow move semantics, use a scoped_ptr.
All pointers allow swap semantics, like p.swap(q). To disallow those, use any const …_ptr.
 
There are situations, where you want to use a scoped_ptr pointing to one of several interchangeable objects: Because of the absence of move semantics, it is quite safe (in respect to obvious bugs) that it will not accidentally point to null because of an unintended move. Worth to mention: scoped_ptr‍s can still be swap‍ped efficiently. To make it movable and/or copyable – but still with these swap semantics – you might want to consider using a shared_ptr pointing to a scoped_ptr pointing to an exchangeable (via scoped_ptr::swap) object.
 
If you wana have everything wrapped in nondebuggable templates and make your code hell lot less unerstandable and readable by lenghty template statements everywhere not mentioning performance. because you are replacing binops with calls everywhere.
 
While it's always good to know all alternatives with pluses and minuses.
 
bugs and lost performance(no move in your scoped_ptr) arise from copy paste of templates
without having and idea what code you created actially does.
"There is always a better way"


modified 3 Oct '12 - 6:04.

GeneralRe: My vote of 2memberJackDingler3 Oct '12 - 6:42 
The latest VS debuggers make viewing templated classes easy. This was an issue VS 6.0, but the later versions, give us a full view of the object contained in the template.
 
Template calls are inlined whenever possible. I know not of these performance issues you speak of.
 
If you don't know what the code you create does, then you have other problems.
GeneralRe: My vote of 2memberPhilippe Mori3 Oct '12 - 8:59 
My code is a minimal sample that I have written in a few minutes mainly to show a minimal scoped pointer that could be used instead of a Scope if someone doesn't want to uses those from STL or boost.
 
For array, options are more discutables. It depends on the size of objects and the number of items and operations that will be done. I would typically use an array of object whenever it make sense...
 
I think that in case where you would need a Scope object (large array of pointers), I would probably put the algorithm in a class itstead of the function and then the destructor of the class would do the actual cleanup.
 
To be honest most of my development is done in C# now and almost all my C++ code has been converted so that it compile with /clr:safe. I have done a lot of C++ coding in the past and I was knowing the language quite good but I am not up-to-date with C++ 11.
Philippe Mori

GeneralRe: My vote of 2memberDewey2 Oct '12 - 20:33 
Ok, just admit that smart pointers are the better way to go, and call it a day.
 
Philippe Mori is right, although I wouldn't have given you a 2, just to make the point.
GeneralRe: My vote of 2memberLadislav Nevery2 Oct '12 - 21:57 
Thx Smile | :)
"There is always a better way"

GeneralRe: My vote of 2memberPhilippe Mori3 Oct '12 - 2:03 
Well, the main reason that I gave that score is because I believe that the article is misleading. In particular, in the case of "scoped" pointer, almost any existing smart pointer can be used even your own like the sample I gave in another answer.
 
For example, the article mention that auto_ptr cannot be used in containers. While it is true, using a Scope for that is not an elegant solution as it severily limits how the container can be used (replacing and removing objects with or without deletions).
 
Also such Scope class encourage poorly designed code where a lot of pointer are used. Why not use objects on the stack whenever possible and have a class for the algorithm itself if it is complex and need a lot of pointers?
 
Finally, your way of doing things goes against the standard way of doing things thus a C++ expert will have to figure out what your class do to understand your code while he already knows standard pointers classes like shared_ptr.
 
By the way, even though old compilers does not support move semantic, you can do quite a lot with them. Just some operation will be a bit less efficient but so is your Scope object.
Philippe Mori

GeneralRe: My vote of 2memberLadislav Nevery3 Oct '12 - 3:43 
Philippe Mori wrote:
Well, the main reason that I gave that score is because I believe that the article is misleading. In particular, in the case of "scoped" pointer, almost any existing smart pointer can be used

 
If you know anything about smart-pointers you would had known that pretty much all stl smart-pointers and there is lot of them are not usable in array I described in article. Tho only ones usable are just two in boost or c++ 11 that majority not yet use having valid licenses for old ides.
 
So much for your reason of voting 2
 
Philippe Mori wrote:
Scope class encourage poorly designed code.where a lot of pointer are used.

 
Since when is "a lot of pointers" poorly designed code.
there is no faster alternative for sorting 1000 pointers to objects as in example.
Go ahead sort 1000 objects. There are no rules just guides. Ie you always need to use your brain not dogmas.
 
The Scope paradigm. Ie automatic cleanup when going out of scope is exacty the same concept you mark "encourages poorly designed code" when I use it while evangelizing it when you propose the same thing like in this yours sentence. Ie you contradict yourself.
 
Philippe Mori wrote:
Why not use objects on the stack whenever possible

 
From your answers you seem to lack basic understanding of performance advantage of pointers hate scopes in one case while promoting them in another.
 
You lack basic understanding of security too. Doing objects and especially arrays of them on stack is
 
A) slower then on heap and severely restricted. Especially read about stack using VirtualAlloc
B) big security hole. stack is data interleaved with controlflow. Go read something about buffer overflows and main source of exploits.
 
You missed the whole purpose of comunity is helping each other not destroyng other peoples hard work for no rational reasons.
 
That being said you pretty much remind me of this tall guy
"There is always a better way"

GeneralRe: My vote of 2memberPhilippe Mori3 Oct '12 - 4:32 
I don't hate scope. I just hate the idea of being dependent on an undefined behavior (the cast to call a virtual destructor) and the fact your Scope object manage object lifetime independantly of the objects themselves which is error prone when not all objects are restricted to local scope.
 
That is when you use a shared_ptr, the object is responsible for a single pointer and you can reset it, transfer it, replace it... On the other hand, when using a Scope, objects are automatically added to the scope by operator new (and in fact, if there is an exception you also need placement delete to ensure that the pointer is removed from the Scope object to avoid double deletion).
 
I haven't consider the security aspect as in my case I develop specialized applications that should not be a target for those attacks and I almost always use std::vector and such as containers so I should not be too much affected by problems like buffer overflow (as the point to memory somewhere else).
Philippe Mori

GeneralRe: My vote of 2memberLadislav Nevery3 Oct '12 - 5:08 
Philippe Mori wrote:
I just hate the idea of being dependent on an undefined behavior (the cast to call a virtual destructor)

 
Now enlighten me. How is deriving from Scope::Obj "undefined". One simple answer.
 
And if the answer is "no it is not undefined"
 
Then you can enlighten me again about what makes you "being dependent" on virtual destructor when whole article is clearly stated that deriving from Scope::Obj is another simple option.
 

Philippe Mori wrote:
and the fact your Scope object manage object lifetime independantly of the objects themselves which

 
Yes Scope manages lifetime of objects exactly like C++ internal scope manages lifetime of static objects. Again you contradict yourself and keep running in circles.
 

Philippe Mori wrote:
is error prone when not all objects are restricted to local scope.

Scope manages only objects that are within it's scope exactly like internal c++ scope no change there either. ie nothing is more eeror prone or less error prone than with standard c++ scope.
 

Philippe Mori wrote:
(and in fact, if there is an exception you also need placement delete

 
No you dont. At least try to read the code you are atempting to bash. Destructors of scope is called and objects are simply deleted.
 

Philippe Mori wrote:
develop specialized applications that should not be a target for those attacks

 
promoting unsafe design patterns because you rightnow doesn't seem to be target of attacks is just wrong.
 
Philippe Mori wrote:
and I almost always use std::vector and such as containers so I should not be too much affected by problems like buffer overflow

 
Man... Did you meant that sentence seriously ? ;D
"There is always a better way"

GeneralRe: My vote of 2memberPhilippe Mori3 Oct '12 - 6:13 
For sure, there are things that you say I don't understand and they are thing that I say that you don't understand...
 
Effectively, deriving from Scope::Obj is not undefined behavior but your design don't enforce it as far as I understand. You would have to override new operator for Scope::Obj instead so that that overload cannot be used with object that does not derives from it.
 
After some testing, it seems that placement delete is not required in your case (and in fact I haven't figure out how to implement it) because your scope object will handle it.
 
http://en.wikipedia.org/wiki/Placement_syntax[^]
 
In simple cases and when uses as intended your Scope object will works... but in a big project it might be difficult to ensure it always properly used. One case that is better handled by C++ 11 is the case where some of the objects that were created should be transfered elsewhere. As long as objects are only added and systematically need to be deleted at the end of the scope, it should works.
Philippe Mori

GeneralRe: My vote of 2memberLadislav Nevery3 Oct '12 - 14:02 
Philippe Mori wrote:
For sure, there are things that you say I don't understand and they are thing that I say that you don't understand...

 
Probably yes.;)
 

Philippe Mori wrote:
You would have to override new operator for Scope::Obj instead so that that overload cannot be used with object that does not derives from it.

 
That's probably one of them. But hey it's too late here anyway ;D
 

Philippe Mori wrote:
After some testing, it seems that placement delete is not required in your case (and in fact I haven't figure out how to implement it) because your scope object will handle it.

 

 
You can find placement delete in scope.h
it's used in rare cases(scopes are usually small) when you wana delete objects before leaving scope(ie you delete and remove them from scope linked list) so leaving scope will not attempt to deallocate them.
 

Philippe Mori wrote:
One case that is better handled by C++ 11 is the case where some of the objects that were created should be transfered elsewhere.

 
Adding scope to class will release object resources only when object is explicitly deleted. ie allow object to be passed around as much as you like and destroying it only when needed for
example you pick task object someone else pushed to work queue. process task object and call delete on it. Internally all pointer related resources associated with internal object scope will get destroyed/freed as it would be the case with object having smartpointers as members.
 
That being said I don't enforce it on anyone.
If you like to use templates why not. use them.
 
This is just alternative solution.
Take care Wink | ;)
"There is always a better way"

GeneralRe: My vote of 2memberJackDingler3 Oct '12 - 7:05 
How is your work destroyed?
 
It's being criticized. If you decide to make software engineering your profession, then this is something you need to become comfortable with. You'll enjoy feedback from your peers for the rest of your career.
 
// Declarations
typedef std::shared_ptr tdMyObjectPtr;
std::map MyObjectMap;
 
// Add a sorted element... you can do this millions of times...
tdMyObjectPtr MyObjectPtr(new CMyObject);
MyObjectMap.insert(std::pair(CMyKey(MyObjectPtr), MyObjectPtr);
 
Shared pointers do have some overhead. I haven't found a situation on modern computer systems where the overhead created performance issues. The time saved in writing code makes it extremely worthwhile. I know exactly what this code does.
 
When you need breakneck speed, you'll avoid the heap the operations altogether, and work with fixed size buffers or static objects.
 
You do know that the heap is allocated the same way as the stack, right? Pages are not allocated until needed. Memory that you haven't allocated is virtualized. It isn't accessible. When a pointer in your code access a valid memory location that is on an unallocated page, the OS traps the exception, and then swaps a page of memory into that space. the heap works as a linked list and thus requires additional operations for housekeeping that are not needed on the stack. So you have it backwards, the stack is faster.
 
Yes the stack is used for exploits, but in this case you're talking about buffer overruns as an exploit. Simply creating objects on the stack does create a security hole. The exploits occur when you write memory past the bounds of an object.
GeneralRe: My vote of 2memberLadislav Nevery3 Oct '12 - 9:33 
JackDingler wrote:
How is your work destroyed?

 

 

criticized is when you do something wrong and an argument is being raised.
downvoting article for claims that are simply not true is not being criticized.
 
JackDingler wrote:
MyObjectMap.insert(std::pair(CMyKey(MyObjectPtr), MyObjectPtr);


if you need to sort known big dataset then
hashtable you created is inferior from performance point of view from simple preallocated pointer array. not mentioning memory and performance overhead.
 

JackDingler wrote:
When you need breakneck speed, you'll avoid the heap the operations altogether, and work with fixed size buffers or static objects.

 
Please notice how much time it took to allocate static array in This benchmark and how much dynamic array. Spend some time contemplating internally why
 

JackDingler wrote:
You do know that the heap is allocated the same way as the stack, right?

 
Yes I do. Every thread have small chunk of physical memory mapped to virtual address space by processor. ie separate Global descriptor table entry of protected mode.
Why separate ? so separate page granularity can be set and that it can grow downwards.
 
Now if you know how mapping of physical to virtual works you know how expensive operation this.
Thats why concept of heap was actually created. when this mapping is so expensive wy map large chunk upfront and just redistribute large chunks of virtual memory keeping track of them in linked lists.
 
Stack doesn't get preallocated on end of stack is one page marked with trap flag. causing very expensive switch from ring 3 to ring 0 and back often covering 30 calls covering sybnchronization delays or more just to allocate stupidly small 4k page similar to virtualalloc. Ie it happens all the time. There is nothing like virtualized memory to mentioned . there is either reserve or comit. by reserving you merely reserve range of addreses that system is giwing away to random threads of process address space. so you can allocate contiguos large chunk by multiple allocations in future. it will not make allocations in future faster only possible Wink | ;)
 
You see now why stack is and allways will be slow and heap fast? It's like bringing expencive truck of food to city from a far by one piece per track or bringing or at large chunks less frequently and redistributing them locally fast upon request from shops.
 
There is meny forms of exploits. using stack is most popular one because of static arrays and sloppy programing practices like "nah nobody will exploit my code"
 

JackDingler wrote:
Simply creating objects on the stack does create a security hole.

 
Yes it does.
 
JackDingler wrote:
but in this case you're talking about buffer overruns as an exploit.

 
There is many ways direct exploits indirect exploits logic exploits rights elevations heap sraying exploits.
 
Placing data you receive from net on static object in stack where you have control flow in language like c++ with no bounds checking you are pretty much reason why antivirus companies exist
 
Siple rule is just don't store data on stack along with controlflow.
It's slow and dangerous.
"There is always a better way"

GeneralRe: My vote of 2memberJackDingler3 Oct '12 - 9:44 
Just because memory locations are chained in the heap does not mean you don't have the same Ring 0 and Ring 3 bounce, when new pages are allocated. Whom ever told you that heap memory is allocated in a fundamentally different way than stack memory, taught you wrong.
 
And I'll bite, how do you write stackless code in C++?
 
Your scope objects are on the stack, as you point out, that makes them a gaping security hole.
 
You're being criticized because you are wrong.
GeneralRe: My vote of 2memberLadislav Nevery3 Oct '12 - 10:10 
JackDingler wrote:
Just because memory locations are chained in the heap does not mean you don't have the same Ring 0 and Ring 3 bounce

 
No you dont' have this bounce after every page ;D
Thats the whole point of heap memory -> speed
 

JackDingler wrote:
Whom ever told you that heap memory is allocated in a fundamentally different way than stack memory, taught you wrong.

 
Go ahead and study linux and windows sources you would be surprised Wink | ;)
 

JackDingler wrote:
And I'll bite, how do you write stackless code in C++?

 
no po point is no stackless program Wink | ;) but not to preffer slower and unsafe storage place to store and work with data and arrays especially.
 
Just because somebody will clean after us. With scopes for heap you have now the same autocleanup advantages and bigger performance + bigger safety.
I said bigger. Because you know there are heap exploits too. but those are several orders harder to do and stem mostly from badly written code doing manual cleanup deallocating comething twice overwriting valid memory. "the mentioned heap linked list" etc-> heap spraying.
 
Another reason to have automatic cleanup for heap.
Wether you prefer templates or not all alternatives need to be known and being worked on so we all have bigger amount of choices.
"There is always a better way"

GeneralRe: My vote of 2memberJackDingler3 Oct '12 - 10:56 
Ladislav Nevery wrote:
No you dont' have this bounce after every page ;D

Thats the whole point of heap memory -> speed

 
Where do you think the 4k pages that make up the heap is mapped on come from?
 
If you watch memory usage for a process, you'll see that they aren't all using 2gb each on a 32 bit processor. This is because pages aren't allocated until they are needed by the heap.
 
When you allocate memory from the heap (current models), it will attempt to find freed memory that will satisfy the requirement. If this isn't possible, then the heap will be expanded to satisfy the requirement. To do so, a ring 0 trap is triggered on the memory location, and the kernel will map memory into the processes heap space.
 
When this occurs, you'll see the memory allocated to the process grow.
 
If the memory in this page is later freed, then the page is returned and a ring 0 event is called to remove the page.
 
When this occurs, you'll see the memory allocated to the process shrink.
 
Expansion and shrinking of the heap and stack work from the same mechanisms.
 
I think you're confusing heap allocation with page management. The heap tracks memory in two linked lists. One linked list tracks allocated memory and another tracks unallocated memory. This mechanism rides on top of the OS's paging mechanism.
 
If you were right that heap memory doesn't get paged in and out, then there would be no page file and every process would have a fixed and unchanging quantity of memory. You'd have to set the heap size at compile time, like we used to in the old days before virtual memory was available on PCs.
GeneralRe: My vote of 2memberLadislav Nevery3 Oct '12 - 12:33 
JackDingler wrote:
Ladislav Nevery wrote:
No you dont' have this bounce after every page ;D


Thats the whole point of heap memory -> speed


 

Where do you think the 4k pages that make up the heap is mapped on come from?

 
Point of the sentence was that it's not mapped in smallest possible chunks = 4k but usually 1-8mb or more. thus loose overhead and gain speed
 
JackDingler wrote:
If you watch memory usage for a process, you'll see that they aren't all using 2gb each on a 32 bit processor.

 
using 2gb of what ? physical? virtual? Where did I said that ?
 
JackDingler wrote:
This is because pages aren't allocated until they are needed by the heap.

 
That is simply not true.
the moment the process runs even before entry point is reached usually 8 mb of heap =physical memory is mapped(reserve+commit) to virtual address space of every process. That's a lot of pages allocated before they are needed. if you put this in your linker settings /HEAP:1500,1500
and run you run your exe your exe will allocate 1.5 gb heap instead of default 8mb one. ie it will have extremely fast malloc for obvious price off course.
 
Quite opposite of what you claim. You see? The speed in malloc or new is gained by eating from this already allocated memory ie no slow physical mapping page after page or ring 0 roundtrip and back. You just make logical hole in your linked list noting down which chunk is in use. making this preallocated chunk fragmented. the whole physical mapping (VirtualAlloc) kicks in again when you exhaust whole 8mb (or whatever you set in linker settinks for exe) and for obvious performance reasons another big chunk is allocated at once(ie no 4k page like in stack case)
 
From cpu clock point of view it is rare event and pretty much happens every million years.
 

JackDingler wrote:
If the memory in this page is later freed, then the page is returned and a ring 0 event is called to remove the page.

 
when you call free() / delete for usuall small chunks in c++ ie heap functions. no ring 0 event returns any page.
 
It just grows. Why? because of performance lost by unmapping/mapping it's simply not worth it.
only when your process workingset actually grows too big and it runs out of
physical memory to map into your process.
 
Only then It will simply page physical pages to pagefile and unmap it from virtuall memory. but when you touch them he maps them back trying to page out some other pages with older Touch date timestamps.
 

JackDingler wrote:
Expansion and shrinking of the heap and stack work from the same mechanisms.

 

No. stack never shrinks. Heap pretty much never shrinks too.
Ie it is very conservetive when it comes to shrinking. Since it expects alloc will follow free. so doing expensive unmap/map is delayed as much as possible.
Small memory allocations that are free'ed to the heap are usually placed into a list that is used for fast allocations.
 
Even without this optimization, the heap mamager is free to hold into the heap bucket from which the allocation was made. In order for memory to be returned to the system (VirtualFree'ed) all blocks in a 64KB block must be free'ed and combined by the heap manager.
 
And as for growing. Heap grows by malloc/new/etc. stack grows by triggering guard pages at their end and only in very limited range. usually less then few mb.
 
JackDingler wrote:
I think you're confusing heap allocation with page management. The heap tracks memory in two linked lists. One linked list tracks allocated memory and another tracks unallocated memory. This mechanism rides on top of the OS's paging mechanism.

 
I will try to explain this again
If you map physical pages in "one page" requests to kernel and back like stack grow mechanism does. it will be slow to allocate anything bigger than 4k on stack becaouse all those trips to ring 0 add together as delay that accumulates with every such trip.
 
It's much more efficient to just allocate contiguous 8mb worth of pages by one trip to ring 0 as heap manager on exe start does and don't call ring 0 or mapping until it's exhausted.
 
Did you finally understood why is heap faster then stack and not oposite as you claim?
If yo still don't think so then explaint those numbers.
Why is allocating same sized static array slower then on heap.
 
www.codeproject.com/Articles/453022/The-new-Cplusplus-11-rvalue-reference-and-why-you#benchmark_results
 

JackDingler wrote:
If you were right that heap memory doesn't get paged in and out,

 
Where did I said that ?
 
JackDingler wrote:
You'd have to set the heap size at compile time, like we used to in the old days before virtual memory was available on PCs.

 
No go and check this linker setting /HEAP:reserve,commit does Big Grin | :-D
Every exe on windows linux etc. In windows case there is field in PE file containing how big the should the process heap be before entering entry point.
"There is always a better way"

GeneralRe: My vote of 2memberPhilippe Mori2 Oct '12 - 14:09 
Your solution require discipline as the compiler cannot ensure that all required points are met (like having a virtual destructor).
 
I think it will also broke with multiple inheritances and it is fragile. For example, someone might remove the destructor to improve the efficiency of a class when refactoring code and then boom the application crash.
 
By using your solution, an application become less type-safe as the compiler will not be able to catch errors or do appropriate action as it would be the case with shared_ptr for example.
Philippe Mori

GeneralRe: My vote of 2memberLadislav Nevery2 Oct '12 - 14:26 
Philippe Mori wrote:
Your solution require discipline

 
What code doesn't.
 
breaking stuff is part of refactoring. I can't count how many times I found comments don't touch this or don't try to optimize. That being said nothing holds you from deriving if you have problem keeping comment why virtual destructor is first virtual method.
 
Now you are Saying stuff like fragile or will somehow break. Yet I don't see code examples.
"There is always a better way"

GeneralRe: My vote of 2memberPhilippe Mori3 Oct '12 - 2:09 
As much as I could, I try to rely of the compiler instead of the programmer.
 
For example, any smart pointer will properly handle classes without virtual desctructor thus it is one less thing that the programmer has to be aware of.
 
I think it help to make robust code that works correctly the first time when it is not possible to do it the wrong way. By the way, it can be very hard to debug if Scope is used and a virtual destructor is missing in a complex function as crashes are not always immediate...
Philippe Mori

GeneralRe: My vote of 2mvpEspen Harlinn3 Oct '12 - 2:10 
Ladislav Nevery wrote:
Stl in current standard don't have any alternative to code I provided since auto_ptr can't be stored to containers

 
FYI: std::shared_ptr<>[^] works very well with containers, while std::auto_ptr<>[^] has been deprecated.
Espen Harlinn
Principal Architect, Software - Goodtech Projects & Services AS

Projects promoting programming in "natural language" are intrinsically doomed to fail. Edsger W.Dijkstra

GeneralRe: My vote of 2memberLadislav Nevery3 Oct '12 - 4:00 
Hi Espen. Nice seeing you Big Grin | :-D .How you doing.
 
Yes it's stated in article along with "shared_ptr. requires c++ 11 ".
 
Most people work with compillers they have licenses for. Ie don't expect it's widespread usage anytime soon keeping it in "one day maybe today boost or our own solution" shelf.
the same goes for unique_ptr.
 
But what you thing about the build in smart pointer idea I play with in clang?
 
Object*~
 
It seems it would simplify and solve whole lot of things.
Please don't answer "solved on library level or I will jump out of window Big Grin | :-D "
"There is always a better way"

QuestionUndefined behaviormemberJohn Bandela2 Oct '12 - 5:22 
There are two instances of undefined behavior in the code snippets
 
1) You are allocating with malloc and using delete
2) You are casting an object to another type that is not void* and not a base class. Further you are calling a virtual function (virtual destructor) on that object.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130516.1 | Last Updated 3 Oct 2012
Article Copyright 2012 by Ladislav Nevery
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid