At 11/12/24 04:44 AM, alsoknownas1 wrote:Some rabble rousing I do support is against over-engineering:
so I did watch the video after all. it is full of strange things, if not errors
first strange thing is that he writes with his pen from right to left from his POV and it looks left to right in our POV. this is such an incredible skill! or maybe he mirrored the video? interesting.
second, dude has a "hand unrolled" loop with four accumulators, but it misses the last one to three elements. did he intend to write a Duff device? but compilers are smart these days. even if he padded the arrays to always be multiple of four, he assigned square enum constant to 0, so he must assign 0 to width and height manually to the padding elements or assign shape type to 5... very strange.
third, both the "clean code" version and his optimized version are full of maintenance problems, due to shotgun surgery anti-pattern. unfortunately, only Java enums or the C preprocessor or a metaprogramming tool like T4 can properly model his example, so it is kinda flawed from the beginning.
fourth, his code also slowly becomes confusing. for circles, "width" and "height" now mean radius, it doesn't even mean diameter anymore.
fifth, he complicates the code adding a "corner count" attribute, which should obviously be static, but he puts it as instance data. unfortunately I only C# 11 to have "static abstract" properties and methods that solve the problem directly. I dunno about Java and C++ tho.
I wonder why are comments disabled...
anyway, here is my C11 compliant take:
#include <assert.h>
#include <stdio.h>
#define _USE_MATH_DEFINES
#include <math.h>
#define SHAPE_XS(X, Y, SEP) \
X(SQUARE , (Y).side * (Y).side , 4)SEP \
X(RECTANGLE, (Y).width * (Y).height , 4)SEP \
X(TRIANGLE , (Y).width * (Y).height / 2 , 3)SEP \
X(CIRCLE , (Y).radius * (Y).radius * M_PI, 0)
#define NIL
#define COMMA ,
#define SEMICOLON ;
#define AS_ENUM(A, ...) SHAPE_##A
enum shape_type { SHAPE_NONE, SHAPE_XS(AS_ENUM, NIL, COMMA), N_SHAPE };
struct shape
{
enum shape_type type;
union {
float radius;
float side;
struct { float width; float height; };
};
};
#define AS_AREA( E, F, C) case SHAPE_##E: total += (F); break
#define AS_WEIGHTED(E, F, C) case SHAPE_##E: total += (1.0/(1+C))*(F); break
#define SHAPE_TOTAL_DECL(NAME, CALC) \
static float shape_total_##NAME(int n, struct shape shapes[static const n]) \
{ float total = 0; \
for (int i = 0; i < n; i++) switch (shapes[i].type) { \
SHAPE_XS(CALC, shapes[i], SEMICOLON); \
default: assert(!"Invalid shape type"); break; \
} \
return total; }
SHAPE_TOTAL_DECL(area, AS_AREA)
SHAPE_TOTAL_DECL(area_weighted_corner, AS_WEIGHTED)
int main(void)
{
struct shape shapes[] = {
{SHAPE_SQUARE , .side = 5}},
{SHAPE_RECTANGLE, .width = 5, .height = 10},
{SHAPE_TRIANGLE , .width = 5, .height = 10},
{SHAPE_CIRCLE , .radius = 5},
};
int n = sizeof (shapes) / sizeof (*shapes);
printf(
"Total area is %f and weighted by corner count is %f\n",
shape_total_area(n, shapes),
shape_total_area_weighted_corner(n, shapes)
);
return 0;
}
I didn't measure anything, but even if it performs worse than any micro-optimized version, I wouldn't really care until it shows in the profiler.