Preprocessor problem

J

jacob navia

Hi

I have succeeded in making a completely general value lists
in a single file. This template file is given its
parameters by a small parameter file that looks
like this:

#define DATA_TYPE int
#define LIST_TYPE intList
#define LIST_ELEMENT intListElement
#define INTERFACE intListInterface
#define INTERFACE_NAME iintList
#define ITERATOR intListIterator

As you cansee, all those names are derived from the name of
the underlying listdata type, in this case "int"

Does anyone here see a way of generating those names
automatically given ONLY the "int" parameter?

The naive approach will not work since in

#define LIST_TYPE(t) t##List

the "t" parameter is NOT evaluated so
LIST_TYPE(DATA_TYPE) will produce
DATA_TYPEList and NOT intList
even if I #defined DATA_TYPE to int.

Any ideas?


Thanks

P.S. I will explain the template file approach in another message
That will allow you to produce automatically a list interface
for ANY datatype without having to loose compiler checking
and use void pointers at all.
 
S

Stefan Ram

jacob navia said:
the "t" parameter is NOT evaluated so
LIST_TYPE(DATA_TYPE) will produce
DATA_TYPEList and NOT intList

#include <stdio.h>
#define EVAL(t) t
#define CAT_(a,b) a##b
#define CAT(a,b) CAT_(a,b)
#define LIST_TYPE(t) CAT(EVAL(t),List)
#define DATA_TYPE int
int main( void )
{ int const DATA_TYPEList = 0;
int const intList = 1;
printf( "%d\n", LIST_TYPE(DATA_TYPE) ); }
 
J

jacob navia

Le 13/05/12 13:41, Chine Bleu, Russie Blanche, et Khmer Rouge a écrit :
@ more t.c; echo ------; cc -E t.c
#define DATA_TYPE int
#define LIST_TYPE(t) CONCAT(t,List)
#define CONCAT(x,y) x##y

LIST_TYPE(DATA_TYPE)

Thanks Mr China Bleu, Russie Blanche et Khmer Rouge!
 
J

jacob navia

Le 13/05/12 13:41, Stefan Ram a écrit :
#include<stdio.h>
#define EVAL(t) t
#define CAT_(a,b) a##b
#define CAT(a,b) CAT_(a,b)
#define LIST_TYPE(t) CAT(EVAL(t),List)
#define DATA_TYPE int
int main( void )
{ int const DATA_TYPEList = 0;
int const intList = 1;
printf( "%d\n", LIST_TYPE(DATA_TYPE) ); }

Thanks!

AFTER looking at it, of course you tell yourself

Why I didn't came up with that earlier?

:)
 
I

Ike Naar

Hi

I have succeeded in making a completely general value lists
in a single file. This template file is given its
parameters by a small parameter file that looks
like this:

#define DATA_TYPE int
#define LIST_TYPE intList
#define LIST_ELEMENT intListElement
#define INTERFACE intListInterface
#define INTERFACE_NAME iintList
#define ITERATOR intListIterator

As you cansee, all those names are derived from the name of
the underlying listdata type, in this case "int"

Does anyone here see a way of generating those names
automatically given ONLY the "int" parameter?

The naive approach will not work since in

#define LIST_TYPE(t) t##List

the "t" parameter is NOT evaluated so
LIST_TYPE(DATA_TYPE) will produce
DATA_TYPEList and NOT intList
even if I #defined DATA_TYPE to int.

Any ideas?


Thanks

P.S. I will explain the template file approach in another message
That will allow you to produce automatically a list interface
for ANY datatype without having to loose compiler checking
and use void pointers at all.

There may be limitations to such a template file approach.

A few questions that immediately pop up:

- Can one have more than one list type in the same program
(e.g. intList and doubleList) ?
- Can one have a list of, say, 'long double' or 'struct foo' ?
- Can one have a list-of-lists type, such as intListList ?
 
J

Jens Gustedt

Hello

Am 13.05.2012 13:13, schrieb jacob navia:
#define LIST_TYPE(t) t##List

others have already answered for the tricks on how you can evaluate
that properly, but I think you should be more careful from the start
about your naming conventions if you want ever that such a thing would
be introduce in a standard library.

Usually naming for the standard works either with reserved prefixes
(such as "str") or with names starting with an underscore and a
capital (or two underscores). The use of an include file should not
pollute the namespace that is available to user code. So something
like

#define LIST_TYPE(T) PASTE2(_List_, T)

with a prefix "_List_" would be more appropriate. If you want to use
and distribute your library before it becomes part of the standard you
should perhaps additionally use your own prefix "CCL_" or something
like that to avoid any possible conflicts with platforms and user
code:

#define LIST_TYPE(T) PASTE3(CCLPREFIX, _List_, T)
P.S. I will explain the template file approach in another message
That will allow you to produce automatically a list interface
for ANY datatype without having to loose compiler checking
and use void pointers at all.

From what you give I can imagine how this looks, I guess something
along the line

#ifndef CCL_DATA_TYPE
# define CCL_DATA_TYPE int
# include <ccl_list_template.h>
# undef CCL_DATA_TYPE
#endif

and this is probably a good way to *implement* emulation template
code within C.

But I'd be much surprised if one day such things would make it as
interface definitions into the standard. In P99 I always prefer to put
such declarations inside two types of macros, one for the interfaces

P99_DECLARE_LIST_TYPE(toto);

and one for the implementation

P99_DEFINE_LIST_TYPE(toto);

since usually the function symbols have to be emitted in a specific
place, too. Macros like that one could even be written such that they
accept a whole list of type names e.g to declare the list types for
all basic types

P99_DECLARE_LIST_TYPE(P99_STD_BASIC_TYPES);

Jens
 
J

jacob navia

Le 13/05/12 14:39, Ike Naar a écrit :
There may be limitations to such a template file approach.

A few questions that immediately pop up:

- Can one have more than one list type in the same program
(e.g. intList and doubleList) ?
yes of course
- Can one have a list of, say, 'long double' or 'struct foo' ?
No. You have to define

typedef long double longdouble;

Then
#define DATA_TYPE longdouble


I do not see that as a big limitation/problem.
- Can one have a list-of-lists type, such as intListList ?

I have done this only for VALUE types, i.e. small types that can be
passed as values. For other types you can use the generic list
interface. For your example you do:

// list of lists pointers
List *ListOfLists = iList.Create(sizeof List *);
List *doubleList = idoubleList.Create(); // list of doubles
// Add the list of doubles pointer to the list of lists
iList.Add(ListOfLists,doubleList);

Note that all data is COPIED into a container. If you would store
the list header directly the copy wouldn't be changed when you
change the doubleList.

Probably (not tested yet) you can also use the new value
interface with

typedef List *pList;
#define DATA_TYPE pList

But I would rather use the old generic interface as shown above.

The objective with the Value types is to make the usage of
the basic types easy: you write

idoubleList.Add(doubleList,3.141592653);

instead of

double pi = 3.141592653;
iList.Add(doubleList,&pi);

jacob
 
J

jacob navia

Le 13/05/12 15:06, Jens Gustedt a écrit :
Hello

Am 13.05.2012 13:13, schrieb jacob navia:

others have already answered for the tricks on how you can evaluate
that properly, but I think you should be more careful from the start
about your naming conventions if you want ever that such a thing would
be introduce in a standard library.

Usually naming for the standard works either with reserved prefixes
(such as "str") or with names starting with an underscore and a
capital (or two underscores). The use of an include file should not
pollute the namespace that is available to user code. So something
like

#define LIST_TYPE(T) PASTE2(_List_, T)

with a prefix "_List_" would be more appropriate. If you want to use
and distribute your library before it becomes part of the standard you
should perhaps additionally use your own prefix "CCL_" or something
like that to avoid any possible conflicts with platforms and user
code:

#define LIST_TYPE(T) PASTE3(CCLPREFIX, _List_, T)

Well, the problem opf your approach is that the user of the
container library would be forced to write:

CCL_List_int *foo;

instead of

intList *foo;

what is shorter and much more readable.

For each container the library exports two object names:

Interface name: iList, iDictionary iVector, etc.
Type name: List, Dictionary, Vector, etc.

Note that all functions names are hidden behind the interface name:

iList.Add, iVector.Append, iDictionary.Find, etc

This reduces considerably the name pollution of the library.
 
J

Jens Gustedt

Am 13.05.2012 16:45, schrieb jacob navia:
Le 13/05/12 15:06, Jens Gustedt a écrit :
Well, the problem opf your approach is that the user of the
container library would be forced to write:

CCL_List_int *foo;

instead of

intList *foo;

No, the user of that should never use the typename directly but always
use the macro:

LIST_TYPE(int) *foo;

which is even more readable :) and if your library becomes part of the
standard and the CCL prefix would be dropped, user code wouldn't have
to change. The typename you use should be an implementation detail,
not an interface.

In any case you'd have to use a prefix and not a suffix, this is how
the conventions in the standard are designed.

But

Listint *foo;

is not very readable so I'd put an underscore in

List_int *foo;

and to be politically correct you'd have to use one as a prefix, too

_List_int *foo;

what is shorter and much more readable.

For each container the library exports two object names:

Interface name: iList, iDictionary iVector, etc.
Type name: List, Dictionary, Vector, etc.

no-go as naming conventions for the standard library, I think
Note that all functions names are hidden behind the interface name:

iList.Add, iVector.Append, iDictionary.Find, etc

Hm, even if you implement "iList" as const qualified object, how do
you avoid the extra indirection? Or is iList just something like

#define iList ((const struct whatever){ .Add = bla, .Append = blo, ... })

But still this would mean that you can't inline any of your
(supposedly) small functions.

A macro that expands an inline functions would fit better, I think.
This reduces considerably the name pollution of the library.

Name pollution is only a problem if you pollute the application name
space. Otherwise nowadays it is neither a problem for compilers nor
linkers. P99 does a lot (really a lot) of such name expansions (for
types, enumeration constants everywhere, inline functions) and hasn't
any significant impact on the compilation or link time. (I never tried
it with your compiler, though :) I don't have a windows platform)

Jens
 
J

jacob navia

Le 13/05/12 17:09, Jens Gustedt a écrit :
No, the user of that should never use the typename directly but always
use the macro:

LIST_TYPE(int) *foo;

I do not understand why the user should do this.

What are your reasons?

Thanks
 
J

Jens Gustedt

Am 13.05.2012 17:18, schrieb jacob navia:
Le 13/05/12 17:09, Jens Gustedt a écrit :

I do not understand why the user should do this.

What are your reasons?

encapsulation

this provides an easy to read interface (even similar to C++'s template
notation) that would work for any named type

LIST_TYPE(myComplicatedUserTYPE) *foo1;

is as easy to capture as for the int case.

But most importantly this leaves all the freedom for an implementation
to do whatever it likes unter the hood.

Similarly

ListAdd(L, 27);

with a macro can be implemented (as you do) with a struct that contains
function pointers, or an inline function (with some naming convention),
or a type generic expression with _Generic, or with a wild mixture of
all of these.

Jens
 
T

Tim Rentsch

Jens Gustedt said:
Am 13.05.2012 16:45, schrieb jacob navia:
Le 13/05/12 15:06, Jens Gustedt a @C3{A9}crit :
Well, the problem opf your approach is that the user of the
container library would be forced to write:

CCL_List_int *foo;

instead of

intList *foo;

No, the user of that should never use the typename directly but always
use the macro:

LIST_TYPE(int) *foo;

[snip elaboration]

I second this suggestion. If a developer wants a shorter
type alias he can define one himself, eg,

typedef LIST_TYPE(int) int_list_t;
 
J

jacob navia

Le 13/05/12 18:46, Jens Gustedt a écrit :
encapsulation

this provides an easy to read interface (even similar to C++'s template
notation) that would work for any named type

LIST_TYPE(myComplicatedUserTYPE) *foo1;

is as easy to capture as for the int case.

But most importantly this leaves all the freedom for an implementation
to do whatever it likes unter the hood.

Similarly

ListAdd(L, 27);

with a macro can be implemented (as you do) with a struct that contains
function pointers, or an inline function (with some naming convention),
or a type generic expression with _Generic, or with a wild mixture of
all of these.

You are right what encapsulation and decoupling is concerned.

The only problem that I see is that macros would add 584 new names
to the name space. At the last count only the list container has
an API of 56 entry points...

This is now somehow reduced since for all the APIs in the list
container only ONE name is exported:

iList

and all the 56 APIs are written with that prefix, that looks
quite natural and similar to ogther computer languages:

iList.Insert(list,position,data);

A macro system would need
1) A prefix. Suppose CCL_
2) The name of the container
3) The name of the API

We would need then

CCL_List_Insert(list,position,data);

It would be shorter in camel case, as used in the CCL
throughout:
ccl_ListInsert(list,position,data);
what is only 2 chars longer...

Look, maybe you are right about this. I have to think it over.
I would have just to rewrite all the documentation (only 380 pages)

:-(
 
J

Jens Gustedt

Am 13.05.2012 22:51, schrieb jacob navia:
You are right what encapsulation and decoupling is concerned.

The only problem that I see is that macros would add 584 new names
to the name space. At the last count only the list container has
an API of 56 entry points...

as I said, I really don't think the number of it should be a problem
by itself. (But you are the compiler programmer, you probably know
that better than I)
This is now somehow reduced since for all the APIs in the list
container only ONE name is exported:

iList

and all the 56 APIs are written with that prefix, that looks
quite natural and similar to ogther computer languages:

iList.Insert(list,position,data);

A macro system would need
1) A prefix. Suppose CCL_
2) The name of the container
3) The name of the API

We would need then

CCL_List_Insert(list,position,data);

I only think that the CCL prefix is necessary for the "hidden" types
and function interfaces. There is as much need to prefix the user
interfaces as before :) "iList" could perfectly serve as prefix for
the macros. For the user it makes not much difference to write
"iList." or "iList_" as a prefix.
It would be shorter in camel case, as used in the CCL
throughout:
ccl_ListInsert(list,position,data);
what is only 2 chars longer...

Look, maybe you are right about this. I have to think it over.
I would have just to rewrite all the documentation (only 380 pages)

:-(

Ah, some sophisticated use of regexp query replace should be able to
solve that one :)

Bon courage

Jens
 
S

Sarah Wren

Maybe you should post elsewhere? Just a thought. (I mean, if you think it
will be better there).
 

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

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top