I’d love to say that the reason I haven’t made any posts for the past week is because I’ve been hard at work on my game engine, but no. Actually what happened is that I started watching Breaking Bad, and while it wasn’t good enough that I would recommend it to anyone, it wasn’t bad enough for me to abandon it, either.
But that’s unimportant. This post is in the “Programming” category, which means we’ll be talking about programming!
So as you can probably guess from the incredibly dry and unreadable last post, I’m not terribly happy with parts of my image generation library. It’s monolithic, messy, disorganized, difficult to maintain, and lacking certain key features. When faced with a challenge like this, most programmers…
Well, most programmers quietly avoid touching the library as much as they can, and hope somebody else will eventually fix it.
I am not most programmers. Also, I can’t exactly hope for someone else to fix it, because everybody else on my team is also me, and they know where I sleep, so I don’t dare risk upsetting them. Which means I’m ripping everything out and fixing it myself.
The problem: A library which accepts commands in text or bytestream format, and uses those commands to call a set of image manipulation functions.
Each function has its own set of parameters, so I can’t just make a function that says “Here, assemble the command like so!”
The system I was using before involved things called “Structures.”
A “Structure,” or “Struct,” in C or C++ is a collection of values and handler functions. Say, for example, you wanted to put together a phone book. You could, say, make each entry a string. “John A. Doe, DOB: June 6, 1965, height: 5 ft, 11 in, weight: 185 lbs.”
That’s a really bad way to do it. People think in text just fine. You think in text, I think in text(actually I think in pictures and for all I know you think in sounds, but we use text as a standard communication medium). Computers think in numbers and, occasionally, electric sheep.
If only there were a way to store the entry as a set of different kinds of data. A string called “Name,” which reads “John A. Doe,” a string called “Birth month” which reads “June,” an integer called “Birth Day,” with a value of 6, an integer called “Birth Year,” with a value of 1965, an integer called “Height_feet,” with a value of 5, “Height_Inches,” value of 11, and a float called “Weight,” which reads 185.
It might look like this:
string name = "John A. Doe";
string birth_month = "June";
int birth_day = 6;
int birth_year = 1965;
int height_feet = 5;
int height_inches = 11;
float weight_pounds = 185;
More verbose, but the computer can make more sense out of it.
Actually, there’s nothing to keep us from nesting our structs. So if I really want to make things look nice…
(Note: this is in D. C, C++, C#, and Java would all look slightly different)
Name name = Name("John", "A.", "Doe");
Date birth_date = Date(Month.JUNE, 6, 1965);
Length height = Height.ft_in(5, 11);
Weight weight = Weight.pounds(185);
this(string initfirst, string initmiddle, string initlast)
first = initfirst;
middle = initmiddle;
last = initlast;
this(Month initmonth, int initday, int inityear)
month = initmonth;
day = cast(ubyte)initday;
year = cast(short)inityear;
static Length ft_in(int feet, int inches)
inches = inches+12*feet;
rval.height_meters = inches*.0254;//2.54 cm/inch * 0.01 meters/cm
static Weight pounds(float weight_pounds)
rval.weight_kilograms = weight_pounds/2.2;
I er… went a bit nuts there, didn’t I?
Regardless, aside from several missing handler functions and data validity checks(rejecting someone born on February 31), that’s how a typical programmer would actually create the “Person” struct. And all of the smaller structs that go together to create it.
C, C++, and D have two types of aggregate data. The first is the Struct, the second is the Class.
As far as I know, there is no functional difference between a Struct and a Class in C and C++.
In D, there are a few very important differences.
-is a Value type
-can not have a null constructor
-defaults to Public visibility
-is a Reference type
-Can, and in fact should, have a null constructor
-defaults to Private visibility
Let’s look at the differences for a moment.
Value vs. Reference type
In D, all primitives are Value types. An Integer is a value type. That means when you work with an integer, you’re saying “Here’s the value of the number.” When working with a Struct, you’re saying “Here are the values of all the variables in this thing.” A Class is a Reference type. That means when you work with a Class, you’re not saying “Here are the values.” You’re saying “Here’s the place in memory to look to find all of the values of the variables.” One side-effect of this is that a Struct, like an Integer, doesn’t need to be initialized to a value before you can use it. You declare the struct, and you can start messing around with stuff right away. A Class, when declared, has a pointer that points to “null” if you’re lucky, and somewhere else if you’re not. You haven’t created the Class, like you have with the Struct. You’ve told the program “There’s going to be a class, and I want you to use this nametag to work with it.” The Class doesn’t actually exist until you type “new Classname()” with optional parameters between the parentheses, depending on any constructors you may have added. Which brings me to…
D prohibits a Struct from having a constructor with no parameters. This is because Structs are Value types. When created, all of the variables are initialized to their default state. If you want something other than the default state, you initialize the variable upon creation. A Class may have to have initial values set for the variables, even if they’re just the default variable values. Doing this is also D’s subtle hint that if you need to do something more complicated than set an initial value for a Struct variable, you should probably be using a Class instead.
Public vs. Private visibility
Public visibility for a variable or function means the variable or function can be viewed and accessed from anywhere in the program that the Struct or Class itself is visible from. Private visibility means that variable or function can ONLY be viewed or accessed from within the source file where the variable or function was declared. D is actually much more permissive than any other language on Private data. Typically, Private visibility means the variable or function can ONLY be viewed or accessed from within the Struct or Class that it was created in. Part of this was so the creators of the language wouldn’t need to add Protected or Friend visibility, both of which are somewhere between Public and Private: some external classes can view and access the members, others can’t. The details vary between language and implementation, and you can actually get a Master’s degree in Computer Science by studying nothing but different forms of member visibility and what exactly they mean for that member. Which means it’s way beyond the scope of a blog post.
This is more on the Value vs. Reference thing. Because a Class is a Reference type, that means when you copy or pass it around, you aren’t passing around all of the data in the class, you’re ONLY passing the pointer to the class. Basically, this means that you’re only passing around 4 bytes on a 32-bit system(which is getting rare these days, actually) or 8 bytes on a 64-bit system. 4 bytes is 32 bits, 8 bytes is 64 bits. When you pass around a Struct, you’re passing all of the data for that struct, meaning you’re only passing 4 bytes if your Struct consists of 1 float, 1 integer, 2 shorts, 1 short and 2 characters or Bytes, or 4 characters or Bytes. Any more than that, and you’re passing around every single byte in the Struct, whether it be 4 or 4000.
So passing Classes is faster than passing Structures, because less data is passed around. But passing Classes is less secure than passing Structures, because anybody who can get their hands on a reference to the Class can call any Public-visible function in that Class, altering the data directly. In short, if I hand you a Struct, I’m handing you a COPY of the Struct to do with as you please, and nothing you do to it will have any effect on the copy I’m holding onto. When I hand you a Class, I’m handing you THE ACTUAL CLASS, and any changes you make will be reflected in the version that I can see.
There are advantages to both of these.
Of course, the opposite is true as well. Remember that subtle hint about whether you should be using a Struct or a Class? Structs initialize faster than Classes, because they ONLY initialize to set-values unless you’ve called them with initial values, while a Class not only must be specifically allocated, but even a no-parameter initialization may have several additional function calls involved.
So how’s all this work out? Here’s the plan: A single Class for the library, which will manage all of the image data, and it has a set of bridges which will talk to… Structs for each of the different image generation library commands. This is actually pretty much the same as what I’ve got now, but I’ll be going about it in a more sensical manner, so it should all be easier to read and maintain in the future.
In the next few updates, I’ll introduce each generation command as I get it added to the system.