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.
BMPL: The language I'll bring to life
Last year, I started working on a language I named SuperCode but then decided BMPL (Builder's Multi-Purpose Language) as the final name. The language would be written using C and up until now, the GitHub repo only consists of a library of functions.
I decided to bring up the language onto Codidact's chat and have even started using it as some programming language, where I'd access the functions and assign them in TIO. To this day, I'd continue making answers on Code Golf CD using the language which is, until now, a library of libraries.
For this review, I want to know anything that can be improved with the functions and what could be fixed from what I made. I'm certain all functions I made before 2021 (all libraries except time.h
and is_type.h
) work properly ever since I tested them.
Here's the most commonly-used function I have:
#define say(X) printf(_Generic((X), \
double: "%f", \
float: "%f", \
char *: "%s", \
int: "%d" \
), (X));
1 answer
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 exampleconst 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);
notsay(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 bugscanf("%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 syntaxchar 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 localstatic
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 casey
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 likecat_str
orconcat_str
etc. Adding two strings together is known as string concatenation (hence the namestrcat
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 torestrict
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
anduppercase
functions contain needless checks sincetolower
/toupper
are already guaranteed to do these checks. Converting the input tounsigned char
is a good idea however, since that rules out various nasty bugs.
1 comment thread