5
\$\begingroup\$

I'm pretty new to C so I decided to implement SRFI-1 (linked-list library) to help me learn.

What I have so far is working, but only handles integers.

typedef struct pair {
  int car;
  struct pair *cdr;
} pair;

From what I've read I can use a void * for the car:

typedef struct pair {
  void *car;
  struct pair *cdr;
} pair;

This works but I get lots of these...

llist.c:262:3: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘void *’
llist.c:27:7: note: expected ‘void *’ but argument is of type ‘int’

I've found some workarounds but before diving in and fixing everything I want to be sure this is the appropriate way to go.

The only other way I can think of is using some sort of generic object type:

typedef struct object {
  union {
    int fixnum;
    double flonum;
  }
}

typedef struct pair {
  object *car;
  struct pair *cdr;
} pair;

Or something similar.

Is there a best practice for generic functions/data types?

The full source is here.

\$\endgroup\$
6
  • \$\begingroup\$ This depends on what your goal is. Do you want to produce a linked list library that will be useful to users in C? Or do you want to produce something in the spirit of the SRFI-1? A straightforward copy of that won't produce idiomatic C code. \$\endgroup\$ May 23, 2011 at 2:46
  • \$\begingroup\$ @Winston Ewert: I'd like to make this idiomatic C even if that means deviating from SRFI-1. The purpose of doing this is to help me learn C (pointers, memory management, etc), so a straightforward port isn't really what I'm going for. \$\endgroup\$
    – Ella
    May 23, 2011 at 4:20
  • \$\begingroup\$ This is not so much a review of working code as a request to help solve a problem. Probably a better fit on Stack Overflow. \$\endgroup\$
    – Adam Davis
    May 23, 2011 at 12:44
  • \$\begingroup\$ @Adam Davis, its really a question about best practices. However, that's still off-topic for this site. \$\endgroup\$ May 23, 2011 at 16:50
  • 1
    \$\begingroup\$ @Jack Trades, there are a number of best practice questions here which haven't been challenged. But the official policy is no best practice questions. \$\endgroup\$ May 23, 2011 at 21:53

1 Answer 1

3
\$\begingroup\$

void* is the standard to implement such generics in C. The trouble with union is that the end-user cannot put their own types/structs in it. With void * a pointer to a user-defined struct can be used. However, be careful about memory leaks!

The warnings you are getting is because you haven't consistently used void*.

llist.c:262:3: warning: format ‘%d’ expects type ‘int’, but argument 2 has type ‘void *’

This indicates that you are printf'ing the data as if it were an int. But you don't what it is so you can't do that. Either don't provide printing options or provide a way to control how the object prints.

llist.c:27:7: note: expected ‘void *’ but argument is of type ‘int’

You have a void * and you are passing it to a function that takes int. Everything inside your library should be using void * not int. Only the actual code using your library should conver the void * into int or whatever they are using.

\$\endgroup\$
9
  • \$\begingroup\$ So I should implement everything with void* and let the user worry about the rest, which would look something like this? int j = 42; void *p = &j; vpair *k = vcons(p, NULL); printf("\n%d\n", *(int *)k->car); Presumably I/the user would create some type of wrapper functions which would hide most of the pointer stuff? I definitely have one foot over the edge of the limit of my knowledge here, so I'm sorry if I'm not making sense. \$\endgroup\$
    – Ella
    May 23, 2011 at 20:41
  • \$\begingroup\$ @Jack Trades, firstly, j appears to be a local variable. You really shouldn't take pointers from local variables because they will become invalid as soon as the function ends. You really need to malloc space for an int, and use that. But then you need to free the memory when its not being used anymore. \$\endgroup\$ May 23, 2011 at 21:50
  • \$\begingroup\$ @Jack Trades, I'm not seeing any sort of wrapping which would improve situation. You could write wrappers for specific versions of the list. I.e. you could write an int linked-list which used the original linked list underneath. However, I don't think it would be very useful. \$\endgroup\$ May 23, 2011 at 21:52
  • \$\begingroup\$ @Winston Ewert: Yes j was a local var and I can see now why that won't work, thank you. So code like printf("%d", *(int *)k->car) is what is expected from generic libraries like this? I was originally thinking of writing wrappers like an int linked-list, but after reading your comments and considering a list of heterogeneous types I realize that would be mostly pointless. My idea of a generic print_list function seems to be drifting further and further away. \$\endgroup\$
    – Ella
    May 24, 2011 at 4:11
  • 1
    \$\begingroup\$ @JackTrades people seem to forget that you can do generics in C. Your linked list can require a pointer to a function that provides print capability. You can set this to a default of your own; include an enumeration in your struct that is like, int = 1, float = 2, user_defined = 3, etc. and your function can handle the built-in types. Users can supply their own functions for code that needs them. This is related to the early C++->C preprocessor compilation techniques; almost everything that C++ can do, C can too, even in terms of templates and generics, even if it's ugly. But this isn't. \$\endgroup\$
    – OrgnlDave
    Apr 11, 2012 at 5:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.