libclc-developers Mailing List for libclc (Page 10)
Status: Planning
Brought to you by:
augestad
You can subscribe to this list here.
| 2003 |
Jan
|
Feb
(1) |
Mar
(170) |
Apr
(73) |
May
(3) |
Jun
(1) |
Jul
(1) |
Aug
|
Sep
(2) |
Oct
|
Nov
|
Dec
|
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2004 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(1) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
| 2008 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
(1) |
Dec
|
|
From: Hallvard B F. <h.b...@us...> - 2003-03-19 11:10:35
|
[Bj=F8rn, you forgot cc: libclc-developers]
Bj=F8rn Augestad writes:
>Hallvard B Furuseth wrote:
>> Here is a function I've seen with several names: stralloc(), concat(=
),
>> pstrcat(). I prefer clc_str<someting>.
>> (...)
>=20
> We need an example here. The required NULL sentinel can easily be=20
> forgotten, which creates funny bugs. A simple example will do IMO.
I modified your example a bit:
EXAMPLE
char *s =3D clc_stralloc("foo", "bar", (char *)0);
if(s !=3D NULL) {
puts(s);
free(s);
}
I prefer (char *)0 over NULL. It's good to teach users to be careful
about the types given to variadic functions. In this case NULL works
too, since (char*)0 has the same representation, but then users may
think this applies to other pointer types as well.
> (...)
> #include <string.h> /* for strlen() */
> (...)
> <paranoid>
> =09len may wrap around if the sum of all strlen's > SIZE_MAX.
> </paranoid>
With that test it needs to set errno, since there are two different
error conditions (too long string and malloc failed). I've called them=
CLC_ETOOBIG and CLC_ENOMEM. Though I'm not sure I like the CLC_ETOOBIG=
name. Should we just use ERANGE instead of CLC_ETOOBIG?
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include "clc_errno.h"
#include "clc_string.h"
char *
clc_stralloc(const char *arg1, ...)
{
size_t len;
const char *arg;
char *ret, *end;
va_list ap;
len =3D 1;
va_start(ap, arg1);
for (arg =3D arg1; arg !=3D NULL; arg =3D va_arg(ap, const char *))=
{
=09size_t arglen =3D strlen(arg);
=09len +=3D arglen;
=09if (len < arglen) {
=09 /* string length too large for size_t */
=09 errno =3D CLC_ETOOBIG;
=09 return NULL;
=09}
}
va_end(ap);
ret =3D malloc(len);
if (ret =3D=3D NULL) {
=09errno =3D CLC_ENOMEM;
} else {
=09end =3D ret;
=09*end =3D '\0';
=09va_start(ap, arg1);
=09for (arg =3D arg1; arg !=3D NULL; arg =3D va_arg(ap, const char *))
=09 end =3D clc_stpcpy(end, arg);
=09va_end(ap);
}
return ret;
}
--=20
Hallvard
|
|
From: Michael B.A. <mb...@io...> - 2003-03-19 09:43:42
|
On Wed, 19 Mar 2003 06:59:32 +0100 Bj=F8rn Augestad <bo...@me...> wrote: > # Sequences > 1. vector > 2. deque > 3. list > 4. slist > 5. bit_vector >=20 > # Associative Containers > 1. set > 2. map > 3. multiset > 4. multimap > 5. hash_set > 6. hash_map > 7. hash_multiset > 8. hash_multimap > 9. hash >=20 > Need more containers? :-) I think we should start with something simple, perfect it, debate it, and let it stew a little. Then we can move on and think about transcendent ideas that facilitate exotic functionality. The linked list is a good start. >=20 > void* is OK for data, I guess. The hard part will be to find a *good*=20 > way to represent keys. Does anyone have a good concept for keys? I'm not sure what you mean. What is a key? How about void *. Mike --=20 A program should be written to model the concepts of the task it performs rather than the physical world or a process because this maximizes the potential for it to be applied to tasks that are conceptually similar and, more important, to tasks that have not yet been conceived.=20 |
|
From: <bo...@me...> - 2003-03-19 05:59:17
|
Bryan Donlan wrote:
>
> What about maps/dictionaries/whatever they're called? It should have
a hashing
> function on the items...
Here's a list of the container classes in STL. That should be sufficient
for most needs and is also a good specification to work from.
# Sequences
1. vector
2. deque
3. list
4. slist
5. bit_vector
# Associative Containers
1. set
2. map
3. multiset
4. multimap
5. hash_set
6. hash_map
7. hash_multiset
8. hash_multimap
9. hash
Need more containers? :-)
void* is OK for data, I guess. The hard part will be to find a *good*
way to represent keys. Does anyone have a good concept for keys?
[snip]
--
boa
|
|
From: Bryan D. <bd...@bd...> - 2003-03-18 22:42:30
|
=2D----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Tuesday 18 March 2003 05:38 pm, Bj=F8rn Augestad wrote: > Bryan Donlan wrote: > > [ Another misdirected message ] > > So I'm not the only one hitting reply instead of reply all... ;-) > > > ---------- Forwarded Message ---------- > > > > To: Bryan Donlan <bd...@bd...> > > Cc: lib...@li... > > Subject: Re: [Libclc-developers] Introducing the double linked list > > interface > > > > On Mon, 17 Mar 2003 19:37:01 -0500 > > > > Bryan Donlan <bd...@bd...> wrote: > >>>typedef struct CLC_DL_NODE > >> > >>I don't think struct of typedef names should be uppercase. IMO, only > >> enums and #defines should be uppercase. > > > > I second that. It appears we need a style document or a consistent > > codebase to model the code after. > > You're right. > > My initial hope was that the code in the clc_bitset thread would be a > template for further adt's. Wrong again... > > As for consistent codebase, I'm currently working as hard as I can to > create a workable example of a framework for containers. I've got the > list and the iterator working now (2 minutes ago), and hope to get > another container adt working just to see that the iterator fits more > than just list. What about maps/dictionaries/whatever they're called? It should have a hash= ing=20 function on the items... > It's influenced by the code found at Michael B. Allens and Thomas > Stegens home pages, as well as some weird code I found on this server ;-) > > http://www.sgi.com/tech/stl/table_of_contents.html > > We really need to get a consistent way of handling containers and > iterators. It's my fault that we haven't had such a discussion prior to > this thread. I'm sorry and am currently trying hard to make up for it by > presenting a coherent approach asap. I'll need a couple of days more, > though. > > The current version is available in cvs if anyone wants to peek. No doc > and not a lot of comments yet, though. :-) > > [snip] > > PS: I just received a really lame email virus addressed to a lot of us > from someone down under. Time to run virus check, anyone? No worries, mate, I use Linux! :) =2D----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.1 (GNU/Linux) iD8DBQE+d6DPx533NjVSos4RAgRLAKDGhpIk7xqXuKvXpc+ipJQfAHgWjQCcC5bQ yqLdTwXTqQYTgrcz2niSQzY=3D =3Dieow =2D----END PGP SIGNATURE----- |
|
From: <bo...@me...> - 2003-03-18 22:38:09
|
Bryan Donlan wrote:
> [ Another misdirected message ]
So I'm not the only one hitting reply instead of reply all... ;-)
>
> ---------- Forwarded Message ----------
>
> To: Bryan Donlan <bd...@bd...>
> Cc: lib...@li...
> Subject: Re: [Libclc-developers] Introducing the double linked list
> interface
>
> On Mon, 17 Mar 2003 19:37:01 -0500
>
> Bryan Donlan <bd...@bd...> wrote:
>
>>>typedef struct CLC_DL_NODE
>>
>>I don't think struct of typedef names should be uppercase. IMO, only enums
>>and #defines should be uppercase.
>
>
> I second that. It appears we need a style document or a consistent
> codebase to model the code after.
You're right.
My initial hope was that the code in the clc_bitset thread would be a
template for further adt's. Wrong again...
As for consistent codebase, I'm currently working as hard as I can to
create a workable example of a framework for containers. I've got the
list and the iterator working now (2 minutes ago), and hope to get
another container adt working just to see that the iterator fits more
than just list.
It's influenced by the code found at Michael B. Allens and Thomas
Stegens home pages, as well as some weird code I found on this server ;-)
http://www.sgi.com/tech/stl/table_of_contents.html
We really need to get a consistent way of handling containers and
iterators. It's my fault that we haven't had such a discussion prior to
this thread. I'm sorry and am currently trying hard to make up for it by
presenting a coherent approach asap. I'll need a couple of days more,
though.
The current version is available in cvs if anyone wants to peek. No doc
and not a lot of comments yet, though. :-)
[snip]
PS: I just received a really lame email virus addressed to a lot of us
from someone down under. Time to run virus check, anyone?
--
boa
Please join the libclc-developers list
at http://lists.sourceforge.net/lists/listinfo/libclc-developers
|
|
From: Bryan D. <bd...@bd...> - 2003-03-18 22:12:59
|
[ Another misdirected message ] ---------- Forwarded Message ---------- To: Bryan Donlan <bd...@bd...> Cc: lib...@li... Subject: Re: [Libclc-developers] Introducing the double linked list interface On Mon, 17 Mar 2003 19:37:01 -0500 Bryan Donlan <bd...@bd...> wrote: > > typedef struct CLC_DL_NODE > > I don't think struct of typedef names should be uppercase. IMO, only enums > and #defines should be uppercase. I second that. It appears we need a style document or a consistent codebase to model the code after. > > CLC_DL_NODE *clc_dl_FindFirst(CLC_DL_LIST *list, > > int type, > > size_t size, > > void *find, > > int (*cmp)(int typekey, > > const void *datakey, > > int typenode, > > const void *datanode), > > int dir, > > CLC_DL_SEARCH_DATA *srch); This is far too complicated. Also these callback interfaces are less pleasant than a simple iterator interface because you frequently operate on variable on the stack. I would just have an iterate function that initializes the search context object and a next object that returns each element. I don't see the advantage of returning the first element with the initial call. It's a little awkward programatically actually. Incedentally the Linux kernel uses a clever list implementation. The clever part probably wouldn't go over too well here but the basic idea of a circular doublely linked list could be extended to provide the traditional interface. The best description of that list ADT that I have seen is in the Linux Device Drivers book which can be read online here: http://www.xml.com/ldd/chapter/book/ch10.html#t5 The list_head could be adapted to include a void *data pointer I suppose. That might make a nice doublely linked list. Mike -- A program should be written to model the concepts of the task it performs rather than the physical world or a process because this maximizes the potential for it to be applied to tasks that are conceptually similar and, more important, to tasks that have not yet been conceived. Re: [Libclc-developers] Introducing the double linked list interface ------------------------------------------------------- |
|
From: Thomas S. <ts...@ci...> - 2003-03-18 22:12:14
|
[Note: This was sent to me, but it appears to be meant for the list. In the future, use 'Reply to all'. Sourceforge does not rewrite headers as much as other lists ] Michael B.Allen wrote: > On Mon, 17 Mar 2003 19:37:01 -0500 > > Bryan Donlan <bd...@bd...> wrote: >>>CLC_DL_NODE *clc_dl_FindFirst(CLC_DL_LIST *list, >>> int type, >>> size_t size, >>> void *find, >>> int (*cmp)(int typekey, >>> const void *datakey, >>> int typenode, >>> const void *datanode), >>> int dir, >>> CLC_DL_SEARCH_DATA *srch); > > This is far too complicated. Also these callback interfaces are less > pleasant than a simple iterator interface because you frequently operate > on variable on the stack. > > I would just have an iterate function that initializes the search context > object and a next object that returns each element. I don't see the > advantage of returning the first element with the initial call. It's > a little awkward programatically actually. Have you people looked at this? http://www.cis.strath.ac.uk/~tstegen/dlib/ It has a generic iterator type which can iterate over any datastructure. It works by using pointers to functions which does all the necessary operations. It is possible to have several iterators over the same datastructure at the same time. If there is any interest I can change it to conform to the guidelines of libclc. -- Thomas. -- Thomas. |
|
From: Hallvard B F. <h.b...@us...> - 2003-03-18 18:55:10
|
Here is a function I've seen with several names: stralloc(), concat(),
pstrcat(). I prefer clc_str<someting>.
This code is written by Hallvard Furuseth and put in the public domain.
Insert any copyright you want.
NAME
clc_stralloc - allocate a concatenation of strings
SYNOPSYS
#include "clc_string.h"
char *clc_stralloc(const char *arg1, ...);
DESCRIPTION
This function takes a number of string arguments followed by
(char*)0. It returns a malloced concatenation of the arguments,
or NULL if space could not be allocated.
#include <stdlib.h>
#include <stdarg.h>
#include "clc_string.h"
char *
clc_stralloc(const char *arg1, ...)
{
size_t len;
const char *arg;
char *ret, *end;
va_list ap;
len = 1;
va_start(ap, arg1);
for (arg = arg1; arg != NULL; arg = va_arg(ap, const char *))
len += strlen(arg);
va_end(ap);
ret = malloc(len);
if (ret != NULL) {
end = ret;
*end = '\0';
va_start(ap, arg1);
for (arg = arg1; arg != NULL; arg = va_arg(ap, const char *))
end = clc_stpcpy(end, arg);
va_end(ap);
}
return ret;
}
--
Hallvard
|
|
From: Hallvard B F. <h.b...@us...> - 2003-03-18 18:48:10
|
Michael B.Allen writes:
>Jan Engelhardt <je...@li...> wrote:
>
>>> *startp = ptr + size - 2;
>
> What would happen if size were 2, 1, or even 0? Those are legitimate
> values.
2 is OK with that code. 0 and 1 don't need to be, we can document
that size must be at least 2 and add clc_assert_arg(ultostr, size>1).
>>> clc_assert_arg(ultostr, base >= 2 && base <= 36);
>>> (...)
>>> if(base < 2 || base > 36) {
>>> errno = ERANGE;
>>> return NULL;
>>> }
I hope this doesn't spawn a too big error handling thread again, but I
think if the function fails with ERANGE when base is out of range, that
should be documented, well-defined behaviour - so there should not be an
assert which makes such an argument crash the program.
> What about negative values?
Not in an _unsigned_ long.
Some other points:
I think the output argument should be first, followed by the size
argument, similar to fread (and strcpy for the output argument).
You forgot to \0-terminate the output.
I'd prefer to get as few digits as possible returned instead of
'0'-padding the output. Even though this means doing memmove()
at the end. (Or we could return a pointer into the middle of the
string, but that invites bugs in the user's program.)
It shuold fail if the output buffer is too small.
... fail with ERANGE? Or is this our chance to invent our first CLC_E*
code - CLC_ENOSPC "not enough space"?
#include <string.h>
#include <errno.h>
#include "clc_assert.h"
char *
clc_ultostr(char *ptr, size_t size, unsigned long num, int base)
{
char *end, *end2;
const char *sym = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
clc_assert_not_null(clc_ultostr, ptr);
clc_assert_arg(clc_ultostr, size > 1);
if(base < 2 || base > 36) { /* or assert 2 <= base && base <= 36 */
errno = ERANGE;
return NULL;
}
end = end2 = ptr + size;
*--end = '\0';
do {
if (end == ptr) {
errno = ERANGE;
return NULL;
}
*--end = sym[num % base];
num /= base;
} while (num);
if (end != ptr)
memmove(ptr, end, end2 - end);
return ptr;
}
|
|
From: Thomas S. <ts...@ci...> - 2003-03-18 08:27:52
|
Michael B.Allen wrote: > On Mon, 17 Mar 2003 19:37:01 -0500 > Bryan Donlan <bd...@bd...> wrote: >>>CLC_DL_NODE *clc_dl_FindFirst(CLC_DL_LIST *list, >>> int type, >>> size_t size, >>> void *find, >>> int (*cmp)(int typekey, >>> const void *datakey, >>> int typenode, >>> const void *datanode), >>> int dir, >>> CLC_DL_SEARCH_DATA *srch); >> > > This is far too complicated. Also these callback interfaces are less > pleasant than a simple iterator interface because you frequently operate > on variable on the stack. > > I would just have an iterate function that initializes the search context > object and a next object that returns each element. I don't see the > advantage of returning the first element with the initial call. It's > a little awkward programatically actually. > Have you people looked at this? http://www.cis.strath.ac.uk/~tstegen/dlib/ It has a generic iterator type which can iterate over any datastructure. It works by using pointers to functions which does all the necessary operations. It is possible to have several iterators over the same datastructure at the same time. If there is any interest I can change it to conform to the guidelines of libclc. -- Thomas. -- Thomas. |
|
From: Michael B.A. <mb...@io...> - 2003-03-18 08:14:47
|
On Mon, 17 Mar 2003 19:37:01 -0500 Bryan Donlan <bd...@bd...> wrote: > > typedef struct CLC_DL_NODE > > I don't think struct of typedef names should be uppercase. IMO, only enums and > #defines should be uppercase. I second that. It appears we need a style document or a consistent codebase to model the code after. > > CLC_DL_NODE *clc_dl_FindFirst(CLC_DL_LIST *list, > > int type, > > size_t size, > > void *find, > > int (*cmp)(int typekey, > > const void *datakey, > > int typenode, > > const void *datanode), > > int dir, > > CLC_DL_SEARCH_DATA *srch); This is far too complicated. Also these callback interfaces are less pleasant than a simple iterator interface because you frequently operate on variable on the stack. I would just have an iterate function that initializes the search context object and a next object that returns each element. I don't see the advantage of returning the first element with the initial call. It's a little awkward programatically actually. Incedentally the Linux kernel uses a clever list implementation. The clever part probably wouldn't go over too well here but the basic idea of a circular doublely linked list could be extended to provide the traditional interface. The best description of that list ADT that I have seen is in the Linux Device Drivers book which can be read online here: http://www.xml.com/ldd/chapter/book/ch10.html#t5 The list_head could be adapted to include a void *data pointer I suppose. That might make a nice doublely linked list. Mike -- A program should be written to model the concepts of the task it performs rather than the physical world or a process because this maximizes the potential for it to be applied to tasks that are conceptually similar and, more important, to tasks that have not yet been conceived. |
|
From: Michael B.A. <mb...@io...> - 2003-03-18 07:55:35
|
On Mon, 17 Mar 2003 17:56:05 +0100 (MET) Jan Engelhardt <je...@li...> wrote: > >> /*=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D > >> ultostr > >> by Jan "Hirogen2" Engelhardt <hirogen2 at gmx de>, 2003 > >> -- distributed under the Frontier Artistic License and GNU General P= ublic > >> -- License. See doc/FAL1.txt and doc/GPL2.txt for details. > > > >The license must be changed to BSD. > What's the thing about BSD? http://www.opensource.org/licenses/bsd-license.php > >> SYNOPSIS > >> unsigned char *ultostr(unsigned long num, unsigned long base, > >> unsigned char *ptr, size_t size); > >unsigned long base is pretty long for the range 2..36 :-) strtoul uses > >int for base. > woho... heh right, that probably should have been unsigned char. If you were very space conscientious that would indeed be the correct datatype. However I think I would use strtoul as a guide here and just use int. > >Unsigned char* are very uncommon in C libraries, and its buddy function > >strtoul uses char*. > And? I like it. It's probably because I cannot feel comfortable when they > assign the '=F6' a value of -10. Functions that take char * for historical reasons will take care to properly convert each character as necessary taking into consideration what the locale encoding is. Using unsigned char * is appropriate for encoding and decoding types of function however in this context considering we're only expecting ASCII character coupled with the fact that again strtoul should probably be used as a model I suggest char *. So just: char * clc_ultostr(unsigned long num, int base, char *ptr, size_t size) { ... > The major aspects of my style are >=20 > >unsigned char *ultostr( > > unsigned long num, > > unsigned long base, > > unsigned char *ptr, > > size_t size) > - one row, or indent-by-1 if longer than 79 chars Sounds good here too. >=20 > >{ > - as well as keeping any { on the line The prevailing technique is to place the bracket on a new line I believe. Some editors actually key on these (vi for one). > > unsigned char *sym =3D "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", > > > > clc_assert_not_null(ultostr, ptr); > > clc_assert_arg(ultostr, base >=3D 2 && base <=3D 36); > > > > *startp =3D ptr + size - 2; What would happen if size were 2, 1, or even 0? Those are legitimate values. > > if(base < 2 || base > 36) { > > errno =3D ERANGE; > > return NULL; > > } > > > > while(--size > 0) { If size were originally 0 size would rollover to a vary high number. > > *ptr =3D sym[num % base]; > > num /=3D base; > > --ptr; > > } What about negative values? Mike --=20 A program should be written to model the concepts of the task it performs rather than the physical world or a process because this maximizes the potential for it to be applied to tasks that are conceptually similar and, more important, to tasks that have not yet been conceived.=20 |
|
From: Bryan D. <bd...@bd...> - 2003-03-18 00:37:06
|
=2D----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Sunday 16 March 2003 10:39 pm, Peter "Shaggy" Haywood wrote: > I submit the following for discussion and (hopefully) the approval of t= he > group. This may also serve as a first draft of the documentation for this > module. > I'll let you ponder this interface and suggest improvements before I po= st > the implementation of this module. > > This document explains how to use the functions and data types defined = by > the clc_dl module. This module implements a generic double linked list > system that is easy to use but flexible. > > Data types: > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > These are the data types defined by this module. They are defined in the > clc_dl.h header file. These types should generally not be touched directly > by users. Leave it to the module to manipulate these types. However, their > definitions are provided to users by defining CLC_DL_ADVANCED_FUNCS before > including the header. This allows extra flexibility and control. > > > typedef struct CLC_DL_NODE I don't think struct of typedef names should be uppercase. IMO, only enums = and=20 #defines should be uppercase. > CLC_DL_NODE *clc_dl_InsertData(CLC_DL_LIST *list, > int dir, > int type, > size_t size, > void *payload, > void (*cleanup)(void *)); > > The clc_dl_InsertData() function is used to create a new node and insert > new data into a linked list. It returns a pointer to the new node, or a > null pointer if memory could not be allocated for the node. The parameters > are: > > list a pointer indicating the linked to insert data into > > dir direction of insertion: use CLC_DL_FRONT or CLC_DL_START > to insert at the start of the list or CLC_DL_END to > insert at the end of the list How about inserting after a given node? > type type of the payload (in case of a list containing more > than one type of data) > > size size of the payload > > payload the data to be copied to the new node's payload > > cleanup pointer to a user supplied function to cleanup any extra > memory associated with a node's payload (but not the > node or payload itself - clc_dl_FreeNode() takes care of > that) Why can't the caller do that? Unless you'll cann it with the payload, of=20 course. But it dosen't say that. > CLC_DL_NODE *clc_dl_FindFirst(CLC_DL_LIST *list, > int type, > size_t size, > void *find, > int (*cmp)(int typekey, > const void *datakey, > int typenode, > const void *datanode), > int dir, > CLC_DL_SEARCH_DATA *srch); > > CLC_DL_NODE *clc_dl_FindNext(CLC_DL_SEARCH_DATA *srch); > > The clc_dl_FindFirst() and clc_dl_FindNext() functions are used to sear= ch > linearly for a node or nodes of a specified type containing a specified s= et > of data. The parameters of the former are: > > list pointer indicating the linked list to search > > type type of payload to search for > > size size of payload to search for > > find data to search for in payloads > > cmp pointer to compare function (compares key type, size and > data to node's payload type, size and data - returns 0 > if match, non-zero otherwise) > > dir direction of search: use CLC_DL_FORWARD to search > forward from the start of the list or CLC_DL_REVERSE to > search backward from the end of the list > > srch pointer to a structure containing data used in the > search process > > clc_dl_FindFirst() searches for the first node that matches (according to > the comparison function passed as the fifth argument) the key data and its > type and size. clc_dl_FindNext(), using a CLC_DL_SEARCH_DATA * previously > passed to clc_dl_FindFirst(), searches for subsequent nodes that match the > search criteria specified in the clc_dl_FindFirst() call. > A special comparison function has been provided, which always compares > equal, called clc_dl_FindAll(). This can be used by clc_dl_FindFirst() and > clc_dl_FindNext() to "find" all nodes of a list in turn. This provides a > way to traverse the list node by node. > clc_dl_FindFirst() returns a pointer to the first node found that match= es > the search criteria. clc_dl_FindNext() returns a pointer to the next node > in turn that matches the search criteria. Both functions return a null > pointer if a match is not found. How about FindPrev to walk the list backwards? What about removing arbitrar= y=20 nodes and replacing them during such a search? > Advanced functions: > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > The following functions are defined in the clc_dl_a.c source file. Thier > prototypes are available by defining a macro called CLC_DL_ADVANCED_FUNCS > before the clc_dl.h header is included. What about GetNext and GetPrev? =2D----BEGIN PGP SIGNATURE----- Version: GnuPG v1.2.1 (GNU/Linux) iD8DBQE+dmotx533NjVSos4RAsK9AJ49YZ6g944+w0eiGLHd+MzpGvmasgCfSeYD Mbeeu1MF9rkc2dNSwsnOp30=3D =3Djv57 =2D----END PGP SIGNATURE----- |
|
From: Hallvard B F. <h.b...@us...> - 2003-03-17 23:00:15
|
Michael B.Allen writes: >Hallvard B Furuseth <h.b...@us...> wrote: You skipped one of the most important parts of my message: > But the standards do not say that malloc will not set errno to > ENOMEM and every malloc ever written _does_ use ENOMEM. Prove it. You can't, right? You can't even prove that _most_ mallocs ever written do this. Which means we won't dare depend on it. Even if we believed you are probably right, a 0.99% chance of being portable here and a 0.99% chance of being portable there quickly adds up to a 10% chance of _not_ being portable. Which is not good enough. We are not building a Library Which Will Probably Work For you. We are building a Library Which Will Work. Period. Incidentally, it took me all of 5 minutes of googling to find a malloc library which does _not_ set errno like you say all mallcos do: http://www.mit.edu/afs/athena/contrib/watchmaker/src/malloclib/ So much for the "I'm sure everyone does this, since all code I've seen does it" approach. >>> Same goes for probably every other ISO function you'll use. >> >> fgetpos() can return ESPIPE (file descriptor is a pipe, FIFO, or socket) >> on my system (Solaris). Do you think that can happen on systems that do >> not support pipes? > > Then they just won't return ESPIPE. I'm not sure I understand your > point. Well, then I sure don't understand yours, when you said "Same goes for probably every other ISO function you'll use." _What_ goes for these functions? > You can't squash the errno code. Nobody is suggesting to do so. >> So far, we don't know if we'll be using any CLC_E* error codes. There >> is little point in setting errno = CLC_ENOMEM in a function which can >> only return one type of error, for example. Then all the user needs it >> to test whether or not an error happened. > > Well you should still set errno to be consistent. If a function returns > an error and the practice has been to check errno you should set it even > if there is only one possible errror code just to be consistent. I don't know anyone who has the practice of checking errno when only one error is possible. (Or if you are talking about checking errno in order to find out if an error happened at all, you have misunderstood errno badly and should quit this discussion until you have read up on how it is used. Errno is *only* for saying which error happened when you already know that an error did happen. You should never check errno in order to find out *if* an error happened - you test the function's return value to find that out.) >> The main point so far is that we want a standardized way which functions >> can return error conditions if they want to. We could have said that >> such functions return an integer error code. We chose not to; we >> preferred to reuse the errno mechanism which already exists. Given >> that, we _had_ to open for the possibility that functions may need to >> set errno to something which does not exist in any errno.h. > > The common E* identifiers are available with all significant libc > systems. Again, prove it. And don't ignore that point this time. > And if they are not there is likely a reason that you should > not try to second guess. Even minimalistic non-POSIX system A number of systems are smaller than Posix. Or very different from Posix, so an error with a name which also exists in POSIX errno.h might exist but with another semantics. > will have all the codes you would need for linked lists, configuration > file modules and other such ANSI stuff. So? Tell me which E* code I'd use to report 'table is full', an error I invented just now. I couldn't find one on my Solaris box. Not that it matters, though. As I've already said, we are not building a Library Which Will Probably Work For you. > (...) Fortunately most implementations follow ISO C which > permits errno to be a macro that evaluates to a modifiable lvalue. This > is reentrant but it still doesn't provide a clean mechanism to communicate > error codes within a multithreaded program. What do you mean? Errno on multithreaded systems typically expands to an expression which refers to a thread-specific variable. That seems clean enough to me, if a little slow. > There are two kernel programming idioms that you might also consider. I'm > not advocating anything here I'm just providing you with options. The > first is to always return the error code on the stack like: > > if (getit(&ans)) == -1) { > return -ENOENT; > } > > If a pointer is normally returned you use ERR_PTR, PTR_ERR, and IS_ERR > macros as described here: > > http://www.win.tue.nl/~aeb/linux/lk/lk-2.html#ss2.7.3 Absolutely not. Casting an integer to a pointer and back is _not_ portable, it might return anything. It's OK for kernel code which is already making assumptions about the compiler, but not for an ISO C library. > As long as we're on the topic of error codes I have another > suggestion. Most library functions return -1 to indicate an error. For > a while my personal library of stuff used 0 to indicate an error occured > whereas 1 meant success. I recenty changed all of my functions to return > -1 on error. There was a thread about that on comp.lang.c a few days ago. I think the idea was that functions that can return several values on success should return 0 on error, or -1/EOF if they can't return 0, but functions that only return success/failure should return 0 or -1. > Notices some multibyte string functions may also return > -2 to indicate a different error although I don't know if any ISO C > functions do this. I don't think so, I think they use errno. > I also feel it is important never to design an interface that provides > no way to return an error indication. Unless no errors can occur upon correct usage, I hope. Like strcat. > This can be easy to do when creating a function that returns a pointer > to something but NULL is an appropriate value for the pointer > (e.g. retrieving something from a table perhaps). But this leaves no > way to indicate to the caller that an error occured. The two suggestions being discussed on comp.lang.c now is to return a struct with the "main" output value and an error code, and to return an error indication and take an output argument in which to put the "main" value. > Finally, I know this is obvious but a library function should never set > errno to 0. Sure. BTW, please read this article before making general suggestions: Subject: Re: libclc: error handling policy (wrapup) Newsgroups: comp.lang.c Date: Thu, 06 Mar 2003 20:34:51 GMT Message-ID: <LhO9a.14422$ZL2...@ju...> -- Hallvard |
|
From: Michael B.A. <mb...@io...> - 2003-03-17 21:28:39
|
On Mon, 17 Mar 2003 17:16:23 +0100 (MET) Jan Engelhardt <je...@li...> wrote: > Hi, > > copied this from my personal lib as I thought > this might be worth sharing, no? > > /*============================================================================= > ultostr > by Jan "Hirogen2" Engelhardt <hirogen2 at gmx de>, 2003 > -- distributed under the Frontier Artistic License and GNU General Public > -- License. See doc/FAL1.txt and doc/GPL2.txt for details. > ------------------------------------------------------------------------------- > NAME > ultostr - convert an unsigned long integer to a string > > SYNOPSIS > unsigned char *ultostr(unsigned long num, unsigned long base, > unsigned char *ptr, size_t size); > > DESCRIPTION > The ultostr() function converts the number NUM to a string, in > base BASE notation. BASE must be between 2 and 36 inclusive. To > summarize it, this function does (nearly) exactly the opposite > of strtoul(). I like it! No point in invoking sprintf just to convert a number like this. Mike -- A program should be written to model the concepts of the task it performs rather than the physical world or a process because this maximizes the potential for it to be applied to tasks that are conceptually similar and, more important, to tasks that have not yet been conceived. |
|
From: Michael B.A. <mb...@io...> - 2003-03-17 21:20:24
|
On Mon, 17 Mar 2003 12:33:52 +0100
Hallvard B Furuseth <h.b...@us...> wrote:
>
> > Same goes for probably every other ISO function you'll use.
>
> fgetpos() can return ESPIPE (file descriptor is a pipe, FIFO, or socket)
> on my system (Solaris). Do you think that can happen on systems that do
> not support pipes?
Then they just won't return ESPIPE. I'm not sure I understand your
point. You can't squash the errno code. There are about 15 error codes
that can be returned by open(2) alone which means fopen can return quite
a few more. If an error occurs calling a libc function you should just
return -1 or NULL or whatever and leave errno alone.
> > You're introducing a technically unncessary level of indirection that
> > contributes to an overall non-standard experience for the sake of a
> > standard. Why are you going to make 99.9999% of the users deal with
> > two different namespaces of error codes for some imaginary platform
> > that doesn't exist? Following the letter of the law for this is just
> > being pedantic.
>
> So far, we don't know if we'll be using any CLC_E* error codes. There
> is little point in setting errno = CLC_ENOMEM in a function which can
> only return one type of error, for example. Then all the user needs it
> to test whether or not an error happened.
Well you should still set errno to be consistent. If a function returns
an error and the practice has been to check errno you should set it even
if there is only one possible errror code just to be consistent.
> The main point so far is that we want a standardized way which functions
> can return error conditions if they want to. We could have said that
> such functions return an integer error code. We chose not to; we
> preferred to reuse the errno mechanism which already exists. Given
> that, we _had_ to open for the possibility that functions may need to
> set errno to something which does not exist in any errno.h.
The common E* identifiers are available with all significant libc
systems. And if they are not there is likely a reason that you should
not try to second guess. Even minimalistic non-POSIX system will have
all the codes you would need for linked lists, configuration file modules
and other such ANSI stuff.
To tell you the truth these days I am beginning to wonder about the
usage of errno at all. POSIX.1 requires that errno is:
extern int errno;
This is not safe. Fortunately most implementations follow ISO C which
permits errno to be a macro that evaluates to a modifiable lvalue. This
is reentrant but it still doesn't provide a clean mechanism to communicate
error codes within a multithreaded program.
There are two kernel programming idioms that you might also consider. I'm
not advocating anything here I'm just providing you with options. The
first is to always return the error code on the stack like:
if (getit(&ans)) == -1) {
return -ENOENT;
}
If a pointer is normally returned you use ERR_PTR, PTR_ERR, and IS_ERR
macros as described here:
http://www.win.tue.nl/~aeb/linux/lk/lk-2.html#ss2.7.3
As long as we're on the topic of error codes I have another
suggestion. Most library functions return -1 to indicate an error. For
a while my personal library of stuff used 0 to indicate an error occured
whereas 1 meant success. I recenty changed all of my functions to return
-1 on error. Notices some multibyte string functions may also return
-2 to indicate a different error although I don't know if any ISO C
functions do this.
I also feel it is important never to design an interface that provides
no way to return an error indication. This can be easy to do when
creating a function that returns a pointer to something but NULL is
an appropriate value for the pointer (e.g. retrieving something from a
table perhaps). But this leaves no way to indicate to the caller that
an error occured. It might be better to pass a pointer to the pointer
in this case. Or you could employ the kernel idiom I mentioned.
Finally, I know this is obvious but a library function should never set
errno to 0.
Mike
--
A program should be written to model the concepts of the task it
performs rather than the physical world or a process because this
maximizes the potential for it to be applied to tasks that are
conceptually similar and, more important, to tasks that have not
yet been conceived.
|
|
From: Jan E. <je...@li...> - 2003-03-17 16:56:13
|
>> /*=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D
>> ultostr
>> by Jan "Hirogen2" Engelhardt <hirogen2 at gmx de>, 2003
>> -- distributed under the Frontier Artistic License and GNU General P=
ublic
>> -- License. See doc/FAL1.txt and doc/GPL2.txt for details.
>
>The license must be changed to BSD.
What's the thing about BSD?
>> NAME
>> ultostr - convert an unsigned long integer to a string
>The name must be prefixed with clc_ .
Was just straight forward copied from my lib, so there is not anything cl=
c
related in it ;)
>> SYNOPSIS
>> unsigned char *ultostr(unsigned long num, unsigned long base,
>> unsigned char *ptr, size_t size);
>unsigned long base is pretty long for the range 2..36 :-) strtoul uses
>int for base.
woho... heh right, that probably should have been unsigned char.
>Unsigned char* are very uncommon in C libraries, and its buddy function
>strtoul uses char*.
And? I like it. It's probably because I cannot feel comfortable when they
assign the '=F6' a value of -10.
>I assume that you mean that errno is set to ERANGE? I guess we won't
Whatever. Take EDOM.
>have to test for legal base. strtoul sets errno to EINVAL if base is out
Oh yeah I know that, but it looks like I swapped EINVAL and EFAULT. Every=
where
where I looked.
>/* Proper libclc format guidelines (unpublished :-)) applied */
this is the thing nobody can really agree to, the discussion for such is =
way
too long.
The major aspects of my style are
>unsigned char *ultostr(
> unsigned long num,
> unsigned long base,
> unsigned char *ptr,
> size_t size)
- one row, or indent-by-1 if longer than 79 chars
>{
- as well as keeping any { on the line
> unsigned char *sym =3D "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
>
> clc_assert_not_null(ultostr, ptr);
> clc_assert_arg(ultostr, base >=3D 2 && base <=3D 36);
>
> *startp =3D ptr + size - 2;
> if(base < 2 || base > 36) {
> errno =3D ERANGE;
> return NULL;
> }
>
> while(--size > 0) {
> *ptr =3D sym[num % base];
> num /=3D base;
> --ptr;
> }
>
> return ptr;
>}
>
>I fail to see where startp is defined.
imagine ptr is startp
>Very good documentation!
Indeed stolen from strtoul
- Jan Engelhardt
|
|
From: <bo...@me...> - 2003-03-17 16:46:51
|
Jan Engelhardt wrote:
> Hi,
>
> copied this from my personal lib as I thought
> this might be worth sharing, no?
Thanks for the code, Jan. Here are my comments.
>
> /*=============================================================================
> ultostr
> by Jan "Hirogen2" Engelhardt <hirogen2 at gmx de>, 2003
> -- distributed under the Frontier Artistic License and GNU General Public
> -- License. See doc/FAL1.txt and doc/GPL2.txt for details.
The license must be changed to BSD.
> -------------------------------------------------------------------------------
> NAME
> ultostr - convert an unsigned long integer to a string
The name must be prefixed with clc_ .
>
> SYNOPSIS
> unsigned char *ultostr(unsigned long num, unsigned long base,
> unsigned char *ptr, size_t size);
unsigned long base is pretty long for the range 2..36 :-) strtoul uses
int for base.
Unsigned char* are very uncommon in C libraries, and its buddy function
strtoul uses char*.
>
> DESCRIPTION
> The ultostr() function converts the number NUM to a string, in
> base BASE notation. BASE must be between 2 and 36 inclusive. To
> summarize it, this function does (nearly) exactly the opposite
> of strtoul().
>
> The output is written to the location pointed to by PTR, whose
> length is passed in SIZE. SIZE is the number of bytes, including
> space for a trailing '\0'. No more bytes than (SIZE - 1) are
> written. The output is null-padded in front.
>
> RETURN VALUE
> ultostr() returns PTR on success, or NULL on error. ERANGE is
> returned if BASE is < 2 or > 36. EFAULT is returned if PTR is
> NULL.
I assume that you mean that errno is set to ERANGE? I guess we won't
have to test for legal base. strtoul sets errno to EINVAL if base is out
of range according to my man page. That page also says that C99 does
*not* set errno if base is out of range, so we shouldn't do it either?
EFAULT does not exist in ANSI C and the libclc doesn't require that we
test for NULL args in release versions. The debug version should have an
clc_assert_not_null(function_name, ptr);
It should also have a
clc_assert_arg(function_name, base >= 2 && base <= 36);
>
> EXAMPLE
> unsigned char buf[12];
> memset(buf, 0, 12);
> printf("%s\n", ultostr(170, 2, 12));
>
> Prints 00010101010 (11 chars).
> =============================================================================*/
> #include <errno.h>
> #include <stdio.h>
>
> unsigned char *ultostr(unsigned long num, unsigned long base,
> unsigned char *ptr, size_t size) {
> unsigned char *sym = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
> *startp = ptr + size - 2;
> if(base < 2 || base > 36) { errno = ERANGE; return NULL; }
> if(ptr == NULL) { errno = EFAULT; return NULL; }
> while(--size > 0) {
> *ptr = sym[num % base];
> num /= base;
> --ptr;
> }
> return ptr;
> }
>
/* Proper libclc format guidelines (unpublished :-)) applied */
unsigned char *ultostr(
unsigned long num,
unsigned long base,
unsigned char *ptr,
size_t size)
{
unsigned char *sym = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
clc_assert_not_null(ultostr, ptr);
clc_assert_arg(ultostr, base >= 2 && base <= 36);
*startp = ptr + size - 2;
if(base < 2 || base > 36) {
errno = ERANGE;
return NULL;
}
while(--size > 0) {
*ptr = sym[num % base];
num /= base;
--ptr;
}
return ptr;
}
I fail to see where startp is defined.
Very good documentation!
I think we need a function like this, especially if it converts numbers
to bit patterns. Lots of people wants to convert integers to bit patterns.
If we add this function, should we add ltostr, lltostr and ulltostr as well?
--
boa
Please join the libclc-developers list
at http://lists.sourceforge.net/lists/listinfo/libclc-developers
|
|
From: Jan E. <je...@li...> - 2003-03-17 16:16:35
|
Hi,
copied this from my personal lib as I thought
this might be worth sharing, no?
/*=============================================================================
ultostr
by Jan "Hirogen2" Engelhardt <hirogen2 at gmx de>, 2003
-- distributed under the Frontier Artistic License and GNU General Public
-- License. See doc/FAL1.txt and doc/GPL2.txt for details.
-------------------------------------------------------------------------------
NAME
ultostr - convert an unsigned long integer to a string
SYNOPSIS
unsigned char *ultostr(unsigned long num, unsigned long base,
unsigned char *ptr, size_t size);
DESCRIPTION
The ultostr() function converts the number NUM to a string, in
base BASE notation. BASE must be between 2 and 36 inclusive. To
summarize it, this function does (nearly) exactly the opposite
of strtoul().
The output is written to the location pointed to by PTR, whose
length is passed in SIZE. SIZE is the number of bytes, including
space for a trailing '\0'. No more bytes than (SIZE - 1) are
written. The output is null-padded in front.
RETURN VALUE
ultostr() returns PTR on success, or NULL on error. ERANGE is
returned if BASE is < 2 or > 36. EFAULT is returned if PTR is
NULL.
EXAMPLE
unsigned char buf[12];
memset(buf, 0, 12);
printf("%s\n", ultostr(170, 2, 12));
Prints 00010101010 (11 chars).
=============================================================================*/
#include <errno.h>
#include <stdio.h>
unsigned char *ultostr(unsigned long num, unsigned long base,
unsigned char *ptr, size_t size) {
unsigned char *sym = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
*startp = ptr + size - 2;
if(base < 2 || base > 36) { errno = ERANGE; return NULL; }
if(ptr == NULL) { errno = EFAULT; return NULL; }
while(--size > 0) {
*ptr = sym[num % base];
num /= base;
--ptr;
}
return ptr;
}
//==[ End of file ]============================================================
- Jan Engelhardt
|
|
From: Hallvard B F. <h.b...@us...> - 2003-03-17 11:34:06
|
Michael B.Allen writes: >Bj=F8rn Augestad <bo...@me...> wrote: > >> >> Seriously, malloc is not, prior to e.g. Unix98, required to set errn= o to=20 >> ENOMEM. This means that we cannot assume that errno contains a=20 >> reasonable value if malloc() fails. We must therefore set it ourselv= es.=20 >> Since we don't know about POSIX (we're pure ANSI C, remember) we mus= t=20 > > set it to CLC_ENOMEM. Oh well... >=20 > But the standards do not say that malloc will not set errno to > ENOMEM and every malloc ever written _does_ use ENOMEM. Prove it. > Same goes for probably every other ISO function you'll use. fgetpos() can return ESPIPE (file descriptor is a pipe, FIFO, or socket= ) on my system (Solaris). Do you think that can happen on systems that d= o not support pipes? > You're introducing a technically unncessary level of indirection that= > contributes to an overall non-standard experience for the sake of a > standard. Why are you going to make 99.9999% of the users deal with > two different namespaces of error codes for some imaginary platform > that doesn't exist? Following the letter of the law for this is just= > being pedantic. So far, we don't know if we'll be using any CLC_E* error codes. There is little point in setting errno =3D CLC_ENOMEM in a function which can= only return one type of error, for example. Then all the user needs it= to test whether or not an error happened. The main point so far is that we want a standardized way which function= s can return error conditions if they want to. We could have said that such functions return an integer error code. We chose not to; we preferred to reuse the errno mechanism which already exists. Given that, we _had_ to open for the possibility that functions may need to set errno to something which does not exist in any errno.h. When libclc is nearing completion, maybe you'll be right and it'll turn= out that almost no functions used this capability. If so, _that_ is th= e time to discuss whether or not to remove CLC_E* codes. Until then we need them, to make sure clc functions return error conditions in a consistent manner. --=20 Hallvard |
|
From: Michael B.A. <mb...@io...> - 2003-03-17 10:23:25
|
I subscribed to the libclc-developers list and sent this message there. On Sun, 16 Mar 2003 22:18:58 +0100 Bj=F8rn Augestad <bo...@me...> wrote: > >>>Why does libclc define it's own error codes? What libclc function could > >>>possibly return CLC_ENODEV? If a platform does not have ISO C and POSIX > >>>error codes available there is probably a reason for it. > >> > >>As an example, malloc() doesn't have to set errno to ENOMEM. If we have= =20 > >>a function which can fail for 2 or more reasons and one of them is "Out= =20 > >>of memory", we must set errno to ENOMEM and CLC_EFOO. Since we cannot=20 > >>rely on the precense of ENOMEM, we need to use CLC_ENOMEM. We try to=20 > >=20 > >=20 > > A malloc() implementation with no ENOMEM? Interesting! Which platform > > is that? >=20 > The DeathStation 9000 :-) > Seriously, malloc is not, prior to e.g. Unix98, required to set errno to= =20 > ENOMEM. This means that we cannot assume that errno contains a=20 > reasonable value if malloc() fails. We must therefore set it ourselves.=20 > Since we don't know about POSIX (we're pure ANSI C, remember) we must=20 > set it to CLC_ENOMEM. Oh well... But the standards do not say that malloc will not set errno to ENOMEM and every malloc ever written _does_ use ENOMEM. Same goes for probably every other ISO function you'll use. You're introducing a technically unncessary level of indirection that contributes to an overall non-standard experience for the sake of a standard. Why are you going to make 99.9999% of the users deal with two different namespaces of error codes for some imaginary platform that doesn't exist? Following the letter of the law for this is just being pedantic. Mike --=20 A program should be written to model the concepts of the task it performs rather than the physical world or a process because this maximizes the potential for it to be applied to tasks that are conceptually similar and, more important, to tasks that have not yet been conceived.=20 |
|
From: <bo...@me...> - 2003-03-17 07:16:18
|
Peter Shaggy Haywood wrote:
> I submit the following for discussion and (hopefully) the approval of the
> group. This may also serve as a first draft of the documentation for this
> module.
Thanks for a very well documented submission, Peter.
Here are my comments, questions and misunderstandings :-)
- Have you considered to provide the cleanup function (dtor) to the
delete-functions instead of storing the dtor in the list? I guess that
you currently store it since we need a per-node dtor right now.
- Why do we need the size of the payload?
> I'll let you ponder this interface and suggest improvements before I post
> the implementation of this module.
The plan is to add the list to the June release of libclc. We may want
to discuss other "container classes" as well as the list so that common
container concepts gets the same solution. This means that we won't
finish this discussion in a long time...
>
> This document explains how to use the functions and data types defined by
> the clc_dl module. This module implements a generic double linked list
> system that is easy to use but flexible.
>
> Data types:
> ===========
> These are the data types defined by this module. They are defined in the
> clc_dl.h header file. These types should generally not be touched directly
> by users. Leave it to the module to manipulate these types. However, their
> definitions are provided to users by defining CLC_DL_ADVANCED_FUNCS before
> including the header. This allows extra flexibility and control.
IMO, the CLC_DL_ADVANCED_FUNCS macro should be removed. All
functionality should always be available, just like the standard C library.
>
>
> typedef struct CLC_DL_NODE
> {
> struct CLC_DL_NODE *prev, *next;
> int type; /* payload type */
> size_t size; /* payload size */
> void *payload; /* payload data */
> void (*cleanup)(void *); /* cleanup function */
> } CLC_DL_NODE;
Does CLC_DL_NODE and CLC_DL_LIST have to be in the header?
>
> CLC_DL_NODE is the basis of the system. It is the double linked list node
> type. The members of this struct are as follows:
>
> prev, next pointers to the previous and next nodes
>
> type the payload type (in case of a linked list containing
> more than one type of data
How likely is it that we have more than one type in the same list? If it
isn't used a lot, maybe we can have more than one list type? CLC_DL_LIST
and CLC_DL_MULTILIST, where the latter 'knows' about types?
Even if we have more than one type in the list, does the list have to
know? A user can easily create a wrapper for the data.
struct type_wrapper {
int type;
void *data;
};
and store this in the list.
>
> size size of the payload
Why do we need the size? If it is needed for copying, what about pointer
members in the payload?
>
> payload pointer to the actual payload itself
>
> cleanup pointer to a user supplied function to cleanup any extra
> memory associated with a node's payload (but not the
> node or payload itself - clc_dl_FreeNode() takes care of
> that)
>
>
> typedef struct CLC_DL_LIST
> {
> CLC_DL_NODE *head, *tail; /* first and last nodes */
> size_t num; /* number of nodes */
> int dyn; /* flag to indicate that this struct was
> dynamically allocated */
Aren't CLC_DL_LIST objects always dynamically allocated?
> } CLC_DL_LIST;
>
> CLC_DL_LIST is a structure intended to store all data needed to access and
> manipulate a linked list, currently comprising the following:
>
> head, tail pointers to the first and last nodes in the list (NULL
> if list is empty)
>
> num number of nodes in the list
>
> dyn flag to indicate whether this struct was dynamically
> allocated with clc_dl_NewList() -
> IMPORTANT NOTE: don't tamper with this member or memory
> faults may occur!
>
> Most accesses to a linked list use this struct. An object of this type may
> be obtained (dynamically allocated) by using the clc_dl_NewList() function.
>
>
> struct CLC_DL_SEARCH_DATA
> {
> CLC_DL_LIST *list; /* list the search applys to */
> CLC_DL_NODE *lastnode; /* last node found by search */
> int type; /* type of payload to search for */
> size_t size; /* size of payload to search for */
> void *find; /* payload data to search for */
> int (*cmp)(int keytype, size_t keysize, const void *keydata,
> int nodetype, size_t nodesize,
> const void *nodedata); /* compare func */
> int dir; /* direction of search:
> CLC_DL_FORWARD = search forward,
> CLC_DL_REVERSE = search backward */
How about "int forward" instead of "int dir". If forward == 0, then it
is backward. It removes the need for CLC_DL_FORWARD and CLC_DL_REVERSE.
> };
[snip]
>
> The clc_dl_FindNext() function uses this data to continue the search using
> the same criteria, and updates the lastnode member each time it is called.
> Users should obtain a pointer to a CLC_DL_SEARCH_DATA object, by using
> clc_dl_NewSearch(), and pass it to clc_dl_FindFirst() and clc_dl_FindNext()
> to search for nodes.
I, as everyone else, have my own list :-) This list adt has a concept of
sublists. A sublist can be created in different ways, e.g. as a result
set from a search within the original list. Then the user can traverse
the sublist. IMHO this is better than FindFirst/Next since the user can
manipulate the result set. The sublist can for example be sorted before
traversal.
See http://www.metasystems.no/metalib/list_8h.html#_details for more
details.
>
>
> Basic functions:
> ================
> The following are the "basic" functions, the ones most commonly used, in
> the clc_dl module. These are defined in the clc_dl.c file. "Advanced"
> functions, which provide extra flexibility but are less commonly used, are
> described below this section.
>
>
> CLC_DL_LIST *clc_dl_NewList(void)
>
> The clc_dl_NewList() function is used to dynamically allocate and
> initialise a CLC_DL_LIST structure. All fields are initialised to reflect an
> empty linked list (one with no nodes).
>
>
> CLC_DL_NODE *clc_dl_InsertData(CLC_DL_LIST *list,
> int dir,
> int type,
> size_t size,
> void *payload,
> void (*cleanup)(void *));
clc_dl_add() or clc_dl_insert()?
[snip]
> CLC_DL_SEARCH_DATA clc_dl_NewSearch(void);
> void clc_dl_FreeSearch(CLC_DL_SEARCH_DATA *sd);
What if we want to search in e.g. a clc_map, clc_deque or some other
container? Is it possible to create a common descriptor for all/most
searchable modules?
>
> clc_dl_NewSearch() dynamically allocates a CLC_DL_SEARCH_DATA structure
> that can be used by clc_dl_FindFirst() and clc_dl_FindNext().
> clc_dl_FreeSearch() is used to de-allocate this structure.
>
>
> CLC_DL_NODE *clc_dl_FindFirst(CLC_DL_LIST *list,
> int type,
> size_t size,
> void *find,
> int (*cmp)(int typekey,
> const void *datakey,
> int typenode,
> const void *datanode),
> int dir,
> CLC_DL_SEARCH_DATA *srch);
>
> CLC_DL_NODE *clc_dl_FindNext(CLC_DL_SEARCH_DATA *srch);
Later you have clc_dl_findall(). How about clc_dl_findfirst() and
clc_dl_findnext(), or just clc_dl_first(), clc_dl_next() and clc_dl_all()?
[snip]
> void *clc_dl_ModifyPayload(CLC_DL_NODE *node,
> int newtype,
> size_t newsize,
> void *newpayload,
> void (*newcleanup)(void *));
clc_dl_replace()?
It doesn't really modify the existing payload, but replaces an existing
payload with a new.
[snip]
>
>
> Comparison functions:
> ---------------------
> A comparison function, as used by clc_dl_FindFirst() and
> clc_dl_FindNext(), compares one payload type and data with another. It must
> have the following prototype:
>
> int cmp(int, size_t, const void *, int, size_t, const void *);
>
> The first three parameters represent key values used to compare against the
> node payload represented by the last three parameters. The int parameters
> represent the payload type. This is useful in the case of a linked list that
> contains more than one type of data. The comparison of data can be skipped
> if the types don't match. The arguments passed to these parameters may
> simply be ignored if the linked list only contains one type of data. The
> size_t parameters represent the size of the data. The const void *
> parameters are pointers to the data to be compared. The function must return
> 0 to indicate a match and non-zero to indicate no match.
How about -1, 0 and 1 for less than, equal and greater than? Then we can
sort the list using the same function.
[snip]
I miss a few functions. copy(), merge(), foreach(),
BTW, I spent 5 hours on a train yesterday thinking about libclc. One of
the things I thought of was a general object adaptor which could be used
in "advanced" modules. Such an object adaptor would know enough about an
object to allocate, free, compare, copy and clone any object. I do *not*
recommend that we use such an adaptor in clc_list as the concept will
probably be too odd for most people. Maybe it can be used in another
list, clc_objlist?
We should probably spend more time designing libclc. :-)
--
boa
Please join the libclc-developers list
at http://lists.sourceforge.net/lists/listinfo/libclc-developers
|
|
From: <pha...@al...> - 2003-03-17 02:37:07
|
I submit the following for discussion and (hopefully) the approval of the
group. This may also serve as a first draft of the documentation for this
module.
I'll let you ponder this interface and suggest improvements before I post
the implementation of this module.
This document explains how to use the functions and data types defined by
the clc_dl module. This module implements a generic double linked list
system that is easy to use but flexible.
Data types:
===========
These are the data types defined by this module. They are defined in the
clc_dl.h header file. These types should generally not be touched directly
by users. Leave it to the module to manipulate these types. However, their
definitions are provided to users by defining CLC_DL_ADVANCED_FUNCS before
including the header. This allows extra flexibility and control.
typedef struct CLC_DL_NODE
{
struct CLC_DL_NODE *prev, *next;
int type; /* payload type */
size_t size; /* payload size */
void *payload; /* payload data */
void (*cleanup)(void *); /* cleanup function */
} CLC_DL_NODE;
CLC_DL_NODE is the basis of the system. It is the double linked list node
type. The members of this struct are as follows:
prev, next pointers to the previous and next nodes
type the payload type (in case of a linked list containing
more than one type of data
size size of the payload
payload pointer to the actual payload itself
cleanup pointer to a user supplied function to cleanup any extra
memory associated with a node's payload (but not the
node or payload itself - clc_dl_FreeNode() takes care of
that)
typedef struct CLC_DL_LIST
{
CLC_DL_NODE *head, *tail; /* first and last nodes */
size_t num; /* number of nodes */
int dyn; /* flag to indicate that this struct was
dynamically allocated */
} CLC_DL_LIST;
CLC_DL_LIST is a structure intended to store all data needed to access and
manipulate a linked list, currently comprising the following:
head, tail pointers to the first and last nodes in the list (NULL
if list is empty)
num number of nodes in the list
dyn flag to indicate whether this struct was dynamically
allocated with clc_dl_NewList() -
IMPORTANT NOTE: don't tamper with this member or memory
faults may occur!
Most accesses to a linked list use this struct. An object of this type may
be obtained (dynamically allocated) by using the clc_dl_NewList() function.
struct CLC_DL_SEARCH_DATA
{
CLC_DL_LIST *list; /* list the search applys to */
CLC_DL_NODE *lastnode; /* last node found by search */
int type; /* type of payload to search for */
size_t size; /* size of payload to search for */
void *find; /* payload data to search for */
int (*cmp)(int keytype, size_t keysize, const void *keydata,
int nodetype, size_t nodesize,
const void *nodedata); /* compare func */
int dir; /* direction of search:
CLC_DL_FORWARD = search forward,
CLC_DL_REVERSE = search backward */
};
CLC_DL_SEARCH_DATA is a structure used in linked list search operations.
Searching in the clc_dl module uses two functions; clc_dl_FindFirst(), which
takes (among other arguments) a pointer to this type, and clc_dl_FindNext()
which takes the same pointer. The former fills in the object this pointer
points to with values related to the search. These values are as follows:
list pointer to the CLC_DL_LIST the search pertains to
lastnode the last found node that matches the search criteria
(a null pointer if search failed)
type payload type of the node data to find
find pointer to data to find in the linked list
cmp pointer to a function that compares the key data, size
and type with a node's payload data, size and type,
returning 0 if they compare equal and non-zero
otherwise.
dir the direction of the search - use CLC_DL_FORWARD to
search forward from the start of the list or
CLC_DL_REVERSE to search backward from the end of the
list.
The clc_dl_FindNext() function uses this data to continue the search using
the same criteria, and updates the lastnode member each time it is called.
Users should obtain a pointer to a CLC_DL_SEARCH_DATA object, by using
clc_dl_NewSearch(), and pass it to clc_dl_FindFirst() and clc_dl_FindNext()
to search for nodes.
Basic functions:
================
The following are the "basic" functions, the ones most commonly used, in
the clc_dl module. These are defined in the clc_dl.c file. "Advanced"
functions, which provide extra flexibility but are less commonly used, are
described below this section.
CLC_DL_LIST *clc_dl_NewList(void)
The clc_dl_NewList() function is used to dynamically allocate and
initialise a CLC_DL_LIST structure. All fields are initialised to reflect an
empty linked list (one with no nodes).
CLC_DL_NODE *clc_dl_InsertData(CLC_DL_LIST *list,
int dir,
int type,
size_t size,
void *payload,
void (*cleanup)(void *));
The clc_dl_InsertData() function is used to create a new node and insert
new data into a linked list. It returns a pointer to the new node, or a null
pointer if memory could not be allocated for the node. The parameters are:
list a pointer indicating the linked to insert data into
dir direction of insertion: use CLC_DL_FRONT or CLC_DL_START
to insert at the start of the list or CLC_DL_END to
insert at the end of the list
type type of the payload (in case of a list containing more
than one type of data)
size size of the payload
payload the data to be copied to the new node's payload
cleanup pointer to a user supplied function to cleanup any extra
memory associated with a node's payload (but not the
node or payload itself - clc_dl_FreeNode() takes care of
that)
The last argument may be a null pointer, in which case no cleanup function
will be called, but the node and its payload will still be de-allocated
normally by clc_dl_FreeNode().
CLC_DL_SEARCH_DATA clc_dl_NewSearch(void);
void clc_dl_FreeSearch(CLC_DL_SEARCH_DATA *sd);
clc_dl_NewSearch() dynamically allocates a CLC_DL_SEARCH_DATA structure
that can be used by clc_dl_FindFirst() and clc_dl_FindNext().
clc_dl_FreeSearch() is used to de-allocate this structure.
CLC_DL_NODE *clc_dl_FindFirst(CLC_DL_LIST *list,
int type,
size_t size,
void *find,
int (*cmp)(int typekey,
const void *datakey,
int typenode,
const void *datanode),
int dir,
CLC_DL_SEARCH_DATA *srch);
CLC_DL_NODE *clc_dl_FindNext(CLC_DL_SEARCH_DATA *srch);
The clc_dl_FindFirst() and clc_dl_FindNext() functions are used to search
linearly for a node or nodes of a specified type containing a specified set
of data. The parameters of the former are:
list pointer indicating the linked list to search
type type of payload to search for
size size of payload to search for
find data to search for in payloads
cmp pointer to compare function (compares key type, size and
data to node's payload type, size and data - returns 0
if match, non-zero otherwise)
dir direction of search: use CLC_DL_FORWARD to search
forward from the start of the list or CLC_DL_REVERSE to
search backward from the end of the list
srch pointer to a structure containing data used in the
search process
clc_dl_FindFirst() searches for the first node that matches (according to
the comparison function passed as the fifth argument) the key data and its
type and size. clc_dl_FindNext(), using a CLC_DL_SEARCH_DATA * previously
passed to clc_dl_FindFirst(), searches for subsequent nodes that match the
search criteria specified in the clc_dl_FindFirst() call.
A special comparison function has been provided, which always compares
equal, called clc_dl_FindAll(). This can be used by clc_dl_FindFirst() and
clc_dl_FindNext() to "find" all nodes of a list in turn. This provides a way
to traverse the list node by node.
clc_dl_FindFirst() returns a pointer to the first node found that matches
the search criteria. clc_dl_FindNext() returns a pointer to the next node in
turn that matches the search criteria. Both functions return a null pointer
if a match is not found.
void clc_dl_FreeList(CLC_DL_LIST *list);
clc_dl_FreeList() removes all nodes and de-allocates all memory associated
with a linked list. Its single parameter, a pointer to CLC_DL_LIST,
indicates the linked list to de-allocate. The function calls
clc_dl_FreeNode() to de-allocate each node.
If the user supplied a cleanup function (when the node was created), then
that function is called before the payload of each node is de-allocated. If
the CLC_DL_LIST structure was allocated with clc_dl_NewList(), it will also
be de-allocated by clc_dl_FreeList().
void clc_dl_FreeNode(CLC_DL_LIST *list, CLC_DL_NODE *node);
clc_dl_FreeNode() removes a node from a linked list and de-allocates it.
The first parameter is a pointer to CLC_DL_LIST indicating the linked list
the node belongs to. If the node to be freed is not in a linked list (not
associated with a CLC_DL_LIST structure), the argument passed to this
parameter must be a null pointer. The second parameter is a pointer to the
node to free. If the user supplied a cleanup function (when the node was
created), then that function is called before the payload and node are
de-allocated.
void *clc_dl_Payload(CLC_DL_NODE *node, void *data);
clc_dl_Payload() stores a copy of a node's payload data in a user supplied
buffer, and returns a pointer to this copied data. The first parameter is a
pointer to the node whose payload data is to be copied. The second parameter
is a pointer to the buffer used to copy the data to. The return value is the
argument passed to the data parameter.
size_t clc_dl_NumNodes(CLC_DL_LIST *list);
int clc_dl_Type(CLC_DL_NODE *node);
size_t clc_dl_Size(CLC_DL_NODE *node);
clc_dl_NumNodes() returns the number of nodes in the linked list specified
by its only parameter, a pointer to CLC_DL_LIST.
clc_dl_Type() returns the payload type of the linked list node specified
by its only parameter, a pointer to CLC_DL_NODE.
clc_dl_Size() returns the payload size of the linked list node specified
by its only parameter, a pointer to CLC_DL_NODE.
int clc_dl_findall(int keytype, size_t keysize,
const void *keydata, int nodetype,
size_t nodesize, const void *nodedata);
clc_dl_findall() is a special function that can be used as the comparison
function for clc_dl_FindFirst() and clc_dl_FindNext(). It always returns 0,
indicating equality, regardless of the values passed to it. This is useful
when you want clc_dl_FindFirst() and clc_dl_FindNext() to "find" every
single node in the list; when, for example, you want to access each node in
turn, one at a time. This is the standard way of traversing the list node by
node.
Advanced functions:
===================
The following functions are defined in the clc_dl_a.c source file. Thier
prototypes are available by defining a macro called CLC_DL_ADVANCED_FUNCS
before the clc_dl.h header is included.
CLC_DL_NODE *clc_dl_NewNode(int type,
size_t size,
void *payload,
void (*cleanup)(void *));
clc_dl_NewNode() creates a new CLC_DL_NODE structure and fills it with
data. The parameters are:
type type of the data to store in the node's payload
size size of data to store in the node's payload
payload pointer to data to store in the node's (dynamically
allocated) payload
cleanup pointer to a user supplied function to cleanup any extra
memory associated with a node's payload (but not the
node or payload itself - clc_dl_FreeNode() takes care of
that)
The last argument may be a null pointer, in which case no cleanup function
will be called, but the node and its payload will still be de-allocated
normally by clc_dl_FreeNode().
void clc_dl_InsertNodes(CLC_DL_LIST *list,
CLC_DL_NODE *location,
CLC_DL_NODE *start,
int dir);
clc_dl_InsertNodes() inserts a "mini-list", a group of one or more linked
nodes that are not part of a linked list (ie., are not associated with a
CLC_DL_LIST structure), into a linked list. The parameters are:
list pointer to the list to insert the mini-list into
location pointer to "target node" where the mini-list will be
inserted (the mini-list will be inserted either before
or after this node)
start pointer to the first node in the mini-list
dir direction of insertion: use CLC_DL_BEFORE to insert
before the target node or CLC_DL_AFTER to insert after
the target node
CLC_DL_NODE *clc_dl_RemoveNode(CLC_DL_LIST *list, CLC_DL_NODE *node);
clc_dl_RemoveNode() removes a node from a linked list, but does not
de-allocate the it. The node is still available (via the returned pointer)
to manipulate as required. The first parameter is a pointer to the linked
list the node belongs to, while the second is a pointer to the node to be
removed.
CLC_DL_NODE *clc_dl_RemoveMultiNodes(CLC_DL_LIST *list,
CLC_DL_NODE *start,
size_t num);
clc_dl_RemoveMultiNodes() removes one or more nodes from a linked list,
but does not de-allocate them. The nodes removed remain linked, forming a
"mini-list", a group of one or more linked nodes that are not part of a
linked list (ie., are not associated with a CLC_DL_LIST structure). The
nodes are still available (via the returned pointer, which points to the
first node in the mini-list) to manipulate as required. The function
parameters are:
list pointer to the list to remove nodes from
start pointer to the first node to remove
num the number of nodes to remove
If the last argument is greater than the number of nodes from the node
indicated by the second argument to the end of the list, then all the nodes
from the start node to the end of the list are removed.
void *clc_dl_ModifyPayload(CLC_DL_NODE *node,
int newtype,
size_t newsize,
void *newpayload,
void (*newcleanup)(void *));
clc_dl_ModifyPayload() is used to change a CLC_DL_NODE's payload and
cleanup function. The original payload is de-allocated. If the user supplied
a cleanup function (when the node was created), then that function is called
before the payload is de-allocated. The parameters are:
node pointer to the node to modify
newtype the new payload type
newsize new payload size
newpayload pointer to data to copy to new payload
newcleanup pointer to a user supplied function to cleanup any extra
memory associated with a node's payload (but not the
node or payload itself - clc_dl_FreeNode() takes care of
that)
The last argument may be a null pointer, in which case no cleanup function
will be called, but the node and its payload will still be de-allocated
normally by clc_dl_FreeNode().
clc_dl_ModifyPayload() returns a pointer to the new payload on success. It
returns a null pointer if memory could not be allocated for the new payload,
in which case the old payload remains intact.
CLC_DL_NODE *clc_dl_GetHead(CLC_DL_LIST *list);
CLC_DL_NODE *clc_dl_GetTail(CLC_DL_LIST *list);
The clc_dl_GetHead() and clc_dl_GetTail() functions return pointers to the
first and last nodes (respectively) in a linked list. The solitary paramer
to each of these functions is a pointer to CLC_DL_LIST representing the
linked list whose head/tail node pointer will be returned.
User supplied functions:
========================
This section explains the purpose and requirements of user supplied
functions used by the clc_dl module. The user must follow these rules when
writing these functions.
Cleanup functions:
------------------
A cleanup function, as used by clc_dl_FreeNode(), clc_dl_FreeList() and
clc_dl_ModifyPayload(), is used to de-allocate any extra memory associated
with a node's payload - but not the payload itself nor the node.
(clc_dl_FreeNode() or clc_dl_FreeList() takes care of that.) Its prototype
must take the form:
void cleanup(void *);
The single parameter is a pointer to a node's payload data.
For example, suppose a node's payload is a struct containing a member
called some_ptr which is a pointer to some dynamically allocated memory. The
node's cleanup function could free the memory pointed to by this pointer
like so:
void cleanup(void *payload)
{
struct foo *bar = payload;
free(bar->some_ptr);
}
IMPORTANT NOTE: users should not de-allocate the payload themselves. This
should be left to clc_dl_FreeNode().
Comparison functions:
---------------------
A comparison function, as used by clc_dl_FindFirst() and
clc_dl_FindNext(), compares one payload type and data with another. It must
have the following prototype:
int cmp(int, size_t, const void *, int, size_t, const void *);
The first three parameters represent key values used to compare against the
node payload represented by the last three parameters. The int parameters
represent the payload type. This is useful in the case of a linked list that
contains more than one type of data. The comparison of data can be skipped
if the types don't match. The arguments passed to these parameters may
simply be ignored if the linked list only contains one type of data. The
size_t parameters represent the size of the data. The const void *
parameters are pointers to the data to be compared. The function must return
0 to indicate a match and non-zero to indicate no match.
For example, suppose you have a linked list containing strings, and you
want to search for a particular string in the list. The comparison function
passed to clc_dl_FindFirst() might look something like this:
int cmp(int keytype, size_t keysize, const void *keydata,
int nodetype, size_t nodesize, const void *nodedata)
{
/* first, make sure the types match */
if(keytype != nodetype)
return 1;
/* no need to compare strings if sizes don't match
(makes this function more efficient) */
if(keysize != nodesize)
return 1;
/* compare the strings */
return strcmp(keydata, nodedata);
}
A special comparison function is provided in the clc_dl module. Called
clc_dl_FindAll(), it always returns 0, indicating a match at every call.
This may be useful if you wish to use clc_dl_FindFirst() and
clc_dl_FindNext() to "find" every node in the list in turn. This is the
standard way of traversing the list node by node.
Eample of use:
==============
Normal use of this module is easy. The following program demonstrates how
simple it is.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "clc_dl.h"
#define PAYLOAD_TYPE_STRING 0
int cmp(int keytype, size_t keysize, const void *keydata,
int nodetype, size_t nodesize, const void *nodedata)
{
/* first, make sure the types match */
if(keytype != nodetype)
return 1;
/* no need to compare strings if sizes don't match
(makes this function more efficient) */
if(keysize != nodesize)
return 1;
/* compare the strings */
return strcmp(keydata, nodedata);
}
int main(void)
{
char *s[] = {"foo", "bar", "baz", "plonk", "baz", "bar", "foo"};
CLC_DL_NODE *node;
CLC_DL_LIST *list;
CLC_DL_SEARCH_DATA *srch;
int i;
char buf[10];
/* first, create the linked list */
list = clc_dl_NewList();
if(!list)
{
fprintf(stderr, "Memory allocation error.\n");
return EXIT_FAILURE;
}
/* next, fill it with data */
for(i = 0; i < (sizeof s / sizeof *s); i++)
{
node = clc_dl_InsertData(list, CLC_DL_START, PAYLOAD_TYPE_STRING,
1 + strlen(s[i]), s[i], NULL);
if(!node)
{
clc_dl_FreeList(list);
fprintf(stderr, "Memory allocation error.\n");
return EXIT_FAILURE;
}
}
/* now that we have filled the list, we can traverse it using
clc_dl_FindFirst() and clc_dl_FindNext() -
first we need a CLC_DL_SEARCH_DATA */
srch = clc_dl_NewSearch();
if(!srch)
{
clc_dl_FreeList(list);
fprintf(stderr, "Memory allocation error.\n");
return EXIT_FAILURE;
}
/* pass clc_dl_FindAll to clc_dl_FindFirst() to find first node */
node = clc_dl_FindFirst(list, 0, 0, NULL, clc_dl_FindAll,
CLC_DL_FORWARD, srch);
while(node)
{
/* we have the first node - now we extract the payload data */
clc_dl_Payload(node, buf);
printf("%s\n", buf);
/* find the next node */
node = clc_dl_FindNext(srch);
}
putchar('\n');
/* searching for data is just as simple - say you want to search for
all nodes containing "baz" */
node = clc_dl_FindFirst(list, PAYLOAD_TYPE_STRING, 1 + strlen(s[2]),
s[2], cmp, CLC_DL_FORWARD, srch);
while(node)
{
/* we have the first matching node -
now we extract the payload data */
clc_dl_Payload(node, buf);
printf("%s\n", buf);
/* find the next matching node */
node = clc_dl_FindNext(srch);
}
/* now we're done - we can clean up and quit */
clc_dl_FreeSearch(srch);
clc_dl_FreeList(list);
return 0;
}
Dig the sig!
_-/~~~~~~/\ |------ pha...@al... -------|
////| /^^\ |-----------------------------------------------|
||||||\ |()()|___ | name Peter Haywood | |
|||||||| | _---6|9- | alias Shaggy | Remove .NOSPAM to reply |
|||||||| |/ \_/| | alias Wolvaen | |
|||||||| | A | | alias HEY YOU! | |
|||||||| | (===/\\/ |-----------------------------------------------|
\||||/ /------\|/ | Dig my groovy web page! |
"""" " | http://alphalink.com.au.au/~phaywood/ |
- Aint I'm a Dawg! --|--------------- Shaggy was here! --------------|
(This sig best viewed with a fixed pitch font.)
|
|
From: <bo...@me...> - 2003-03-13 07:57:04
|
I'm not sure if anyone is subscribing to this list, but I just want everyone to know that I'll probably not be online until sunday evening. Small vacation, much needed. :-) See you all on sunday! -- boa Please join the libclc-developers list at http://lists.sourceforge.net/lists/listinfo/libclc-developers |
|
From: Nolan S. <imm...@fr...> - 2003-03-09 03:21:54
|
Hallvard B Furuseth wrote: > This is a reply to message > https://sourceforge.net/forum/message.php?msg_id=1917616 > by augestad. > >> [snip] This is my preffered coding style. /* Program description ** max 80 characters wide */ /* function comment goes here */ int func( char *a, int b, double c ) { /* or here, 2 spaces after the longest part of the ** multiline statement. Comment not normaly here ** because it doesnt fit well. */ } char *p; /* * next to variable name, though char * p; might be ok, char* is ** not because there is no such type in my opinion, it is a mod ** qualifier, like unsigned char */ i++; i + 1; i = 1; /* comment */ j = i + 1; /* comment 2 spaces after the longest line in this group */ j > i; j >= i; for ( i = 0; i < j; i++ ) { /* all parts spaced for readability */ } while ( i < j ) { } do { } while ( i < j ); if ( j == 0 || j == 5 ) { } atoi( array[ i + 1 ] ); array[ k + 1 ]; 2 space indents except in aligning multilined statements |