Generic programming in C.

J

jacob navia

When I presented an example of the interface here (thread CCL vs STL, a
comparison), many posters complained about the syntax:

int main(void)
{
List *L;
int data;

L = iList.Create(sizeof(int));

data = 0;
iList.Add(L,&data);
iList.PushFront(L,&data);
data = 2;
iList.InsertAt(L,1,&data);
// ...
}

It was said that passing a void pointer couldn't be checked by
the compiler (what is trure of course) and that the whole wasn't
very easy to use (maybe).

I have developed a "generic" approach using a parametized file.

For instance, if you want a list of integers, you write a small file
like this:

---------------------------intlist.c (the implementation)
#include "containers.h"
#undef DATA_TYPE
#define DATA_TYPE int
#include "listgen.c"
#undef DATA_TYPE

----------------------------intlist.h (the declarations)
#include "containers.h"
#undef DATA_TYPE
#define DATA_TYPE int
#include "listgen.h"
#undef DATA_TYPE

That's all. This defines following stuff:

A type called "intList" that implements ALL the functions
of the list container for the int data type. The requirements
for the data type are that it must be a value type, i.e. the
code assumes that it is passed by value. At the same time
the "iintList" interface is defined with the same entry
points as the generic list interface iList.

Besides it must be a single token, for instance for using
long long you must

typedef long long longlong;
#define DATA_TYPE longlong

Besides those two points, the file allows now to do as follows:

-------------------------------using specialized list container
#include "containers.h"
#include "intlist.h"
static int PrintInt(int data,void *extraArgs)
{
fprintf(extraArgs,"%d ",data);
}
int main(void)
{
intList *L;
int data;

L = iintList.Create(sizeof(int));
iintList.Add(L,0);
iintList.PushFront(L,0);
iintList.InsertAt(L,1,2);

iintList.Add(L,5);

iintList.Add(L,6);
iintList.Apply(L,PrintInt,stdout);
iintList.Finalize(L);
}

Note that the "Create" function still takes the same
arguments as the original generic one. I want to keep
all code compatible since it would be better to avoid code bloat.

Each instanstiated type adds 18.6K to the program (x86+gcc/OS X Mac)

If you have several types, code could be shared in many instances
since the data type remains compatible, and the signatures weren't
changed.

For the time being code is NOT shared.


What do you think?

jacob
 
R

Rui Maciel

jacob said:
What do you think?

Is the programmer forced to write a pair of source files for each data type
used in a list? And what happens when a programmer must use lists of
different data types in the same source file?


Rui Maciel
 
J

jacob navia

Le 18/05/12 14:51, Rui Maciel a écrit :
Is the programmer forced to write a pair of source files for each data type
used in a list? And what happens when a programmer must use lists of
different data types in the same source file?


Rui Maciel

The programmer is not "forced " to do anything. He is a free man and can
decide:

1) Use the generic interface with void pointers.
2) Use the generic interface with a special file for each type.
3) Abandon C and use C#

Note that solution (2) means he has to add the resulting object file to
a private library for the application where all those classes for
all the types are stored.

For instance in the Mac he would have to write

mycontainers.a: intList.c
gcc -c -Os intlist.c
ar -r mycontainers.a intlist.o

or something similar.

And what happens when a programmer must use lists of
different data types in the same source file?

Nothing. He just includes the header files for the different types.

But I see where you want to go:

Yes, I know. C++ is the best invention since sliced bread. I know.
 
B

BGB

When I presented an example of the interface here (thread CCL vs STL, a
comparison), many posters complained about the syntax:

int main(void)
{
List *L;
int data;

L = iList.Create(sizeof(int));

data = 0;
iList.Add(L,&data);
iList.PushFront(L,&data);
data = 2;
iList.InsertAt(L,1,&data);
// ...
}

It was said that passing a void pointer couldn't be checked by
the compiler (what is trure of course) and that the whole wasn't
very easy to use (maybe).

I have developed a "generic" approach using a parametized file.

For instance, if you want a list of integers, you write a small file
like this:

---------------------------intlist.c (the implementation)
#include "containers.h"
#undef DATA_TYPE
#define DATA_TYPE int
#include "listgen.c"
#undef DATA_TYPE

----------------------------intlist.h (the declarations)
#include "containers.h"
#undef DATA_TYPE
#define DATA_TYPE int
#include "listgen.h"
#undef DATA_TYPE

That's all. This defines following stuff:

A type called "intList" that implements ALL the functions
of the list container for the int data type. The requirements
for the data type are that it must be a value type, i.e. the
code assumes that it is passed by value. At the same time
the "iintList" interface is defined with the same entry
points as the generic list interface iList.

Besides it must be a single token, for instance for using
long long you must

typedef long long longlong;
#define DATA_TYPE longlong

Besides those two points, the file allows now to do as follows:

-------------------------------using specialized list container
#include "containers.h"
#include "intlist.h"
static int PrintInt(int data,void *extraArgs)
{
fprintf(extraArgs,"%d ",data);
}
int main(void)
{
intList *L;
int data;

L = iintList.Create(sizeof(int));
iintList.Add(L,0);
iintList.PushFront(L,0);
iintList.InsertAt(L,1,2);

iintList.Add(L,5);

iintList.Add(L,6);
iintList.Apply(L,PrintInt,stdout);
iintList.Finalize(L);
}

Note that the "Create" function still takes the same
arguments as the original generic one. I want to keep
all code compatible since it would be better to avoid code bloat.

Each instanstiated type adds 18.6K to the program (x86+gcc/OS X Mac)

If you have several types, code could be shared in many instances
since the data type remains compatible, and the signatures weren't
changed.

For the time being code is NOT shared.


What do you think?

I once did vaguely similar using an extended preprocessor and block macros.

#defmacro FOO(type)
....
lots of stuff...
....
#endmacro

....

FOO(int)

the block-macro differed from #define mostly in that it could handle
larger globs of code and didn't need endless '\' characters.

there were lots of other features as well (such as delayed preprocessor
commands, ...).

IMO, it is a little nicer, but didn't really get lots of use since it is
an extra hassle to set up code to be passed through an extra tool prior
to compilation (unless it stood between make and the compiler or
something, which it didn't do).


otherwise:
passing parameters into specialized header files via #define is a trick
I have used before. it works I guess.

a particular example is this of the "main loop" stub for my 3D engine,
where due to technical reasons I ended up having to "hijack" main (or
WinMain), and a special header generates the main loop.

parameters are used for, among other things, setting GC options (initial
heap-size limits, ...) and indicating which functions to call (functions
are called at various locations in order to do whatever, such as set-up
or tear-down app, redraw the window, ...).


or such...
 
I

Ike Naar

-------------------------------using specialized list container
#include "containers.h"

Is this necessary, given that intlist.h includes containers.h ?
#include "intlist.h"
static int PrintInt(int data,void *extraArgs)
{
fprintf(extraArgs,"%d ",data);

Nit: PrintInt promises to return an int, but fails to do so.
}
int main(void)
{
intList *L;
int data;

L = iintList.Create(sizeof(int));
iintList.Add(L,0);
iintList.PushFront(L,0);
iintList.InsertAt(L,1,2);

iintList.Add(L,5);

iintList.Add(L,6);
iintList.Apply(L,PrintInt,stdout);
iintList.Finalize(L);
}

Note that the "Create" function still takes the same
arguments as the original generic one. I want to keep
all code compatible since it would be better to avoid code bloat.

There is no need to supply the size of the data type to
iintList.Create(), because the function knows (or at least should now)
that the value must be sizeof(int).
The user is forced to specify a value: if the correct value is passed
it offers no new information to the function, if a wrong value is passed
it may lead to subtle runtime errors. Nonzero cost, zero benefit.

You say you want to avoid code bloat, but as far as I can tell, a
parameterless iintList.Create() needs to be no more than a minimal
wrapper around the generic iList.Create(),

intList *iintList.Create(void) { return iList.Create(sizeof(int)); }

That's not much code bloat.
 
R

Rui Maciel

jacob said:
The programmer is not "forced " to do anything. He is a free man and can
decide:

1) Use the generic interface with void pointers.
2) Use the generic interface with a special file for each type.
3) Abandon C and use C#

Note that solution (2) means he has to add the resulting object file to
a private library for the application where all those classes for
all the types are stored.

For instance in the Mac he would have to write

mycontainers.a: intList.c
gcc -c -Os intlist.c
ar -r mycontainers.a intlist.o

or something similar.

So, basically your technique requires the programmer to add a couple source
files for each list that stores a different data type. Is this true?

Nothing. He just includes the header files for the different types.

Could you please provide an example where you use, say, 3 of your lists,
with each one handling a different data type?


Rui Maciel
 
J

jacob navia

Le 18/05/12 23:30, Rui Maciel a écrit :
So, basically your technique requires the programmer to add a couple source
files for each list that stores a different data type. Is this true?

Well you can reduce all files to one if you wish:

#include "containers.h"
#undef DATA_TYPE
#define DATA_TYPE int
#include "listgen.h"
#undef DATA_TYPE


#define DATA_TYPE double
#include "listgen.h"
#undef DATA_TYPE

#define DATA_TYPE float
#include "listgen.h"
#undef DATA_TYPE


Lists for strings (wchar_t and plain char) are already provided. I will
deliver the package with all the basic data types already done
(int, double, etc).

But yes, in principmle you have to add those defines

Could you please provide an example where you use, say, 3 of your lists,
with each one handling a different data type?


#include "containers.h"
#undef DATA_TYPE
#define DATA_TYPE int
#include "listgen.h"
#undef DATA_TYPE


#define DATA_TYPE double
#include "listgen.h"
#undef DATA_TYPE

#define DATA_TYPE longlong
typedef long long longlong;
#include "listgen.h"
#undef DATA_TYPE


int main(void)
{
intList *L;
doubleList *D;
longlongList *LL;

L = intList.Create(sizeof(int))
D = doubleList.Create(sizeof(double));
LL = longlongList.Create(sizeof(longlong));

doubleList.Add(D,3.141592653);
longlongList.Add(LL,0xDEADBEEFULL);
// etc
}
 
E

Edward Rutherford

jacob said:
Le 18/05/12 14:51, Rui Maciel a écrit :

The programmer is not "forced " to do anything. He is a free man

Sorry, but this sort of casual sexism really gets my goat - it
embarrasses our whole discipline.

It's no wonder that so few women pursue careers in software engineering
when this sort of neanderthal attitude is omnipresent, seeking to exclude
them.

Using gender-neutral language costs the author nothing and is simple
politeness.

IMO. YMMV.

//EPR
 
K

Kaz Kylheku

Sorry, but this sort of casual sexism really gets my goat - it
embarrasses our whole discipline.

It's no wonder that so few women pursue careers in software engineering
when this sort of neanderthal attitude is omnipresent, seeking to exclude
them.

Using gender-neutral language costs the author nothing and is simple
politeness.

Very well, let us continue. Where were we?
Ah yes, "He or she is a free man ..."
 
J

jacob navia

Le 19/05/12 08:34, Edward Rutherford a écrit :
Sorry, but this sort of casual sexism really gets my goat - it
embarrasses our whole discipline.


Look, I am not responsible for the sexism of the english
language. English is not my mother language but I think
that the generic word for human being is "man".

The dictionary says:

man:

1. an adult male person, as distinguished from a boy or a woman.
2.
a member of the species Homo sapiens or all the members of this species
collectively, without regard to sex: prehistoric man.

My mistake would have been that I used the "he" instead of he/she
because I tried to make the "he" compatible with the first
meaning of "man" and not the second.

I agree that there are less women than men in here, for instance
there isn't a single women in this group as far as I know, and
that this situation is bad.

In any case no sexism was intended.

jacob
 
M

Malcolm McLean

בת×ריך ×™×•× ×©×‘×ª,19 במ××™ 2012 08:08:24 UTC+1, מ×ת jacob navia:
Look, I am not responsible for the sexism of the english
language. English is not my mother language but I think
that the generic word for human being is "man".
In English the male embraces the female. So we say "dog" unless we specifically mean a female dog, when we use "bitch", we call our country the "United Kingdom" even though we are currently ruled by a Queen, we man the barricades during our riots.

Some feminist-inclined women are trying to change this rule, so you'll often see constructions like s/he, or "he or she". But then they decide that it's still sexist to have the "he" first, so we have "she or he", and the whole thing collapses into self-consciousness. If you must use feminist langage, it's better to rewrite sentences with "they". So instead of "he must be a qualified draughtsman" "they must have a qualification in draughting technical drawings".
 
M

Malcolm McLean

בת×ריך ×™×•× ×©×‘×ª,19 במ××™ 2012 08:08:24 UTC+1, מ×ת jacob navia:
I agree that there are less women than men in here, for instance
there isn't a single women in this group as far as I know, and
that this situation is bad.
Because the truth is that very few women are interested in computer programming. There are many who are interested in careers in computer programming,but that's not the same thing, and comp.lang.c doesn't directly translate to a career benefit.

There are odd individual exceptions, of course, we we don't currently have one on the newsgroup.
 
B

BartC

Edward Rutherford said:
jacob navia wrote:

Sorry, but this sort of casual sexism really gets my goat - it
embarrasses our whole discipline.

It's no wonder that so few women pursue careers in software engineering
when this sort of neanderthal attitude is omnipresent, seeking to exclude
them.
Using gender-neutral language costs the author nothing and is simple
politeness.

I think there is a cost. You would either have to change half the English
language (which often uses 'man' in words to mean man or woman), or have to
write in stilted, obviously contrived language that will come across as
forced, especially in a field which *is* mostly men.

("It is one small step for Man - or Woman - ..." doesn't have quite the same
ring.)
 
J

Jens Gustedt

Hello,

I already had expressed my ideas about this on how to do this with
macros in the other thread, I'll just try to give a brief summary.

Am 18.05.2012 11:14, schrieb jacob navia:
I have developed a "generic" approach using a parametized file.

For instance, if you want a list of integers, you write a small file
like this:

---------------------------intlist.c (the implementation)
#include "containers.h"
#undef DATA_TYPE
#define DATA_TYPE int
#include "listgen.c"
#undef DATA_TYPE

Instead of this if done with macros my "implementation" file for the
type int would simply contain

---------------------------intlist.c (the implementation)
#include "containers.h"

ILIST_DEFINE(int);


and the header file would just be

----------------------------intlist.h (the declarations)
#include "containers.h"

ILIST_DECLARE(int);

That's all.

that's even less and would even be sort of readable, and less subject to
typos.

The code in containers would be slightly more difficult to read and
write (those macros can become a bit lenghty)

A user could just declare lists for different types without problems

#include "containers.h"

ILIST_DECLARE(int);
ILIST_DECLARE(unsigned);
ILIST_DECLARE(toto);
ILIST_DECLARE(toto*);

and would have to provide

ILIST_DEFINE(toto);
ILIST_DEFINE(toto*);

in exactly one of his compilation units. (Supposing that the ones for
the standard types are provided by the library.)

This defines following stuff:
A type called "intList" that implements ALL the functions
of the list container for the int data type. The requirements
for the data type are that it must be a value type, i.e. the
code assumes that it is passed by value. At the same time
the "iintList" interface is defined with the same entry
points as the generic list interface iList.

I still don't get the point of this interface. I think that is is
substantially clearer and less error prone to write

intList_toto_create()

than

iintList.Create(sizeof(toto))

since the first version could give you stronger typing of the return
value. If for implementation reasons you'd like to have this all as
one generic list type under the hood (which can be a valid
implementation choice) this shouldn't concern the user of your library
and shouldn't influence on the interface.

If you want it as a functional expression in the type just define
something like

#if __STDC_VERSION__ < 201100L
# define intList_create(T) intList_ ## T ## _create()
#else
# define intList_create(T) _Generic((T){0}, int: intList_int_create,
unsigned: intList_unsigned_create, ...)()
#endif

"..." here for all other types that your interface is
implemented. That even would avoid to be dependent on the particular
"spelling" a user choses for T.

Jens
 
B

Ben Bacarisse

BartC said:
I think there is a cost. You would either have to change half the English
language (which often uses 'man' in words to mean man or woman), or have to
write in stilted, obviously contrived language that will come across as
forced, especially in a field which *is* mostly men.

You realize, I hope, that you are making the case for gender-neutral
language? Everyone who finds it stilted and contrived is evidence of
it's continued importance.

I never (knowingly) use gender-biased terms. Do you find my posts
stilted and contrived? If so, please say where because I am always on
the look-out for ways to improve my writing.
("It is one small step for Man - or Woman - ..." doesn't have quite the same
ring.)

He intended to say (and has always claimed he did say) "That's one small
step for *a* man..." which has the benefit of being correct, as well as
being entirely gender-appropriate. Presumably you wanted to make a
point what followed.
 
B

Ben Bacarisse

Malcolm McLean said:
בת×ריך ×™×•× ×©×‘×ª, 19 במ××™ 2012 08:08:24 UTC+1, מ×ת jacob navia:
In English the male embraces the female. So we say "dog" unless we
specifically mean a female dog, when we use "bitch", we call our
country the "United Kingdom" even though we are currently ruled by a
Queen, we man the barricades during our riots.

That's a good summary of the problem. In everyday language, to be
female is to be the other, the alternative, the notable case.

... it's better to rewrite sentences with
"they". So instead of "he must be a qualified draughtsman" "they must
have a qualification in draughting technical drawings".

That's a good suggestion and one I almost always use.
 
B

Ben Bacarisse

Kaz Kylheku said:
On 2012-05-19, Edward Rutherford
<[email protected]>

Very well, let us continue. Where were we?
Ah yes, "He or she is a free man ..."

If you feel you are on a roll, are there any other significant issues
you'd like to mock? Might as well get it all out in one thread...
 
J

James Kuyper

On 05/19/2012 05:08 AM, Malcolm McLean wrote:
....
In English the male embraces the female.

The fact that it has done so in the past is no good reason for
continuing to do the same stupid thing in the present. Blind obedience
to that sexist tradition ironically leaves English with no way to
distinguish a reference exclusively to a male from a reference that
includes either a male or a female.
 
M

Marco

A programmer is not "forced " to do anything. You are free to do as you like.
("It is one small step for Man - or Woman - ..." doesn't have quite the same
ring.)

"mankind" means all people
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,034
Messages
2,570,356
Members
47,002
Latest member
RobertoLip

Latest Threads

Top