![]() |
Forum Index : Microcontroller and PC projects : CMM2: Proposal for standardizing shared reusable .inc libraries
Author | Message | ||||
mkopack73 Senior Member ![]() Joined: 03/07/2020 Location: United StatesPosts: 261 |
Hey gang, After looking at https://www.thebackshed.com/forum/ViewTopic.php?FID=16&TID=12534 and having written a High Score library, I'm thinking this is the time we should start talking about how to standardize libraries before we end up with a hideous mess... This is more about trying to keep things organized and reusable for the long haul while still providing people with reusable libraries of capabilities to speed up development. So, let's talk about this... Here's a few guidelines I would recommend based on my 20+ years of professional SW engineering experience: 1) a mechanism for stating minimum OS version required (because this library uses something in a specific version and higher) - for example function libname_getMinimumOS() 2) a mechanism for reporting this library's version to the calling program. - for example function libname_getVersion() 3) a standardized number scheme for use in #2 (see #7 below for some thoughts on this). 4) where possible use local variables to isolate and reduce the possibility of name collisions. Consider prefixing (or postfixing) variable names with something identifying the library if it has to be a global. 5) all functions and subs should be named with a prefix or postfix to indicate the library it came from (for example in the highschool library I put "hs_" in front of every sub/func name to reduce the possibility of a name collision. If the function is a helper that the end users shouldn't be calling directly (like something that provides help to one of the API calls you DO want exposed) maybe include PRIVATE in the sub/func name to help indicate to the user not to use it. 6) don't rely upon option legacy, or option default <whatever> - be explicit with all your variable types and names so the code will work regardless to how the end user sets things up for their program. 7) likewise (and I'm guilty of this one) consider making either 2 versions of your library (one for option order 1, another for option order 0) or come up with a way to support both from the same code base... You won't know and don't want to force the end user into one option or the other... I personally prefer option order 1 because that's how Commodore basic that I grew up with worked, but others prefer and use Option order 0... The library should work for either group. Maybe have functions for each type and include the order # in the sub/func name call (like hs0_XYZ and hs1_XYZ for the 2 versions of the XYZ function that work for either order version and leave it to the user to call the correct one based on what they use. This approach would double the size of your library with only 1/2 being used at any one time, but might prove to be the easier way to support things. 8) new versions - whenever possible, try to keep the API the same or ADD to it, but don't remove older calls so you don't break older end user code that expects those functions to still be there even if they put a new version of your library on the card. Alternative to this is to include the version number of the library in the filename so we can easily have multiple versions in the same path on the SD card so sw that needs a specific version can still find it on the card while newer stuff can have the version it needs as well... I would suggest (going with #3) that if you are simply fixing a bug or adding new functions to the library, you keep the same major version number (1.x) and increment the minor version numbers. if you make major revamps that change the API, or remove's or changes old functions then you update the major revision number (X.) Anyhow, these are just suggestions... Anyone have any others we should consider? The sooner we get this straightened out and we follow it when writing new libraries, the easier and more maintainable everything will be moving forward. We're the initial crowd so we can help set the standards for the future of the community. Edited 2020-08-12 10:23 by mkopack73 |
||||
Womble![]() Senior Member ![]() Joined: 09/07/2020 Location: United KingdomPosts: 267 |
Not a bad idea, but will involve a fair amount of work policing/maintaining the library code. also you mentioned: The thread: CMM2 - Slowdown not returning from SUBs has the following comment from Peter: In other words, there are performance penaties inherant in your suggestion 4. and 5. You might get away with using a postfix library identifier. Regards Womble |
||||
mkopack73 Senior Member ![]() Joined: 03/07/2020 Location: United StatesPosts: 261 |
Yes it's certainly a trade off... Global variables from library code can cause a lot of weird behavior for the end users though - Let's say I use xpos in my library and you use it in your code. You call my function and now xpos gets changed and you don't know why without digging through my code to figure it out... MAJOR headache. With OO based languages there is encapsulation and thus isolation to help eliminate this, but most basic implementations can't do scoping rules. Having the locals are helpful, but like you said cause some performance penalties. So it's something that might need to be balanced. If it's a function/sub that will be called often, then it probably makes sense to use a global. If it's something that only gets called once or a few times (like hs_displayscores) then the performance penalty probably isn't a deal breaker. As far as policing - I simply suggest these as "best practices" to follow when writing a library that you plan to distribute for others to use. If you don't plan to have others use it, do whatever you want! :) But following guidelines like this will make it much easier down the line for people to be able to use your library and for you to support it long term. I plan to rework my high score lib to do all of these. |
||||
mkopack73 Senior Member ![]() Joined: 03/07/2020 Location: United StatesPosts: 261 |
Might be a good idea to do some performance tests to see just how big the impact of using a local vs a global vs a static is... Same with longer vs shorter variable names with and without unique first 4 vs postscripting names... |
||||
Womble![]() Senior Member ![]() Joined: 09/07/2020 Location: United KingdomPosts: 267 |
I quite agree, good coding style, plenty of comments, and sensible naming schemes are a really good idea to make the code reuseable and maintainable. Same with longer vs shorter variable names with and without unique first 4 vs postscripting names... I'm pretty certain I have seen threads/posts testing this and discussing how to get the best out of your code eg. Peters thread I quoted above. I was simply suggesting that going for a "suffix" identifying the library would be more appropriate on the CMM" implementation of Basic. |
||||
mkopack73 Senior Member ![]() Joined: 03/07/2020 Location: United StatesPosts: 261 |
Yup that's all cool... If you happen to find someplace where the performance differences are benchmarked please link it here.. That would be good to help inform choices. Suffix it is! for both variables and sub/func names... Ok, if we're going to suggest in some cases to use globals then I suggest we make it very clear in the comments near the top of the library code any names of reserved variable names that need to be global for the use of the library (again to help inform users so they don't accidentally collide). Sort of a list of "reserved variable names" |
||||
MikoKisai Newbie ![]() Joined: 10/07/2020 Location: GermanyPosts: 6 |
On this particular point, MMBasic 5.05.04 added a BOUND function which lets you query the current OPTION BASE setting (and the upper bound as well, for that matter) Post where the relevant beta was announced (and the function explained): https://www.thebackshed.com/forum/ViewTopic.php?TID=12250&PID=149774 I'm sure there are cases where it might be crucial to save every cycle and have two different versions, but this should be useful in most cases where the code needs to be generic. |
||||
mkopack73 Senior Member ![]() Joined: 03/07/2020 Location: United StatesPosts: 261 |
On this particular point, MMBasic 5.05.04 added a BOUND function which lets you query the current OPTION BASE setting (and the upper bound as well, for that matter) Post where the relevant beta was announced (and the function explained): https://www.thebackshed.com/forum/ViewTopic.php?TID=12250&PID=149774 I'm sure there are cases where it might be crucial to save every cycle and have two different versions, but this should be useful in most cases where the code needs to be generic. Agreed! I haven't tried this yet to see how to use it, but yeah should solve the issue. |
||||
Womble![]() Senior Member ![]() Joined: 09/07/2020 Location: United KingdomPosts: 267 |
The posts I am refering to tend to be scattered about on the forum, often in the threads where Peter posts firmware updates and amongst the bug reports. For example CMM2 and timing some simple code. Benchmarking is good, and useful, but I am fairly sure that Peter (matherp) has done more testing than any of us during the development of the CMM2. One could say he wrote the gospel ![]() Ok, if we're going to suggest in some cases to use globals then I suggest we make it very clear in the comments near the top of the library code any names of reserved variable names that need to be global for the use of the library (again to help inform users so they don't accidentally collide). Sort of a list of "reserved variable names" Absolutely ... comprehensive comments are a must for this sort of reuseable code. |
||||
thwill![]() Guru ![]() Joined: 16/09/2019 Location: United KingdomPosts: 4311 |
Hi folks, As a "data point": I have been using 2 or 3 character prefixes with an underscore, e.g. fi_is_absolute() for a function declared in "file.inc" that determines if a path is absolute. I've been prefixing global variables similarly. If performance has then been an issue then I've been using my transpiler to crunch function and variable names for the final "executable". As an opinion: Given that disk and memory space are not likely to be a problem I personally do not see a benefit in having a root "/include" directory which multiple programs from different authors depend upon ... that way lies the CMM2 version of "DLL hell". If I need someone else's code and the license permits then I will just copy the latest version of it into my own project as "local code" and take responsibility for keeping it up-to-date should I need it. If the license doesn't permit such use then I simply won't use it. Regards, Tom Edited 2020-08-12 21:09 by thwill MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures |
||||
Womble![]() Senior Member ![]() Joined: 09/07/2020 Location: United KingdomPosts: 267 |
Tom ... I agree, pretty much what we have been doing all along. I think mkopack73's point is to make it easier to maintain "borrowed" code in ones project. At least thats my understanding. Womble |
||||
mkopack73 Senior Member ![]() Joined: 03/07/2020 Location: United StatesPosts: 261 |
Fair point there, but I don’t see it as much of “DLL Hell” - it’s not like there is a registry that the OS is using to hunt out and find which library to use. It seems like most people who are making games or whatever are putting all the files needed into a zipped up folder for distribution. So for example the little 285 Rush Hour Game ive been working on would get distributed in a zip that when unzipped would make a 28RushHour folder and inside it would be everything for the game, including and .incs needed. It would be up to me as the game author to include the .incs I use and the proper version my code needs. (I prefer this approach to be honest). The alternative would be to have everyone standardize on some sort of directory structure like A:/libraries and we always put .inc in there and have our code always look for them in there. But then you will end up with having multiple versions of the same library (with different fillenames) in there which could get very messy. But again, the specific version would be getting called out by name in the calling code - there’s no registry finding it for you. But since there isn’t much of an OS here to begin with and nothing enforcing a “standard” layout of your SD card, I don’t see us getting everyone to follow one approach or the other. |
||||
mkopack73 Senior Member ![]() Joined: 03/07/2020 Location: United StatesPosts: 261 |
Tom ... I agree, pretty much what we have been doing all along. I think mkopack73's point is to make it easier to maintain "borrowed" code in ones project. At least thats my understanding. Womble Exactly.. this is meant more for the people who create the libraries to make them easier for end users to make use of them. Thwill - you're certainly welcome to take the code (at least my code) and do whatever you want with it, but if you just want to use it as is as an actual reusable library, then there are things I can do as the library developer to help facilitate that and make it easier for you. If I were to follow all the guidelines we've talked about so far, then you could easily: 1) know which version of my library you're working with 2) be sure that my library isn't going to have name collisions with your code's funcs/subs and variables 3) know that it will work regardless of how you've configured your options 4) know if my library requires features from a specific firmware release (so that you can then make your code rely upon that release or higher) All of those are good things that make people WANT to reuse your library, rather than just yank the code and modify it to their needs, reducing the amount of work they have to do by that much more. Nothing saying what you're doing is wrong. It's just a different usage philosophy and that's perfectly fine! I see this as more like "Hey, let's make 1 library for doing libc functions we'll call it glibc and that way everyone can just use it and we don't have to keep reinventing the wheel over and over..." |
||||
thwill![]() Guru ![]() Joined: 16/09/2019 Location: United KingdomPosts: 4311 |
Hi "mkopack73", I understand. I thought you were suggesting that programs might share a single copy of a .inc file in a known location as opposed to having private copies. Regards, Tom MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures |
||||
mkopack73 Senior Member ![]() Joined: 03/07/2020 Location: United StatesPosts: 261 |
I understand. I thought you were suggesting that programs might share a single copy of a .inc file in a known location as opposed to having private copies. Regards, Tom Well, that certainly COULD be done, but like you said, it could easily turn into DLL Hell... Personally I always preferred how OS/2 handled that - your program if it made use of a library, packaged the version of the library it used with the program. When running the program the system would first look in the program's local directory for a copy before asking the system to find a copy of the library elsewhere. (Of course this had other unintended issues). But yeah we COULD do a "common includes directory" and just have 12 different versions of say my highscore.inc in there with different versions and different filenames, but that would become a nightmare because you wouldn't know which ones you could delete because you don't know what programs would be depending upon which versions. So yeah, I like the first method of keeping the .inc with the code that uses it in it's own subdirectories WITH the code using it. Private copies as you say. With the version numbering and such it just makes it easier for you as the end user to decide if you want to upgrade to the new .inc version and make any changes to your code to utilize the updates or not. This just gives you a way to be informed. |
||||
thwill![]() Guru ![]() Joined: 16/09/2019 Location: United KingdomPosts: 4311 |
Sounds like we are in agreement ![]() Also whilst your logic is sound I think using a "suffix" for the library will look "weird". Is this really necessary given that in any case where the overhead of the function or variable name lookup is significant most of the identifier names will need crunching and where possible the constants inlining for a "final executable" anyway ... yes, I'm pimping my transpiler again, I wish I wasn't since it means I have to keep working on and then maintain it ![]() Note in case you are unaware, the period character '.' is valid in variable, function and subroutine names, so the prefix (or suffix) for library "foo.inc" could be "fo." instead of "fo_". I've gone with underscores myself but on another day I might have made a different decision. Tom MMBasic for Linux, Game*Mite, CMM2 Welcome Tape, Creaky old text adventures |
||||
mkopack73 Senior Member ![]() Joined: 03/07/2020 Location: United StatesPosts: 261 |
![]() Also whilst your logic is sound I think using a "suffix" for the library will look "weird". Is this really necessary given that in any case where the overhead of the function or variable name lookup is significant most of the identifier names will need crunching and where possible the constants inlining for a "final executable" anyway ... yes, I'm pimping my transpiler again, I wish I wasn't since it means I have to keep working on and then maintain it ![]() Note in case you are unaware, the period character '.' is valid in variable, function and subroutine names, so the prefix (or suffix) for library "foo.inc" could be "fo." instead of "fo_". I've gone with underscores myself but on another day I might have made a different decision. Tom Yeah, honestly, I don't think it really matters WHICH way the library author does it, so long as they do SOMETHING to make it clear to prevent namespace collisions... If you want to use a . and I use an _ it doesn't matter so long as we do something and document it for the end user to be aware "Don't use these names!" |
||||
![]() |
![]() |
The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |