H. S. Teoh
Posted in reply to Adam D Ruppe
| On Mon, Nov 01, 2021 at 10:00:27PM +0000, Adam D Ruppe via Digitalmars-d wrote:
> On Monday, 1 November 2021 at 21:51:31 UTC, H. S. Teoh wrote:
> > - A math exploration tool for rendering images of 2D equations that
> > can handle arbitrary equations in 2 variables. Does both PNG
> > output and on-the-spot OpenGL rendition. Contains a minimal
> > wrapper around libfreetype for font rendering.
>
> you should have used my libs for this too, you get font, opengl, and png all in there for you!!!
Haha, the font wrapping was pretty easy, the freetype headers have the interesting property that error codes are defined with C macros that are defined externally; by suitably redefining these macros and #include'ing the header file twice, I managed to generate D code from the C header file. The input file is an abominable mixture of CPP macros and D code snippets. :-D It was lots of fun.
OpenGL was no problem at all, I already had the DPP-transcribed GL headers from my Android project, so all I needed was to call EGL to initialize the context, then call GL functions away.
PNG output was from a previous iteration of the program that generated the PNG binary representation using purely range-based code. In retrospect, though, I should've gone with a buffer-based approach, 'cos the range-based implementation is dog-slow. (I *could* insert caches in the UFCS pipelines at strategic points, but at that point it kinda defeats the purpose of using ranges in the first place and I might as well just write a "traditional" implementation instead.)
> > - A program for dissecting midi files (incomplete).
>
> i also have midi
>
> and of course cgi modules
>
> my libs do it all
LOL... I love your libs 'cos they have like no dependencies, I can just drop the file into my directory and it Just Works(tm). Having been bitten multiple times by library versioning hell in long-running code, I've come to believe that external dependencies are a net negative in the long run. Yes, in the short run it lets you get off the ground faster with less effort and less time, but in the long run all sorts of maintenance problems crop up:
1) Upstream abandons the lib that you critically depend on, and now you're stuck having to maintain it yourself. Good luck if it has stopped compiling 2 compiler releases ago and you don't understand the code well enough to know how to fix it.
2) Upstream releases a new version that has an incompatible API, so
you're stuck with the old version. Eventually, you end up in problem
(1).
3) Upstream releases a new version and doesn't change the API, but *does* change the semantics of some obscure use case that you critically depend on. Maybe they think that particular behaviour is a bug or something they don't want to support. Goto problem (2).
4) Upstream's new version doesn't change the semantics, but *does* change the performance characteristics of critical functionality (in a very detrimental way). They refuse to fix it, because their brand new features X, Y, Z all depend on the new implementation in an irreplaceable way, and besides, your usage of it is not a supported use case for performance purposes. Luckily, your program still compiles and works as before. Unluckily, it has become unusably slow, and customers are angry and leaving by the droves. Again goto problem (2).
5) Upstream decides that they need to depend on libraries X, Y, Z, all of which potentially suffer from (1), (2), (3), or (4). As the end user, you of course inherit all of the problems. The worst of these problems is that any one of the 150 recursive dependencies may suddenly release a new version that breaks critical functionality you depend on, and since you didn't even know that the library depended on such a thing, you haven't the slightest clue which of the 150 recursive dependencies is causing the problem, let alone figure out how to fix it.
6) If you're unlucky enough to depend on a package system that dynamically updates packages from the intarweb, beware: nasty surprises await on the day of your big upcoming release when one of the 150 upstream dependencies suddenly decide to upload a new version that breaks *everything*.
7) Actually, that's if you're lucky. Maybe on that day one of those 150 dependencies mysteriously falls off the face of the internet, leaving your code uncompilable. Hope and pray that you still have a copy of it somewhere in your cache, and that you haven't conveniently cleared your cache recently because it was growing too big, because you don't know when it's coming back online (if ever!).
8) There's a security hole in one of those 150 dependencies that's making your app today's worldwide favorite 0-day exploit, and you have no idea that your code depended on that vulnerable module, let alone know how to fix it. Upstream, of course, has conveniently fallen off the face of the internet and aren't responding. (Or they're "working on a fix" but are taking a long time, and you can't wait since customers are leaving by the droves.) Or worse, the entire repo has gone offline (probably pulled because of the 0-day) and all you have now is a cached copy of the stale, and still-vulnerable code.
9) Your code compiled and worked perfectly fine 5 years ago when you last built it, and you have come to depend on the executable for your critical business functionality. Then today you found a trivial bug with a 1-line fix. Unfortunately, the code doesn't compile anymore because 37 of the 150 modules have released new, incompatible versions that your code no longer works with, and 17 modules (13 of which you had no idea you even depended on) have vanished into the ether never to be found again. What you thought would be a 1-line fix turns into days, weeks, months, or, God forbid, *years* of effort to reengineer the now-missing functionality.
10) You're on the road away from any access point, and you want to make a 1-line change to your code. Of course, you can't recompile, 'cos the intarweb is down... Enjoy your vacation!
After being burned by the above problems, I have come to the conclusion that any dependencies must:
1) Have source code. Binary-only libs are a no-no because of all of the above, plus you cannot fix the problem even if by some miracle you have infinite time and energy to invest in fixing it.
2) Exist in a *permanent* form on the local hard drive (caches are not counted because they can be easily lost). Preferably as part of the working source tree (out-of-tree modules are bad because they can disappear, point to the wrong thing, be inadvertently upgraded to an incompatible version, etc.).
3) Have as few other dependencies as possible, preferable none at all.
4) Exist in a compact form (preferably a single file) that can just be copied and dropped into your source tree at will, without a ton of hassles. And easy to upgrade (just copy the new version into the source tree) without running into problems like partial upgrades (forget to copy 1 of 2 files, now they are of incompatible versions and may appear to work but introduce subtle bugs).
Adam's libs fit the bill very nicely indeed.
T
--
Unix is my IDE. -- Justin Whear
|