Advertisement
Guest User

Untitled

a guest
May 19th, 2024
184
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.16 KB | Software | 0 0
  1. So when your programs are compiled, they are in object files which is like the raw binary instructions. They get assembled into a platform specific runtime. They can either be a running application (ELF on Linux, EXE on Windows) or a shared library (SO om Linux, DLL on Windows). The functions and other data are saved as 'symbols' within these formats. When you run an application, it will copy the relevant code into RAM and run it. It will also look for any shared libraries and copy their symbols and relevant code into RAM so it can also be utilized. A quick side note: static libraries 'copy' their code and symbols and inject it into whatever application/shared library you trying to create and when time for the OS to put it into ram it sorts out any confusion with symbols.
  2. One of the difficulties I came across recently is the internal state of any sort of library. I think that is one of the reasons you may have been discouraged from using the standard library is for example: if multiple libraries used `stdlib.h` and `time.h` then used `rand()`, the internal state of `rand` is shared across all the libraries regardless of it being static or shared. So if at the beginning of your program in `main` you call `srand((unsigned int) time(NULL))` then you call another function from a shared library that says `srand(0)`, then `rand` will not work as you would expect. However, that is only for things with that kind of internal state, where there is basically some kind of singleton you have no access to.
  3. Since a lot of time has passed, it is very common for libraries to have some kind of init function that returns a handle to the library's state.
  4. FreeType for example:
  5.  
  6. FT_Library library;
  7. int error = FT_Init_FreeType(&library);
  8. If you were to modernize `rand`, it would be something more like
  9. RAND *rnd = srand((unsigned int) time(NULL));
  10. int r = rand(rnd);
  11.  
  12. I am personally a HUGE FAN of shared libraries. They are perfect, unless you are on Windows. The reason I stray away from static libraries due to copying of code (redundancy if multiple programs use the same library) and if an update comes out to an existing static library, you must recompile your application where instead you could update the package on Linux or replace the DLL on Windows.
  13. The only problem with shared libraries is the potential vulnerability of DLL injection.
  14. A lot of this information was stuff I wish I knew and had to experiment to find out. I wanted to create a logging system as a shared library, but it had an internal state similar to that mentioned of `rand`. I wanted to see if a static library would fix that, it does not due to the symbols still being copied into RAM. Learned a lot, your best bet is to have the internal state inside a structure, and have an initialize function for it with the user having some sort of way to refer to that structure.
  15.  
  16. Overall it would be something like
  17.  
  18. MyApi *lib = myapi_init(...);
  19. double d = myapi_some_calculation(lib, 3.14);
  20. MyApiObject *obj = myapi_object_create(lib, ...);
  21. myapi_object_something(obj);
  22. myapi_free(lib);
  23.  
  24. It really does not get much more complicated than that. I think it is fair to explain why a lot of API's and libraries use this kind of structure.
  25. A personal opinion I have though (and I don't know if it is that controversial...) is that a lot of libraries do stupid things with typedefs. I don't personally care if people use typedef on struct or not, but what I do care about are two things. I do not like it when libraries include the pointer syntax in the typedef for their handles.
  26.  
  27. VkInstance instance;
  28. VkResult = vkCreateInstance(&appinfo, NULL, &instance);
  29.  
  30. Is the above an opaque structure or not? I am not sure. I have to look at the docs to find out that VkInstance is typedef'd with a pointer. This is also annoying with these other styles of libraries.
  31.  
  32. typedef struct MyApi * MyApi; // <-- This SUCKS
  33. ----
  34. MyApi api = myapi_create(...);
  35. MyApi api;
  36. myapi_create(&api, ...);
  37. // X11 example
  38. Window win = XCreateWindow(...); // Is this a pointer, structure, or integer?
  39.  
  40. It is unclear if these I have access to or not without having to peek the docs. I would better prefer
  41.  
  42. typedef struct MyApi MyApi; // This RULES
  43. ---
  44. MyApi *api = myapi_create(...);
  45. MyApi *api;
  46. myapi_create(&api);
  47. // X11 example
  48. Display *display = XOpenDisplay(...); // Now THIS is obviously an opaque structure
  49. // standard lib example
  50. FILE *file = fopen(...); // Now THIS is obviously an opaque structure
  51.  
  52. To me, it is much clearer to see tell that `MyApi` is likely an opaque type based off the code. And another personal gripe with typedefs, don't typedef all the int types. A lot of libraries do it, I don't need my global space cluttered with `int, int32_t, int32, GLint, EGLint, Int32, ma_int32, DWORD` and all there unsigned, 8 bit, 16 bit, and 64 bit variants. If the size of integers are important, `stdint.h`/`inttypes.h` work perfectly fine.
  53. I feel everything I have explained goes towards general libraries. A lot of libraries however are header only or include source, I personally do not like them as they are virtually the same as static libraries. Annoying and stupid. Most the time I see these being usefully is if you want a library to have a separate internal state from another instance of the same library, but I have only tested that by generating a library so that way they have different symbol names. A yucky mess when doing what I explained above already works fine. Compiling with headers are also slow for some reason and annoying to work with. Stick with shared libraries as much as you can IMO.
  54. Besides the background knowledge of the internal state of a library, there is not really a whole lot more than that, just don't name your functions and types as anything stupid or inconsistent. The X11 examples with the typedefs are real, Display and Window have no prefix on them like `XDisplay` or `XWindow` and the pointer typedef is another inconsistency where you cannot tell what Window truly is. Try to be absolutely consistent and always try to prefix your code `myapi_xxxx_xxxx(...)` or `MyApiXxxXxx(..)`, or whatever, just pick a prefix and pick a formatting and make it consistent.
  55. Also, if you ever write an API that soars, please have good documentation for it.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement