I’ve been doing this job for decades and I can’t remember a time when people weren’t carrying on about data types. To my way of thinking, this hysteria has often resulted in overdesigned solutions that are so safe it isn’t actually practical to use them. Sort of like wearing a seat belt while sitting at the kitchen table.
It always seemed to me that the way out of the problem was fairly clear, if somewhat inelegant, from the language designer’s point of view: Make it easy to do the right thing. Make it clear what the right thing is. Then ,ask people to do the right thing. After all, good behavior is its own reward. Somewhere, way back in the prehistory of Symbian evolution, there exists my intellectual twin soul, because this objective is exactly what we accomplish with the Symbian data typing conventions. Notice the bon mot "conventions". This is where the "asking you to do the right thing" part comes in.
Rewinding a bit before we push play, recall that the last installment ended with this pivotal point: When programming at this level, you must always be aware of where your space is coming from. On a mobile device, storage space is at an absolute premium. Coding well in this environment requires informed, conscious choice-making about where we store data, and always, always, always having a plan for releasing that storage back into the memory pool as rapidly as we practically can. The fun and the challenge of this kind of programming, after all, is to achieve wonderful things with the lightest possible touch.
Symbian C++ and its development framework are designed from the bottom up to make it as easy as possible to accomplish these near miracles of technology. But you have to want to. Symbian type adherence isn't enforced. You can go around it. But this is more like wearing the seat belt in a bush plane. It’s there for a reason.
prej’u dice (noun) 1. Holding of ill-formed or pre formed opinions based on insufficient knowledge, irrational feelings or inaccurate stereotypes
Making small device programming work for you starts with a grasp of the fact that it is mostly about relationships (synergies, if you like that better). In any relationship that intends to thrive and grow, one of the first things the parties involved must do is examine the preconceptions and prejudices with which they arrived, and then, whole heartedly and with vigor, heave the useless and self defeating ones out the door. Your relationship to stack based storage is about to change.
Beginning with our earliest C programming experiences, we take native types (int, bool, char, etc.) pretty much for granted, because, of all things coded, these tend to fairly intuitive, readable programming prose. We get items of fundamental type simply by saying we want some. Or lots. These types are also the most likely to be defined as locals. You don’t typically explicitly allocate space for them and you don’t have to worry about what happens to the space when you’re done with them. In essence, they reliably come and go because you said so, and this is just what they ought to do. Period. Say hello to the first of the aforementioned prejudices-- These ideas about stack based data are artifacts of early learning in an environment of unlimited resources.
Think of mobile device programming this way: In a severely resource constrained environment, we are always locked in a "zero sum" game. If your app withdraws resources (screen, memory, battery power), other apps can’t get those things. This regime very seldom operates on the desktop, where vast acreages of virtual memory, a wall socket and an unlimited display z-order insulate you from such worries. So. The first thing to consider in terms of our relationship to the host environment is that we can and do run out of things, and this is true of things of every sort. Resource constraint is the general state of affairs. If we run out of resources, our function is likely to Leave. For this reason we need to be conscious of what sort of data we put on the stack, and conservative about how much stack space we consume.
Another widely held assumption, and one from which it is really painful to disengage, is that we can count on the comforts of platform stability. Unlike the desktop, where predictable, entirely standard compiler targets are the rule, we have no way of knowing what microprocessor might show up in smartphones next week. This means that your code could be targeting a device where an integer is 47 ½ bits wide the next time you have to port it. I’m being glib, of course, but you see the difficulties. If you write any code relying on the implementation properties of native types, it could break in bizarre, unpredictable ways when it moves across platforms. The need for Leave safety and cross device portability are why we use Symbian T Classes in place of native types.
Meet The T Classes
Symbian T Classes provide the behaviors of C++ built in types in a compiler independent way. (They take their name from the typedefs used to implement them.) They entirely insulate you from differences in native type implementation across microprocessors. Here they are in relation to their C++ counterparts:
• TAny* replaces void
• TBool replaces bool
• TInt replaces int
• TUint replaces uint
• TReal replaces float
• TInt64 replaces long long
T Classes are optimized for proper recycling of storage space and play a strategic part in Symbian Leave Safety. By definition, T Classes have no destructor. This means that they can only store three types of data: built in types, other T class objects and pointers or references that express a "uses-a" relationship. It also implies that most of the time, they’ll be stack based. Again, because of the restrictions on what they can store, there is no chance that they’ll orphan heap memory when they go out of scope, even in the event of a Leave. Because stack space is limited, in most cases T Class objects should be less than 512 bytes in size. Larger than that and you’ll be playing it safe to allocate the T Class object on the heap. Typically, if you do so, you’d also push a pointer to it onto our old friend the Clean Up Stack, to ensure that it got deleted in the event of a Leave.
T Classes are used to implement enum types and can be used in preference to C++ structs, providing you observe the restrictions on what goes in them. T Class member functions follow the same conventions. T Class objects can usually be assigned by simple bitwise copy, and so typically don’t need copy constructors or specialized assignment operators.
TAny* is worth extra note, as it does double duty. It provides a Symbian replacement for standard C++ void pointers, which basically mean "pointer to whatever"; and in the form TAny, it is used to designate functions which have no return value. For example:
TAny TaciturnFxn( TAny* pMysteryPointer );
Declares a function that returns no value, and takes a void* as its single parameter.
Next we’ll examine Symbian C Classes. Derived from CBase, C Classes are the conventional Symbian mechanism for heap based data storage.
I don't see the T classes as particularly helpful...For the last 40 years, a byte is 8 bits (it was different before that), and nowadays it's extremely difficult to find a microprocessor with non-standard integer and real types. If one worries about having to use different datatypes when porting embedded-device code to another device, then typedef is the solution.
Achilleas- It is very true that not only will typedefs work for the purpose you cite; they are explicitly allowed by Symbian C++. For a very smart, meticulous person (yourself, for example) this is both an expedient and traditional approach. But on the other hand, look at it from the point of view of someone a little less acute. (Me, for example.) I lose track of my car keys, my dog and even on occasion, my kids. If I revisit code I wrote more than six months previously, it might as well have been written by someone else. I have to read up before I even attempt to change it. For these reasons, I rely on habits that keep me “safe”. I comment thoroughly, use documentation extractors if they are available and am ever receptive to conventions that will tip me off to subtle but important considerations. Somewhere very close to the top of the “important considerations” batting order is the matter of keeping track of where a particular datum is stored. Using the T Classes tells me with a single quick read thru what is on the stack. It flags things that should not be significantly increased in size and shouldn’t store objects which could orphan heap memory if a function Leaves. It is lightweight, but effective. And you’re right. There won’t be any 47 ½ bit integers out there in the real world. But if some weird native type does turn up in a future target, by using the T classes I ensure that it won’t be problem.