Advertisement

Exceptions...

Started by April 28, 2000 07:39 AM
60 comments, last by null_pointer 24 years, 6 months ago
quote: Original post by msn12b

You''re designing the data model incorrectly; while in real life you need to know pretty much everything about a certain weapon before using it, but in a game, using an objectified approach, you can use abstractions to correctly model the solution. These abstractions don''t need to model real life at all. (Since we''re dealing w/ the C++ Standard here, take a look at how streams and iterators were adapted to coexist with each other.)

Weapons, in general, have several traits: they can be equipped, they deal damage, they have accuracy modifiers, and they alter the current appearance and reaction of the equipping character. All these traits can be expressed in a base class, with the derived classes providing specific functionality.


Wow! That''s a lot different than what I did! What I did was to put several traits (data members) in a base class (weapon), with the derived classes (gun, sword) providing specific functionality (pull_trigger, manage_ammo, swing, etc.). I''m glad you told me how to do it correctly! LOL


quote: Original post by msn12b

This is blatantly incorrect. Object oriented programming is not designed to model real life; it is a paradigm that treats the problem domain as a set of objects that interact with each other, where an object is a collection of data that can respond to messages. While objects tend to coincide with how people generally view the world, they do not have to model the real world AT ALL.

null, while your philosophical approach to software engineering is admirable, you have to realize that, in the end, it is engineering, not philosophy, and as such needs to be more strongly grounded in reality. Waxing philosophical is nice, but you have been missing out on the basic tenets of programming in general. Real life and software life are two completely different realms.


Um, yeah, get back to the real world and stop acting like software is meant to model the real world!

1) Software exists in real life, it exists to interact with other parts of real life, it is created by people in real life, and it has a real purpose. To work 100% correctly with every other part of real life, software needs to model real life. Saying we should model imaginary objects using improper abstraction (like your gun example) to make programs that simulate reality (and all games do this to a certain extent) will only create trouble down the road. It''s not open to expansion by any means other than breaking the class, which is not modular, and destroys encapsulation.

2) Stop and think about your gun model. You are moving things that should be in the character class (used with all weapons, like evade percent, armor value, etc.) to the weapon class. Try to write a character class and a weapon class to work together, to do the entire attack, and it either won''t work or you''ll have to use public data members (or simulate them with writing accessors). I''m sick of seeing classes that have so many pitfalls that they won''t work. Why don''t people just use what is there (reality), and then make classes from that? It''s stupid to adjust reality to your thinking. I''m sorry, but I haven''t found a place where modeling classes according to the real life objects they are expected (by the user of the class) to model has not turned out better.

Elighten me, O Great One! Grant unto me the divine creative power of irreality! The unbelievable organizational freedom of Chaos!! LOL

(Perhaps I misunderstood, no?)


quote: Original post by chippydip

I should have been clearer. I was thinking more along the lines of a painting, piece of music, or other art form. These all exist in some physical form, yet the evaluation of thier beauty it completely subjective. The same is true of computer code. It exists in a physical form, yet its interpretation as "readable" is purely subjective. The biggest difference between these two examples is that programmer''s views of readability are generally much closer than artists'' or musicians'' views of how aesthetic their respective arts are. Despite this pseudo-standard view of readability, there is still no definite, external, standard so readability still remains subjective.


No, it''s still a bad analogy. You just can''t be clear with over-simplified examples.

1) Art may exist in a physical form, but beauty does not. The only link between the physical form and the beauty is people. Beauty wouldn''t exist without people. (As pertains to my idea I don''t care how many people have to read it or what they think; I''m concerned what the compiler sees! How many times do I have to say this?

2) Code, unlike art, is not created to be viewed by other people. It is there to be interpreted by computers. It uses the English language as a mere convenience -- the standard was already implemented when coding languages came on the scene, so it was used. No matter what you say, subjective viewpoints have no effect on the compiler''s interpretation of the code.


quote: Original post by chippydip

Like I said, however, once you have this function, the probability that two "best" solutions exist is really a subjective evaluation. I think that it is quite possible and you think that its nearly impossible.


Why? I thought we were supposed to quantify them objectively?


quote: Original post by chippydip

Sort of. If everyone had a different idea of what made readable code then the idea would be truly useless. Programmers in general tend to have similar (but not identical) views of what makes readable code. This makes it useful to aspire to generally readable code, but there is no exterior standard which can be used to say that one coding style is more readable than another. The final judgement is subjective.


bzzt! Check out your reasoning here...rationalisms...rationalisms...rationalisms...

Reconcile the second sentence in that paragraph with the last sentence in that paragraph, and I may concede.


quote: Original post by Kylotan

You are using my labels to enforce definition again Just because you use a language construct only as it exactly matches the name, doesn''t mean I will, -especially- not when using English

...

If you expect me to be able to converse with you in an intelligent way, you -must- use the English standard. The efficiency of our communication is directly proportional to your compliance with the English standard.


If you expect me to be able to converse with you in an intelligent way, you -must- use the English standard. The efficiency of our communication is directly proportional to your compliance with the English standard.


quote: Original post by Kylotan

The alternative model, a single Exception class with a string parameter, was something I proposed for argument''s sake. You could give it 2 parameters, 1 for the function-name that threw the exception, and 1 for the error message. RTTI could give you the error message, given explicit and long-winded enough typenames, but it won''t give you the function in which it was thrown. So you need 1 parameter anyway, may as well have 2. If, in your design you never need to execute conditional code based on the nature of exceptions you catch, a hierarchy is overkill.


1) The function from which it was thrown? Function exception specifications.

2) I only said that my model was better than trying to do type-checking another way. I never said you couldn''t add data members.


quote: Original post by Kylotan

Although, is there anything to stop the use of a switch to call virtual functions (which may have to be overriden by further switches in user code)? Ugly.


How is that different from allocators?


quote: Original post by Kylotan

On that note, C++ lacks local functions. This could perhaps be considered a deficiency in the language. You can simulate them with private members, but then any other member could call that function when it makes no sense to do so. You could also simulate them with objects having an overloaded operator(), but that doesn''t seem right either.


Local classes.


quote: Original post by Kylotan

Interesting viewpoint... you say to use typechecking, which allows the compiler to do checks for us, rather than needing to use switch statements or string comparisons... and I agree. But garbage collection is built on a similar premise... tell the system when you need memory, and let it worry about checking whether it is still needed or not. The same goes for auto_ptr as mentioned in the other thread. It''s about standardising (your favourite process ) the access to a certain system, process or routine and minimising the interface to reduce the chance of programmer error. Having to remember an explicit ''delete'' to match each ''new'' is a source of many bugs and memory leaks. I would agree that if a C++ programmer moved to a garbage-collected language, then yes, they would introduce hidden bugs, but only because they don''t really appreciate how the allocation model works. Once you have it mastered in your head, it is cleaner. (Quicker/more efficient? That''s another story. Choose the right tool for the job. )


1) Actually, new()-ing and delete()-ing are easy operations. Add one in the constructor, the other in the destructor, and you''re set. You can also define the copy constructor and operator=() if you wish, or just make them private.

2) Your analogy is bad. Type-checking is a language feature. Garbage-collection is re-writing the new and delete operators. If the purpose or re-writing is not to provide an add-on (perhaps fail-safe wrappers when exceptions occur?) to the language, then it is stupid to use it. If garbage collection does nothing but increase the code size over corresponding new and delete operators, then it is truly useless.


quote: Original post by Kylotan

quote:
--------------------------------------------------------------------------------
This "design by contract" thing seems to be a concept that has been used by programmers for ages: responsibility. You can enforce it with exceptions, and type-checking, scope resolution, etc. There are so many tools in C++.
--------------------------------------------------------------------------------


*gasp* You''re not suggesting that a single feature can be implemented in numerous different ways, are you?


No, in this case the problem is very complex and requires several language features, each doing their particular task, and working together to accomplish one big task. I still don''t understand what advantage exceptions have over assertions, from the viewpoint that assertions are unusable across software layers.



- null_pointer
Sabre Multimedia
quote: Original post by Kylotan

See Why C++ Sucks for an alternative view.


I did -- LOL! It''s truly amazing how so many instances of bad logic can exist on one page on one subject by one person...

Anyway, I haven''t time to destroy the monster properly.



- null_pointer
Sabre Multimedia
Advertisement
quote: If you expect me to be able to converse with you in an intelligent way, you -must- use the English standard. The efficiency of our communication is directly proportional to your compliance with the English standard.


I was I was using the English definition of type. Unfortunately you were using the C++ definition of type which is a more stringent subset

quote:
1) The function from which it was thrown? Function exception specifications.

That doesn''t tell the receiving function where the exception came from, right? Only the limitations on what exceptions coulde possibly be thrown, which isn''t the same. Of course, I could be misunderstanding this.

And sadly, all that is largely irrelevant since Visual C++ doesn''t support the mechanism, and that is the industry ''standard'' (word used loosely...) compiler.

quote:
On that note, C++ lacks local functions. This could perhaps be considered a deficiency in the language.


Local classes.

I was under the impression that classes can only be defined in global or class scope. This means the whole containing class has access to the inner class, when I only need one function to have access to it. It''s no better than just using another private member function.

Of course, the same old complication arises anyway: "Function definitions are not permitted in local class declarations in Microsoft C++."

Now, local functions are rarely necessary, but they''d be nice to have. They are a tool that C++ simulates, rather than has intrinsically.


quote: 1) Actually, new()-ing and delete()-ing are easy operations. Add one in the constructor, the other in the destructor, and you''re set. You can also define the copy constructor and operator=() if you wish, or just make them private.

What about allocating variable sized buffers at run time? You want to make a wrapper class each time you want to do that? It''s not always that simple, unless you use an auto_ptr style thing, which you say you don''t…

quote: 2) Your analogy is bad. Type-checking is a language feature. Garbage-collection is re-writing the new and delete operators. If the purpose or re-writing is not to provide an add-on (perhaps fail-safe wrappers when exceptions occur?) to the language, then it is stupid to use it. If garbage collection does nothing but increase the code size over corresponding new and delete operators, then it is truly useless.

No, it is a good analogy, as we were comparing the tools available in different languages. In C++, you have to use a workaround to get what another language will give you for free, which is a logical way of abstracting another layer away from you. It simplifies code over using new and delete and reduces the chance of memory leaks, just like there are so many other features designed to simplify your work, such as virtual functions instead of endless switches.

It''s as if you''re saying abstraction and language tools are great if they''re native to C++, but in any other language they are over the top or unnecessary. Which I respect as an opinion, just as I respect that guy on these forums who does all his work in assembly But it''s just a personal coding style, nothing more.

quote: No, in this case the problem is very complex and requires several language features, each doing their particular task, and working together to accomplish one big task. I still don''t understand what advantage exceptions have over assertions, from the viewpoint that assertions are unusable across software layers.

Well, they require less resources in the code, for one. The same would go for implementing your own streamlined RTTI if you don''t need the full implementation. Although there is a supposed premise of ''not paying for what you don''t use'' in C++, if you turn exception handling on, the code tends to run about 5% slower, even if you never throw any exceptions. I assume this is down to code bloat, as the file size does swell up a bit.

A second advantage to assertions is that they are quicker to implement. This should only be a factor when testing in-house, obviously. When you release software you should be going for robustness, not the quickest way of producing semi-working feature-packed software. (Now, tell that to MS et al.)

quote:
See Why C++ Sucks for an alternative view.

I did – LOL! It''s truly amazing how so many instances of bad logic can exist on one page on one subject by one person…


Although I disagree with a lot of what the guy said, and there are some ''logic wormholes'' in there, some of it does make sense
quote: Original post by Kylotan

quote:
--------------------------------------------------------------------------------

On that note, C++ lacks local functions. This could perhaps be considered a deficiency in the language.
--------------------------------------------------------------------------------

Local classes.

I was under the impression that classes can only be defined in global or class scope. This means the whole containing class has access to the inner class, when I only need one function to have access to it. It''s no better than just using another private member function.

Of course, the same old complication arises anyway: "Function definitions are not permitted in local class declarations in Microsoft C++."...


No, I mean classes that are local to a function, like this (I just learned how to do this!):


void reverse_bytes(byte* memory, unsigned int size)
{
// local class
class swap_bytes_function
{
public:
void operator(){byte& b1, byte& b2)
{ byte temp; temp = b1; b1 = b2; b2 = temp; }
};

// instantiate the class
swab_bytes_function swap_bytes;

for( unsigned int x=0; x{
swap_bytes(memory[size-x], memory[x]);
}

}



That''s not the best example in the world, but it should work (barring serious bugs I may have introduced ).

If you need "simple" functions, why not use goto: and labels? That''s why they exist.


quote: Original post by Kylotan

quote:
--------------------------------------------------------------------------------
If you expect me to be able to converse with you in an intelligent way, you -must- use the English standard. The efficiency of our communication is directly proportional to your compliance with the English standard.
--------------------------------------------------------------------------------

I was I was using the English definition of type. Unfortunately you were using the C++ definition of type which is a more stringent subset


From my dictionary:


    type - A group of persons or things having in common certain traits or characteristics that set them apart from others as a distinct and indentifiable class



quote: Original post by Kylotan

quote:
--------------------------------------------------------------------------------
1) Actually, new()-ing and delete()-ing are easy operations. Add one in the constructor, the other in the destructor, and you''re set. You can also define the copy constructor and operator=() if you wish, or just make them private.
--------------------------------------------------------------------------------

What about allocating variable sized buffers at run time? You want to make a wrapper class each time you want to do that? It''s not always that simple, unless you use an auto_ptr style thing, which you say you don''t...


Just use delete[] and it''ll automatically delete the number of bytes you allocated dynamically with new[]. Or you could create a buffer class, that can hold any type of data. Then just use buffer in any project you want. Or use std::vector, etc.


quote: Original post by Kylotan
No, it is a good analogy, as we were comparing the tools available in different languages. In C++, you have to use a workaround to get what another language will give you for free, which is a logical way of abstracting another layer away from you. It simplifies code over using new and delete and reduces the chance of memory leaks, just like there are so many other features designed to simplify your work, such as virtual functions instead of endless switches.

It''s as if you''re saying abstraction and language tools are great if they''re native to C++, but in any other language they are over the top or unnecessary. Which I respect as an opinion, just as I respect that guy on these forums who does all his work in assembly But it''s just a personal coding style, nothing more.


1) It''s silly to have auto_ptr just delete memory for you when you can (even more easily) add in the calls yourself. Also, if you''re using auto_ptr, how does that work with your buffer theory? It would have to use delete[] on the bytes...

2) Since I don''t have enough experience to say that it is never useful, so I''ll leave it at this: "if auto_ptr simplifies some immensely difficult task that would require more programming than the code auto_ptr uses, then I would use auto_ptr." Otherwise, I''d just new() and delete() on my own intellect.

3) For free? I think you are missing something...nothing is free. You get that for which you have paid (corrected english grammar -- somehow it lost something...). If it implements garbage collection and C++ does not, then which is correct? I consider a rather easy principle that if you new() memory, you must delete() it. At least it is much easier than memorizing rules for when things go out of scope. Garbage collection can also introduce bugs that are very hard to track, and those bugs can really slow the program down. I don''t see garbage collection as worth anyone''s time learning for normal commercial software.


quote: Original post by Kylotan

Well, they require less resources in the code, for one. The same would go for implementing your own streamlined RTTI if you don''t need the full implementation. Although there is a supposed premise of ''not paying for what you don''t use'' in C++, if you turn exception handling on, the code tends to run about 5% slower, even if you never throw any exceptions. I assume this is down to code bloat, as the file size does swell up a bit.

A second advantage to assertions is that they are quicker to implement. This should only be a factor when testing in-house, obviously. When you release software you should be going for robustness, not the quickest way of producing semi-working feature-packed software. (Now, tell that to MS et al.)


1) You are talking about assertions? I was talking about "design by contract" as a way of coding. I said that various tools in C++ work together to provide that. I think that many languages are just like "additions" to C++, because C++ is very logcial.

2) Exception specifications are going to be implemented in VC 7.0 (hopefully) and they are a way of optimizing code that does not use exceptions.

3) How are assertions quicker to implement?


quote: Original post by Kylotan

Although I disagree with a lot of what the guy said, and there are some ''logic wormholes'' in there, some of it does make sense


I didn''t mean to offend you -- I though you were being funny in mentioning it... However, I don''t see what is to be gained by making a page like that. Why don''t people do productive things like post on forums?



- null_pointer
Sabre Multimedia
quote: Original post by null_pointer

No, I mean classes that are local to a function, like this (I just learned how to do this!):


MSVC5 just says global or class scope, and not even in an ''MS specific'' section either. Wouldn''t be surprised if this wasn''t true though. Either way, why make a whole new object just for 1 extra function? You don''t normally have to make 1 object per function. And using goto with labels is a bit icky, no? Especially when there is a perfectly good function system in C++ already. Local functions would be handy.

quote:
From my dictionary:


    type - A group of persons or things having in common certain traits or characteristics that set them apart from others as a distinct and indentifiable class


And mine makes no mention of ''group'', for example. Either way, there is nothing in English that says the word ''type'' has to correspond to ''class'' in C++. Just because I use the word type, should not mean such a rigid definition. You will notice I used the term ''word'' in the last sentence, but I did not mean a 2-byte integer.


quote:
Just use delete[] and it''ll automatically delete the number of bytes you allocated dynamically with new[].


I gave an example of problems with this a few messages back.
{auto_ptr buffer = new char[size];if (!DoSomething(buffer))    return -1;if (!DoSomethingElse(buffer))    return -2;if (!DoSomethingAgain(buffer))    return -3;DoAFourthThing();} 

Now write the equivalent version using delete. Far less convenient, you need to add 3 extra pairs of braces and 4 explicit calls to delete. And all the extra typing buys you nothing except to save the minimal overhead in using an auto_ptr. auto_ptr guarantees that your stuff will be deallocated. How else do you use pointers to dynamic variable-sized buffers in a system with exceptions? Unless they are wrapped in automatic objects, and hence will have their destructor called, your exceptions are not gonna be able to do their job properly. A buffer class is too complex when all you need is a dynamically sized array of a given type.

quote:
1) It''s silly to have auto_ptr just delete memory for you when you can (even more easily) add in the calls yourself. Also, if you''re using auto_ptr, how does that work with your buffer theory? It would have to use delete[] on the bytes...

Yes, it does, so you don''t have to. If you don''t understand this concept, I have to ask if you even use destructors etc? I pointed out above how it simplifies deallocation.

How come you are confident in the ability of the language makers on everything -but- auto_ptr? It''s a useful feature, to denounce it as not really being useful is just like calling virtual functions not useful. After all, you can always implement your own vtable...

quote:
3) For free? I think you are missing something...nothing is free. You get that for which you have paid (corrected english grammar -- somehow it lost something...). If it implements garbage collection and C++ does not, then which is correct? I consider a rather easy principle that if you new() memory, you must delete() it. At least it is much easier than memorizing rules for when things go out of scope. Garbage collection can also introduce bugs that are very hard to track, and those bugs can really slow the program down. I don''t see garbage collection as worth anyone''s time learning for normal commercial software.

I can''t agree with you on garbage collection. Having to manage your own pointers is the source of most bugs in modern software. Dereferencing invalid pointers, or pointers named after your good self, is the source of nearly every crash in modern code. Garbage collection vastly reduces these concerns.

quote: 3) How are assertions quicker to implement?

assert(some condition)
is quicker than
if (!some condition)
throw something relevant;
(catch it elsewhere).
At the worst, you would have to insert an extra catch statement for your exception, or create a new exception subclass, etc etc. asserts are useful to have while you''re developing.


quote:
I didn''t mean to offend you -- I though you were being funny in mentioning it... However, I don''t see what is to be gained by making a page like that. Why don''t people do productive things like post on forums?

You didn''t offend me. But for every language that you or I might like, there are valid concerns and criticisms of it. The same goes for any feature or tool in the language. We are quick to denounce any feature that takes care of something we can do already as useless, and any feature we can''t work out yet as being unnecessary. I grew up on Z80 assembly and Basic, and I couldn''t see the need for anything so structured as a GOSUB for a long time Never mind aggregate types or *gasp* data structures. As we gather coding experience, previous paradigms fall by the wayside as we find better ways of doing things, either within the language, or in a new language. I take the existence of more than 1 programming language as firm evidence that none of them are perfect.
quote: Original post by Kylotan

Either way, there is nothing in English that says the word ''type'' has to correspond to ''class'' in C++.


"class" in C++ is just a way of defining new types, as are struct and union.


quote: Original post by Kylotan

I gave an example of problems with this a few messages back.


{
auto_ptr buffer = new char[size];
if (!DoSomething(buffer))
return -1;
if (!DoSomethingElse(buffer))
return -2;
if (!DoSomethingAgain(buffer))
return -3;
DoAFourthThing();
}

Now write the equivalent version using delete. Far less convenient, you need to add 3 extra pairs of braces and 4 explicit calls to delete. And all the extra typing buys you nothing except to save the minimal overhead in using an auto_ptr. auto_ptr guarantees that your stuff will be deallocated. How else do you use pointers to dynamic variable-sized buffers in a system with exceptions? Unless they are wrapped in automatic objects, and hence will have their destructor called, your exceptions are not gonna be able to do their job properly. A buffer class is too complex when all you need is a dynamically sized array of a given type.


quote: auto_ptr::~auto_ptr documentation

~auto_ptr();

If the ownership indicator is true, the destructor deletes the object designated by the stored pointer p by evaluating the delete expression delete p.


Your buffer isn''t being deallocated properly -- just the first byte. See how garbage collection can introduce memory leaks?


quote: Original post by Kylotan

quote:
--------------------------------------------------------------------------------

1) It''s silly to have auto_ptr just delete memory for you when you can (even more easily) add in the calls yourself. Also, if you''re using auto_ptr, how does that work with your buffer theory? It would have to use delete[] on the bytes...

--------------------------------------------------------------------------------

Yes, it does, so you don''t have to. If you don''t understand this concept, I have to ask if you even use destructors etc? I pointed out above how it simplifies deallocation.

How come you are confident in the ability of the language makers on everything -but- auto_ptr? It''s a useful feature, to denounce it as not really being useful is just like calling virtual functions not useful. After all, you can always implement your own vtable...


Perhaps I didn''t phrase that correctly? Let me try it another way, then. I mean that it''s silly to learn a new class for something on which you can call "delete". When I say smaller code, I mean smaller total code, not smaller imagined code because the code is in a class and separate from the individual function.

1) auto_ptr is not part of the language -- it is a class. new and delete are the language features, and they are what I said to use...you''ve got it backwards!

2) Simplification of code is only good so long as it does not make more code than necessary without simplification.

3) Destructors take care of tasks that should be written anyway - they don''t add extra code. That is, if I write an array class, it shouldn''t involve more code within each member function than I would need to normally use a C++ array (i.e. "new char[256]" etc.). That is, if I write a deallocate() function for the array, it should call delete[], and possibly do any checks to maintain its proper state.



quote: Original post by Kylotan

I can''t agree with you on garbage collection. Having to manage your own pointers is the source of most bugs in modern software. Dereferencing invalid pointers, or pointers named after your good self, is the source of nearly every crash in modern code. Garbage collection vastly reduces these concerns.


Even garbage collection cannot make up for bad coding. It merely masks bad coding. I still don''t see what is so hard about new and delete!


quote: Original post by Kylotan

assert(some condition)
is quicker than
if (!some condition)
throw something relevant;
(catch it elsewhere).

At the worst, you would have to insert an extra catch statement for your exception, or create a new exception subclass, etc etc. asserts are useful to have while you''re developing.


1) So, at the worst case, it''s not much more than a few (short) lines of typing?

2) Assertions don''t -do- anything but halt the program. They''re stupid for debugging because they can''t clean up; that is totally unacceptable across software layers. Because software is becoming increasingly modular, I am surprised that someone actually maintains that it is better than exceptions.


quote: Original post by Kylotan

You didn''t offend me. But for every language that you or I might like, there are valid concerns and criticisms of it. The same goes for any feature or tool in the language. We are quick to denounce any feature that takes care of something we can do already as useless, and any feature we can''t work out yet as being unnecessary. I grew up on Z80 assembly and Basic, and I couldn''t see the need for anything so structured as a GOSUB for a long time Never mind aggregate types or *gasp* data structures. As we gather coding experience, previous paradigms fall by the wayside as we find better ways of doing things, either within the language, or in a new language. I take the existence of more than 1 programming language as firm evidence that none of them are perfect.


I liked BASIC too, but found it hard to grasp the notion of functions.

Anyway, enough reminiscing about the past... I was just saying that it does all evaluate to assembly (different assembly on different computers, of course). If assertions don''t do everything they need to do for debugging, and exceptions do, then exceptions are better for debugging. That''s not a matter of difference of opinion or preference.

However, specialized applications introduce new priorities. I''m talking about the machines used to monitor heart transplants here, not the next Quake! At base all programs are just different combinations of C++ keywords and user-defined types. Very few programs actually require something different (as in 1/1,000,000 programs produced).

I must admit I didn''t like the Eiffel site, primarily because of the hype that I sensed there.




- null_pointer
Sabre Multimedia
Advertisement
Busy week at work, hence the delay in replying

quote: Original post by null_pointer

Your buffer isn''t being deallocated properly -- just the first byte. See how garbage collection can introduce memory leaks?


Firstly, a true garbage collection system would deal with this. Any memory leaks in a garbage collected language are because the programmer failed to correctly terminate a reference, just as a memory leak in C is because someone forgot to free the memory. I think you are biased here because you are comfortable with delete and not with garbage collection. I contend that they are both adequate for the task, except new/delete/malloc/free give you extra power at the expense of extra management on your part. Having to do more micromanagement is the opposite direction to the way computing is going. We want to be able to encapsulate a perfect system and move on. Having to track your own memory allocation is a far more difficult system.

My auto_ptr example would work fine with some other class. It saves typing, guarantees a destructor call, reduces the chance of errors. To me, that equals Good Thing

quote:
Perhaps I didn''t phrase that correctly? Let me try it another way, then. I mean that it''s silly to learn a new class for something on which you can call "delete".

When I say smaller code, I mean smaller total code, not smaller imagined code because the code is in a class and separate from the individual function.


This is madness. You are now arguing against encapsulation! Take repeated code out of your algorithms and move it into separate functions/modules/objects... that is the whole essence of programming. In the example I gave, allocating the memory is not part of the algorithm, it is just an added necessity. Therefore it makes sense to abstract it away rather than have to micromanage it yourself.

quote:
1) auto_ptr is not part of the language -- it is a class. new and delete are the language features, and they are what I said to use...you''ve got it backwards!

auto_ptr is part of the standard. It is therefore still part of the language, although more tenuously. If you know lingustics, this is like the difference between the ''closed set'' of vocabulary (pronouns etc) and the ''open set'' (verbs, nouns). It''s all part of the language. You''re not fully ANSI C++ compliant unless you supply auto_ptr.

quote: 2) Simplification of code is only good so long as it does not make more code than necessary without simplification.

I disagree. Quite often it is ''necessary'' to introduce code which, on the surface, seems redundant, but is there for some other purpose, such as abstraction for example. That extra code may be useless now, but may form an interface which makes your classes more reusable, etc. Necessary is a subjective term. What you don''t need today, you may need later, and it will take twice as long to add in later... it''s a tradeoff.

I don''t see using auto_ptr as being ''extra code'', though. Maybe you are referring to garbage collection in general? Either way, I''d rather have a stable program running at 80% efficiency than an unstable one at 99% efficiency. But wait: you''re a Windows fan, aren''t you? Just teasing.

quote:
Even garbage collection cannot make up for bad coding. It merely masks bad coding. I still don''t see what is so hard about new and delete!


I don''t see what is so hard about using globals effectively, or just using normal functions rather than objects. But I know certain things make my life easier. And everything that I can do more easily, gives me more time for the harder stuff and reduces the chance of introducing bugs.

I don''t want to micromanage my memory any more than I want to have to deal with all my files a character at a time. Memory management is there for when you want to be close to the machine, just like the bitshifting operators, etc. If I''m working at a higher level, this should be one of the main things to abstract away, in my opinion.

quote:


assert(some condition)
is quicker than
if (!some condition)
throw something relevant;
(catch it elsewhere).


1) So, at the worst case, it''s not much more than a few (short) lines of typing?

It''s always a few lines less typing. It also looks more clearly like a pre/postcondition, whereas exception handling spreads out the code across the function, or even across the program. Usually exception handling does a good job of separating the error code from the algorithm code. With an assert, that is not your concern - your concern is stopping the code at the exact line that there is a problem, not passing that down to somewhere else. Most debuggers will work in tandem with asserts to drop you into the code at that line and let you examine all the variables. Throwing an exception down to some function to catch it might well be able to tell you exactly where the error happened, but by then all your stack variables have been deallocated… hence, useless for debugging.

quote:
2) Assertions don''t -do- anything but halt the program. They''re stupid for debugging because they can''t clean up;

See above: they are -perfect- for debugging because they -don''t- clean up.

quote:
that is totally unacceptable across software layers.

It is totally unacceptable in released software. It is, however, a useful debugging tool.
heh heh just when I thought everyone quit arguing...


There seem to be two major issues that we are discussing, and I feel we are miscommunicating. They are 1) garbage collection and 2) exceptions vs. assertions.


1) GARBAGE COLLECTION

I have a view of encapsulation that can best be understood by the following three functions (the example is somewhat long). Here is the first function, with encapsulation:


// this function draws some text using the current
// drawing tool and mode
void display::output(const string& text, vector &ltimage>* font)
{
// exceptions found in function
class null_pointer : public exception {}
class invalid_font : public exception {}

// check arguments (this can be removed in release)
if( font == NULL )
throw null_pointer; // custom exception, tells location

if( font.size() != (sizeof(char)^8) )
throw invalid_font; // also tells location

// declare our constants
const unsigned int number_of_letters = text.size();
unsigned int x = 0; // x and y are the
unsigned int y = 0; // upper left corner

// draw the letter, using its bitmapped font,
// at the location we calculate
for( unsigned long j=0; j{
// at the left corner
x += font[j].get_width();

// at the bottom of the screen
y = (this->height - font[j].get_height());

// draw it
this->blit(font(char(text[j])), x, y);
}
}



Whew! that's a lot of typing...anyway, it's a lot less than would be possible without encapsulation. Let's move on to the next version of that function, which is the same except it contains the code from all the functions we called in the previous example, etc. which means it contains the code from list&ltletter>::size(), list&ltletter>::operator[], vector&ltimage>::operator[], this->blit(), etc. I'm not going to list it, though. Just think of how it would be.

The third example of the function is as if we had coded the function without using other classes -- not even procedural code...everything here is coded in one straight line, from the beginning of the program to the end, and all in main().


Now, the second example should have the efficiency of the third example, if it is written correctly. The first example should have the efficiency of the second example, if it is written correctly. This is a round-about way of saying that if you could dump all the code from all the functions and classes you use right into main(), it should look the same (providing both are written correctly) as if you had done it that way to begin with. The point is that classes and functions should not add extra code. This is definitely part of "overhead" as it affects the code just like useless extra function calls or who knows what optimization nightmares people create. "overhead" is anything that is unnecessary over and above the third example. I say that good encapsulation involves no "overhead." If it does, then you obviously have either duplicate code or unnecessary code. Classes and functions merely organize code. I am most certainly not arguing against the use of them.

The following code snippet is ludicrous:


void do_something()
{
bool own_memory = false;

byte* heap_memory = new byte[256];
own_memory = true;

if( own_memory )
delete[] heap_memory
}



Isn't this ludicrous, too:


void do_something()
{
auto_ptr heap_memory(new byte[256]);
}



It uses the "same" code.

I said, if you need it, use it. Otherwise, simple calls to new() and delete() will do. To put it another way, if you are going to add in a bool anyway to keep track of it, then use auto_ptr. The point of encapsulation is to organize code, and not to cover the programmer's mistakes or to save typing (although it tends to do that). If you want to totally separate new() and delete() for some reason in addition to allocating/deallocating memory (instance counting, etc.) the by all means put it in a class.


2) EXCEPTIONS VS. ASSERTIONS

I think our primary misunderstanding here is whether you are debugging between different "layers" of software or just testing the same "layer" of software. The definition of a "layer" of software that I am referring to here is code encapsulated into any sort of organizational separation (i.e., classes in C++).

There are two distinct cases in software development. 1) When you have communication between code from different classes, that communicate, you are debugging between different "layers" of software. 2) When you do not have communication between code from different classes, you are not debugging between different layers of software.


CASE 1 -- ACROSS LAYERS

Because "layers" of software (again, classes in C++) should NOT be dependent on each other to maintain a valid state, assertions are not good across them. An assertion triggered in my linked_list class might demolish my ddraw_wrapper class, and cause haywire on my PC. Exceptions, on the other had, are made for this case; they provide cleanup, notification, and as much information as you could want.


CASE 2 -- WITHIN ONE LAYER

Within one "layer" of software, you have no dependencies, and thus assertions have every advantage over exceptions because it is not conceivable that you will ever need to clean up -- every condition is known and can be easily controlled. Exceptions are merely excess overhead and totally unnecessary here.


The only problem with case 2 is that it doesn't exist in real life! "Layers" of software aren't just classes; they include the OS, the BIOS, other programs, different parts of your program, the classes they use, etc. Having everything co-dependent upon each other with assertions is the equivalent of mass suicide in release apps (as you pointed out). In debug apps it is equally unacceptable for me, as I do not want my dev PC crashing constantly. Exceptions provide a stable environment for debugging. Sometimes you must have a performance hit to do it right.


Now that I have stated my observations and conclusions, I will work on the quotes.


quote: Original post by Kylotan

Firstly, a true garbage collection system would deal with this. Any memory leaks in a garbage collected language are because the programmer failed to correctly terminate a reference, just as a memory leak in C is because someone forgot to free the memory. I think you are biased here because you are comfortable with delete and not with garbage collection.


1) I'm a C++ programmer. I had better know what delete() does!

2) I was not indicating that garbage collection libraries cannot handle code like that. Some of them are quite sophisticated and elegant. I was merely pointing out that it makes the code more complex, and that is why you made the mistake in the first place.

3) Extra effort must be justified. Every C++ programmer had better know how to use new() and delete(). Garbage collection in C++ is, then, extra work. Can you justify it? It was not made to cover laziness. Laziness with garbage collection will only net you slower code and increased frustration. I want to perfectly clear that I am not calling your example lazy, here, but that I am talking about a very common misconception here: if you don't spend the extra time learning garbage collection and using it properly, you won't get anything out of it. If you are only looking to garbage collection to hide your own inability to understand new() and delete(), then you have a rude awakening ahead.


quote: Original post by Kylotan

In the example I gave, allocating the memory is not part of the algorithm, it is just an added necessity. Therefore it makes sense to abstract it away rather than have to micromanage it yourself.


1) No, allocating the memory was obviously a necessary part of the algorithm, or you wouldn't have done it. You need memory to operate on, or else the operation is pointless.

2) This is my point: why use auto_ptr? You "added in" extra code involving a bool variable, a class instance, etc. which were totally unnecessary for the algorithm. Small overhead, but unnecessary and pointless. Also, auto_ptr didn't save you any typing. If you want to eliminate micro-management in your example, use the built-in "auto_ptr": the stack.


There is no substitute for good coding.



- null_pointer
Sabre Multimedia


Edited by - null_pointer on May 21, 2000 11:24:19 AM
My god, you certainly are pedantic. Please read this before replying to any further posts about C++ as it relates to exceptions.

quote: Original post by null_pointer
2) This is my point: why use auto_ptr? You "added in" extra code involving a bool variable, a class instance, etc. which were totally unnecessary for the algorithm. Small overhead, but unnecessary and pointless. Also, auto_ptr didn''t save you any typing. If you want to eliminate micro-management in your example, use the built-in "auto_ptr": the stack.


Now you''re being really ignorant. Smart pointers, e.g. auto_ptr and the like (as well as most of the STL container classes), deal with situations where you need to dynamically control memory to write exception safe code. Using auto_ptr guarentees (when used properly) that ownership of memory is maintained and that when the owning object goes out of scope the memory is released. Even in the face of exceptions.

E.g.:

foo* create_and_initialize_foo_and_do_other_stuff()
{
auto_ptr&ltfoo> srpfoo( new foo );
srpfoo->init();
srpfoo->otherstuff();
return srpfoo.release();
}

This is exception-safe code. This is not equivalent to:

foo* unsafe_create_and_initialize_foo_and_do_other_stuff()
{
foo* pfoo = new foo;
pfoo->init();
pfoo->otherstuff();
return pfoo;
}

If foo::init or foo::otherstuff throw an exception in the latter case, the memory allocated by new foo will not be freed; hence, memory leak, and not exception safe.

Please, try to research these areas before posting ignorant information.

MSN
quote: Original post by msn12b

Please, try to research these areas before posting ignorant information.


I politely ask, first, that you do the same. I was referring to a specific example cited by Kylotan where the memory was used only within the function in which it was allocated, and it was deallocated in all cases before the function exited. Such a case is quite obviously a use for stack allocations.

This is why I usually use LONG quotations so that other people would have to purposely skip reading the examples cited and purposely post ignorant information. I was trying not to use such long quotations so the posts wouldn''t take so much time to load, but I see that doesn''t work.


quote: Original post by msn12b

my *beep*, you certainly are pedantic. Please read this before replying to any further posts about C++ as it relates to exceptions.


1) I like to think that when I say "why use auto_ptr?" I am asking a question and not being "pedantic." Also, when I make what seems to be a logical point, I am merely working with the knowledge I have and not being "pedantic." I''m not sure but it may help me more if you answered my question with a little more tact, but I understood it anyway with my usual narrow-mindedness. Or perhaps you could explain how I am being "pedantic."

2) I did read the first 5 pages of it, and I do understand how exceptions work (from other material). I did a model of a generic resource using a resource_handle class, complete with copy constructors, assignment operators, etc. which is like auto_ptr on steroids, and it is already in use in my library. I can post it here if you like, but it''s rather large (then again, so is everything else in this post ).


quote: Original post by msn12b

E.g.:

foo* create_and_initialize_foo_and_do_other_stuff()
{
auto_ptr srpfoo( new foo );
srpfoo->init();
srpfoo->otherstuff();
return srpfoo.release();
}

This is exception-safe code. This is not equivalent to:

foo* unsafe_create_and_initialize_foo_and_do_other_stuff()
{
foo* pfoo = new foo;
pfoo->init();
pfoo->otherstuff();
return pfoo;
}


If you re-read at my examples and explanations in my previous post, you''ll see that is not my point. I know how exceptions work. I do understand how destructors are called, why they are called, what auto_ptr does, etc. I was not questioning the validity of auto_ptr as a replacement for memory allocated on the heap, but the validity of auto_ptr as a replacement for memory allocated on the heap that could have been allocated on the stack.





- null_pointer
Sabre Multimedia

This topic is closed to new replies.

Advertisement