Multiple serial-dependent allocations

Started by
23 comments, last by Jan Wassenberg 18 years, 9 months ago
Quote:consider a C version of my code...


bool foo(){bool error = false;int* one = 0;int* two = 0;int* three = 0;int stage = 0;while (true){   one = new int[10];   if (!use_one(one)) {     error = true;     break;   }   stage++;   two = new int[10];   if (!use_two(two)) {      error = true;      break;   }   stage++;   three = new int[10];   is (!use_three(three)) {      error = true;      break;   }   stage++;   break;}switch (stage){   case 3: delete [] three;   case 2: delete [] two;   case 1: delete [] one;   default:}return error;}


Quote:if, for example, the second "if" condition is not satisfied, does not deallocate ptr1


Of course not; your auto-pointers are globals! Move them into function scope.
Advertisement
Quote:Original post by CoreMeltdown
Your solution
*** Source Snippet Removed ***

That wasn't my solution, that was swiftcoder's solution and it wouldn't work for arrays anyway. Look at my solution for the correct (C++) answer (as Deyja says, with the smart pointers in function scope). Globals are evil anyway.
Quote:Original post by swiftcoder
*** Source Snippet Removed ***
so when you call 'return', anywhere in your function, all of the auto_ptrs will go out of scope, and delete the object they point to.

Uhmn... No. They're globals (thus they don't go out of scope until the program ends).
Quote:Original post by Andrew Russell
Quote:Original post by CoreMeltdown
Your solution
*** Source Snippet Removed ***

That wasn't my solution, that was swiftcoder's solution and it wouldn't work for arrays anyway. Look at my solution for the correct (C++) answer (as Deyja says, with the smart pointers in function scope). Globals are evil anyway.


I'm really sorry... it wasn't your solution.

But anyway... your solution has local pointers...
Maybe I wasn't clear, I'll try to explain better, writing a dummy example in C:

typedef struct tagMyStruct {  SomeType1 * pT1;  /* ... */  SomeTypeN * pTN;} MyStruct;MyStruct * InitMyStruct(int dummySize) {  MyStruct * ms;  ms = (MyStruct *)malloc(sizeof(MyStruct));  if (ms == 0) {    return (0);  }  ms->pT1 = (SomeType1 *)malloc(dummySize * sizeof(SomeType1));  if (ms->pT1 == 0) {    free(ms);    return (0);  }  /* ... */;  /* ... */;  /* ... */;  ms->pTN = (SomeTypeN *)malloc(dummySize * sizeof(SomeTypeN));  if (ms->pTN == 0) {    /* ... */;    /* ... */;    /* ... */;    free(ms->pT1);    free(ms);    return (0);  }  return (ms);}
Code like this is one strong reason to switch to C++, but there are idioms to deal with it in C.
MyStruct * InitMyStruct(int dummySize) {  MyStruct * ms;  jmp_buf done;  int status;    status = setjmp(done);  if (status != 0) {    /* long jmp here to clear ms */    if (ms->pTN) free(ms->pTN);    /* ... */    /* ... */    /* ... */    if (ms->pT1) free(ms->pT1);    free(ms);    return NULL;  }  ms = (MyStruct *)malloc(sizeof(MyStruct));  if (ms == 0) return NULL;  memset(ms, 0, sizeof(ms));  ms->pT1 = (SomeType1 *)malloc(dummySize * sizeof(SomeType1));  if (ms->pT1 == 0) longjmp(done, -1);  /* ... */;  /* ... */;  /* ... */;  ms->pTN = (SomeTypeN *)malloc(dummySize * sizeof(SomeTypeN));  if (ms->pTN == 0) longjmp(done, -1);  return ms;}

If you're a hardcore SESE evangelist, you can fold the other return values into the longjmp block.
Quote:Original post by CoreMeltdown
I'm really sorry... it wasn't your solution.

But anyway... your solution has local pointers...
Maybe I wasn't clear, I'll try to explain better, writing a dummy example in C:

*** Source Snippet Removed ***


You still havn't been clear if you want C or C++. Here is the same thing in C++, not using local pointers this time:

struct MyStruct{    boost::shared_array<MyType> p1;    boost::shared_array<MyType> p2;    boost::shared_array<MyType> p3;};std::auto_ptr<MyStruct> MyFunction(size_t dummySize){    std::auto_ptr<MyStruct> ms(new MyStruct);    ms->p1(new MyType[dummySize]);    ms->p2(new MyType[dummySize]);    ms->p3(new MyType[dummySize]);    // if(!CheckOrUse(p3)) return std::auto_ptr<MyStruct>();    return ms;}

You may notice I've not checked the pointers for NULL (as you do in the code I'm working off). If they somehow fail to allocate (or a constructor fails), then it will throw an error (unless you are specificly writing with a non-throwing new).

If you're still doing the CheckOrUse function, I've left in an example use. Alternitivly you may want to catch the std::bad_alloc error and return a NULL auto pointer to match the behaviour of your old system if new fails. Alternitivly, you could have CheckOrUse throw an error if it fails (which matches the behaviour if new failed and could be nicer than returning NULL pointers).



Of course, you could go even more C++'ish, you could do it like the following. I've also left a sample CheckOrUse call in there. In this case, it must throw (being in a constructor). IMO this is the nicest version of the code.

class MyClass{    boost::shared_array<MyType> p1;    boost::shared_array<MyType> p2;    boost::shared_array<MyType> p3;public:    MyClass(size_t dummySize)    {        ms->p1(new MyType[dummySize]);        ms->p2(new MyType[dummySize]);        ms->p3(new MyType[dummySize]);        // if(!CheckOrUse(p3)) throw SomeErrorClass();    }};
Quote:Original post by Andrew Russell
... Chunk of code that returns an auto_ptr ...

Eh, don't do that.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Quote:Original post by Washu
Quote:Original post by Andrew Russell
... Chunk of code that returns an auto_ptr ...

Eh, don't do that.

Eh, why not? (see)

(note to CoreMeltdown: you could replace std::auto_ptr with boost::shared_ptr if you have problems)
Quote:Original post by Andrew Russell
Quote:Original post by Washu
Quote:Original post by Andrew Russell
... Chunk of code that returns an auto_ptr ...

Eh, don't do that.

Eh, why not? (see)

Yes, I know... I also know that not all compilers deal with that properly, hence why I typically avoid it (even though I haven't written anything for such a compiler in a while... but you never know when it will crop up in a production project).
Quote:
(note to CoreMeltdown: you could replace std::auto_ptr with boost::shared_ptr if you have problems)

Or if you want to store it in a container, or use it in any other way not mentioned in the article linked above.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

What good are these "move semantics" of auto_ptr anyway? Why didn't anyone think to include the equivalent but with "copy semantics" in the standard library as well (and perhaps an array version as well)? Perhaps policy-based design (like std::auto_ptr<typename T, typename AssignmentPolicy>) would be useful here?

This topic is closed to new replies.

Advertisement