But you can't do it in C. __typeof__ is a GNU extension.
> Note that wl_list is not a very safe data structure, in the sense that a programmer need to know exactly how to use it.
You can do it in C++ with template, and it would be type safe.
Yes you can. You just have to provide the type every time you use the macro yourself. You can even create a macro that creates helper “methods” for you.
With that said, I really dislike how common it is for C programmers to just use whatever their compiler's authors felt like adding and still calling it “portable C”. At this point, if you dislike “plain” C so much, you might as well just switch to a subset of C++ or D's “Better C” mode.
#define wl_container_of(ptr, sample, member) \
(void*)((char *)(ptr) + ((char*)(sample) - (char*)(&(sample)->member)))You can do it in C, you just can't do it in the part of C the standards committee has decided to standardize.
I'm not sure what the value is of defining C as standard C when loads of software has shipped for decades using this pattern.
No but really, there isn’t anything to it. Just think about how a CPU sees memory and registers and it becomes intuitive. But that doesn’t make it easy to use because it doesn’t give you any guard rails to show you the one true way to accomplish a task.
The issues with C are around some of the weirder bits of the parsing process, namely the context-sensitive parser and the "interesting" corner cases of how the C preprocessor works.
Edit:
Here's a fun example - try it with and without the typedef line commented out:
typedef int T1;
int main() {
const T1 (*T1);
printf("%d\n", _Generic((T1), const int *: 1, const int (*)(int *): 2));
}C macros on the other hand, should die. I use C a lot but never ever use c macros, they are evil. Extremely hard to debug, confusing and very weak.
When we want macros, we use Lisp, that have proper macros.
But I do not think this trick or list implementation is newly introduced in Wayland. It existed at least as far back as kernel 2.6.11 [2].
[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin...
[2] https://elixir.bootlin.com/linux/v2.6.11/source/include/linu...
Note that the one that ships in linux is pretty simple, the one that is in BSD is a lot more complete.
It's used pretty much in every OS kernel, Windows included.
I don't see what this extra complexity of sizeof() offset calculations gains you.
Ref:
https://en.wikipedia.org/wiki/Offsetof
https://stackoverflow.com/questions/26906621/does-struct-nam...
I prefer not to use it due to the loss of type information and the potential for screwups, but you can write ANSI portable C using it.
https://stackoverflow.com/questions/15832301/understanding-c...
EDIT: check out some examples to see for yourself: https://elixir.bootlin.com/linux/latest/ident/hlist_add_head
>It’s a simple doubly linked list. Here’s it’s definition
The first one's right, the second should be its.
Though almost most native speakers seem to do this, so I guess it won't be 'wrong' for much longer...
The gorilla guards it's bananas.
Oh right, thank you, I didn't know the history. Short version: https://www.merriam-webster.com/words-at-play/the-tangled-hi...
Did you mean, some people now alive were taught that "it's" is right (in "it's definition"), or just ownership generally (Harry's). Well, it's better than using apostrophes for plural's, which seems very common too.
It makes sense now, thanks.
ifndef TTD_SWITCH_STRING
#define TTD_SWITCH_STRING
#define TTD_CASE(str) if ([__s__ isEqualToString:(str)])
#define TTD_SWITCH(s) for (NSString *__s__ = (s); ; )
#define TTD_DEFAULT
#endifWhile you're at it, just make the feature generic. Handle arbitrary arrays, structs, unions, and floating-point types.
The ... feature supported by gcc is also a huge usability improvement. Don't allow weird sorting order issues with strings. Don't allow it for structs and unions.
const char * s const = "bar";
switch (s, strcmp)
{
case "foo":
printf("I got foo\n");
break;
case "bar":
printf("nopes, I got bar\n");
break;
default:
printf("neither foo nor bar :/\n");
}
Which would be fine/acceptable, but not exactly how C tends to roll.1) In C++ you can use struct inheritance to get the same functionality without macros or templates. ie: struct ThingList : wl_list { /* data elements */ }
2) Like in the C++ case above, the next and prev elements should probably be at the top if you plan on iterating a lot since the next/prev pointers will be in the first cacheline that you load when you dereference previous list elem. In this case, it's also worth allocating list nodes with posix_memalign.
OTOH, there might be reasons why you want the list buried deep in the struct, like if the other data element access times are more important.
I think the most common reason for this generic-location offset-of style is so that the struct data can have multiple list nodes and be on multiple lists (without any extra allocation). This is pretty common in the kernel.
No way to do this cleanly in C++. At best, it's going to be an iterator galore, at worst some boost-style frankencode.
struct foo { uint8_t data[3]; struct wl_list link; };
For an application link member may be at offset 3 while for another it would be at offset 4.
That may be the reason link should be put as the first member, and a simple cast would let you walk the list.
Unless you want to write them somewhere or put it as part of API and ABI. Which you probably shouldn't.
http://www.catb.org/esr/structure-packing/#_structure_alignm...
Except languages with parametric polymorphism, such as ML and Haskell.
C++ Preprocessor is pretty much the same as C (they might go formally out of sync from o e standard release to another but in practice compilers implement the same features as far as I know).
http://man7.org/linux/man-pages/man3/queue.3.html
https://github.com/freebsd/freebsd/blob/master/sys/sys/queue...
const std = @import("std");
const wl_list = struct {
prev: *wl_list,
next: *wl_list,
fn init(list: *wl_list) void {
list.prev = list;
list.next = list;
}
fn insert(list: *wl_list, elem: *wl_list) void {
elem.prev = list;
elem.next = list.next;
list.next = elem;
elem.next.prev = elem;
}
};
const Data = struct {
data: i32,
link: wl_list,
fn init(data: i32) Data {
return Data{
.data = data,
.link = undefined,
};
}
};
pub fn main() void {
var foo_list: wl_list = undefined;
foo_list.init();
var e1 = Data.init(1);
var e2 = Data.init(2);
var e3 = Data.init(3);
foo_list.insert(&e1.link);
foo_list.insert(&e2.link);
e2.link.insert(&e3.link);
var entry = @fieldParentPtr(Data, "link", foo_list.next);
while (&entry.link != &foo_list) : (entry = @fieldParentPtr(Data, "link", entry.link.next)) {
std.debug.warn("{}\n", entry.data);
}
}
[1] https://ziglang.org/documentation/master/#fieldParentPtrHence I recommend kernel refugees start with CCAN's list: http://ccodearchive.net/info/list.html
https://en.wikipedia.org/wiki/Mixin
Also, is this macro magic, or simply memory layout magic? The macros look like appreviations for readability, not magic required to make the structure work.
Check e.g. https://docs.microsoft.com/en-us/windows/desktop/api/ntdef/n... and https://git.reactos.org/?p=reactos.git&a=search&h=HEAD&st=gr...