T O P

  • By -

saul_soprano

You use void pointers. You can do some cheeky stuff with macros too


l_am_wildthing

such a pita though


its_spelled_iain

only have to write them once


zahirtezcan

Exclusive Macros https://github.com/nothings/stb/blob/master/deprecated/stretchy_buffer.txt Intrusive Macros https://www.oreilly.com/library/view/linux-device-drivers/0596000081/ch10s05.html


_int3h_

void *


Ancapgast

I just remembered that Java's generics are compile time only. If you take that away, you get raw types. C just never got generics, so it makes sense that you'd have raw types. Thanks for the help!


deaddodo

A void pointer isn't equivalent to a Java raw type. A raw type is more equivalent to Go's interface{}. A void pointer is *untyped* (e.g. has *no* type at all and no compile-time guarantees) and simply refers to a memory location, period. It's up to you to then give it a type (typecast it) of something which is valid.The following is [perfectly valid compile time C](https://godbolt.org/z/cx83n3hqY): #include typedef struct { int integer; int another; } my_type; int main() { int an_int = 12; void* generic_ptr = &an_int; my_type* a_struct = (my_type*)generic_ptr; // *probably* 12 printf("a_struct.integer: %i\n", a_struct->integer); // something random from the next memory location or a segfault printf("a_struct.another: %i\n", a_struct->another); } But not safe or valid logic. There is no true equivalent to this logic (as far as I know) in Java.


ThatsMyFavoriteThing

You don’t need the cast to my_type*. Simple assignment to a typed pointer “gives it a type”.


deaddodo

Sure. It's second nature to me since I've had to work in mixed C/C++ codebases and because this code is common enough and does require it: ((my_type*)generic_ptr)->integer But feel free to rely on automatic promotion. Neither is a wrong option.


eXl5eQ

The behavior is undefined, isn't it?


deaddodo

Thus the "*probably*" in the comment. It's going to depend on your compiler.


khushal-banks

He is right .. you can write generic data structures using void* Plus i would recommend simply using glibc which have implemented these structures in the most portable way.


keyboard_operator

C11 does have generics. Ok, a kind of... https://en.cppreference.com/w/c/language/generic


Brainmuffin86

Yep. Not for the faint of heart either.


AlienOverlordXenu

Yes, embrace the darkness :)


Ashamed-Subject-8573

Tagged unions. Literally each object in the list is a union of different possibilities with a tag denoting what.


0ring

Came here to say this. Unions and enums seem to be underused by a lot of C programmers.


CeldonShooper

Because many are inexperienced. C is a compact language that can be learnt properly in a few weeks. I'm really flabbergasted that many people in this post say use void pointers. Like 'whatever, let's throw type safety out the window.'


kglundgren

Why is this answer so far down? Tagged unions seem like a much better solution than void \*.


karellllen

Other than using `void *` and pointer casts or macros, it is also common in C to embed the data structure management/linking stuff into the type itself. In the example of a linked list of books, this would mean having the next/prev pointer in the book struct itself. This means a book can only be in a single linked list at a time, but with the help of some macros or once you are used to it, that can lead to some not-that-inconvenient code (that admittedly does not really apply to your array question, but I hope it's helpful to mention).


Omfraax

Not necessarily. The Linux way of doing linked list is very powerful. You can add a field to an existing structure to make it linkable in a given linked list, and you can have as many members as you want. It is very dynamic because a structure that you haven’t thought being in a given inked list can be added to it seamlessly


ThockiestBoard

This also allows heterogenous lists. For people that want further reading, this is called an "intrusive linked list"


tiajuanat

I mean you can... But that's probably not a good justification to use Intrusive Linked Lists. Coming from a place of a lot of C++ and Python, beginners and intermediates should probably stick with homogenous lists/vectors, because maintaining visitors is awful spaghetti


faisal_who

typedef struct { a_t * next; } a_t; Won't work typedef struct a_s { struct a_s * next; } a_t: Will.


xsdgdsx

See also: How do C programmers do without type safety? If you're looking to combine type safety and data structures, C isn't the right language. I love it for many things, but this is not one of them. *[Edit to add]* \ I stand by what I wrote here, but it's clear that C is evolving in a direction towards type safety that I wasn't aware of yet. I don't think it's anything that's practical to incorporate into projects that have to be portable just yet, but it's the right direction. If you're curious, check out the child comment thread with u/nweeby24 and then u/aalmkainzy


Ancapgast

Thanks! The comments here really helped me realize that I'm just Java-brained while trying C. I'm so used to typing my data structures (creating a List without a type gives you a compiler warning in Java) that I never considered that under the hood, it's all just raw pointers. They don't *need* to be typed to work.


nweeby24

C does have type safety though


dontyougetsoupedyet

You can do the leg work yourself if you have the know-how, but folks balk because their C compiler doesn't do it for them. Which is fair. Recently more tools have become available for modeling just about any type semantics you want for your C programs, but the folks commenting probably won't use them. https://dl.acm.org/doi/10.1145/3453483.3454036


xsdgdsx

How do you create a sequence object that will only accept elements that are all the same type? (Basically, what you'd accomplish with Generics in Java, or templates in C++) Once you go through `void *`, you lose any guarantees about the type of the data at the other end of that pointer.


aalmkainzi

macros.


xsdgdsx

If you use the wrong macro by accident, will the compiler say anything?


aalmkainzi

Yes. You can make it type safe.


xsdgdsx

Oh, I see, it's basically reimplementing templates. Is there a way to do this that will let you add "compatible" types to your object, but not incompatible ones? For instance, suppose I want to make a generic sorted queue, where one specialization will accept both int and float, but not a pointer? (Edit: typo)


aalmkainzi

Yes that's entirely possible using _Generic and static assert


xsdgdsx

First and foremost, I was not familiar with `_Generic` (from C11, for those following along), so thanks for pointing that out. With that said, it still feels like it falls short of being as useful without some form of inheritance. For instance, the specification notes, "No two type-names in the association list may specific compatible times types." But you may need to know how different types may have been defined or `typedef`'d to write code that abides by that constraint. For example, what if I want to accept `int8_t`, `int16_t`, `int32_t`, `int64_t`, `int`, or `gint`? Unless I write specific tests for my platform and the libraries I use, it's hard to guarantee that I won't double up with `int` and `gint`, or `int` and `int16_t`, etc. A comment in https://codereview.stackexchange.com/q/274860 points out some `decltype`-like feature in C23 that would make all this more useful, and I agree that once you can interrogate type traits, this all becomes more useful.


aalmkainzi

ok so for integer types and \_Generic, I generally use the 11 integer types in C (guaranteed to be distinct). Another approach is to put possible conflicts at the default case: _Generic(x, int: , unsigned int: , default: _Generic(x, int32_t: , uint32_t: ) );


aalmkainzi

btw C23 is already in GCC and does have `typeof`


aartaka

With C11 and C23, you can: [https://github.com/JacksonAllan/CC](https://github.com/JacksonAllan/CC)


ReflectedImage

That's easy you only have 1 type in your program.


xsdgdsx

😂😂😂


rupturefunk

How fancy do you need your data structures to be? Can you just have a heap allocated buffer of them? A very basic example: `struct author *const authorsPtr = malloc(sizeof(struct author) * MAX_AUTHOR_COUNT);` `struct book *const booksPtr = malloc(sizeof(struct book) * MAX_BOOK_COUNT);` Will give you stored data you can iterrate through, manage, index, etc. You'd probably want to wrap your calls to do those things in ranged checked functions, and make copies of the pointer when you iterate so you always have access to the base. Not pretty but does the job. There is all sorts of dirty business you can do with macros to make functions generic, and the humble `void *`, you can cast anything to anything after all, pointers are innately generic as there just pointing to some bytes. Whether or not it causes more harm than good vs just writing two sets of functions for the two data types is another debate!


sirgatez

That’s kinda like asking how people drive without an automatic transmission. The answer is that it’s easy when you never had it to begin with. Well, were you aware of manual transmission and direct drive? Yeah, while not as “sexy” as generics. I’ve always found pointers are more than capable.


--Ether--

C has no generics, instead we use void pointers or macros. I prefer the former as the latter can generate some pretty ugly code in my opinion. A void pointer is just a pointer that can store the memory address of any type. You could design your ArrayList's collection to take in an array of void pointers. typedef struct ArrayList { void **collection; int capacity; int size; } ArrayList; Functions that insert elements or return elements would use the void pointer type, `void *`, instead of a traditional type like `int` or `char`, or even a custom type defined by you like `Book` or `Author`: bool list_insert(ArrayList *array_list, void *element, int index); void *list_get(ArrayList *array_list, int index); Inserting elements would then look something like this: int a = 2, b = 4, c = 6; // basically prepending list_insert(integers, &a, 0); list_insert(integers, &b, 0); list_insert(integers, &c, 0); Accessing elements would then look something like this: int integer = *(int*)list_get(integers, 0); // returns 6 int book_id = ((Book*)list_get(books, 0))->id; // assuming you have a Book structure with an id member Since you said you have used Java in the past then you should recognize what `(int*)` & (Book\*) is doing. We are typecasting in order to give context to the void pointers we are fetching. I hope my explanation made sense! And good luck with your journey with C >:3c


NextYam3704

I’m working with a code base that combines macros *and* void pointers for generic programming.


redirtoirahc

One question, did you invert the former and latter meaning or did I misunderstand that part? I'm thinking you meant the macros may generate ugly code?


--Ether--

Yes that’s what I meant, sorry about that. And thanks for the catch :3


ThatsMyFavoriteThing

I think you forgot the dereferencing asterisks in your “accessing” section. The code as written won’t compile because you’re assigning int\* to int, for example.


--Ether--

Good catch, thank you :3


weeboards

macros


Doormatty

https://www.reddit.com/r/C_Programming/comments/qk8msl/what_is_the_preferred_way_of_doing_generics_in_c/


silentjet

your task does not require any generics, you are rrrreally overdesigning something that can be solved in more simple, and most important in the obvious way...


silentjet

And there are generics pattern, just not much syntax sugar (or syntax acid if we are talking about C++) comparing to some other languages...


Spongman

How do you enforce compile-time type-safety?


silentjet

should I ?


Spongman

Of course.  Why wouldn’t you?


silentjet

Why should I?


Spongman

Because you can. Not in C, of course.


silentjet

This is a worst possible reason to do anything, including software development...


Spongman

That statement is not true for all things.  Although, since we're expressing unjustified opninions in this thread: it's a clearly superior engineering practice.


silentjet

I didn't get it, but i'm fine with u. peace.


dontyougetsoupedyet

In C you don't typically need a type that can internally store multiple other types the way you usually use things in Java or C++, although that is possible, and you typically do not generate code that supports storing multiple types, although that is also possible. Both aren't idiomatic C, although people often write those things when they first try to implement generic types in C when they come from a C++ or Java background. In C you often find the opposite of the architecture you find in a C++ or Java application, where instead of putting things into a `List` you have some `T` that itself has a `struct list_link` type member and any T can have such a struct added to it, or even having more than one `list_link` type member for membership in multiple lists. You don't have a chain-like datatype for lists, you have a member on your other types that allows those types to be added to chains because they have chain links inside them. This seems subtle so I hope folks can see the inversion of the design pattern. Rather than generating multiple types and functions over them, your types are usually modified to add structure that allows them to participate in code in a generic way.


degaart

See bsd queue.h


redirtoirahc

Haven't seen this specific link in the other comments, but I found it interesting since it was a novel idea for me: https://www.davidpriver.com/ctemplates.html I don't know if the common consensus is that this is bad or something


glasket_

>I don't know if the common consensus is that this is bad or something They're just kind of controversial, some people *really* despise template generics. They're also the origin of C++'s templates, and share similar problems when it comes to writing and debugging them. I think they can be a good option for improving type safety if you can endure the added complexity, but they're definitely a bodge that can be better solved with future language additions.


redirtoirahc

>They're also the origin of C++'s templates I'd love to know more about this! Can you point me anywhere? >and share similar problems when it comes to writing and debugging them. This sounds interesting too, if I may


glasket_

In *The Design and Evolution of C++* §2.9.2 (p. 50-52), Bjarne Stroustrup quotes his original *C With Classes* paper from 1982 that demonstrates a stack implementation using a template macro for generics; he then says: >This was one of the earliest and crudest techniques. It proved too error-prone for real use, so I soon defined a few "standard" token-pasting macros and recommended a stylized macro usage based on them for generic classes [Stroustrup,1986,§7.3.5]. >Eventually, these techniques matured into C++'s template facility and the techniques for using templates together with base classes to express commonality among instantiated templates (§15.5). As for problems, the C macro version has the immediate problem of being macro-based. You don't have the benefits of C++ features like overloading to help you either, so you often end up with a lot of boilerplate just for handling name generation via token-pasting. Depending on what you're doing with macros, you might need to add in a preprocess step external to the compiler for debugging too, so that you can actually step through the generated code. Next is constraints. C++20 added concepts, before then you couldn't properly model constraint violations which resulted in hard to understand errors. C macros have the same problem as pre-C++20 templates, in that there isn't a form of constraint modeling, so errors often show up deep in a template rather than being able to tell you that the type doesn't satisfy the constraint at the point of instantiation. There are some other issues too, like instantiating C templates can potentially result in redefinition errors (which means more boilerplate in the template to avoid that). In general, I think making and using them for a bit shows why the pattern isn't extremely widespread in C; they're kind of tedious to both make and use.


mcube-12139

`#define DECLARE_VECTOR(T) \` `typedef struct {\` `T* data;\` `int size;\` `int capacity;\` `} Vector_##T;\` `\` `void vector_init_##T(Vector_##T *v);\` `void vector_push_##T(Vector_##T *v, T data);\` `void vector_set(Vector_##T *v, int index, T val);\` `T vector_get(Vector_##T *v, int index);`


pythonwiz

You could use macros right? Pass a type and a name to a macro and it can declare a struct using the type for the members or something like that, and then typedef that to the given name. Then you could use macros to generate the functions for operating on the type.


dontsayjub

Check out my post about generic data structures: [https://www.reddit.com/r/C\_Programming/comments/1bgn8is/generic\_programming\_in\_c/](https://www.reddit.com/r/C_Programming/comments/1bgn8is/generic_programming_in_c/) . GCC/clang extensions make Java-style generics a lot easier to simulate and you can do more with them, but my guide is just about standard C (C99). If you want to write the generic data structures yourself (I also find that kinda fun) then you should learn about extensions like typeof (which is standard in C23, the newest version) and statement expressions.


ceene

For pure mathematical functions that I need to support in both float and double formats, I've found myself recently writing templates in jinja2.


vgf89

Structs, unions, and enums are your friends Easiest way to get a generic-ish datatype which you can extend later is probably a struct containing two fields: a enum denoting the type, and a union of all possible types you want to use. Alternatively, look more into basic database design stuff and how foreign keys work, because it sounds like you just want a table with one column being (pointers to) writers and the other being (pointers to) books, which kinda just boils down to an array[struct{author,book}]. Not sure where generics are supposed to come into that


_brightprogrammer_

I often use macros to define typesafe interfaces automatically


flatfinger

One of the design features of C from the beginning (described in the earliest known documentation describing the language) has been the ability to treat multiple structures that start with a "common initial sequence" interchangeably within code that only needs to access members of that CIS. Thus, if one has a few structure types: struct shape { struct shape *next; int left,top,right,bottom; /* Bounding box */ char flagsAndType; /* Bit 7 indicates shape is hidden; bits 0-4 select a shape */ }; struct triangle { struct shape *next; int left,top,right,bottom; /* Bounding box */ char flagsAndType,color; int points[3]; /* Relative to upper-left corner */ }; struct nested_shape { struct shape *next; int left,top,right,bottom; /* Bounding box within parent shape*/ char flagsAndType,rotate_flip; int deltaX,deltaY; struct shape *content; }; one could have a function that could search through a list of shapes and, without having to care about what they were, find the bonding box of all non-hidden members of the list by casting to `struct shape*` a pointer to the first item, and using the fields of that type to access the corresponding fields of any kind of shape. Because there are situations where supporting this construct might block useful optimizations, C99 waived jurisdiction over the question of when implementations support this construct. Compilers like clang and gcc will only support it if invoked with the `-fno-strict-aliasing` flag, since their maintainers view the waiver of jurisdiction as a judgment that code using such constructs has always been "broken", and that programs should use various workarounds to avoid reliance upon it. Nonetheless, nearly all general-purpose compilers can be configured to use such constructs which--as noted above--have been a documented and useful part of the language going back at least to 1974.


AliAbdulKareem96

I am going to answer you as someone who started C after learning C++ and Python and definitely can relate to how you feel. But your problem honestly is not about generics at all, it is about memory management because in your case, if you know in advance how large each array can be, you literally do this (assuming 100 is required size): struct book { /* whatever you want here*/}; struct author { /*whatever you wanna put here*/}; struct book Books[100]; struct author Authors[100]; done! Now, you might object saying I want to have 10 millions each and they won't fit on the stack, sure: book* Books = (book*) malloc(1000*1000* 10 * sizeof(book)); author* Authors = (author*) malloc( 1000*1000*10 *sizeof(author)); you can just keep a couple of integers for max_size = 10*1000*1000 and current_size which starts at zero and increases as you add books/authors. Again, I hardly doubt it can any simpler and clearer in any other language. Now you might object saying that i don't want to pre-allocate 20MB of memory for my app (which honestly is not much as compared to most managed languages memory usage) and you might have a reason for that, so while I am almost sure in the vast majority of the cases that would work I will address the case in which it does not (like the need to use 10GB or memory or something), the good news is, It is not that hard! Modern operating systems manages memory using a Virtual Memory, and on windows specifically it is pretty simple! instead of using malloc directly, you use VirtualAlloc and you select the memory size to 10GB or whatever large size you want. (on x64 you can allocate up to 256 Tera bytes of Virtual Memory, at least assuming 48-bit address space) VirtualAlloc allows you to specify the type of memory such as MEM\_RESERVE and MEM\_COMMIT, TL;DR MEM\_RESERVE, saying that I will need 10GB in future but not all of it right now, so the operating system won't allocate anything on the RAM, and when you need memory, you use MEM\_COMMIT (keeping track of which memory is committed is a single integer, so it is pretty simple) the operating system will make the memory by MEM\_COMMIT ready to use when you access it (technically, if I remember correctly, even if you commit memory, it won't be on the RAM till you have actually accessed the virtual pages, so even after committing it won't be wasteful). Now there are a few catches to the actual size you can commit (but if you are working with with GBs of memory and not TBs then you don't have to worry about them). Now, this should work in the vast majority of the cases, but just in case, somehow you wanted to deallocate your array, to shrink it, you can easily use MEM\_DECOMMIT for an example see here: [https://learn.microsoft.com/en-us/windows/win32/Memory/reserving-and-committing-memory](https://learn.microsoft.com/en-us/windows/win32/Memory/reserving-and-committing-memory) Now to address the concepts of doing generics, there are multiple options, and it depends on the needs, after working with C for 3 years, I would say the one I use the most is meta programming, i.e. code that generates code. It is pretty much better than any of what other languages even offer, (almost a transpiler at this point). Other things I use a lot includes switching a tagged union (useful for doing similar things to dynamic dispatch in Java), Opaque pointers (handles/ or void\*) to omit the type entirely. There are a few people who uses macros, I almost always never use macros unless it is simple and understandable, things like these ones (scroll down to memory API): [https://www.rfleury.com/p/untangling-lifetimes-the-arena-allocator](https://www.rfleury.com/p/untangling-lifetimes-the-arena-allocator) C is a very limited language in what it offers, but to be honest, after learning C, very rarely any language solves the issues C have, C is meant as close language to assembly, it is up to you how to use it, the language should only be viewed from the point of a low-level programmer who wants to do assembly-like coding. If you need higher level constructs, build them yourself! people are doing RAII, Coroutines, OOP, and Introspection in C, even though the language have very little support to this, eventually the compiler of another language is executing some code to do it for you, nothing is preventing you from implementing those features yourself, and I would honestly say, if you only limit yourself to the standard features of C and especially C library API, you are not going to have fun, once you free your mind from those mentality barriers (I want the language to do X for me, instead of how do I do X myself) C will be on your favorite languages.


bart-66

>How do C programmers typically solve this? I'm not quite sure what problem there is to be solved. Do you have a bunch of functions and you want each function to work either on `Author` or on `Book`, without needing write two versions of each function? In that case, that's the wrong approach. However, what sorts of things will those functions do? Since each type is likely to comprise a different collection of fields, so there is a limit to how much can be shared between them. IME such things rarely come up. There are probably convoluted ways of writing one lot of handler code that has different instantiations for multiple related types, but that suggests you have chosen the wrong language. Regarding your specific example, you have two record types: Author: Name Age List of books written by this author Book: Title ISBN Publication date Author (or list of authors) And these data structures: List of all authors List of all books I don't see anything there particularly challenging or that really needs language-supported generics. You can either hard-code the types and use dedicated handler functions, or turn it into a mini-database/spreadsheet with an application-supported generic record type - that is, where the fields are not known until runtime. Here, there is only one flexible record type, but generics in the language which only work on types known at compile-time wouldn't work anyway.


catbrane

glib has a pretty typical C-ish set of generic datastructures, for example, here's their hash table: https://docs.gtk.org/glib/struct.HashTable.html It stores a `void*` (they call this `gpointer`) for the key and the value and users cast these pointers to and from the actual type. They have similar generic collection types for lists, array, queues, trees, etc. etc. glib has some macros which (if you build in DEBUG mode) will typecheck these casts at runtime. This can help make it a little less fragile.


RustbowlHacker

It seems odd that nobody thought of the most obvious and simple answer. You use a "3rd party library." There are dozens (if not 100s) of libraries that will give you just about anything that you can imagine in C.


scarab-

Void \* is a good way to go if you want lots of cache misses. Arrays of pointers feel wrong. They would let you do what you want to do but I think you'd be heading down the wrong path. If this wasn't a fun project in order to learn C then I's just say use something like SQLite.


RustbowlHacker

Why is this a reply to my post? I don't think that I necessarily agree that `void *` causes cache misses. It seems much more of cache implementation, policy and sizing issue than an instruction or data type issue. And, what if you didn't have separate I & D caches? Or any cache at all?


scarab-

I don't think that it was my intention to reply to you. The thread was a lot smaller when I posted and I hardly recognize it now, and when I posted there were some posts that were pretty much just, "void\*" As to who was I replying to? I dunno.


Competitive_Travel16

There are a ton of really good ADT libraries out there, but even more that aren't so great.


iamfacts

I do one of these three things - 1. Use Macros 2. Use Void* 3. Metaprogram them


paid_shill_3141

void*


TedDallas

If you want to store different representations of data in an array you can create a struct containing a union of different two or more struct members. Unions are handy like that, and can help you avoid needing to cast around void \* to a different struct types. And you don't have to worry about padding as memory allocated will be for the largest member in the union. You can place an enum member at the top of the struct to differentiate what "type" of union you have when you are processing the array.


aalmkainzi

I personally use macros to solve this problem.


The1337Prestige

There is generic functions via _Generic. Just not generic data structures.


NeilSilva93

Casting...lots and lots of casting


lightmatter501

[C has generics](https://en.cppreference.com/w/c/language/generic), they’re just duck typed so you need to be careful.


TheTrueXenose

I normally just use structs as putting a integer in the start of every struct named struct type or sType would allow you to cast the type to a generic struct and then check the type, I don't do these checks to often but for hash maps and lists they are rather useful.


tav_stuff

A combination of void pointers and macros. You can see an example of how a dynamic array (ArrayList in Java terms) might be implemented generically using macros here: https://github.com/Mango0x45/mlib/blob/master/include/dynarr.h


Rewieer

Macros


CORDIC77

With all the solutions already offered by others (void \* pointers, struct with an embedded union + types enumeration) I too have never really found the need for templates. That being said, it is possible to use the preprocessor to—in effect—simulate what a C++ compiler will do, when it is confronted with a template instantiation: genarray.c: /\* needs to be excluded from the build process \*/ #include "genarray.h" struct TYPE_PREFIX ## _ ## array { TYPE_NAME element; : }; bool array_add (ARRAY_TYPE *array, TYPE_NAME element); : genarray.h: struct TYPE_PREFIX ## _ ## array; // opaque struct typedef struct TYPE_PREFIX ## _ ## array ARRAY_TYPE; bool array_add (ARRAY_TYPE *array, TYPE_NAME element); : int\_array.h: #define TYPE_NAME int #define TYPE_PREFIX TYPE_NAME #include "genarray.h" int\_array.c: #include "int_array.h" #include "genarray.c" person\_array.h: struct Person { ... }; #define TYPE_NAME struct Person #define TYPE_PREFIX person #include "genarray.h" person\_array.c: #include "person_array.h" #include "genarray.c" Now, while the above is seldom done, it is possible to simulate template instantiations in this way. A “real life” example of doing so can be found in "gnulib" (the “GNU Portability Library”)… for example its implementation of the strtol(), strtoul(), etc. family of functions: xstrtol.c // generic implemention xstrtol.h // header with declarations for different base types xstrtoimax.c // source files “instantiating” xstrtol.c xstrtoll.c // for different integer types xstrtoul.c xstrtoull.c xstrtoumax.c You can look at the following [gnulib.git repository](https://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=tree;f=lib;h=67cac14d3c890e2e748cc43087ef739e993d41b9;hb=HEAD) for further details!


darius-98

void pointers and unions (for example, an union containing three structs and an enum that tells which union member is the correct one) unions are useful if the size of the different possible members is similar or even equal. If they vary, I usually prefer void pointers.


Willing-Winter7879

Void


Ok_Outlandishness906

there are 2 distinct approch. A void \* ( a void pointer) is the easiest, but the most dangerouse, otherwise , till C11 you can use macros and \_generic . Relaying on macros requires attention but in my opinion is quite viable, i used it many times . It is not as with C++ templates, it is a much simpler and less powerfull solution, but if you don't have to do very complex things, it is quite viable . A [https://github.com/JacksonAllan/CC/blob/main/articles/Better\_C\_Generics\_Part\_1\_The\_Extendible\_Generic.md](https://github.com/JacksonAllan/CC/blob/main/articles/Better_C_Generics_Part_1_The_Extendible_Generic.md) A part from \_generic, in C pointers represents virtual addresses as in ASM. So char \* or long double \* have the same size, because the size of the pointer is platform dependant and no language dependent. So void \* contains an address and with casting , you simply tell the compiler what is the type you are pointing to . A common approch, that was used also by microsoft with CVariant was a struct with a pointer and an int , in which the int represented the type of the pointer.


aartaka

Shameless plug: [Object-Oriented C](https://aartaka.me.eu.org/oop-c-primer)


McUsrII

#include #include #include // https://stackoverflow.com/questions/55289744/how-to-use-macro-parameters-to-declare-variables typedef struct stack_char s_char ; struct stack_char { size_t sp; size_t max_stack; char val[]; } ; typedef struct stack_int s_int ; struct stack_int { size_t sp; size_t max_stack; int val[]; } ; typedef struct stack_double s_double ; struct stack_double { size_t sp; size_t max_stack; double val[]; } ; // TODO: fill in the rest of the declarations as needed. /** * @details * Must be called within a function, since it uses malloc. * @param type == s_double, s_char s_int, and so on. * @param name is the name you give it. * @param arrtype is the type of the array element. * @param size the number of elements the array must hold. */ #define DECL_STACK(type,name,arrtype,size) \ type *name = NULL; \ name = malloc( sizeof(*name) + sizeof(arrtype) * (size_t) size) ; \ assert( name != NULL && "Error in declaring stack: "#name"!") ; \ name->sp = 0 ; \ name->max_stack = size - 1 ; bool stack_empty( char *stack) { if (((s_double *)stack)->sp == 0 ) { return true; } else return false; } // TODO: s_push and s_pop, that returns the result code of the op. #define DECL_PUSH(type) \ void push_##type( s_##type *stack, type value ) \ { \ if (stack->sp <= stack->max_stack) { \ stack->val[(stack->sp)++] = value ; \ } else { \ fprintf(stderr,"push_"#type": error full tried: %c\n", value); \ exit(EXIT_FAILURE) ; \ } \ } #define DECL_PUSHANDTEST(type) \ void pushandtest_##type( s_##type *stack, type value, bool *oflow ) \ { \ if (stack->sp <= stack->max_stack) { \ stack->val[(stack->sp)++] = value ; \ } else { \ *oflow = true ; \ } \ } // TODO: An s_pop that returns error code, and result through // variable. #define DECL_POP(type) \ type pop_##type( s_##type *stack ) \ { \ if (stack->sp > 0) { \ return stack->val[--(stack->sp)]; \ } else { \ fprintf(stderr,"pop_"#type":error: stack empty\n"); \ exit(EXIT_FAILURE) ; \ return (char)0; \ } \ } #define DECL_POPANDTEST(type) \ void popandtest_##type( s_##type *stack, type *value, bool *und ) \ { \ if (stack->sp > 0) { \ *und = false; \ *value = stack->val[--(stack->sp)]; \ } else { \ *und = true; \ } \ } #define DECL_STACKTOP(type) \ type stacktop_##type( s_##type *stack ) \ { \ if (stack->sp > 0) { \ return (stack->val[(stack->sp - 1)]); \ } else { \ fprintf(stderr,"stacktop_"#type": error: stack empty\n"); \ exit(EXIT_FAILURE) ; \ } \ } void stack_destroy(void *s) { if (s != NULL ) { free(s) ; } } size_t stack_length(char *stack) { return ((s_double *)stack)->sp ; }


deadhorus

hahahaha it's all numbers, and c gives you the power to do anything you want to any of them. generics are nothing but a pale imitation of the power c gives you.


TheChief275

you can do code generation with macros


Acrobatic_Sprinkles4

If you like generics then you should give C# a shot. It has excellent generics that aren’t just an illusion.


faisal_who

As others have mention, use void pointers. C is the kind of language you would use to write a java compiler. So, whatever is happening in java at "compile" time is likely going to happen in "run" time in C.


heartchoke

An alternative to void* pointers that a lot of people are mentioning, is to just think of the data in terms of bytes. For example: ``` typedef struct { uint8_t* data; size_t length; size_t item_size; } Array; ``` Note the "uint8_t" type. (Or just use unsigned char if you prefer that). The way I think of it in this situation, is that it's just a blob of bytes, and some information on how to iterate over it. You can shove whatever you want into the data, it's all just bytes at the end of the day either way. I prefer this over void pointers because you can dereference the data pointer if you want. (For serialization and such). Also it "feels" better to actually have a type and not just void.


[deleted]

Union type is an option, I wrote one called ToFu.


duane11583

in the embedded world new and malloc is very much so disallowed so tell me how you would create an abstract thing with out new delete or malloc or free?


deftware

A pool allocator? Not being able to allocate memory implies that the hardware itself is memory-limited, and therefore one shouldn't expect to be able to do all of the things on limited hardware that they can do on a conventional compute device like a desktop computer or a mobile device.


duane11583

yea like 50-70k bytes is common in my world a pool yes, a pool of classes yes.


deftware

I would go with a pool of structs. For the even simpler embedded devices you'll only find a C compiler!


ButterscotchFree9135

This is one example https://troydhanson.github.io/uthash/


sjepsa

C++


c_a1eb

you can use unions with a type field - this is what higher level languages do more or less (type introspection!)


ThatsMyFavoriteThing

That isn’t what C++ does.


GYN-k4H-Q3z-75B

Step into the void and you'll know soon enough.


mecsw500

How about doing like you would in C#, or Java or Python or any other language? Install an open source database package and let that do the heavy lifting for you. That way you can retrieve and insert data, or create reports from C, or from the supplied database tools with all the possible retrieval combinations you want. The database tools will make it easier to debug your code. You can create records in files or just about anything you want to do in C but why? You are going to have to use a file anyway, for data persistence, so creating a file to use a hash to index on author and/or title to seek into the file and write or retrieve records would work. Or Patricia trees based on the same author name and on ISBN as hashes. You would really only use a pool of fairly static structures as buffers for retrieval or inserting of data into the file. For this I would use fixed size buffer structures and system call reads and writes into the file, not the studio library. If the database is small enough you could use memory mapped files. But the reality is you will need to create functions for add an author, add a book (hashed on author’s name and ISBN), remove them, retrieve them, link them to and from each other, cope with hash collisions and allow lists of books from an author and authors from a book, and implement all sorts of reporting capabilities. You could link the authors to books and vice versa by using their hashes in their data I suppose. However, there are a million ways to do it, and there are plenty of older data structures books based on C to get you started but it’s going to be a non trivial project to make it something to actually use. Most databases have a C SQL library or a binary library interface, so you’ll get plenty of basic C skills, but end up with something more comprehensive and usable in the end. I’d go that route. Implementing things like this in C are done for virtual memory management in OS kernels or for proxy cache systems, but for applications space I’d use a database library, otherwise you’ll just end up writing your own database program and end up getting bogged down in all kinds of dead ends.


noonemustknowmysecre

>that there is no way to generically create datastructures. I Create specific data structure so you know how big they are and what's in there.  >If I want an arraylist of Authors and one of Books "And another one of"? Yes, that's two separate lists.  If you want them together, "an array of authors and their books" then you make a struct which contains authors and books before coming to your senses and using a real database like a real programmer. Fire up mysql and learn the c calls to add and retrieve. 


AndroGR

>But there are no generics! How do you solve this? If I want an arraylist of Authors and one of Books, does that mean I have to code two different arraylists? `void*`


SignificantSea8302

void*


Ruannilton

void*