Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Post History
First of all, I realize that this is mostly a library you've implemented while learning programming, for the sake of learning, which is great. So I would rather not put a "wet blanket" over your at...
Answer
#1: Initial revision
First of all, I realize that this is mostly a library you've implemented while learning programming, for the sake of learning, which is great. So I would rather not put a "wet blanket" over your attempts of coding and coming up with libraries, keep on doing that! However, I hope you realize that as C libraries go, this one is quite naive. There's a long way to go until it reaches something close to a commercial production-quality library by/for professional programmers. Writing such libs is very qualified work and one needs many years of C programming experience for that. --- **General concerns** - No header/include guards. This is mandatory for all header files, particular library ones. See https://stackoverflow.com/questions/7109964/creating-your-own-header-file-in-c - It is not recommended to place function definitions or variable declarations in headers. Especially not when you have no include guards. This will cause all manner of linking head aches and also in some cases multiple functions with the same name existing in different translation units. (The rare exception is `static inline` functions but I'd ignore those for now.) All of these functions should have a prototype function _declaration_ only in the header (for example convert.h), then the corresponding definition in a convert.c file which includes the header. - Headers are supposed to come with at least some function use documentation in the form of comments. What are the parameters for, how should they be used, what will the function return, how does it handle errors etc. **basic.h** - Your use of `_Generic` doesn't address qualifiers. For example `const int`. There was a language ambiguity how to deal with qualifiers when `_Generic` was released, so gcc and clang ended up implementing them differently. This was fixed in C17 - now `_Generic` expressions are assumed to work like "lvalue conversion", where qualifiers are discarded (though not pointed-at-type qualifiers). For this reason I'd recommend to either document that the code expects C17 to work correctly, or to use some macros checking `__STDC_VERSION__` and changing the code accordingly. - Function-like macros should not end with semicolon. I expect to use the macro as `say(123);` not `say(123)`. - It's recommended to check the result of `scanf` to get rugged code. - `take_int` doesn't work. You need to return the result somewhere. Decent compilers will warn against the bug `scanf("%i", result);`, missing `&`. **convert.h** - Making wrapper functions for something as trivial as casting isn't a good idea. Code such as `char ch = (char)val;` is already the most readable form. Inventing some alternative syntax `char ch = make_char(val);` only serves two purposes: confusing other C programmers and potentially making the program perform worse. __Always assume that the user of a library is a C programmer who already knows C.__ - `make_str` is problematic since returning a pointer to a local `static` buffer isn't safe in multi-threaded environments - it is not "thread-safe". So it might create more problems than it solves. **is_type.h** - Again, we should assume that the user of the library knows C. But they do not yet know your library. Providing functions that are nothing but wrappers around standard C library functions is very bad practice - all it achieves is to force people to learn an alternative syntax when they already know C and already know how to use ctype.h. And potentially these functions will not get inlined by an optimizing compiler either, so they are potentially just causing function call overhead bloat. The same problems exist in multiple of these headers. Unfortunately these remarks render large portions of your libs useless, but please trust me on this: do not attempt to re-invent the C language. Standardization exists for a reason. If you'd go ahead and do something like that in a professional setting, you would find yourself in some serious trouble. **mathematics.h** - `power` doesn't work correctly in case `y` is 0. - `square_root` I'm not sure if this works as expected, though why only provide one for fixed point? **memory.h** - `free` doesn't return anything so I'm not sure how you managed to get this code compiling. **str.h** - `add_str` could do with a name like `cat_str` or `concat_str` etc. Adding two strings together is known as string concatenation (hence the name `strcat` in the std lib). - You have no error handling in case `malloc` runs out of memory. - `compare_str` forgot const correctness for the 1st parameter. - `copy_str` could be improved in many ways for efficiency (a real standard lib implementation will look extremely different), but one easy thing to do is to `restrict` qualify the pointers. This tells the compiler that it can assume that the pointers don't point at overlapping memory and therefore give it a chance to produce slightly more efficient code: char *copy_str(char* restrict dest, const char* restrict src) - `get_length` forgot const correctness. Same remark for most of these functions. - The `lowercase` and `uppercase` functions contain needless checks since `tolower`/`toupper` are already guaranteed to do these checks. Converting the input to `unsigned char` is a good idea however, since that rules out various nasty bugs.