learning to code
I have been trying to learn to code programs for more than 2 decades. Currently I am reading within Emacs the An Introduction to Programming in Emacs Lisp book. I figure I should try an introductory book as I never got any real programs off the ground as something requiring more than 2 files. One obstacle is that I find that I misunderstand what the excercises actually want and I usually find it as a deterrant to actually wanting to write the answer.
I understand that programming is actually a discipline that takes time (you cannot learn it in a weekend), but it seems that other having racing past me. I usually am learning it solo, although twice I went to school for it.
I can code a little bit in C and python, but object oriented programming has been more than confusing to me over the years.
I never looked at that book before but since I learned Lisp already (even though it was 30 years ago), it feels rather easy to read for me. On the opposite, I tried reading a book on Python (that I never learned), I felt miserable there and gave up.
I am also confused with object-oriented programming. For example, I wonder how different a class is from a C structure and associated functions, and a list of preambles of functions provided as interface to the structure.
In C++, a class only differs from a struct by the fact that its members (variables and functions) are private by default. In a struct, they are public by default. In pure C, can a struct have functions?
I don't think a struct can have functions in pure C but there could be one or a set of .c files defining functions acting on one or multiple instances of a struct, provided by argument as pointers and a .h file defining the struct, including prototypes of the functions expected to be used elsewhere in the code, and comments describing the use of these functions, mentioning that only those should be used and in which .c should any new function acting on instances of struct should be added.
Perhaps the class is a way to do the same without the need for comments? Still, I like to have comments that explain design principles and when I was writting C I was normally writing such comments (and man pages).
The point is more to encapsulate, to hide the internals: the public functions are exposed to the "exterior", i.e., it is visible to the code only using the class. Those public functions can call private functions and access private variables, but those are invisible to the exterior. In this way, the class represents a type you ideally interact with in an abstract way, with a minimal interface, totally ignoring how things are implemented underneath. For instance, does the class store the data in an array or in a list? The exterior needs not know such a "detail", which can even be changed without affecting the interface, i.e., the signatures of the public functions.
There are other features to object-oriented programming, of course. Inheritance, for instance. It aims to create specializations of a class, sub-types. For those, it may make sense to access some of the internals of the parent class (to avoid code redundancy, because the specialization does not change that). That is why, at least in C++, besides "public" and "private", there is also the "protected" visibility. The derived classes can access the members (variables and functions) declared in the public and protected sections of the parent class. They cannot access its private section.
And once you have inheritance, you can do polymorphism, i.e., call public functions in a parent class that will actually run different codes in the different derived classes. In this way, you can have a collection of "shapes" (the parent class), call a function "area" on each of them but the actual way the area is computed depends on the specific type of shape (for instance a "rectangle", a "disk", etc.), which derives from "shape" and can implement an area function that overrides that in the parent class (if any; if there is no way to compute the area of a generic shape, then the class "shape" is said "abstract"; only objects of the derived classes can be constructed).
Thanks for the explanations. I feel I somehow understand private vs. public, which as I said can be done also in C but with comments and source file separation. Of course, the C compiler won't complain if you don't follow what the comments say, but with C++ you can also change something from private to public if you want not to follow the existing encapsulation.
I have a lot more difficulties to figure out how to use the concepts of protected and inheritance. It is already difficult to make a proper design separating what is internal and what is not, but the concepts of protected and inheritance are adding yet one more level of complexity for the design, meaning more risk of design mistakes. Also, I have read those examples in books about "area", "rectangle", "disk", etc. They seem to suggest using human language categories to systematically create classes with inheritance, but every additional level of inheritance increases complexity, so it does not look like a good design principle to me.
On this "polymorphism", an example that I would understand better is that a device driver implementation for the kernel needs to define the read and write operations, so each device driver needs to provide these functions, that have the same name regardless of the device type but for each device type there is a different function.
(Damn. I wrote a novel and the "Forbidden" error strikes. I will try to send it paragraph by paragraph.)
If a function should be the same whatever the device, you put it in the parent class, instead of duplicating the code in every derived class. Take storage devices. They all have a capacity. As a consequence, it makes sense to have a variable named "capacity" in the class "storage". A function "get_capacity" that returns the capacity, maybe depending on a unit (kB, MB, etc.) given in argument, would be in that parent class, "storage". Writing absolutely nothing in the derived classes ("hard_disk", "rom", ..., which may themselves be the bases of derived class, a whole hierarchy), but the fact that they indeed derive from "storage", the function "get_capacity" can be called on any object of any derived class.
Functions in the derived classes certainly need to read (and maybe write) the variable "capacity", which should however remain invisible to outside code only using the devices. That is the point of having the "capacity" variable in the "protected" section of "storage", rather than in its public section (outside code would be able to change the capacity of a device) or in its private section (derived classes could not read/write the capacity).
Maybe the class "storage" has a public function named "write", to write data, but, this time, no associated code in the "storage" class, because the specific device must be known to write data onto it. If so, the class "storage" is said abstract and the compiler will not let you construct an object of that generic class ("storage"), only objects of classes that derive from it and implement "write" (themselves or between "storage" and them; again, there may be a whole hierarchy of classes).
Those "write" implementations may however all call a common helper function in "storage", which maybe verifies that the data to write is well-formed, whatever that means. If that helper function is useful outside the code for devices, we give it a public visibility. If only the specific devices need it, we prefer the protected visibility: that is one less function to care about (it is not exposed) when, later, somebody will write code that merely uses the storage devices.
Instead of a total absence of code for "write" in the "storage" class, you can have a default code there. For instance a code that actually refuses to write. That makes sense for several different derived classes, standing for read-only storage devices ("rom", "cd", etc.). Thanks to that default code in "storage", those derived classes need not override the "write" function: calling "write" on a read-only storage will execute the code in "storage". Again, that avoids code redundancy.
Maybe the default code for "write" in "storage" throws an "exception". That is another common feature of object-oriented programming. Exceptions manage issues that never happen in normal circumstances (here writing to a read-only device). In this way, functions can actually return what they ares supposed to compute, instead of error codes. And the arguments of the functions can ideally be all "const". Also, if not "caught" by the direct caller, the exception is "rethrown", until reaching a caller that catches the exception (or the main function and the program terminates with the exception reported on the standard output). That allows to process the exception only where it makes sense and not after every call that may throw an exception.
In the end, object-oriented programming indeed adds a layer of complexity. C++ is a more complicated language than C. In fact, C++ is essentially a superset of C. However the additional complexity is supposed to help having a cleaner design, avoiding that any instruction can write any variable, avoiding code redundancy, minimizing the interfaces between modules, not having half of the code dealing with errors codes, etc. You can achieve the same with any Turing-complete language, but that may require more discipline.
What irks me about C is that I cannot create structures that are local, everything must be global. How to manage large projects in C?
In C++ data confinement is easily achieved within a class using private and or protected designations. The code is visibly manageable as long as it's my own.
However when compiling and running a particularly easy C++ graphics implementation I was disappointed how verbose the code needed to remain for continued access beyond drawing the demo with the green square inside a circle inside a loop.
As a result am using bash with rare calls to zenity for a user interface to text-only programs. Am steering clear of AI implementations or other SASS approaches to the learning curve. Any coding advice here would be welcome.
An advice to make GUIs? I am not the one to ask! Anyway, if I were you, I guess I would take look at libadwaita or Qt. I love the shell. Well, I actually especially like small Shell scripts calling the classical text processing commands (head, tail, shuf, cat, tr, wc, cut, paste, sort, comm, join, uniq and, of course, grep), my C++ command-line programs and/or AWK programs. But I am biased: I work with data analysis. I have already used Zenity too: https://dcc.ufmg.br/~lcerf/en/utilities.html#trivialibre

