That is a whole subject unto itself. The notion of the "curtain" that in effect separates the developers from the users, on the one hand, is generally there for a good reason, like if a full "debug version" of a program runs 50x slower than its release counterpart. But there are also problems with software - where "Uh oh! something bad happened!" which you never want to have to happen with your flight control system, or your robotic surgery unit, or your previously self-driving car, or robotic whatever, whether "it", whatever "it is" tried to re-enact a scene out of the movie "Lawn-Mower Man", or "Eagle Eye", or not.
So let's talk about debugging, code portability, and code bloat. Here is a fun piece of code, but what does it do, what was it intended to do, and what can it really do?
typedef enum { CHAR1, CHARPTR1, DOUBLE1, FLOAT1, INT1, SIZE1 } PTYPE;
class deug_param
{
public:
PTYPE m_type;
union
{
char ch;
char *str;
double d;
float f;
int i;
size_t sz;
};
deug_param (char arg);
deug_param (char* arg);
deug_param (double arg);
deug_param (float arg);
deug_param (int arg);
deug_param (size_t arg);
};
Before we answer some of those questions in further detail, let's examine some of the implementation details.
deug_param::deug_param (int arg)
{
m_type = INT1;
i = arg;
}
deug_param::deug_param (size_t arg)
{
m_type = SIZE1;
sz = arg;
}
O.K., before you say "nothing to see here, these aren't the droids you are looking for", take a closer look. What this little snippet of C++ does is capture the type of any variable that we want to pass to it, which allows for run-time type checking of floats, ints, strings, etc. This means that we can do THIS in C++!
// EXCERPT from void PASCALCOMPILER::CERROR(int ERRORNUM)
....
if (NOISY)
WRITELN(OUTPUT);
else if (LIST&&(ERRORNUM<=400))
return;
if (LINESTART==0)
WRITE(OUTPUT,*SYMBUFP,SYMCURSOR.val);
else
{
ERRSTART=SCAN(
-(LINESTART.val-1),char(EOL),&(*SYMBUFP[LINESTART.val-2])
+ (size_t)(LINESTART.val)-1);
MOVELEFT(&(*SYMBUFP[ERRSTART]),&A[0],SYMCURSOR-ERRSTART);
WRITE(OUTPUT,A /*SYMCURSOR-ERRSTART*/ );
};
WRITELN(OUTPUT," <<<<");
WRITE(OUTPUT,"Line ",SCREENDOTS,", error ",ERRORNUM,":");
if (NOISY)
WRITE(OUTPUT," <sp>(continue), <esc>(terminate), E(dit");
char param[] = ":";
WRITE(OUTPUT,param);
}
.... etc ....
Now like I said earlier with the right set of macros and some clever "glue" the C/C++ pre-processor+compiler can be made to chow down on Pascal programs, quite possibly without modification. There is some debate of course, as to whether the C/C++ pre-processor is actually Turing complete, since there is something like a 64 character limit on labels, and a 4096 character limit on line length, IIRC; so that "at best" the pre-processor is considered by some to be a kind of "push down automation". Yet, as I said, with the right "persuasion" the grammar of any well-defined programming language should be able to be transmogrified into any other.
Oh, and by the way - here is how you get, at least in part, the C++ pre-processor to chow down on Pascal WRITE and WRITELN statements, even if I haven't fully implemented Pascal-style text formatting, yet. (it's not a high priority).
First, we use our "debug parameters class" to capture variable types during compilation.
void WRITE(int uid, deug_param w1)
{
_WRITE(uid,1,&w1);
}
void WRITE(int uid, deug_param w1, deug_param w2)
{
_WRITE(uid,2,&w1,&w2);
}
void WRITE(int uid, deug_param w1, deug_param w2, deug_param w3)
{
_WRITE(uid,3,&w1,&w2,&w3);
}
.// etc ...
// NOTE that this can also be done with templates or macros.
Then we use another version of _WRITE or _WRITELN which accepts C style VAR_ARGS, and it WORKS, even though the VAR_ARGS method otherwise doesn't propagate type information.
void _WRITE(int uid, size_t sz,...)
{
char buffer1[256];
char buffer2[256];
deug_param *val;
unsigned int i;
va_list vl;
va_start(vl,sz);
i=0;
memset(buffer1,0,256);
memset(buffer2,0,256);
while(i<sz)
{
val=va_arg(vl,deug_param*);
switch (val->m_type)
{
case CHAR1:
sprintf_s(buffer1,256,"%c",val->ch);
break;
case CHARPTR1:
sprintf_s(buffer1,256,"%s",val->str);
break;
case DOUBLE1:
sprintf_s(buffer1,256,"%lf",val->d);
break;
case FLOAT1:
sprintf_s(buffer1,256,"%f",val->f);
break;
case INT1:
sprintf_s(buffer1,256,"%d",val->i);
break;
case SIZE1:
sprintf_s(buffer1,256,"%s"," ");
break;
default:
break;
}
strcat_s(buffer2,256,buffer1);
i++;
}
SYSCOMM::OutputDebugString(buffer2);
va_end(vl);
}
Presto! Now a Pascal program that is being written in C/C++ can run with far less modification, such as NOT having to do this!
#if 0
void PASCALCOMPILER::CERROR(int err)
{
char buffer[64];
sprintf_s(buffer,64,"Compiler Error: %d",err);
WRITELN(OUTPUT,buffer);
}
#endif
Thus, our Pascal to C/C++ code can remain in a more pristine state, on the one hand., i.e., by not having to clutter it up with unwanted printf's, scanf's, and other things that might not even be available on a recent IDE that works with a modern OS (which might require UNICODE strings for example; and which in turn has led to millions, if not billions of lines of broken or unmaintainable code; and which sadly might mean that in some cases, older "tried and true" applications that might have been written a "long time ago" in some language like COBOL or ADA or who knows what -- get replaced with code that is "completely rewritten" but also "completely broken." And then what? Do the planes fall out of the sky? Does the reactor melt down? Do buildings collapse because someone can't import the plans from the old CAD package into the new one - so they don't even know how serious the problem is?
I think that you can figure out the consequences of "inadequate" software tools.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.