Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #22497 > unrolled thread

Design decisions

Started byjacob navia <jacob@spamsink.net>
First post2012-06-21 22:19 +0200
Last post2012-06-25 00:27 -0600
Articles 20 on this page of 35 — 13 participants

Back to article view | Back to comp.lang.c


Contents

  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 →


#22497 — Design decisions

Fromjacob navia <jacob@spamsink.net>
Date2012-06-21 22:19 +0200
SubjectDesign 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]


#22499

FromAndrew Cooper <amc96@cam.ac.uk>
Date2012-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]


#22501

From"BartC" <bc@freeuk.com>
Date2012-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]


#22507

FromGareth Owen <gwowen@gmail.com>
Date2012-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]


#22517

Fromjacob navia <jacob@spamsink.net>
Date2012-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]


#22522

FromIan Collins <ian-news@hotmail.com>
Date2012-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]


#22646

From"Joe." <antelope3@news.net>
Date2012-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]


#22523

Fromgwowen <gwowen@gmail.com>
Date2012-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]


#22524

Fromjacob navia <jacob@spamsink.net>
Date2012-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]


#22545

FromGareth Owen <gwowen@gmail.com>
Date2012-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]


#22546

Fromjacob navia <jacob@spamsink.net>
Date2012-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]


#22559

FromIan Collins <ian-news@hotmail.com>
Date2012-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]


#22560

Fromjacob navia <jacob@spamsink.net>
Date2012-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]


#22561

FromIan Collins <ian-news@hotmail.com>
Date2012-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]


#22567

Fromjacob navia <jacob@spamsink.net>
Date2012-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]


#22569

FromIan Collins <ian-news@hotmail.com>
Date2012-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]


#22576

Fromjacob navia <jacob@spamsink.net>
Date2012-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]


#22577

FromIan Collins <ian-news@hotmail.com>
Date2012-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]


#22579

Fromjacob navia <jacob@spamsink.net>
Date2012-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]


#22581

FromGareth Owen <gwowen@gmail.com>
Date2012-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