It's fair to say that regardless of their many advantages, "systems languages", like C, are much harder to use than their more abstract cousins.Vendors know this, which is why they often find a way to integrate across language boundaries. You might write critical functions in C or C++, then invoke them in Python or from Swift or… Visual Basic 6.
And crossing those language boundaries can pose other challenges. For example, Python has a built-in boolean type. C, for quite a long time didn't. Which means a lot of C code has blocks like this:
#define BOOL int
#define FALSE 0
#define TRUE 1
#define FILE_NOT_FOUND 2
Carl C provides that block, just for illustration purposes. Awhile back, he inherited a big pile of antique COM+ and the associated VB6 front end, along with a herd of "COM Wizards" and "Junior VB Programmers" to help maintain it.
The idea of the system was that all the "hard stuff" would be done in C++, while the UI would be a VB6 application. The C++ COM layer talked to some hardware which could be attached to the PC, and the VB6 layer let the user check the status and interact with the device.
Unfortunately, the Junior VB Programmers quickly encountered a problem: they could NEVER get the device online. Plugging, unplugging, rebooting, trying different ports, different computers, it never worked. But when the "COM wizards" tossed them a diagnostic program written in C++, things worked fine.
"Must be a problem in your VB code," was the obvious conclusion.
Dim oHardware as New HardwareServer
' Initialize Hardware
oHardware.Initialize 0
If oHardware.ONLINE = True Then
Set oActuator = oHardware.Actuator
Else
MsgBox "Hardware did not initialize correctly."
End
End If
Reading through that code, it's hard to see at a glance what could be wrong about it. Could the problem be in the COM layer?
interface IHardwareServer : IDispatch
{
[propget, id(1)] HRESULT Actuator([out, retval] IActuator* *pVal);
[propget, id(2)] HRESULT ONLINE([out, retval] BOOL *pVal);
[id(3)] HRESULT Initialize(short interfaceID);
};
coclass HardwareServer
{
[default] interface IHardwareServer;
};
While the COM approach to defining a property is a little awkward, nothing at a glance looks wrong here, either. ONLINE
is a property that returns a BOOL
.
But this is a C++ boolean. Defined so that true
is one and false
is zero.
Visual Basic, in addition to letting arrays start at 1 if you really wanted to, had another quirk. It was pretty loosey-goosey with types (defaulting to the handy Variant
type, which is a fancy way of saying "basically no type at all"), and the internal implementation of its types could be surprising.
For example, in VB6, False
was zero, much like you'd expect. And True
was… -1
. Yes, -1
. Carl suggests this was to "kinda sorta mostly hide the distinction between bitwise and logical operations", which does sound like the sort of design choice Visual Basic would make. This is also TRWTF.
Now, it's easy to see how the Visual Basic code above is wrong: oHardware.ONLINE = True
is testing to see if ONLINE
is equal to -1
, which is not true. A more correct way of writing the Visual Basic would be simply to test if oHardware.ONLINE then…
. Visual Basic is okay with falsy/truthy statements, so whether ONLINE
is 1
or -1
, that would evaluate as true.
That doesn't let the COM programmers off the hook though. COM was designed to work across languages, and COM was designed with the understanding that different languages might have different internal representations of boolean values.
As Carl adds:
Of course if they were really COM wizards they would have used the VARIANT_BOOL type in the first place, and returned VARIANT_TRUE or VARIANT_FALSE.