Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #22497 > unrolled thread
| Started by | jacob navia <jacob@spamsink.net> |
|---|---|
| First post | 2012-06-21 22:19 +0200 |
| Last post | 2012-06-25 00:27 -0600 |
| Articles | 20 on this page of 35 — 13 participants |
Back to article view | Back to comp.lang.c
Design decisions jacob navia <jacob@spamsink.net> - 2012-06-21 22:19 +0200
Re: Design decisions Andrew Cooper <amc96@cam.ac.uk> - 2012-06-21 21:43 +0100
Re: Design decisions "BartC" <bc@freeuk.com> - 2012-06-21 22:02 +0100
Re: Design decisions Gareth Owen <gwowen@gmail.com> - 2012-06-21 23:23 +0100
Re: Design decisions jacob navia <jacob@spamsink.net> - 2012-06-22 09:12 +0200
Re: Design decisions Ian Collins <ian-news@hotmail.com> - 2012-06-22 19:44 +1200
Re: Design decisions "Joe." <antelope3@news.net> - 2012-06-25 00:32 -0600
Re: Design decisions gwowen <gwowen@gmail.com> - 2012-06-22 01:00 -0700
Re: Design decisions jacob navia <jacob@spamsink.net> - 2012-06-22 10:08 +0200
Re: Design decisions Gareth Owen <gwowen@gmail.com> - 2012-06-22 17:18 +0100
Re: Design decisions jacob navia <jacob@spamsink.net> - 2012-06-22 18:27 +0200
Re: Design decisions Ian Collins <ian-news@hotmail.com> - 2012-06-23 08:44 +1200
Re: Design decisions jacob navia <jacob@spamsink.net> - 2012-06-22 23:58 +0200
Re: Design decisions Ian Collins <ian-news@hotmail.com> - 2012-06-23 10:21 +1200
Re: Design decisions jacob navia <jacob@spamsink.net> - 2012-06-23 02:13 +0200
Re: Design decisions Ian Collins <ian-news@hotmail.com> - 2012-06-23 12:59 +1200
Re: Design decisions jacob navia <jacob@spamsink.net> - 2012-06-23 11:03 +0200
Re: Design decisions Ian Collins <ian-news@hotmail.com> - 2012-06-23 21:52 +1200
Re: Design decisions jacob navia <jacob@spamsink.net> - 2012-06-23 12:32 +0200
Re: Design decisions Gareth Owen <gwowen@gmail.com> - 2012-06-23 13:54 +0100
Re: Design decisions "Joe." <antelope3@news.net> - 2012-06-25 00:36 -0600
Re: Design decisions jacob navia <jacob@spamsink.net> - 2012-06-22 10:42 +0200
Re: Design decisions Gareth Owen <gwowen@gmail.com> - 2012-06-22 17:18 +0100
Re: Design decisions jacob navia <jacob@spamsink.net> - 2012-06-24 18:32 +0200
Re: Design decisions James Kuyper <jameskuyper@verizon.net> - 2012-06-21 18:43 -0400
Re: Design decisions "Joe." <antelope3@news.net> - 2012-06-25 00:40 -0600
Re: Design decisions "Joe." <antelope3@news.net> - 2012-06-25 00:28 -0600
Re: Design decisions "io_x" <a@b.c.invalid> - 2012-06-22 08:35 +0200
Re: Design decisions Neil Cerutti <neilc@norwich.edu> - 2012-06-22 12:40 +0000
Re: Design decisions gwowen <gwowen@gmail.com> - 2012-06-22 06:01 -0700
Re: Design decisions Neil Cerutti <neilc@norwich.edu> - 2012-06-22 14:26 +0000
Re: Design decisions Ben Bacarisse <ben.usenet@bsb.me.uk> - 2012-06-22 14:01 +0100
Re: Design decisions David Resnick <lndresnick@gmail.com> - 2012-06-22 06:13 -0700
Re: Design decisions Jens Gustedt <jens.gustedt@loria.fr> - 2012-06-22 19:05 +0200
Re: Design decisions "Joe." <antelope3@news.net> - 2012-06-25 00:27 -0600
Page 1 of 2 [1] 2 Next page →
| From | jacob navia <jacob@spamsink.net> |
|---|---|
| Date | 2012-06-21 22:19 +0200 |
| Subject | Design decisions |
| Message-ID | <jrvvlb$8k1$1@speranza.aioe.org> |
In the new "templated" form of the containers library I have now that all data retrieving functions return the data directly: int (*GetElement)(intList *l,size_t idx); The problem is that it is not possible to return an error with this configuration: Since the return value is an int, any error code could be actually some data!! The solution is to return a pointer to the data and NULL if there is an error, as the other functions of the library do. But this doesn't look great with the syntax viewed from the user's side: Instead of: int m = iintList.GetElement(mylist,23); you should write: int m = *iintList.GetElement(myList,23); risking a core dump if there is a NULL return. Obviously if the list has only 20 elements, the core dump is actually a good thing since it will show you the error immediately. Before returning, the "GetElement" API calls the error function and will return ONLY IF the error function returns. I was relying in that as error handling, and just returning zero... After some reflection I am convinced now that this would be a bad design decision. What do you think? jacob
[toc] | [next] | [standalone]
| From | Andrew Cooper <amc96@cam.ac.uk> |
|---|---|
| Date | 2012-06-21 21:43 +0100 |
| Message-ID | <IHLEr.186327$of1.178068@fx06.am4> |
| In reply to | #22497 |
On 21/06/2012 21:19, jacob navia wrote: > In the new "templated" form of the containers library I have now that > all data retrieving functions return the data directly: > > int (*GetElement)(intList *l,size_t idx); > > The problem is that it is not possible to return an error > with this configuration: Since the return value is an > int, any error code could be actually some data!! > > The solution is to return a pointer to the data and NULL > if there is an error, as the other functions of the library do. > > But this doesn't look great with the syntax viewed from the > user's side: > > Instead of: > int m = iintList.GetElement(mylist,23); > you should write: > int m = *iintList.GetElement(myList,23); > > risking a core dump if there is a NULL return. > > Obviously if the list has only 20 elements, the core dump > is actually a good thing since it will show you the error immediately. > > Before returning, the "GetElement" API calls the error function > and will return ONLY IF the error function returns. > > I was relying in that as error handling, and just returning zero... > > After some reflection I am convinced now that this would be a bad design > decision. > > What do you think? > > jacob (Other than the fact you appear to be trying to get C++ style code into C) One design you could use is: bool_t (*GetElement)(intList *l, size_t idx, int * dst); so your code looks like: int m; if ( iintList.GetElement(myList, 23, &m) == FALSE ) // Some error else // All Good ~Andrew
[toc] | [prev] | [next] | [standalone]
| From | "BartC" <bc@freeuk.com> |
|---|---|
| Date | 2012-06-21 22:02 +0100 |
| Message-ID | <js025k$hjm$1@dont-email.me> |
| In reply to | #22497 |
"jacob navia" <jacob@spamsink.net> wrote in message news:jrvvlb$8k1$1@speranza.aioe.org... > In the new "templated" form of the containers library I have now that all > data retrieving functions return the data directly: > > int (*GetElement)(intList *l,size_t idx); > > The problem is that it is not possible to return an error > with this configuration: Since the return value is an > int, any error code could be actually some data!! > > The solution is to return a pointer to the data and NULL > if there is an error, as the other functions of the library do. ... > What do you think? How does C++ deal with it? My feeling is that GetElement should be as simple and convenient to use as possible, especially for the 99.999% of calls (and usually 100%) where there is no error. Perhaps have two versions, one where any errors are handled internally (either aborting, returning an agreed default value, or whatever), and another which has full error returns for people who insist on checking every single list access themselves. -- bartc
[toc] | [prev] | [next] | [standalone]
| From | Gareth Owen <gwowen@gmail.com> |
|---|---|
| Date | 2012-06-21 23:23 +0100 |
| Message-ID | <87a9zwb8xs.fsf@gmail.com> |
| In reply to | #22501 |
"BartC" <bc@freeuk.com> writes: > How does C++ deal with it? Most (all?) C++ STL containers give a bounds-checked accessor that returns a reference on success, but throws an exception on out-of-bounds access (I assume that's the main failure condition Jacob has in mind for GetElement also). Exceptions give you a return path which, when combined with well encapsulated objects, automatically cleans up any resources. Of course, writing such exception-safe objects is a non-trivial undertaking. jacob navia <jacob@spamsink.net> writes: > Before returning, the "GetElement" API calls the error function and > will return ONLY IF the error function returns. What do you have in mind for a well designed, non-returning error function. Obviously one that calls abort()/exit() would work but what else? Could the error function provide a default value (e.g. out-of-bounds access always returns a 0 value, or attempts to expand the vector and pad with zero)? Or were you thinking in terms of longjmp()ing to some prior state?
[toc] | [prev] | [next] | [standalone]
| From | jacob navia <jacob@spamsink.net> |
|---|---|
| Date | 2012-06-22 09:12 +0200 |
| Message-ID | <js15sm$pu9$1@speranza.aioe.org> |
| In reply to | #22507 |
Le 22/06/12 00:23, Gareth Owen a écrit :
>
> What do you have in mind for a well designed, non-returning error
> function. Obviously one that calls abort()/exit() would work but what
> else?
>
Obviously a longjmp combined with a prior setjmp can establish a
recovery point where all errors in a program (or in a part of the
program) would end.
If you use pooled memory access you can release the memory pool
associated with the current error, and you have the exact mechanism
of the C++ exceptions combined with cleanup by stack unwinding in
a much cheaper and efficient way.
> Could the error function provide a default value (e.g. out-of-bounds
> access always returns a 0 value, or attempts to expand the vector and
> pad with zero)?
>
I had this strategy but it is unworkable since you can't know in
advance if a zero will appear in the data!
Attempting to expand the vector is also a bad decision because:
1) In case the error is an index error you are just masking the error
making it more difficult to find. GetElement retrieves the element's
data but you have no data for an inexistent element so that you fake
it. But then the rest of the software that expects that data point
will give WRONG RESULTS!
Debugging a wrong result bug is orders of magnitude more difficult
than debugging a simple crash.
2) If you get a huge index (say, 75876) you will start trying to
allocate 75876-20 elements and you could get a memory allocation
failure, making a double fault!
3) If you expand the vector you are probably overstretching the used
memory, specially if the wrong index is too big, provoking a memory
failure later in the code. Then, you will have to start figuring
out why you are using so much memory, and it could take a LONG while
until you figure out that a wrong index forced you to allocate
several megabytes of vector space...
[toc] | [prev] | [next] | [standalone]
| From | Ian Collins <ian-news@hotmail.com> |
|---|---|
| Date | 2012-06-22 19:44 +1200 |
| Message-ID | <a4im3jFglkU1@mid.individual.net> |
| In reply to | #22517 |
On 06/22/12 07:12 PM, jacob navia wrote: > Le 22/06/12 00:23, Gareth Owen a écrit : >> >> What do you have in mind for a well designed, non-returning error >> function. Obviously one that calls abort()/exit() would work but what >> else? >> > > Obviously a longjmp combined with a prior setjmp can establish a > recovery point where all errors in a program (or in a part of the > program) would end. > > If you use pooled memory access you can release the memory pool > associated with the current error, and you have the exact mechanism > of the C++ exceptions combined with cleanup by stack unwinding in > a much cheaper and efficient way. Er, no you don't. Allocated memory isn't the only thing you may want to clean up. This situation is much the same as the early return cases discussed at great length on the recent goto thread. -- Ian Collins
[toc] | [prev] | [next] | [standalone]
| From | "Joe." <antelope3@news.net> |
|---|---|
| Date | 2012-06-25 00:32 -0600 |
| Message-ID | <js90ia$6g6$2@speranza.aioe.org> |
| In reply to | #22522 |
"Ian Collins" <ian-news@hotmail.com> wrote in message news:a4im3jFglkU1@mid.individual.net... > On 06/22/12 07:12 PM, jacob navia wrote: >> Le 22/06/12 00:23, Gareth Owen a écrit : >>> >>> What do you have in mind for a well designed, non-returning error >>> function. Obviously one that calls abort()/exit() would work but what >>> else? >>> >> >> Obviously a longjmp combined with a prior setjmp can establish a >> recovery point where all errors in a program (or in a part of the >> program) would end. >> >> If you use pooled memory access you can release the memory pool >> associated with the current error, and you have the exact mechanism >> of the C++ exceptions combined with cleanup by stack unwinding in >> a much cheaper and efficient way. > > Er, no you don't. Allocated memory isn't the only thing you may want to > clean up. > > This situation is much the same as the early return cases discussed at > great length on the recent goto thread. > > -- > Ian Collins Who could not respect someone with a name like yours? Aren't you a "golden boy" playin it safe by "being good"? (Maybe you and James Kanze should team up and conquer the world?).
[toc] | [prev] | [next] | [standalone]
| From | gwowen <gwowen@gmail.com> |
|---|---|
| Date | 2012-06-22 01:00 -0700 |
| Message-ID | <a80c870c-0427-4f04-bbd3-770d56b717de@cu1g2000vbb.googlegroups.com> |
| In reply to | #22517 |
On Jun 22, 8:12 am, jacob navia <ja...@spamsink.net> wrote:
> Le 22/06/12 00:23, Gareth Owen a écrit :
> > What do you have in mind for a well designed, non-returning error
> > function. Obviously one that calls abort()/exit() would work but what
> > else?
>
> Obviously a longjmp combined with a prior setjmp can establish a
> recovery point where all errors in a program (or in a part of the
> program) would end.
>
> If you use pooled memory access you can release the memory pool
> associated with the current error, and you have the exact mechanism
> of the C++ exceptions combined with cleanup by stack unwinding in
> a much cheaper and efficient way.
Really? Do you really believe that? What about resources that are not
memory - mutex/critical sections, network connections, open ports,
file descriptors.
How would I use your error handler to do this:
extern DAQ_source* daq;
int npieces = 100;
while(true){
try {
std::vector<std::vector<uint8_t> datapts;
for(int i=0; i<npieces; ++i){
std::vector<uint8_t> signal;
{
// Acquire the next signal from DAQ
Mutex DAQ_lock(daq); // mutex lock acquired
signal = daq->acquire(); // can generate errors
// mutex lock released
}
Filter multitap; // can throw
multitap.process(signal); // can generate errors
datapts.push_back(signal); // can generate errors
}
analyse(datapts);
}
catch(const std::runtime_error& e)
{
// all resources automatically cleaned up
log << e.what();
sleep(100); // retry on error
}
}
> > Could the error function provide a default value (e.g. out-of-bounds
> > access always returns a 0 value, or attempts to expand the vector and
> > pad with zero)?
>
> I had this strategy but it is unworkable since you can't know in
> advance if a zero will appear in the data!
But it might be desirable for a user to be able to specify such
behaviour in his error handler. For example, in signal processing its
often useful to assume that the data-array is zero-padded-to-infinity,
so I may use a C++ object like this:
class zero_padded_array
{
private:
public std::vector<double> v_;
public:
const double& at(size_t idx) const {
if(idx >= v_.size()){
return 0.0;
} else {
return v_[idx];
}
}
// Rest of interface.
}
Can I use an error handler to handle out-of-bounds access in you
library to get that behaviour?
> Attempting to expand the vector is also a bad decision because:
>
> 1) In case the error is an index error you are just masking the error
> making it more difficult to find. GetElement retrieves the element's
> data but you have no data for an inexistent element so that you fake
> it. But then the rest of the software that expects that data point
> will give WRONG RESULTS!
You misunderstand me. My point is that insertion
e.g. In Matlab I can say:
vec = [1 2 4 8];
vec(6) = 9; % inserting out-of-bounds automatically expands the vector
% vec is now [1 2 4 8 0 9];
x = vec(9); % retrieving out-of-bounds is an error
class resizing_array
{
private:
public std::vector<double> v_;
public:
double& at(size_t idx) {
if(idx >= v_.size()){
v_.resize(idx+1);
}
return v_[idx];
}
// Rest of interface....
}
Can I use an error handler to get that behaviour? Exceptions make it
easy.
> 2) If you get a huge index (say, 75876) you will start trying to
> allocate 75876-20 elements and you could get a memory allocation
> failure, making a double fault!
I regularly create C++ matrices with far in excess of 76000 elements -
occasionally even as temporaries - knowing that properly designed
exceptions make memory allocation failure easy to handle. And since
these are designed to run on 64-bit processors, I know that memory
failure is actually unlikely.
> 3) If you expand the vector you are probably overstretching the used
> memory..
It works really well in matlab and C++. Exceptions make it easy.
[toc] | [prev] | [next] | [standalone]
| From | jacob navia <jacob@spamsink.net> |
|---|---|
| Date | 2012-06-22 10:08 +0200 |
| Message-ID | <js1968$26c$1@speranza.aioe.org> |
| In reply to | #22523 |
Le 22/06/12 10:00, gwowen a écrit : > On Jun 22, 8:12 am, jacob navia <ja...@spamsink.net> wrote: >> If you use pooled memory access you can release the memory pool >> associated with the current error, and you have the exact mechanism >> of the C++ exceptions combined with cleanup by stack unwinding in >> a much cheaper and efficient way. > > Really? Do you really believe that? What about resources that are not > memory - mutex/critical sections, network connections, open ports, > file descriptors. > > How would I use your error handler to do this: > [snip code] Very easy. Just use a pushdown stack. Each time you acquire a mutex lock you push it in the stack, each time you release it you pop it. If an exception occurs, you pop the whole mutex stack. This would require just a few lines more of code.
[toc] | [prev] | [next] | [standalone]
| From | Gareth Owen <gwowen@gmail.com> |
|---|---|
| Date | 2012-06-22 17:18 +0100 |
| Message-ID | <87sjdn8gm0.fsf@gmail.com> |
| In reply to | #22524 |
jacob navia <jacob@spamsink.net> writes: > Very easy. Just use a pushdown stack. Each time you acquire a > mutex lock you push it in the stack, each time you release it you pop it. Which is, of course, precisely C++ exceptions give you for (almost) free. > If an exception occurs, you pop the whole mutex stack. Which is fine if *all* your resources are mutexes. Again, if you have nested resources that must be acquired and released in precisely the correct order, everything gets a little more complicated. I guess you just have to use a double stack of function pointers / data pointers that get called in reverse order? > This would require just a few lines more of code. Any chance of an example?
[toc] | [prev] | [next] | [standalone]
| From | jacob navia <jacob@spamsink.net> |
|---|---|
| Date | 2012-06-22 18:27 +0200 |
| Message-ID | <js26e1$ct5$1@speranza.aioe.org> |
| In reply to | #22545 |
Le 22/06/12 18:18, Gareth Owen a écrit :
> jacob navia <jacob@spamsink.net> writes:
>
>> Very easy. Just use a pushdown stack. Each time you acquire a
>> mutex lock you push it in the stack, each time you release it you pop it.
>
> Which is, of course, precisely C++ exceptions give you for (almost) free.
>
>> If an exception occurs, you pop the whole mutex stack.
>
> Which is fine if *all* your resources are mutexes. Again, if you have
> nested resources that must be acquired and released in precisely the
> correct order, everything gets a little more complicated.
>
Maybe, depends on what you have in mind. In any case the unwinding of
the stack done by C++ is just that: stack unwinding and you can
simulate that with a generic stack without any big problems. Of course
it is a bit ofmore work for you but you do NOT pay for an
enormous complexity increase!
> I guess you just have to use a double stack of function pointers / data
> pointers that get called in reverse order?
>
Not really. A generic stack using generic pointers prefixed by an opcode
would suffice:
#define MUTEX 1
#define FILE_TO_CLOSE 2
#define OBSERVER_TO_FREE 3
typedef struct tagStackInfo {
int opcode;
void *data;
} StackUnwindInfo;
You push that, giving the opcode and the pointer (FILE MUTEX, whatever)
The unwind procedure acts given the opcode and closes the file, releases
the mutex, or breaks the binding of the observer procedure.
NOTE:
This is C, not C++. The philosophy is that you do NOT rely in an
incredible complex compiler/runtime that nobody fully understands.
You rely on your own skills.
Unsatisfied?
Use C++, it is a great language!
>> This would require just a few lines more of code.
>
> Any chance of an example?
>
Well, pushing and popping from a stack is not that really complicated.
[toc] | [prev] | [next] | [standalone]
| From | Ian Collins <ian-news@hotmail.com> |
|---|---|
| Date | 2012-06-23 08:44 +1200 |
| Message-ID | <a4k3q3FglkU3@mid.individual.net> |
| In reply to | #22546 |
On 06/23/12 04:27 AM, jacob navia wrote: > Le 22/06/12 18:18, Gareth Owen a écrit : >> jacob navia<jacob@spamsink.net> writes: >> >>> Very easy. Just use a pushdown stack. Each time you acquire a >>> mutex lock you push it in the stack, each time you release it you pop it. >> >> Which is, of course, precisely C++ exceptions give you for (almost) free. >> >>> If an exception occurs, you pop the whole mutex stack. >> >> Which is fine if *all* your resources are mutexes. Again, if you have >> nested resources that must be acquired and released in precisely the >> correct order, everything gets a little more complicated. >> > > Maybe, depends on what you have in mind. In any case the unwinding of > the stack done by C++ is just that: stack unwinding and you can > simulate that with a generic stack without any big problems. Of course > it is a bit ofmore work for you but you do NOT pay for an > enormous complexity increase! The complexity in both case will be equal. In your case the complexity has to be managed by the manually programmer, in the C++ case it is managed automatically by the compiler. The complexity becomes really hard to manage manually when the jump target is several layers up the call stack. -- Ian Collins
[toc] | [prev] | [next] | [standalone]
| From | jacob navia <jacob@spamsink.net> |
|---|---|
| Date | 2012-06-22 23:58 +0200 |
| Message-ID | <js2pp5$t7c$1@speranza.aioe.org> |
| In reply to | #22559 |
Le 22/06/12 22:44, Ian Collins a écrit : > On 06/23/12 04:27 AM, jacob navia wrote: >> Le 22/06/12 18:18, Gareth Owen a écrit : >>> jacob navia<jacob@spamsink.net> writes: >>> >>>> Very easy. Just use a pushdown stack. Each time you acquire a >>>> mutex lock you push it in the stack, each time you release it you >>>> pop it. >>> >>> Which is, of course, precisely C++ exceptions give you for (almost) >>> free. >>> >>>> If an exception occurs, you pop the whole mutex stack. >>> >>> Which is fine if *all* your resources are mutexes. Again, if you have >>> nested resources that must be acquired and released in precisely the >>> correct order, everything gets a little more complicated. >>> >> >> Maybe, depends on what you have in mind. In any case the unwinding of >> the stack done by C++ is just that: stack unwinding and you can >> simulate that with a generic stack without any big problems. Of course >> it is a bit ofmore work for you but you do NOT pay for an >> enormous complexity increase! > > The complexity in both case will be equal. In your case the complexity > has to be managed by the manually programmer, in the C++ case it is > managed automatically by the compiler. > Sure, but that "aautomatic" managemengt by an incredibly complex compiler has a price in difficulty of learning, understanding and using all that machinery. > The complexity becomes really hard to manage manually when the jump > target is several layers up the call stack. > No, since it is conceptually simple: a stack. For each pointer that you need to free/release whatever you add an opcode and a pointer. You push that into the stack. It is very easy.
[toc] | [prev] | [next] | [standalone]
| From | Ian Collins <ian-news@hotmail.com> |
|---|---|
| Date | 2012-06-23 10:21 +1200 |
| Message-ID | <a4k9f8FglmU2@mid.individual.net> |
| In reply to | #22560 |
On 06/23/12 09:58 AM, jacob navia wrote:
> Le 22/06/12 22:44, Ian Collins a écrit :
>> On 06/23/12 04:27 AM, jacob navia wrote:
>>>
>>> Maybe, depends on what you have in mind. In any case the unwinding of
>>> the stack done by C++ is just that: stack unwinding and you can
>>> simulate that with a generic stack without any big problems. Of course
>>> it is a bit ofmore work for you but you do NOT pay for an
>>> enormous complexity increase!
>>
>> The complexity in both case will be equal. In your case the complexity
>> has to be managed by the manually programmer, in the C++ case it is
>> managed automatically by the compiler.
>
> Sure, but that "aautomatic" managemengt by an incredibly complex
> compiler has a price in difficulty of learning, understanding and using
> all that machinery.
If you find
struct ManageSomething
{
Something thing;
ManageSomething( Something s ) : thing(s) {}
~ManageSomething() { release( thing ); }
};
difficult to understand, I'd be worried.
I guess at some time in the past, our distant ancestors found wheels
complex, but they sure beat pushing things along the ground. Developing
technical solutions to repeating problems is what we do.
>> The complexity becomes really hard to manage manually when the jump
>> target is several layers up the call stack.
>>
>
> No, since it is conceptually simple: a stack. For each pointer that you
> need to free/release whatever you add an opcode and a pointer. You push
> that into the stack. It is very easy.
Easy, tedious, repetitive and error prone. Isn't the normal
programmer's reaction to a tedious, repetitive and error prone problem
automation?
--
Ian Collins
[toc] | [prev] | [next] | [standalone]
| From | jacob navia <jacob@spamsink.net> |
|---|---|
| Date | 2012-06-23 02:13 +0200 |
| Message-ID | <js31mt$dci$1@speranza.aioe.org> |
| In reply to | #22561 |
Le 23/06/12 00:21, Ian Collins a écrit :
> On 06/23/12 09:58 AM, jacob navia wrote:
>> Le 22/06/12 22:44, Ian Collins a écrit :
>>> On 06/23/12 04:27 AM, jacob navia wrote:
>>>>
>>>> Maybe, depends on what you have in mind. In any case the unwinding of
>>>> the stack done by C++ is just that: stack unwinding and you can
>>>> simulate that with a generic stack without any big problems. Of course
>>>> it is a bit ofmore work for you but you do NOT pay for an
>>>> enormous complexity increase!
>>>
>>> The complexity in both case will be equal. In your case the complexity
>>> has to be managed by the manually programmer, in the C++ case it is
>>> managed automatically by the compiler.
>>
>> Sure, but that "aautomatic" managemengt by an incredibly complex
>> compiler has a price in difficulty of learning, understanding and using
>> all that machinery.
>
> If you find
>
> struct ManageSomething
> {
> Something thing;
>
> ManageSomething( Something s ) : thing(s) {}
> ~ManageSomething() { release( thing ); }
> };
>
> difficult to understand, I'd be worried.
>
It is difficult to understand, maybe because "s" is undefined :-)
ManageSomething(Something s) looks like a function call but probably
isn't one since you are declaring a struct, so it must be a declaration
of a constructor. Yes, and then a further function call (or similar)
separated by a colon. Mmmm I have seen that sometimes in C++ code
and "thing" is a "Something" so that must be a function call?
Or not?
You can be worried, I do not parse all that and I do not care actually
to parse that. You like C++Ian, go ahead, it is a GREAT language when
you dedicate 50% of your memory space (brain memory space I mean) to
memorizing by heart all the fine points of a language that has no rules
but a "tradition" that you must also learn by heart.
Googling around I find
http://www.cplusplus.com/forum/beginner/4124/
where "Initializer lists" are explained and it should mean that
the constructor of "ManageSomething" will be passed "Something"
that will initialize "thing" to the passed parameter "s".
Why that?
Because it can save the call to the default constructor. OK.
And why I should learn all that?
I do not care, I like a language with LESS syntax, with LESS
complexity.
If you do not use initializer lists this can happen:
MyClass::MyClass( const std::string& s1, const std::string& s2 )
{
string1 = s1;
string2 = s2;
}
1. std::string default constructor is called to default construct string1;
2. std::string default constructor is called to default construct string2;
3. std::string assignment operator is called to assign s1 to string1;
4. std::string assignment operator is called to assign s2 to string2.
With initializer lists there are only two extra function calls
"by using an initializer list, you are telling the compiler that instead
of calling the default constructor for string1 and string2 it can
instead call the copy constructor, which means only two function calls
instead of four."
BIG PROGRESS.
Ian, go ahead and keep C++. It is a great language but for people
outside its scope it looks like an abomination came true.
> I guess at some time in the past, our distant ancestors found wheels
> complex, but they sure beat pushing things along the ground. Developing
> technical solutions to repeating problems is what we do.
>
Sure, sure, and then somebody invented square wheels and all people
but some die hards cried GREAT!
SQUARE WHEELS that can climb up the stairs instead of just
rolling around. They are our "BETTER WHEELS".
And everybody started using those. Cars bumped horribly, the ride was
awful but... PROGRESS IS PROGRESS cried the crowd.
And then came a "solution to the bumping problem" proposed by the square
wheels people: a new motor would push down and up the carriage
automatically so that the passangers did not felt the square wheels.
And now really everybody was happy: fuel consumption skyrocketed but
that was a small hardware problem that got pushed beneath the rug.
:-)
>>> The complexity becomes really hard to manage manually when the jump
>>> target is several layers up the call stack.
>>>
>>
>> No, since it is conceptually simple: a stack. For each pointer that you
>> need to free/release whatever you add an opcode and a pointer. You push
>> that into the stack. It is very easy.
>
> Easy, tedious, repetitive and error prone. Isn't the normal
> programmer's reaction to a tedious, repetitive and error prone problem
> automation?
>
Yes. Up to 30% or more of space are now taken by tables describing the
stack movements to the unwinding virtual machine that the C++ run time
needs.
But this has no sense Ian. Keep C++. I will go on developing simple
solutions to simple problems. A stack is a stack, and I prefer pushing
the things to cleanup into a stack with:
pushCleanup( Opcode,pointer);
each time I need to cleanup something, and then I just write:
UnwindCleanupStack();
and I am done. And I write pushCleanup() and Unwind once and I can
use them again and again.
[toc] | [prev] | [next] | [standalone]
| From | Ian Collins <ian-news@hotmail.com> |
|---|---|
| Date | 2012-06-23 12:59 +1200 |
| Message-ID | <a4kinoFglmU3@mid.individual.net> |
| In reply to | #22567 |
On 06/23/12 12:13 PM, jacob navia wrote:
> Le 23/06/12 00:21, Ian Collins a écrit :
>> On 06/23/12 09:58 AM, jacob navia wrote:
>>> Le 22/06/12 22:44, Ian Collins a écrit :
>>>> On 06/23/12 04:27 AM, jacob navia wrote:
>>>>>
>>>>> Maybe, depends on what you have in mind. In any case the unwinding of
>>>>> the stack done by C++ is just that: stack unwinding and you can
>>>>> simulate that with a generic stack without any big problems. Of course
>>>>> it is a bit ofmore work for you but you do NOT pay for an
>>>>> enormous complexity increase!
>>>>
>>>> The complexity in both case will be equal. In your case the complexity
>>>> has to be managed by the manually programmer, in the C++ case it is
>>>> managed automatically by the compiler.
>>>
>>> Sure, but that "aautomatic" managemengt by an incredibly complex
>>> compiler has a price in difficulty of learning, understanding and using
>>> all that machinery.
>>
>> If you find
>>
>> struct ManageSomething
>> {
>> Something thing;
>>
>> ManageSomething( Something s ) : thing(s) {}
>> ~ManageSomething() { release( thing ); }
>> };
>>
>> difficult to understand, I'd be worried.
>
> It is difficult to understand, maybe because "s" is undefined :-)
>
> ManageSomething(Something s) looks like a function call but probably
> isn't one since you are declaring a struct, so it must be a declaration
> of a constructor. Yes, and then a further function call (or similar)
> separated by a colon. Mmmm I have seen that sometimes in C++ code
> and "thing" is a "Something" so that must be a function call?
>
> Or not?
>
> You can be worried, I do not parse all that and I do not care actually
> to parse that.
Somewhat disingenuous coming from someone who claims to be paid to
maintain C++ code don't you think?
>>>> The complexity becomes really hard to manage manually when the jump
>>>> target is several layers up the call stack.
>>>>
>>>
>>> No, since it is conceptually simple: a stack. For each pointer that you
>>> need to free/release whatever you add an opcode and a pointer. You push
>>> that into the stack. It is very easy.
>>
>> Easy, tedious, repetitive and error prone. Isn't the normal
>> programmer's reaction to a tedious, repetitive and error prone problem
>> automation?
>>
>
> Yes. Up to 30% or more of space are now taken by tables describing the
> stack movements to the unwinding virtual machine that the C++ run time
> needs.
Now where did you dig up that "fact" from? Automatic destruction works
perfectly well without run time support.
> But this has no sense Ian. Keep C++. I will go on developing simple
> solutions to simple problems. A stack is a stack, and I prefer pushing
> the things to cleanup into a stack with:
>
> pushCleanup( Opcode,pointer);
>
> each time I need to cleanup something, and then I just write:
>
> UnwindCleanupStack();
>
> and I am done. And I write pushCleanup() and Unwind once and I can
> use them again and again.
I prefer to let the compiler do the pushing and popping for me. Lazy?
maybe, but I know the compiler won't have a bad hair day and forget to
pop something.
--
Ian Collins
[toc] | [prev] | [next] | [standalone]
| From | jacob navia <jacob@spamsink.net> |
|---|---|
| Date | 2012-06-23 11:03 +0200 |
| Message-ID | <js40oi$9lc$1@speranza.aioe.org> |
| In reply to | #22569 |
Le 23/06/12 02:59, Ian Collins a écrit : > On 06/23/12 12:13 PM, jacob navia wrote: >> Yes. Up to 30% or more of space are now taken by tables describing the >> stack movements to the unwinding virtual machine that the C++ run time >> needs. > > Now where did you dig up that "fact" from? Automatic destruction works > perfectly well without run time support. > Confirmed. Most C++ programmers have no idea of what is needed behind the scene to make all those nice syntax constructs work. AUTOMATIC DESTRUCTION MUST WORK IN THE CASE OF A THROW. So, since the compiler can't know if a throw will go through the procedure that you are compiling it must generate a description of all stack movements just in case. The stack unwinding machinery needs information about push/pop and any stack addition/subtraction. This is described in detail by gcc/MSVC and other c++ compilers, and by lcc-win, since I do generate that information also. Surprised? lcc-win generates that information because even if it never invokes a throw, it could be that a throw passes through tghe code of lcc-win when the lcc-win libraries are used with C++.
[toc] | [prev] | [next] | [standalone]
| From | Ian Collins <ian-news@hotmail.com> |
|---|---|
| Date | 2012-06-23 21:52 +1200 |
| Message-ID | <a4lhv0Fe9mU1@mid.individual.net> |
| In reply to | #22576 |
On 06/23/12 09:03 PM, jacob navia wrote:
> Le 23/06/12 02:59, Ian Collins a écrit :
>> On 06/23/12 12:13 PM, jacob navia wrote:
>
>>> Yes. Up to 30% or more of space are now taken by tables describing the
>>> stack movements to the unwinding virtual machine that the C++ run time
>>> needs.
>>
>> Now where did you dig up that "fact" from? Automatic destruction works
>> perfectly well without run time support.
>
> Confirmed. Most C++ programmers have no idea of what is needed behind
> the scene to make all those nice syntax constructs work.
Confirmed, you are talking out of the wrong end again.
> AUTOMATIC DESTRUCTION MUST WORK IN THE CASE OF A THROW.
Not if you turn exceptions off, which is a better comparison in this
case (C++ as a better C). I use C++ (mainly to get automatic resource
management) in driver and kernel module code where there isn't any run
time support, so I most certainly do know what goes on behind the scenes.
For example, the following
extern "C" int* get();
extern "C" void done(int*);
extern "C" void process(int*);
struct Manage {
int* n;
Manage( int* n ) : n(n) {}
~Manage() {done(n);}
};
void f() {
int* n = get();
Manage m(n);
process(n);
}
Compiles (with g++ -S -O3 -fno-exceptions) to:
.file "z.cc"
.text
.p2align 4,,15
.globl _Z1fv
.type _Z1fv, @function
_Z1fv:
pushl %ebp
movl %esp, %ebp
pushl %ebx
subl $20, %esp
call get
movl %eax, %ebx
movl %eax, (%esp)
call process
movl %ebx, (%esp)
call done
addl $20, %esp
popl %ebx
popl %ebp
ret
.size _Z1fv, .-_Z1fv
.ident "GCC: (GNU) 4.5.2"
Which is probably tighter than any manual stack pushing and popping.
--
Ian Collins
[toc] | [prev] | [next] | [standalone]
| From | jacob navia <jacob@spamsink.net> |
|---|---|
| Date | 2012-06-23 12:32 +0200 |
| Message-ID | <js4601$lm8$1@speranza.aioe.org> |
| In reply to | #22577 |
Le 23/06/12 11:52, Ian Collins a écrit : > On 06/23/12 09:03 PM, jacob navia wrote: >> AUTOMATIC DESTRUCTION MUST WORK IN THE CASE OF A THROW. > > Not if you turn exceptions off, If you turn it off, the whole program must turn them off if not it will crash. But OK, you can do that and use C++ in a 'lite' mode :-)
[toc] | [prev] | [next] | [standalone]
| From | Gareth Owen <gwowen@gmail.com> |
|---|---|
| Date | 2012-06-23 13:54 +0100 |
| Message-ID | <87pq8qxk7f.fsf@gmail.com> |
| In reply to | #22577 |
Ian Collins <ian-news@hotmail.com> writes: >> Confirmed. Most C++ programmers have no idea of what is needed behind >> the scene to make all those nice syntax constructs work. We know full well. Error handling is hard. The question is - do I want to worry about unwinding the stack and releasing the resources manually using a hand-managed stack, or do I want the compiler to do it for me -- it will need to be done in both cases. I want compiler writers to handle that machinery for me.
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | comp.lang.c
csiph-web