Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Function factories

167 views
Skip to first unread message

Edward Rutherford

unread,
Aug 21, 2012, 4:36:31 PM8/21/12
to
Hello

I want to make a factory that produces other functions according to a
parameter.

The following code does not compile:

typedef int *(foo)(int);

foo adder_factory(int x)
{
int f(int a)
{
return a + x;
}
return &f;
}

The idea is that adder_factor(42) should be a function that adds 42 to
whatever it gets passed.

What's the right way of doing this in C? How can I get closures or
something similar in C?

Nobody

unread,
Aug 21, 2012, 4:55:41 PM8/21/12
to
On Tue, 21 Aug 2012 20:36:31 +0000, Edward Rutherford wrote:

> I want to make a factory that produces other functions according to a
> parameter.
>
> The following code does not compile:
>
> typedef int *(foo)(int);
>
> foo adder_factory(int x)
> {
> int f(int a)
> {
> return a + x;
> }
> return &f;
> }

C does not have inner functions or closures.

> The idea is that adder_factor(42) should be a function that adds 42 to
> whatever it gets passed.
>
> What's the right way of doing this in C? How can I get closures or
> something similar in C?

typedef struct foo {
int (*func)(const struct foo *, int);
int param;
} *foo;

static int adder(const struct foo *closure, int x) {
return closure->param + x;
}

foo adder_factory(int x)
{
foo p = malloc(sizeof(*p));
p->func = adder;
p->param = x;
return p;
}

#define call_foo(f, x) ((f)->func((f), (x))


David Brown

unread,
Aug 22, 2012, 2:34:52 AM8/22/12
to
You can't.

You can get sort-of simulated closures in complicated and verbose ways,
but there is no good way to do it. gcc supports nested functions as an
extension to C, but that won't give you closures like this.

If you want to stick close to C, then C++11 supports lambda functions
and closures.

If you want to write code that makes strong use of closures in the
style, then use a programming language that supports it. The latest C++
standard will get you the basics, but if you want to write code in a
more functional programming style, there are much better languages to
work with (ocaml, python, haskell, etc.)

Nick Keighley

unread,
Aug 22, 2012, 4:52:53 AM8/22/12
to
On Aug 22, 7:34 am, David Brown <da...@westcontrol.removethisbit.com>
wrote:
scheme

(and does Python have particularly good support for functional
programming?)

David Brown

unread,
Aug 22, 2012, 5:34:51 AM8/22/12
to Nick Keighley
Python has quite good support for functional programming styles, though
it is mainly a procedural / object oriented language. You do get
closures, list comprehension, functions as first-class objects, and a
sort of lazy evaluation (through iterators). It does not have pattern
matching in the way languages like Haskell have, which would have been nice.

jacob navia

unread,
Aug 22, 2012, 6:32:45 AM8/22/12
to
Le 21/08/12 22:36, Edward Rutherford a écrit :
If you have the JIT of lcc-win you write:

typedef int (*foo)(int);


foo adder_factory(int value)
{
foo fnPtr;
char buf[512];

sprintf(buf,"int fn(int a) { return a + %d;}",value);
fnPtr = (foo)compile(buf);
return fnPtr;
}


tom st denis

unread,
Aug 22, 2012, 10:40:01 AM8/22/12
to
You could accomplish similiar [though more longwinded] via compiling
code and using dlopen() to load it into your process space.

Both of which are completely OT for this group though.

BTW: your code doesn't seem to do any sort of error checking. What if
compile() fails?

Tom

James Kuyper

unread,
Aug 22, 2012, 11:05:47 AM8/22/12
to
On 08/22/2012 10:40 AM, tom st denis wrote:
> On Aug 22, 6:32 am, jacob navia <ja...@spamsink.net> wrote:
...
>> If you have the JIT of lcc-win you write:
>>
>> typedef int (*foo)(int);
>>
>> foo adder_factory(int value)
>> {
>> foo fnPtr;
>> char buf[512];
>>
>> sprintf(buf,"int fn(int a) { return a + %d;}",value);
>> fnPtr = (foo)compile(buf);
>> return fnPtr;
>
> You could accomplish similiar [though more longwinded] via compiling
> code and using dlopen() to load it into your process space.

The intent was for the definition of the adder to be contained inside
the definition of adder_factory().

> Both of which are completely OT for this group though.
>
> BTW: your code doesn't seem to do any sort of error checking. What if
> compile() fails?

From the way it's used, it would appear to return a pointer. If
compile() fails, it might return a null pointer, in which case
adder_factory() will also return a null pointer, leaving it for the
caller to decide what to do about it. That's a perfectly reasonable way
of passing on the information that the call failed.

However, I think that compile() looks horribly type-unsafe.

Edward Rutherford

unread,
Aug 22, 2012, 12:36:09 PM8/22/12
to
Thanks for the suggestion. Unfortunately I need an actual function, not a
macro, to be able to pass as a comparator function to qsort.

Guess I just have to communicate what I need through a global variable?
It would be nice if qsort took an extra "void *data" parameter, like
functions in Glib (where it's called a gpointer of course)...

James Kuyper

unread,
Aug 22, 2012, 1:01:10 PM8/22/12
to
On 08/22/2012 12:36 PM, Edward Rutherford wrote:
> Nobody wrote:
>> On Tue, 21 Aug 2012 20:36:31 +0000, Edward Rutherford wrote:
...
>>> The idea is that adder_factor(42) should be a function that adds 42 to
>>> whatever it gets passed.
>>>
>>> What's the right way of doing this in C? How can I get closures or
>>> something similar in C?
>>
>> typedef struct foo {
>> int (*func)(const struct foo *, int); int param;
>> } *foo;
>>
>> static int adder(const struct foo *closure, int x) {
>> return closure->param + x;
>> }
>>
>> foo adder_factory(int x)
>> {
>> foo p = malloc(sizeof(*p));
>> p->func = adder;
>> p->param = x;
>> return p;
>> }
>>
>> #define call_foo(f, x) ((f)->func((f), (x))
>
> Thanks for the suggestion. Unfortunately I need an actual function, not a
> macro, to be able to pass as a comparator function to qsort.
>
> Guess I just have to communicate what I need through a global variable?

That's one alternative. However, you can avoid creating a global, with
all of the attendant problems. When used correctly, qsort() never calls
the comparison function with null pointers. You can take advantage of
that fact by making x a static variable local to the comparison
function, and if the first argument to that function is a null pointer,
interpret the second argument as a pointer to a new value for x.

> It would be nice if qsort took an extra "void *data" parameter, like
> functions in Glib (where it's called a gpointer of course)...

Yes. It's a well-known problem with all of the original C standard
library's callback functions. I think C11 has introduced some new
callbacks, but I haven't had time to learn much about them.



Edward Rutherford

unread,
Aug 22, 2012, 2:33:24 PM8/22/12
to
That is very clever.

It does feel like a little bit of a hack to me, and I'd probably want to
comment what was going on quite carefully.

It also has the downside of introducing an extra branch instruction
(check against NULL) in a function that could be called many many times
in a sort.

Still, ingenious! Thanks.

James Kuyper

unread,
Aug 22, 2012, 10:26:44 PM8/22/12
to
On 08/22/2012 01:01 PM, James Kuyper wrote:
> On 08/22/2012 12:36 PM, Edward Rutherford wrote:
...
>> It would be nice if qsort took an extra "void *data" parameter, like
>> functions in Glib (where it's called a gpointer of course)...
>
> Yes. It's a well-known problem with all of the original C standard
> library's callback functions. I think C11 has introduced some new
> callbacks, but I haven't had time to learn much about them.

The new callback function is the one passed to
set_constraint_handler_s(). You cannot pass set_constraint_handler_s() a
corresponding void*data to be passed to the constraint handler when it
is called, so it looks like the committee has opted for consistency with
previous callback functions, rather than more flexible functionality.

constraint_handler_t functions take a second argument that is a void*,
but it is either null or points at an implementation-defined object,
which means that portable code can't do anything more useful with that
pointer than reporting whether or not it's null (which isn't
spectacularly useful).
--
James Kuyper

jacob navia

unread,
Aug 23, 2012, 5:08:09 AM8/23/12
to
Le 22/08/12 17:05, James Kuyper a �crit :
> On 08/22/2012 10:40 AM, tom st denis wrote:
>> On Aug 22, 6:32 am, jacob navia <ja...@spamsink.net> wrote:
> ...
>>> If you have the JIT of lcc-win you write:
>>>
>>> typedef int (*foo)(int);
>>>
>>> foo adder_factory(int value)
>>> {
>>> foo fnPtr;
>>> char buf[512];
>>>
>>> sprintf(buf,"int fn(int a) { return a + %d;}",value);
>>> fnPtr = (foo)compile(buf);
>>> return fnPtr;
>>
>> You could accomplish similiar [though more longwinded] via compiling
>> code and using dlopen() to load it into your process space.
>
> The intent was for the definition of the adder to be contained inside
> the definition of adder_factory().
>
>> Both of which are completely OT for this group though.
>>
>> BTW: your code doesn't seem to do any sort of error checking. What if
>> compile() fails?
>
> From the way it's used, it would appear to return a pointer.

By convention it returns a pointer to the first function it finds.
This is highly dependent on the application and customer setup.

Some customers want to have a "main" function that starts the thing.
In that case the JIT will return a pointer to that function. Others
will put the main function at the start of the file and the Jit returns
a function to the first function it compiles.

In case of error (syntax error, no more memory, whatever catastrophe
occurs), it returns NULL. Diagnostics are printed in stderr, whatever
that is. In most applications stderr is a pipe connected to some
file.

Obviously in most applications the code buffer is machine generated.
It could be however that it is used as a C interpreter.

The resulting code is dynamically linked to the running program. There
is no preprocessor.

> If
> compile() fails, it might return a null pointer, in which case
> adder_factory() will also return a null pointer, leaving it for the
> caller to decide what to do about it. That's a perfectly reasonable way
> of passing on the information that the call failed.
>
> However, I think that compile() looks horribly type-unsafe.
>

In a sense yes, since it can return a pointer to ANY kind of function
by definition... It is up to you to cast the result pointer into
the right stuff. I do not see how to improve that without destroying
the generality of the program.

But we are used to it. fread() will read ANY kind of stuff into its
buffer, it is up to you to interpret the data correctly.

Chicken McNuggets

unread,
Aug 23, 2012, 8:27:07 AM8/23/12
to
On 22/08/2012 16:05, James Kuyper wrote:

>
> However, I think that compile() looks horribly type-unsafe.
>

Type safety in C? That's a novel concept :).

James Kuyper

unread,
Aug 23, 2012, 11:13:54 AM8/23/12
to
Is it? Try compiling the following code:

int main(void)
{
double d;
int *pi = &d;
return 0;
}

Heinrich Wolf

unread,
Aug 23, 2012, 12:18:58 PM8/23/12
to

"James Kuyper" <james...@verizon.net> schrieb im Newsbeitrag
news:503648B2...@verizon.net...
...
>> Type safety in C? That's a novel concept :).
>
> Is it? Try compiling the following code:
>
> int main(void)
> {
> double d;
> int *pi = &d;
> return 0;
> }

It compiles! On both Turbo C 2.0 and Borland C++Builder 5.

There are just 2 warnings:

Warning J:\C\TURBO2\NONAME.C 4: Suspicious pointer conversion in function
main
Warning J:\C\TURBO2\NONAME.C 6: 'pi' is assigned a value which is never used
in function main

Pascal is said to be more type safe. But I am astonished: Delphi 5 does not
issue a warning at all, just a hint:

program TypeSafe;
{$APPTYPE CONSOLE}

var d : double;
pi : ^integer;
begin
pi := @d;
exitcode := 0;
end.

[Hint] TypeSafe.dpr(7): 'pi' is assigned a value which is never used



Heiner

Anand Hariharan

unread,
Aug 23, 2012, 12:29:55 PM8/23/12
to
One additional line (that doesn't involve any casts) is sufficient -

int main(void)
{
double d;
void *pv = &d;
int *pi = pv;
return 0;
}

$ gcc -W -Wall -ansi -pedantic foo.c
foo.c: In function ‘main’:
foo.c:6:10: warning: unused variable ‘pi’


James Kuyper

unread,
Aug 23, 2012, 12:40:04 PM8/23/12
to
On 08/23/2012 12:18 PM, Heinrich Wolf wrote:
>
> "James Kuyper" <james...@verizon.net> schrieb im Newsbeitrag
> news:503648B2...@verizon.net...
> ...
>>> Type safety in C? That's a novel concept :).
>>
>> Is it? Try compiling the following code:
>>
>> int main(void)
>> {
>> double d;
>> int *pi = &d;
>> return 0;
>> }
>
> It compiles! On both Turbo C 2.0 and Borland C++Builder 5.
>
> There are just 2 warnings:
>
> Warning J:\C\TURBO2\NONAME.C 4: Suspicious pointer conversion in function
> main
> Warning J:\C\TURBO2\NONAME.C 6: 'pi' is assigned a value which is never used
> in function main

The most that the C standard ever requires an implementation to do when
processing a program that has any kind of defect is to produce at least
one diagnostic message, contents unspecified; whether it then continues
to generate an executable is entirely up to the implementation. This is
one such case, a constraint violation (6.5.16.1p1). Any implementation
for which this program fails to produce at least one diagnostic is
non-conforming.


James Kuyper

unread,
Aug 23, 2012, 12:40:23 PM8/23/12
to
On 08/23/2012 12:18 PM, Heinrich Wolf wrote:
>
> "James Kuyper" <james...@verizon.net> schrieb im Newsbeitrag
> news:503648B2...@verizon.net...
> ...
>>> Type safety in C? That's a novel concept :).
>>
>> Is it? Try compiling the following code:
>>
>> int main(void)
>> {
>> double d;
>> int *pi = &d;
>> return 0;
>> }
>
> It compiles! On both Turbo C 2.0 and Borland C++Builder 5.
>
> There are just 2 warnings:
>
> Warning J:\C\TURBO2\NONAME.C 4: Suspicious pointer conversion in function
> main
> Warning J:\C\TURBO2\NONAME.C 6: 'pi' is assigned a value which is never used
> in function main

jacob navia

unread,
Aug 23, 2012, 12:44:39 PM8/23/12
to
Le 23/08/12 18:29, Anand Hariharan a �crit :
> foo.c: In function �main�:
> foo.c:6:10: warning: unused variable �pi�
>
>

C allows you to interpret ANY part of memory as something else if you
(as you have shown) are careful to avoid the built-in barriers of the
language.

Interpreting doubles as integers is very useful if you want to test
some bits or manipulate some bits of the binary representation without
going through expensive floating point operations. The whole Sun math
library is built using this kind of code, and you can do that in C since
it is a language that doesn't IMPOSE anything to the programmer, like
C#, C++ or Cwhatever.


James Kuyper

unread,
Aug 23, 2012, 12:46:29 PM8/23/12
to
On 08/23/2012 12:29 PM, Anand Hariharan wrote:
> On Aug 23, 10:13 am, James Kuyper <jameskuy...@verizon.net> wrote:
>> On 08/23/2012 08:27 AM, Chicken McNuggets wrote:
>>
>>> On 22/08/2012 16:05, James Kuyper wrote:
>>
>>>> However, I think that compile() looks horribly type-unsafe.
>>
>>> Type safety in C? That's a novel concept :).
>>
>> Is it? Try compiling the following code:
>>
>> int main(void)
>> {
>> double d;
>> int *pi = &d;
>> return 0;
>> }
>
> One additional line (that doesn't involve any casts) is sufficient -
>
> int main(void)
> {
> double d;
> void *pv = &d;
> int *pi = pv;
> return 0;
> }

Sure, C's support of type safety is weak, and easily circumvented. But
it's not non-existent, as implied by Chicken's first comment above. I've
used several languages with substantially less type safety; mostly ones
where type mismatches are not detected until something blows up at run
time. I greatly prefer catching them at compile time (a concept which
doesn't even apply to some of those languages).

Chicken McNuggets

unread,
Aug 23, 2012, 1:02:07 PM8/23/12
to
Yes. It compiles with a warning as expected. Not a great example of type
safety.

This code on the other hand compiles with no warnings:

#include <stdlib.h>

int main(void)
{
int *ptr = (int*) malloc(sizeof(long));
*ptr = 10;
short blah = *ptr;
free(ptr);
exit(EXIT_SUCCESS);
}

so please explain how C is a type safe language?

The fact that malloc() returns a void pointer that can be cast to any
type destroys any type safety C may have.

Try Haskell for a real type safe language.

jacob navia

unread,
Aug 23, 2012, 1:24:06 PM8/23/12
to
Le 23/08/12 19:02, Chicken McNuggets a �crit :
> On 23/08/2012 16:13, James Kuyper wrote:
>> On 08/23/2012 08:27 AM, Chicken McNuggets wrote:
>>> On 22/08/2012 16:05, James Kuyper wrote:
>>>
>>>>
>>>> However, I think that compile() looks horribly type-unsafe.
>>>>
>>>
>>> Type safety in C? That's a novel concept :).
>>
>> Is it? Try compiling the following code:
>>
>> int main(void)
>> {
>> double d;
>> int *pi = &d;
>> return 0;
>> }
>>
>
> Yes. It compiles with a warning as expected. Not a great example of type
> safety.
>
> This code on the other hand compiles with no warnings:
>
> #include <stdlib.h>
>
> int main(void)
> {
> int *ptr = (int*) malloc(sizeof(long));
> *ptr = 10;
> short blah = *ptr;
> free(ptr);
> exit(EXIT_SUCCESS);
> }
>
> so please explain how C is a type safe language?
>

Because you can assign a 64 bit integer into a short. If there is an
overflow the behavior is undefined but it can't be any overflow in your
example since 10 will fit into a short.

What is the problem?

> The fact that malloc() returns a void pointer that can be cast to any
> type destroys any type safety C may have.
>
> Try Haskell for a real type safe language.

OK
Lets suppose I have a double precision number and that I want to reset
the sign bit without doing any expensive floating point operations.

How do I do that in Haskell?

James Kuyper

unread,
Aug 23, 2012, 1:35:40 PM8/23/12
to
On 08/23/2012 01:02 PM, Chicken McNuggets wrote:
> On 23/08/2012 16:13, James Kuyper wrote:
>> On 08/23/2012 08:27 AM, Chicken McNuggets wrote:
>>> On 22/08/2012 16:05, James Kuyper wrote:
>>>
>>>>
>>>> However, I think that compile() looks horribly type-unsafe.
>>>>
>>>
>>> Type safety in C? That's a novel concept :).
>>
>> Is it? Try compiling the following code:
>>
>> int main(void)
>> {
>> double d;
>> int *pi = &d;
>> return 0;
>> }
>>
>
> Yes. It compiles with a warning as expected. Not a great example of type
> safety.

It's a lot better than it would be if no warning were mandated. An
implementation is free to reject such code - whether or not it actually
does so is a matter of QoI, outside the scope of the standard.

> This code on the other hand compiles with no warnings:
>
> #include <stdlib.h>
>
> int main(void)
> {
> int *ptr = (int*) malloc(sizeof(long));
> *ptr = 10;
> short blah = *ptr;
> free(ptr);
> exit(EXIT_SUCCESS);
> }
>
> so please explain how C is a type safe language?

Type safety is not an absolute yes or no issue. Some languages have more
type safety than others. C has static type checking - in many different
contexts, a diagnostic is mandatory if different types are not
compatible. Implicit conversions are often safe conversions, the most
dangerous type conversions are required to be explicit (except those
pointer conversions which can be done using void* as an intermediate
type). There's plenty of room for improvement in C's type safety, but
it's still a lot better than having no type checking, or deferring type
checking until run-time (both of which I've seen in other languages).

> The fact that malloc() returns a void pointer that can be cast to any
> type destroys any type safety C may have.

No, it doesn't. It just adds another type-unsafe mechanism to a language
that has only a moderate amount of type safety without it.

lcc-win32's compile() looks to me like it compromises type safety much
more than malloc() does, which is what prompted my original comment. A
pointer to a function that might point have the wrong function type
seems much more serious to me than a pointer to an object that might
have the wrong object type; there's a larger variety of ways you can
have a mis-match, and a correspondingly larger number of different
failure modes available. It's not good, either way.

jacob navia

unread,
Aug 23, 2012, 2:13:27 PM8/23/12
to
Le 23/08/12 19:35, James Kuyper a �crit :
> lcc-win32's compile() looks to me like it compromises type safety much
> more than malloc() does, which is what prompted my original comment. A
> pointer to a function that might point have the wrong function type
> seems much more serious to me than a pointer to an object that might
> have the wrong object type; there's a larger variety of ways you can
> have a mis-match, and a correspondingly larger number of different
> failure modes available. It's not good, either way.

But how can "compile()" know what type of function you are going to define?

There is absolutely NO WAY to change this fact. But of course if you
have one solution please let me know...

Melzzzzz

unread,
Aug 23, 2012, 3:21:56 PM8/23/12
to
On Thu, 23 Aug 2012 11:13:54 -0400
James Kuyper <james...@verizon.net> wrote:

> int main(void)
> {
> double d;
> int *pi = &d;
> return 0;
> }
gcc by default compiles just fine, but issues warning ;)
g++ gives error and that I don;t like ...


--
drwx------ 2 bmaxa bmaxa 4096 Aug 23 21:19 .

James Kuyper

unread,
Aug 23, 2012, 3:36:00 PM8/23/12
to
On 08/23/2012 03:21 PM, Melzzzzz wrote:
> On Thu, 23 Aug 2012 11:13:54 -0400
> James Kuyper <james...@verizon.net> wrote:
>
>> int main(void)
>> {
>> double d;
>> int *pi = &d;
>> return 0;
>> }
> gcc by default compiles just fine, but issues warning ;)
> g++ gives error and that I don;t like ...

Personally, I like it when a compiler lets me know I've written
dangerously bad code. Why do you feel differently?

Melzzzzz

unread,
Aug 23, 2012, 4:09:14 PM8/23/12
to
On Thu, 23 Aug 2012 15:36:00 -0400
James Kuyper <james...@verizon.net> wrote:

> On 08/23/2012 03:21 PM, Melzzzzz wrote:
> > On Thu, 23 Aug 2012 11:13:54 -0400
> > James Kuyper <james...@verizon.net> wrote:
> >
> >> int main(void)
> >> {
> >> double d;
> >> int *pi = &d;
> >> return 0;
> >> }
> > gcc by default compiles just fine, but issues warning ;)
> > g++ gives error and that I don;t like ...
>
> Personally, I like it when a compiler lets me know I've written
> dangerously bad code. Why do you feel differently?
>

I wasn't clear. c++ issues error and that I don;t like,
while behavior of C compiler is just fine IMO ;)


--
drwx------ 2 bmaxa bmaxa 4096 Aug 23 22:07 .

James Kuyper

unread,
Aug 23, 2012, 4:20:55 PM8/23/12
to
No, you were quite clear, but perhaps my response wasn't. It seems to me
that treating such code as an error is entirely appropriate; a warning
seems inadequate. Why do you feel differently?

If you actually have a legitimate need to do the conversion, and know
something implementation-specific that makes is safer than it is in the
general case, make those facts explicit by using a cast (in C) or a
reinterpret_cast<> (in C++). Requiring something as clumsy and as easily
searched for as reinterpret_cast<> in order to perform such a dangerous
conversion also strikes me as a good idea.


Melzzzzz

unread,
Aug 23, 2012, 4:33:25 PM8/23/12
to
On Thu, 23 Aug 2012 16:20:55 -0400
James Kuyper <james...@verizon.net> wrote:

> On 08/23/2012 04:09 PM, Melzzzzz wrote:
> > On Thu, 23 Aug 2012 15:36:00 -0400
> > James Kuyper <james...@verizon.net> wrote:
> >
> >> On 08/23/2012 03:21 PM, Melzzzzz wrote:
> >>> On Thu, 23 Aug 2012 11:13:54 -0400
> >>> James Kuyper <james...@verizon.net> wrote:
> >>>
> >>>> int main(void)
> >>>> {
> >>>> double d;
> >>>> int *pi = &d;
> >>>> return 0;
> >>>> }
> >>> gcc by default compiles just fine, but issues warning ;)
> >>> g++ gives error and that I don;t like ...
> >>
> >> Personally, I like it when a compiler lets me know I've written
> >> dangerously bad code. Why do you feel differently?
> >>
> >
> > I wasn't clear. c++ issues error and that I don;t like,
> > while behavior of C compiler is just fine IMO ;)
>
> No, you were quite clear, but perhaps my response wasn't. It seems to
> me that treating such code as an error is entirely appropriate; a
> warning seems inadequate. Why do you feel differently?

It seems that reduces number of casts in code. Warning is enough
if it is accidental error.

>
> If you actually have a legitimate need to do the conversion, and know
> something implementation-specific that makes is safer than it is in
> the general case, make those facts explicit by using a cast (in C) or
> a reinterpret_cast<> (in C++). Requiring something as clumsy and as
> easily searched for as reinterpret_cast<> in order to perform such a
> dangerous conversion also strikes me as a good idea.

Well compiler warnings are enough aren't they? Cast just masks warning
and makes compiler happy which is not good thing.
Well if you have to search trough code to find that one error is much
more difficult rather then just check compiler warning.
I don;t like to shut up compiler warnings with casts and that's
why C's approach is better.


James Kuyper

unread,
Aug 23, 2012, 5:07:48 PM8/23/12
to
On 08/23/2012 04:33 PM, Melzzzzz wrote:
> On Thu, 23 Aug 2012 16:20:55 -0400
> James Kuyper <james...@verizon.net> wrote:
>
>> On 08/23/2012 04:09 PM, Melzzzzz wrote:
>>> On Thu, 23 Aug 2012 15:36:00 -0400
>>> James Kuyper <james...@verizon.net> wrote:
>>>
>>>> On 08/23/2012 03:21 PM, Melzzzzz wrote:
>>>>> On Thu, 23 Aug 2012 11:13:54 -0400
>>>>> James Kuyper <james...@verizon.net> wrote:
>>>>>
>>>>>> int main(void)
>>>>>> {
>>>>>> double d;
>>>>>> int *pi = &d;
>>>>>> return 0;
>>>>>> }
>>>>> gcc by default compiles just fine, but issues warning ;)
>>>>> g++ gives error and that I don;t like ...
>>>>
>>>> Personally, I like it when a compiler lets me know I've written
>>>> dangerously bad code. Why do you feel differently?
>>>>
>>>
>>> I wasn't clear. c++ issues error and that I don;t like,
>>> while behavior of C compiler is just fine IMO ;)
>>
>> No, you were quite clear, but perhaps my response wasn't. It seems to
>> me that treating such code as an error is entirely appropriate; a
>> warning seems inadequate. Why do you feel differently?
>
> It seems that reduces number of casts in code. Warning is enough
> if it is accidental error.

As opposed to a deliberate error?
I have to disagree with you about whether reducing the number of casts
is a good idea. See below.

>> If you actually have a legitimate need to do the conversion, and know
>> something implementation-specific that makes is safer than it is in
>> the general case, make those facts explicit by using a cast (in C) or
>> a reinterpret_cast<> (in C++). Requiring something as clumsy and as
>> easily searched for as reinterpret_cast<> in order to perform such a
>> dangerous conversion also strikes me as a good idea.
>
> Well compiler warnings are enough aren't they? Cast just masks warning
> and makes compiler happy which is not good thing.

No, in the unlikely event that you have legitimate reason to write such
code, a cast explicitly requests that the conversion should occur (and
in some cases makes a conversion occur that could not occur implicitly).
It documents the developer's assertion that there is a legitimate reason
for performing what is ordinarily a rather dangerous operation. Code
that is correctly written to do what it does should not generate
warnings, even if it does use dangerous methods to get there. The danger
should be marked by the presence of the cast, not by generation of
warning messages. Producing warning messages from correct code
desensitizes developers to warning messages associated with incorrect
code. The cast should be easy to notice and search for, which is one
reason why reinterpret-cast<> is better than a C-style cast.

> Well if you have to search trough code to find that one error is much
> more difficult rather then just check compiler warning.

That doesn't explain why you think a compiler error is worse than a
compiler warning. If the error message is written properly, both will
give you the location of the problem, so there's no need to go searching.

> I don;t like to shut up compiler warnings with casts and that's
> why C's approach is better.

C doesn't make any different approach than C++ in this case; the
construct is equally an error in both languages, and neither language
specifies what an implementation should do about it, beyond issuing the
mandatory diagnostic. It's the compiler that's decided to handle these
two languages differently, not the language specifications.

Note that, given C's anti-aliasing rules, in the unlikely event that you
do have a legitimate reason to write code like that, it would be better
to use code like this:

union { double d, int i} u;
int *pi = &u.i;

Keith Thompson

unread,
Aug 23, 2012, 5:51:59 PM8/23/12
to
Melzzzzz <m...@zzzzz.com> writes:
[...]
> Well compiler warnings are enough aren't they? Cast just masks warning
> and makes compiler happy which is not good thing.
> Well if you have to search trough code to find that one error is much
> more difficult rather then just check compiler warning.
> I don;t like to shut up compiler warnings with casts and that's
> why C's approach is better.

The code in question was:

double d;
int *pi = &d;

and the question is whether a compiler should reject it with an error
message or accept it with a mere warning.

Personally, I would never deliberately write that. As far as the C
standard is concerned, it's a constraint violation, requiring a
diagnostic from any conforming compiler. If a compiler issues a warning
and continues to compile the code, it might seem reasonable to assume
that it will generate a conversion from `double*` to `int*` -- but in
fact the language standard does not say or imply that that's what will
happen. The behavior is undefined; a compiler could legally generate
code that sets `pi` to a null pointer, for example.

Whereas if I *want* to do that conversion, there's a perfectly valid way
to do it that likely won't result in a warning:

double d;
int *pi = (int*)&d;

A problem I have with gcc is that it doesn't clearly distinguish between
warnings about constraint violations, like the above, and warnings about
legal but questionable code, such as writing `if (x = 42)` rather than
`if (x == 42)`. Again, this doesn't violate the C standard; I just
personally find it annoying.

That's just my opinion (that all syntax errors and constraint violations
should be treated as fatal errors, not as warnings -- or at least that
there should be an option that works that way). What's yours? Which
errors do you think should be fatal, and which should merely trigger a
warning?

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Will write code for food.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Melzzzzz

unread,
Aug 23, 2012, 6:09:28 PM8/23/12
to
On Thu, 23 Aug 2012 14:51:59 -0700
Keith Thompson <ks...@mib.org> wrote:

> Melzzzzz <m...@zzzzz.com> writes:
> [...]
> > Well compiler warnings are enough aren't they? Cast just masks
> > warning and makes compiler happy which is not good thing.
> > Well if you have to search trough code to find that one error is
> > much more difficult rather then just check compiler warning.
> > I don;t like to shut up compiler warnings with casts and that's
> > why C's approach is better.
>
> The code in question was:
>
> double d;
> int *pi = &d;
>
> and the question is whether a compiler should reject it with an error
> message or accept it with a mere warning.
>
> Personally, I would never deliberately write that. As far as the C
> standard is concerned, it's a constraint violation, requiring a
> diagnostic from any conforming compiler. If a compiler issues a
> warning and continues to compile the code, it might seem reasonable
> to assume that it will generate a conversion from `double*` to `int*`
> -- but in fact the language standard does not say or imply that
> that's what will happen. The behavior is undefined; a compiler could
> legally generate code that sets `pi` to a null pointer, for example.

I didn't knew that this is undefined behavior.

>
> Whereas if I *want* to do that conversion, there's a perfectly valid
> way to do it that likely won't result in a warning:
>
> double d;
> int *pi = (int*)&d;
>

Problem is that we wont get warning in this case IMO.

> A problem I have with gcc is that it doesn't clearly distinguish
> between warnings about constraint violations, like the above, and
> warnings about legal but questionable code, such as writing `if (x =
> 42)` rather than `if (x == 42)`. Again, this doesn't violate the C
> standard; I just personally find it annoying.
>
> That's just my opinion (that all syntax errors and constraint
> violations should be treated as fatal errors, not as warnings -- or
> at least that there should be an option that works that way). What's
> yours? Which errors do you think should be fatal, and which should
> merely trigger a warning?

I don't think that converting one pointer type to other is error.
It seems to me that in spirit of C it should be allowed as it is.
It should issue error if types are incompatible, say pointer to int
has less bytes than pointer to double.
Problem is that if code that uses such conversions will work at all.
Say copying data as int / reading as double.
It might work in one case and might not in other.
It would be very difficult to find out bug (wrong result etc) without
compiler warning IMO.


James Kuyper

unread,
Aug 23, 2012, 7:01:43 PM8/23/12
to
You shouldn't. A cast is a way of saying "I'm doing something dangerous.
Trust me to know that I'm doing it right". If you don't know that the
conversion is safe, you shouldn't write the cast in the first place. If
you do know it's correct, a warning message would be inappropriate and
distracting. If you have a problem that you've traced into piece of code
containing a cast, you should always consider the possibility that the
cast is the problem.

>> A problem I have with gcc is that it doesn't clearly distinguish
>> between warnings about constraint violations, like the above, and
>> warnings about legal but questionable code, such as writing `if (x =
>> 42)` rather than `if (x == 42)`. Again, this doesn't violate the C
>> standard; I just personally find it annoying.
>>
>> That's just my opinion (that all syntax errors and constraint
>> violations should be treated as fatal errors, not as warnings -- or
>> at least that there should be an option that works that way). What's
>> yours? Which errors do you think should be fatal, and which should
>> merely trigger a warning?
>
> I don't think that converting one pointer type to other is error.

The C standard says otherwise. More precisely, depending upon the
context, it is sometimes a constraint violation, and sometimes has
undefined behavior for other reasons.

> It seems to me that in spirit of C it should be allowed as it is.
> It should issue error if types are incompatible, say pointer to int
> has less bytes than pointer to double.

The types are incompatible in this case, which is precisely what's wrong
with this code. I think you're using a different meaning for "compatible
types" than the C standard uses. Pointers to incompatible types are
themselves incompatible pointer types. Types of different sizes are
never compatible, regardless of which one is bigger, but types of the
same size can be incompatible too.

> Problem is that if code that uses such conversions will work at all.
> Say copying data as int / reading as double.
> It might work in one case and might not in other.
> It would be very difficult to find out bug (wrong result etc) without
> compiler warning IMO.

Keep in mind that, because the code contains a constraint violation, the
compiler is free to refuse to compile it, even if you're absolutely
certain that the code is safe. You'll have your compiler warning, but
you won't have a bug, because you won't have an executable.
If you're not certain, don't write such code. If you are certain, you
may need the cast to force your compiler to accept it.

Keith Thompson

unread,
Aug 23, 2012, 7:31:49 PM8/23/12
to
A compiler *could* issue a warning (because the conversion is
potentially unsafe), but most don't. A cast, particularly a pointer
cast, is effectively a way of telling the compiler "Shut up, I know what
I'm doing".

Are you saying you'd *want* a warning for the cast?

>> A problem I have with gcc is that it doesn't clearly distinguish
>> between warnings about constraint violations, like the above, and
>> warnings about legal but questionable code, such as writing `if (x =
>> 42)` rather than `if (x == 42)`. Again, this doesn't violate the C
>> standard; I just personally find it annoying.
>>
>> That's just my opinion (that all syntax errors and constraint
>> violations should be treated as fatal errors, not as warnings -- or
>> at least that there should be an option that works that way). What's
>> yours? Which errors do you think should be fatal, and which should
>> merely trigger a warning?
>
> I don't think that converting one pointer type to other is error.

Converting from one pointer type to another using a cast is not an
error, though the result is implementation-defined, and using the
converted pointer can lead to undefined behavior.

*Assigning* a pointer value of one type to an object of another
incompatible pointer type is a constraint violation (apart from
special cases involving void*). Likewise for initialization and
argument passing.

> It seems to me that in spirit of C it should be allowed as it is.
> It should issue error if types are incompatible, say pointer to int
> has less bytes than pointer to double.

The word "incompatible" has a specific meaning in C, and it's not
related to the sizes of the types. For example, `int*` and `long*` are
incompatible, even if `int` and `long` happen to have exactly the same
size and representation.

> Problem is that if code that uses such conversions will work at all.
> Say copying data as int / reading as double.
> It might work in one case and might not in other.
> It would be very difficult to find out bug (wrong result etc) without
> compiler warning IMO.

I'm still not sure what you're saying. A compiler *must* issue a
diagnostic for an assignment between incompatible pointer types.
In my opinion, that diagnostic *should* be a fatal error.

Are you saying that a mere warning is preferable? Why?

eq mail

unread,
Aug 23, 2012, 8:57:11 PM8/23/12
to
On 23/08/2012, 20:02, Chicken McNuggets wrote:
> On 23/08/2012 16:13, James Kuyper wrote:
> > On 08/23/2012 08:27 AM, Chicken McNuggets wrote:
> >> On 22/08/2012 16:05, James Kuyper wrote:
> > Is it? Try compiling the following code:
> >
> > int main(void)
> > {
> >    double d;
> >    int *pi = &d;
> >    return 0;
> > }
>
> Yes. It compiles with a warning as expected. Not a great example of type
> safety.

That's up to your implementation. The fact that you do get a
diagnostic tells that a level of type-safety is there, but you should
probably look for settings that would make such diagnostics also fatal
errors.

> This code on the other hand compiles with no warnings:
>
> #include <stdlib.h>
>
> int main(void)
> {
>         int *ptr = (int*) malloc(sizeof(long));
>         *ptr = 10;
>         short blah = *ptr;
>         free(ptr);
>         exit(EXIT_SUCCESS);
>
> }
>
> so please explain how C is a type safe language?

I don't think this specific example succeeds at showing the
deficiencies of C and it's type system nearly as much as it highlights
several bad coding practices and a pitfall of one standard library
function.

eq mail

unread,
Aug 23, 2012, 9:07:09 PM8/23/12
to
On 23/08/2012, 20:24, jacob navia wrote:
> Le 23/08/12 19:02, Chicken McNuggets a crit :
> > This code on the other hand compiles with no warnings:
>
> > #include <stdlib.h>
>
> > int main(void)
> > {
> >      int *ptr = (int*) malloc(sizeof(long));
> >      *ptr = 10;
> >      short blah = *ptr;
> >      free(ptr);
> >      exit(EXIT_SUCCESS);
> > }
>
> > so please explain how C is a type safe language?
>
> Because you can assign a 64 bit integer into a short.

Is that really an explanation for how C is a type safe language?

> If there is an overflow the behavior is undefined
> but it can't be any overflow in your example since
> 10 will fit into a short.

Integer conversions, such as the one above, do not yield undefined
behaviour.

Eric Sosman

unread,
Aug 23, 2012, 9:30:34 PM8/23/12
to
The program is (technically) at risk of undefined behavior
on any machine where sizeof(int) > sizeof(long). No such machine
has ever been rumored, but would be within the realm of possibility
for a C implementation: int might be 40 bits wide (including 15
padding bits) while long is 32 with no padding.

Also, there's the possibility that malloc() returns NULL.

But turning to the conversion: Yes, it's well-defined -- But
that's only because the value 10 is within both the range of int
and the range of short. Change the 10 to 123456789101112 and you
get quite a different picture.

--
Eric Sosman
eso...@ieee-dot-org.invalid

eq mail

unread,
Aug 24, 2012, 12:46:31 AM8/24/12
to
This is a strange reply. I chose not to dissect the example (although
I mentioned bad coding practices in general else-thread).

>      But turning to the conversion: Yes, it's well-defined -- But
> that's only because the value 10 is within both the range of int
> and the range of short.  Change the 10 to 123456789101112 and you
> get quite a different picture.

Yes and no. It won't be UB no matter what, so the statement still
stands.

Nick Keighley

unread,
Aug 24, 2012, 5:37:28 AM8/24/12
to
On Aug 23, 9:33 pm, Melzzzzz <m...@zzzzz.com> wrote:
> On Thu, 23 Aug 2012 16:20:55 -0400
> James Kuyper <jameskuy...@verizon.net> wrote:
> > On 08/23/2012 04:09 PM, Melzzzzz wrote:
> > > On Thu, 23 Aug 2012 15:36:00 -0400
> > > James Kuyper <jameskuy...@verizon.net> wrote:
> > >> On 08/23/2012 03:21 PM, Melzzzzz wrote:
> > >>> On Thu, 23 Aug 2012 11:13:54 -0400
> > >>> James Kuyper <jameskuy...@verizon.net> wrote:

> > >>>> int main(void)
> > >>>> {
> > >>>>   double d;
> > >>>>   int *pi = &d;
> > >>>>   return 0;
> > >>>> }
>
> > >>> gcc by default compiles just fine, but issues warning ;)
> > >>> g++ gives error and that I don;t like ...
>
> > >> Personally, I like it when a compiler lets me know I've written
> > >> dangerously bad code. Why do you feel differently?

Melzzzzz, isn't the term "dangerously bad code" unclear in some way?

> > > I wasn't clear. c++ issues error and that I don;t like,
> > > while behavior of C compiler is just fine IMO ;)
>
> > No, you were quite clear, but perhaps my response wasn't. It seems to
> > me that treating such code as an error is entirely appropriate; a
> > warning seems inadequate. Why do you feel differently?
>
> It seems that reduces number of casts in code. Warning is enough
> if it is accidental error.

since when was "reduce[ing] number of casts" a programming goal?
Reducing *unnecessary* casts, yes

> > If you actually have a legitimate need to do the conversion, and know
> > something implementation-specific that makes is safer than it is in
> > the general case, make those facts explicit by using a cast (in C) or
> > a reinterpret_cast<> (in C++). Requiring something as clumsy and as
> > easily searched for as reinterpret_cast<> in order to perform such a
> > dangerous conversion also strikes me as a good idea.
>
> Well compiler warnings are enough aren't they?

my personnel (and company) programming standards require a warning
free compile. System test won't start testing code until it compiles
without warnings (for some Microsoft errors this actually means
suppressing some warnigns).

Gareth Owen

unread,
Aug 24, 2012, 12:29:30 PM8/24/12
to
David Brown <da...@westcontrol.removethisbit.com> writes:

> If you want to stick close to C, then C++11 supports lambda functions
> and closures.

And all C++ supports functors (i.e. classes with operator()) which will
certainly do the job in many use-cases...

Gareth Owen

unread,
Aug 24, 2012, 12:30:03 PM8/24/12
to
Nick Keighley <nick_keigh...@hotmail.com> writes:

> scheme
>
> (and does Python have particularly good support for functional
> programming?)

Is the second line an aside, or sample Scheme code?

Melzzzzz

unread,
Aug 24, 2012, 12:31:39 PM8/24/12
to
On Thu, 23 Aug 2012 16:31:49 -0700
Well, warning would be welcome , yes ;)

>
>
> > Problem is that if code that uses such conversions will work at all.
> > Say copying data as int / reading as double.
> > It might work in one case and might not in other.
> > It would be very difficult to find out bug (wrong result etc)
> > without compiler warning IMO.
>
> I'm still not sure what you're saying. A compiler *must* issue a
> diagnostic for an assignment between incompatible pointer types.
> In my opinion, that diagnostic *should* be a fatal error.
>
> Are you saying that a mere warning is preferable? Why?

I think that forcing to cast from one type to another
is just too verbose, but I'm not sure anymore since
Im now changing my mind. All in all you two (James)
convinced me that I am wrong...





Andrew Cooper

unread,
Aug 24, 2012, 9:35:48 PM8/24/12
to
On 23/08/2012 18:02, Chicken McNuggets wrote:
> On 23/08/2012 16:13, James Kuyper wrote:
>> On 08/23/2012 08:27 AM, Chicken McNuggets wrote:
>>> On 22/08/2012 16:05, James Kuyper wrote:
>>>
>>>>
>>>> However, I think that compile() looks horribly type-unsafe.
>>>>
>>>
>>> Type safety in C? That's a novel concept :).
>>
>> Is it? Try compiling the following code:
>>
>> int main(void)
>> {
>> double d;
>> int *pi = &d;
>> return 0;
>> }
>>
>
> Yes. It compiles with a warning as expected. Not a great example of type
> safety.
>
> This code on the other hand compiles with no warnings:
>
> #include <stdlib.h>
>
> int main(void)
> {
> int *ptr = (int*) malloc(sizeof(long));
> *ptr = 10;
> short blah = *ptr;
> free(ptr);
> exit(EXIT_SUCCESS);
> }
>
> so please explain how C is a type safe language?

C is not a strongly typed language.

Data is raw.
The numbers you find in RAM are just integers.

C will help you with types in 90% of cases, but the remaining 10% is
left up to the developer. This is the price of having complete and
utter control over anything you need if you wish (with the option of
degrading to assembly if you absolutely need to).

~Andrew
0 new messages