Mechanism to generate annotated "error codes"

K

Kaz Kylheku

Hi Stefan,



Sure! But how do you know "YES" is ever USED anywhere?
(replace YES with MOTOR_OVERHEATED and every other
"error" condition that this file IMPLIES exists)

Also, you have now created a dependency that "every"
file relies upon. Add a new error code and every file
that #includes "errorcodes.h" has to be recompiled.

Your build system could make an exception for that file. Adding an error code
to the header isn't a change that requires anything to be recompiled which does
not refer to that code, and those files which refer to the code will get
recompiled because they have been modified to refer to that code.

A deletion followed by an addition which re-uses a previous code is
troublesome, as is any renumbering operation.
 
S

Shao Miller

You can have:

/* exceptions.h */
#define EXCEPTION_SIGNATURE 0x4213
typedef char exception_t[1];

/* some_exceptions.h */

struct s_exception {
int signature;
int * exception_type;
struct s_exception * next;
/* Members for __FILE__, __LINE__, message, etc. */
};

Oops! I ought to have typed 'char * exception_type', given the
'typedef' above it.
 
D

Don Y

Hi Kaz,

Your build system could make an exception for that file. Adding an error code
to the header isn't a change that requires anything to be recompiled which does
not refer to that code, and those files which refer to the code will get
recompiled because they have been modified to refer to that code.

A deletion followed by an addition which re-uses a previous code is
troublesome, as is any renumbering operation.

Great! Wonderful! How are you going to address all of the *other*
criteria/criticisms I raised?

"Good news! We managed to set your broken leg! The cast
looks wonderful! And, everyone on the hospital staff has
taken time out of their busy day to sign it! Unfortunately,
the infection will (still) kill you by morning..."
 
I

Ian Collins

Regardless of the readability, the main merit of the fully parenthesized
macro is that it works as expected even if used like:

x = clamp(x | 0x0F, a& 0xF0, b ^ 0xA5) + 12;

The macro without the parentheses does not - and is therefore unsafe,
however much more 'readable' it is. (Granted, GCC warns about all sorts
of issues with the unparenthesized version, but those are best fixed by
parentheses in the macro definition and not by requiring parentheses in
the invocation.

Given x, a and b each ranging over the range 0..255, there are 30976 out
of 16777216 sets of values where the two clamp macros yield the same result.

#include<stdio.h>

#define clamp1(x,low,high) x< low ? low : x> high ? high : x
#define clamp2(x, low, high) ((x)< (low) ? (low) : (x)> (high) ?
(high) : (x))

int main(void)
{
for (int x = 0; x< 256; x++)
{
for (int a = 0; a< 256; a++)
{
for (int b = 0; b< 256; b++)
{
int y = clamp1(x | 0x0F, a& 0xF0, b ^ 0xA5) + 12;
int z = clamp2(x | 0x0F, a& 0xF0, b ^ 0xA5) + 12;
if (y != z)
printf("x=%.2X, a=%.2X, b=%.2X, y=%.2X, z=%.2X,\n",
x, a, b, y, z);
}
}
}
}

I don't think it is a valid excuse to say "but clamp() is supposed to be
used with double arguments"; macros are not type aware.

Yet another reason not to use function like macros.
 
D

Don Y

Hi Shao,

On 3/2/2012 01:05, Don Y wrote:
If you can't pack all of the information you wish to have
associated with an exception or error condition or whatever into an
'int', then perhaps you're better off allocating some status and
passing it around:

/* my.h */
/* A simple error code */
typedef int myerr_t;

/* Extended status information */
typedef struct s_status s_status;

struct s_status {
const char * message;
/* ... */
};

/* my.c */
myerr_t operation_foo(
int * param1,
char * param2,
somes_status * status
) {
myerr_t result;
s_status status_internal;

/*
* If the caller hasn't provided storage for status,
* then they're not interested. Use our own for
* called functions
*/
if (!status) {
status = &status_internal;
memset(status, 0, sizeof *status);
}
/* ... */
result = operation_bar(13, param2, status);
if (!MYERR_SUCCESS(result)) {
/* Actually, we have specific info for this case */
SetStatusMessage(status, "Bar failed while blargling");
SetStatusSourcePoint(status, __FILE__, __LINE__);
/* Twiddle the case for out-of-range major error type */
if (MYERR_TEST(result, MyErrOutOfRange))
SetStatusTwiddle(status);
goto err_bar;
}

/* Success! */
result = SetStatusGeneralSuccess(status);

err_bar:

return result;
}

[...and more...]

However, it's possible that you've already considered and dismissed
material akin to this response post's content. :)

<grin> I've tried to think hard about the sorts of housekeeping
to which any *manual* system would be vulnerable. Since I am
operating in a rather "flush" environment, I am planning on
using those resources to make the code and user interfaces
more robust and full-featured.

Just out of curiosity, did you skip over the 's_status' part of my
previous response, or dismiss it as Not The Right Strategy For You?

I'm trying to avoid complicating the discussion with the introduction
of mechanisms of passing "qualifying information" (what you call
"extended status information") up to the caller. This is a whole
'nother can of worms as each potential error/result could potentially
have its own idea as to "what's important/pertinent".

I think you've misunderstood or I've failed to communicate or both. :)

If I've read all of your posts correctly, including your older posts in
different threads, you're interested in satisfying the following
requirements for any exceptional condition:

- In which translation unit did the exception occur?

And exactly *where* it was "detected" by the code (i.e., which
conditional "failed")
- What was the nature of the exception?

You have to rely on the developer to add semantic value, there.
- How does is the low-level exception relevant as part of the
higher-level's usage?

Again, the upper level has to provide a context for explaining
this. To simply repeat a message from a lower level (usually)
results in an report that is too general and ignores the
semantics of the abstraction(s) in play *at* that higher level.
(remember, levels nest "indefinitely")
- Attach some human-readable text to the exception or allow for such a
look-up

And allow that text to reside wherever it is appropriate for a
particular implementation! E.g., why clutter TEXT space with
something that you could load from secondary storage (as needed)?
- Avoid manual accounting of the infinitude of possible exceptions in a
giant header file

And avoid manually having to write/maintain code that deals
with the *mechanisms* of this. I.e., in:

if (*ptr == '.') {
if (decimalPointEncountered == TRUE) {

MakeErrorCode(Err_TooManyDecimalPoints,
"Multiple decimal points encountered.",
"Only a single decimal point is allowed in numeric
values."
)

result = Err_TooManyDecimalPoints;
} else {
decimalPointEncountered = TRUE;
}
}
...
return (result);

the only "overhead" (i.e., extra keystrokes as well as cognitive
load) that the developer encounters is the cost of "MakeErrorCode".
He (presumably) would have had to check for multiple decimal points
even if he *didn't* clarify the exact nature of the "error"
(exception).

He doesn't have to pack structs to pass the error code and
message around. He doesn't have to track all the various
symbolic names he (and others) have chosen to represent these
"exceptions". he doesn't have to MANUALLY worry about collisions
in that namespace.

He concentrates on writing the code to implement the functionality
desired (as specified in the contract), detecting the exceptions
that are encountered (also in the contract) and DESCRIBING those
exceptions -- *here* (where their nature is most apparent) without
requiring those descriptions to *remain* "here" (in the deployed
code).
- Allow for a change in a called function, such as a new ability to
detect an exceptional condition, to be transparent to the caller, but
not prohibitive of the caller being able to be changed to detect this
new exception

To be able to *handle* the condition in a specific way, the caller
would have to be rewritten to explicitly see that "new" exception.
But, a legacy caller could still understand that "this is an error,
I don't understand the specifics of it but I *do* know it is an
error". And, the caller (or, some higher level acting in this
capacity) could still offer the explanation of the error to the user
without necessitating a rewrite.
Is that right? Are some requirements missing? It's a bit of a
"put-together."

You also have to be able to (easily) add L10N/I18N related variants
without having to modify any code -- changing a DESCRIPTION of an
exception shouldn't require new code (espeically per instance).

[Grrrr... I was just distracted by a friend's (banal) question
which caused me to forget what I was going to write! :< ]

As I said, I already *have* all this. Now, I am trying to have it
EFFICIENTLY! :>

E.g., the "common/shared header" forces all modules that reference
that header to be examined/rebuilt each time you modify it. (make(1)
is such a wonderful tool exactly BECAUSE it enforces this discipline
for you -- instead of requiring you to remember what needs to be
updated for each change).

You can omit the shared header and *somehow* come up with a
*careful* ordering of this module defines an error code that this
*other* module will need to be able to resolve/reference. But,
this means you spend your time constantly modifying makefiles
(or make depend) each time a module decides it wants to reference
an error code generated by another module.
I don't understand why the code example I provided above fails to
satisfy these.

You're exposing all of the mechanism (a *particular* mechanism!)
to the developer. And, forcing him to explicitly implement
that mechanism in his code.

Compare the example I offered to the minimal sort of "error
code reporting" equivalent that it *could* have been:

<some definition of Err_TooManyDecimalPlaces>
...
if (*ptr == '.') {
if (decimalPointEncountered == TRUE) {
result = Err_TooManyDecimalPoints;
} else {
decimalPointEncountered = TRUE;
}
}
...
return (result);

It's a lot easier to "get it right" when all of the mechanism
is hidden from you.

Embellishing a report becomes more involved as does the actual
*reporting* (to the user). But, each can easily degrade to
these simple cases.
If you're concerned about the distinction between 'myerr_t' and
's_status', then you can omit 'myerr_t' altogether.

There is nothing that I'm aware of to prevent something like an
's_status' from being a linkable node in a linked-list. A low-level
function can determine that "there are an invalid number of decimal
points when a decimal number is expected," then the calling function can
catch that and further attach that "the speed of the car is expected to
be a decimal number," and its calling function to catch that and further
attach that "the time taken to reach the destination cannot be computed,
given the input," etc.

Yes. I hide this in the mechanism (the example above doesn't show
how errors are "layered" -- it was intentionally a low level function
that just concentrated on exposing detail that is invariably *hidden*
inside those functions).
Again, if everything you need can't fit in an 'int', or you don't want
to manually account which 'int' values map to which errors, you can use
pointers. If you are concerned about constants, maybe you could consider
"address constants," where the compiler worries about accounting.

You can have:

/* exceptions.h */
#define EXCEPTION_SIGNATURE 0x4213
typedef char exception_t[1];

/* some_exceptions.h */

struct s_exception {
int signature;
int * exception_type;
struct s_exception * next;
/* Members for __FILE__, __LINE__, message, etc. */
};

struct s_exception_too_many_decimal_points {
struct s_exception common[1];
int decimal_points_found;
const char * copy_of_user_input;
};

extern exception_t ExceptionTooManyDecimalPoints;

Now a called function can produce and populate a 'struct
s_exception_too_many_decimal_points' object, attach it to a chain of one
or more exceptions (or status, or whatever else you might wish to call
it), and allow the caller to detect it:

/* caller.c */

void some_calling_func(
const char * user_data,
struct s_exception ** exception_list
) {
#define user_data "42..13" /* Dummy example */
struct s_exception * exception;
double d;

/* So don't bother with returning a value */
get_decimal_number(&d, user_data, exception_list);

for (exception = *exception_list;
exception;
exception = exception->next) {
/* Something odd happened */
if (exception->exception_type ==
ExceptionTooManyDecimalPoints) {
PushNewGeneralException(
exception_list,
"Speed must be a decimal number",
__FILE__,
__func__,
__LINE__
);
continue;
}
/* Handle other exceptions we understand */
/* ... */
/* Handle unknown case */
UnhandledException(
exception_list,
"Oh no!",
__FILE__,
__func__,
__LINE__
);
/* UnhandledException will terminate */
/* Can't reach here */
continue;
}

/* Check the range */
if (d < 0.0 || d > 200.0) {
PushNewGeneralException(
exception_list,
"Speed cannot be greater than 200.0",
__FILE__,
__func__,
__LINE__
);
}

return;
}

Again, you're exposing mechanism to the developer and forcing
him to actively adopt the details of that mechanism. I just
let you concentrate on passing things up the call tree (a
result_t is actually a list; you "(cons my_result lower_results)"
and return *that*. The caller sees "(car result)" and decides
if all went well *or* if he has to resolve this and pass it
along to the user (and, possibly, explore (cdr result) as well)

But, the mechanism for doing this is not exposed to the
developer. He doesn't care where the information comes from.
All he needs to know is how to extract it and augment it.
If the implementation *changed*, his code would not need to
be rewritten -- just recompiled.
 
D

Don Y

Hi Shao,

You can have:

/* exceptions.h */
#define EXCEPTION_SIGNATURE 0x4213
typedef char exception_t[1];

/* some_exceptions.h */

struct s_exception {
int signature;
int * exception_type;
struct s_exception * next;
/* Members for __FILE__, __LINE__, message, etc. */
};

Oops! I ought to have typed 'char * exception_type', given the 'typedef'
above it.

<grin> Understood. I read what you were trying to "say". I have more
important things to do than pick nits ;-)
 
I

Ian Collins

Hi,

[Please take the time to understand what I'm looking for before
replying. I've spent a lot of time describing it; it would
be courteous for you to make a similar investment before
commenting on it.]

[I think I have!]
I have a suite of projects that utilize a common approach to
error reporting. Instead of a PASS/FAIL result, most functions
return information regarding the nature of the "failure" (which
can also include "partial failures"... "warnings"). These
reports allow upper levels of the application to convey those
details to the user without exposing all of the *mechanism*
involved to those upper levels.

My usual technique for this class of problem is to build the tables in a
programmer and machine friendly format, typically XML in the guise of an
Open Office document.

The programmer(s) can easily maintain and version control the document
and the machine can easily perform validation of the data (one of my
teams developed an XML schema validation tool for this purpose:
http://sut.sourceforge.net) and generate the necessary headers.

A "solution" also needs to address the problem of *explaining*
the "result" (to the ultimate user). The point at which the
result is "signaled" contains the most information about the
nature of the "problem" being reported though it is in a
very narrowly defined context: i.e., you can tell the user
"Multiple decimal points encountered" -- though you can't
indicate the role that the value being entered plays in the
application (is this a person's wages? the weight of a
newborn child? the average lifespan of a male tortoise?).

Tales in an Office document are ideal for this. You can include as much
information is you want, Your code generation can select various
subsets of the data to present as user messages.
 
M

Malcolm McLean

Yet another reason not to use function like macros.
The problem is that clamp() is very likely to be in an inner loop. For
instance you might be ensuring that the pixels you write after merging
two images are in the range 0-255. So the function version that
operates on doubles isn't adequate, unless you can really trust your
compiler with aggressive optimisations.
 
I

Ian Collins

The problem is that clamp() is very likely to be in an inner loop. For
instance you might be ensuring that the pixels you write after merging
two images are in the range 0-255. So the function version that
operates on doubles isn't adequate, unless you can really trust your
compiler with aggressive optimisations.

s/aggressive/trivial/
 
M

Malcolm McLean

You can check to see if two error codes are equivalent
(whatever that means).  But, there are no ordering operators
to *rank* error codes.
But some are supersets of others. DISK_FULL is an IO_ERROR is a
SAVE_FAILED error. But of course IO_ERROR could also be a LOAD_FAILED
error. So there's not strict nesting, unless you carefully define it
to be so.
 
D

Don Y

Hi Malcolm,

But some are supersets of others. DISK_FULL is an IO_ERROR is a
SAVE_FAILED error. But of course IO_ERROR could also be a LOAD_FAILED
error. So there's not strict nesting, unless you carefully define it
to be so.

Yes. That's the responsibility of <whatever> detects the
"condition" and repackages it. There's no need (IMO) to
provide tools by which a developer can ask:
isSupersetOf(DISK_FULL, IO_ERROR);

The scheme I am outlining doesn't *eliminate* careful thought
regarding what an error *is* or how they should be structured.
Rather, it tries to automate the drudgery that would otherwise
discourage developers from spending *any* time trying to report
anything other than one of a *handful* of errors -- as NUMBERS!
 
D

Don Y

Hi Ian,

On 03/ 2/12 08:48 AM, Don Y wrote:

My usual technique for this class of problem is to build the tables in a
programmer and machine friendly format, typically XML in the guise of an
Open Office document.

The programmer(s) can easily maintain and version control the document
and the machine can easily perform validation of the data (one of my
teams developed an XML schema validation tool for this purpose:
http://sut.sourceforge.net) and generate the necessary headers.

Yes, but now you have to manually relate the messages to the
code from which they were "reasoned". It would be like keeping
the *comments* for your source code in a MSWord document (i.e.,
sure, you could *do* it but you KNOW the two documents will
quickly get out of sync as pressure has you updating one and
defering the effort of updating the other).

E.g., the approach I am taking here is analagous to how Literate
Programming couples documentation with source code (except I am
only tackling the "error reporting" aspect of the task).

s/Tales/Tables/ ?
in an Office document are ideal for this. You can include as much
information is you want, Your code generation can select various subsets
of the data to present as user messages.

I extract the error code, short message and long message (as well as
the locale indicator) and stuff those into a (real) database
(you could just as easily create a big array of struct's and
index/search that based on the "(error code, locale)" presented).

This moves all that "text" out of the "TEXT" section of the program
(it could reside on a different *processor*!). It also lets someone
non-technical make and review translations without having to understand
the actual code surrounding it (though the code acts as the final
arbiter of what the error message actually *should* convey).
 
D

Don Y

Hi Stefan,

When I say »the application does this and that«, this
includes the possibility that the application might use
library functions to accomplish parts of »this and that«.

However, the application is in charge of what those library
functions do, because it calls them to do certain thinks
(the library functions are just »vicarious agents« or
»auxiliary person«, they act on behalf of the application).
One says that Gustave Eiffel built the Eiffel Tower, while,
in reality, other people took part in that effort, too.

Sure, but the application is intentionally ignoring (delegating)
the details of that activity *to* it's underling (the "library",
in your example). The application doesn't *want* to know what
is involved in parsing a string for a particular token, etc.

The underling produces the result for the application; *or*,
complains that it was not *able* to produce the result.

I am advocating a mechanism whereby it is easy for the underling
to embellish that "go/no-go" indication with more specific/detailed
results *and* textual descriptions that the application can pass
on to the user, if asked.

You have some (medical) test performed. Chances are, your MD doesn't
perform it but delegates this to an agency that does it at his
request/direction. The agency returns the results to the doctor's
office (this seems to be pretty much the rule, here, for most tests).
The doctor reviews the result, makes notes in your file and tells
<someone> (nurse? office manager? receptionist??) what to report
to *you*, the patient.

Usually, what you hear is a terse summary of the result: "your
EKG was normal", "your blood sugar was elevated", "your ...".

For most/many folks, this is probably enough. It is often accompanied
by a recommended course of action: "cut down on your salt intake",
"get some exercise", etc.

For those of us who want more than a "summary" of the result, you
press that <someone> for more details: "How was the EKG abnormal?",
"What was my A1c?", etc.

That <someone> often has the answer to those questions readily
available (flip to the next page on your "chart") or can get back
to you with them in short order (some offices require the doctor
to decide exactly what the patient is told).

Imagine if the agency that performed the test ONLY gave the doctor
a go/no-go result. Or, if you were never allowed to "see" the
actual results. How would you *understand* the doctor's (secondhand
via that said:
When I write a (recursive-descend) parser, usually parsing
char by char is more easy and feels mor safe to me. For example:

(example is provided below as appendix 1.)


That is tha magic of layers: They are called »layers« for a
reason! They are opaque! So, when you look at a layer from
the top, you just see that layer and not possible layers
below it.

Sure! But layers have documented INTERFACES to the layer(s)
above/below. I am advocating including this sort of information
*in* that interface. Otherwise, you're stuck taking the word
of the agency that ran your medical test: "Your EKG was bad"

What's your remedy? Try changing your lifestyle in various ways
and run the test *again* -- HOPING for a "good" result?
In a sense, to any layer, there is always only one
layer: The layer /directly/ below it. It does not have to be
aware of the layer above it. And surely not of the layers
below the layer below it. So there are always just two
layers to consider. That is how layering reduces complexity!

Otherwise, they are not layers, but components or subsystems
or so.

For example, when writing TCP code, you are just concerned
with IP, not with the hardware layer below it.

So, if a packet can't get delivered, you don't care if the
reason is because a cable was unplugged, the network card
was MISSING, the heap ran dry, etc.?

"Please try again"

That's how users get annoyed when they do something that they
*think* is correct and are met with a message that presents
very little detail. They try again and get the same unhelpful
message.

Should they just try random actions in the hope that they
stumble on something that DOESN'T yield an error?

I bet you'd sure like to know that the network cable was
unplugged before you start checking CIDR settings, etc.

Or, troubleshooting your code...
 
S

Shao Miller


Good day again, Don!
And exactly *where* it was "detected" by the code (i.e., which
conditional "failed")

So '__FILE__', '__func__', '__LINE__' would satisfy that?
You have to rely on the developer to add semantic value, there.

Ok.


Again, the upper level has to provide a context for explaining
this. To simply repeat a message from a lower level (usually)
results in an report that is too general and ignores the
semantics of the abstraction(s) in play *at* that higher level.
(remember, levels nest "indefinitely")

Ok.


And allow that text to reside wherever it is appropriate for a
particular implementation! E.g., why clutter TEXT space with
something that you could load from secondary storage (as needed)?

Oh, so a 'const char *' to a message right in the code-base doesn't
satisfy that. You want some other kind of index, then.
And avoid manually having to write/maintain code that deals
with the *mechanisms* of this. I.e., in:

if (*ptr == '.') {
if (decimalPointEncountered == TRUE) {
MakeErrorCode(
Err_TooManyDecimalPoints,
"Multiple decimal points encountered.",
"Only a single decimal point is allowed"
" in numeric values."
)

result = Err_TooManyDecimalPoints;
} else {
decimalPointEncountered = TRUE;
}
}
...
return (result);

the only "overhead" (i.e., extra keystrokes as well as cognitive
load) that the developer encounters is the cost of "MakeErrorCode".
He (presumably) would have had to check for multiple decimal points
even if he *didn't* clarify the exact nature of the "error"
(exception).

I'm not sure that I follow, here. Are you saying that this is what you
want or don't want? Are you saying that ideally, 'MakeErrorCode' would
be a macro or extension that would allow for a translation process to
associate 'Err_TooManyDecimalPoints' with the provided text and position
in the code as a one-to-one mapping, and have it possible for the
additional detail to be stored apart from the final executable and not
embedded within it, and for 'Err_TooManyDecimalPoints' to be a unique value?
He doesn't have to pack structs

I'm not sure what "pack structs" means, exactly.
to pass the error code and
message around. He doesn't have to track all the various
symbolic names he (and others) have chosen to represent these
"exceptions". he doesn't have to MANUALLY worry about collisions
in that namespace.

I don't follow here, either. Are you saying that, in this example, the
'Err_TooManyDecimalPoints' in this function would not collide with
another function using 'Err_TooManyDecimalPoints' in a similar fashion?

If so, what would a caller use to detect that error?
He concentrates on writing the code to implement the functionality
desired (as specified in the contract), detecting the exceptions
that are encountered (also in the contract) and DESCRIBING those
exceptions -- *here* (where their nature is most apparent) without
requiring those descriptions to *remain* "here" (in the deployed
code).

Aha! This seems to answer one of the questions I had, above. You wish
for the additional detail associated with the exceptional condition to
be [possibly] stored _outside_ of the final resulting program. This
really seems like you could benefit from some extensions that allow you
to specify sections for things. Then a linker script can omit those
sections from the final resulting program and build something else from
those sections, used for error lookup.

Am I following your meaning, here? Are you doing this already?
To be able to *handle* the condition in a specific way, the caller
would have to be rewritten to explicitly see that "new" exception.
But, a legacy caller could still understand that "this is an error,
I don't understand the specifics of it but I *do* know it is an
error". And, the caller (or, some higher level acting in this
capacity) could still offer the explanation of the error to the user
without necessitating a rewrite.

Ok.


You also have to be able to (easily) add L10N/I18N related variants
without having to modify any code -- changing a DESCRIPTION of an
exception shouldn't require new code (espeically per instance).

Do you mean that the programmer uses their convenient "default" language
with 'MakeErrorCode', but the entire blob of additional error code
detail can be swapped with one for another language, etc.?
[Grrrr... I was just distracted by a friend's (banal) question
which caused me to forget what I was going to write! :< ]

As I said, I already *have* all this. Now, I am trying to have it
EFFICIENTLY! :>

Do you mean, you want a rebuild process to rebuild only those things
whose detail/dependencies have changed, and not the entire code-base?
E.g., the "common/shared header" forces all modules that reference
that header to be examined/rebuilt each time you modify it. (make(1)
is such a wonderful tool exactly BECAUSE it enforces this discipline
for you -- instead of requiring you to remember what needs to be
updated for each change).

You can omit the shared header and *somehow* come up with a
*careful* ordering of this module defines an error code that this
*other* module will need to be able to resolve/reference. But,
this means you spend your time constantly modifying makefiles
(or make depend) each time a module decides it wants to reference
an error code generated by another module.

This sure reminds me of iPXE's logic for dealing with similar challenges.
You're exposing all of the mechanism (a *particular* mechanism!)
to the developer. And, forcing him to explicitly implement
that mechanism in his code.

Well I figured you could use macros and other tricks to hide the
details, if you like. I was just trying to demonstrate how it might
look underneath macros.
Compare the example I offered to the minimal sort of "error
code reporting" equivalent that it *could* have been:

<some definition of Err_TooManyDecimalPlaces>
...
if (*ptr == '.') {
if (decimalPointEncountered == TRUE) {
result = Err_TooManyDecimalPoints;
} else {
decimalPointEncountered = TRUE;
}
}
...
return (result);

It's a lot easier to "get it right" when all of the mechanism
is hidden from you.

So what are you saying, that that's what you want to be able to type,
without using the 'MakeErrorCode' at all? Or are you saying that the
'MakeErrorCode' inclusion is about as far away from this type of pattern
as you're willing to get?
Embellishing a report becomes more involved as does the actual
*reporting* (to the user). But, each can easily degrade to
these simple cases.
If you're concerned about the distinction between 'myerr_t' and
's_status', then you can omit 'myerr_t' altogether.

There is nothing that I'm aware of to prevent something like an
's_status' from being a linkable node in a linked-list. A low-level
function can determine that "there are an invalid number of decimal
points when a decimal number is expected," then the calling function can
catch that and further attach that "the speed of the car is expected to
be a decimal number," and its calling function to catch that and further
attach that "the time taken to reach the destination cannot be computed,
given the input," etc.

Yes. I hide this in the mechanism (the example above doesn't show
how errors are "layered" -- it was intentionally a low level function
that just concentrated on exposing detail that is invariably *hidden*
inside those functions).

Ok.
Again, if everything you need can't fit in an 'int', or you don't want
to manually account which 'int' values map to which errors, you can use
pointers. If you are concerned about constants, maybe you could consider
"address constants," where the compiler worries about accounting.

You can have:

/* exceptions.h */
#define EXCEPTION_SIGNATURE 0x4213
typedef char exception_t[1];

/* some_exceptions.h */

struct s_exception {
int signature;
int * exception_type;
struct s_exception * next;
/* Members for __FILE__, __LINE__, message, etc. */
};

struct s_exception_too_many_decimal_points {
struct s_exception common[1];
int decimal_points_found;
const char * copy_of_user_input;
};

extern exception_t ExceptionTooManyDecimalPoints;

Now a called function can produce and populate a 'struct
s_exception_too_many_decimal_points' object, attach it to a chain of one
or more exceptions (or status, or whatever else you might wish to call
it), and allow the caller to detect it:

/* caller.c */

void some_calling_func(
const char * user_data,
struct s_exception ** exception_list
) {
#define user_data "42..13" /* Dummy example */
struct s_exception * exception;
double d;

/* So don't bother with returning a value */
get_decimal_number(&d, user_data, exception_list);

for (exception = *exception_list;
exception;
exception = exception->next) {
/* Something odd happened */
if (exception->exception_type ==
ExceptionTooManyDecimalPoints) {
PushNewGeneralException(
exception_list,
"Speed must be a decimal number",
__FILE__,
__func__,
__LINE__
);
continue;
}
/* Handle other exceptions we understand */
/* ... */
/* Handle unknown case */
UnhandledException(
exception_list,
"Oh no!",
__FILE__,
__func__,
__LINE__
);
/* UnhandledException will terminate */
/* Can't reach here */
continue;
}

/* Check the range */
if (d < 0.0 || d > 200.0) {
PushNewGeneralException(
exception_list,
"Speed cannot be greater than 200.0",
__FILE__,
__func__,
__LINE__
);
}

return;
}

Again, you're exposing mechanism to the developer and forcing
him to actively adopt the details of that mechanism.

Would it be difficult to wrap this up with a macro and call that macro
'MakeErrorCode'? Or is that what you're doing?
I just
let you concentrate on passing things up the call tree (a
result_t is actually a list; you "(cons my_result lower_results)"
and return *that*. The caller sees "(car result)" and decides
if all went well *or* if he has to resolve this and pass it
along to the user (and, possibly, explore (cdr result) as well)

Sorry, is that LISP?
But, the mechanism for doing this is not exposed to the
developer. He doesn't care where the information comes from.
All he needs to know is how to extract it and augment it.
If the implementation *changed*, his code would not need to
be rewritten -- just recompiled.

So taking the example of 'Err_TooManyDecimalPlaces':

#1 Before that is ever used in any code anywhere, you build your code.
Then one day you come along and realize it's an exceptional condition
that you'd like to be able to document and catch. So are you saying
you'd like to be able to make a minimal change to 'whatever.c' where you
introduce the exception and associate additional detail (documentation)
with it, and not have to recompile any of the other translation units
(roughly: .c files) files but simply be able to re-link and have the
"additional detail database" updated accordingly?

#2 You realize that a function in 'caller.c' that calls the function in
'whatever.c' ought to catch the 'Err_TooManyDecimalPlaces' condition and
add some valuable detail to how that condition is relevant. You want to
be able to test the return-value (or some result) of the called function
with 'Err_TooManyDecimalPlaces' without having to add
'Err_TooManyDecimalPlaces' to a header where every other TU #including
that header will require recompilation? If so, perhaps you could
satisfy this by having a one-to-one relationship between exceptional
condition and header file.

Did you consider "address constants" at all? This avoids a progressive
ordering of strict numeric codes, while still allowing for a symbolic
identifier and comparison for equality.

If I'm following along, perhaps I can dig up the relevant portions of
iPXE and provide examples of techniques you might be interested in.
What implementations are you interested in compatibility with? GCC? ICC?
 
S

Stefan Ram

Don Y said:
The underling produces the result for the application; *or*,
complains that it was not *able* to produce the result.

When you say »result«, you must think of value function, i.e.,

T name( parameter )

, where T is/ not/ void, so »name( args )« has a value, which is
the result.

In such a case, I deem the function to /always/ produce a result.
This might include a success and a status code, e.g.,

struct result r = parseint( string );
if( r.success )... r.value ... else ... r.status ...

So, the »parseint« function above /never/ fails.
It always returns a result according to its specifications.
I am advocating a mechanism whereby it is easy for the underling
to embellish that "go/no-go" indication with more specific/detailed
results *and* textual descriptions that the application can pass
on to the user, if asked.

And which natural language is used for those textual
descriptions?
So, if a packet can't get delivered, you don't care if the
reason is because a cable was unplugged, the network card
was MISSING, the heap ran dry, etc.?

This is not part of the TCP code, but might be handled in
another layer (usually in a lower layer).
 
I

Ian Collins

Hi Ian,



Yes, but now you have to manually relate the messages to the
code from which they were "reasoned". It would be like keeping
the *comments* for your source code in a MSWord document (i.e.,
sure, you could *do* it but you KNOW the two documents will
quickly get out of sync as pressure has you updating one and
defering the effort of updating the other).

Not at all.

First the user or log messages have to be documented somewhere, for user
documentation and testing. Scattering them throughout the code isn't a
great way to do that.

Second, the document is the *source* for the messages and error codes,
so it will always be up to date.
E.g., the approach I am taking here is analagous to how Literate
Programming couples documentation with source code (except I am
only tackling the "error reporting" aspect of the task).

In my case, the documentation *is* part of the source code.
s/Tales/Tables/ ?
:)


I extract the error code, short message and long message (as well as
the locale indicator) and stuff those into a (real) database
(you could just as easily create a big array of struct's and
index/search that based on the "(error code, locale)" presented).

This moves all that "text" out of the "TEXT" section of the program
(it could reside on a different *processor*!). It also lets someone
non-technical make and review translations without having to understand
the actual code surrounding it (though the code acts as the final
arbiter of what the error message actually *should* convey).

Our approaches appear to be opposite. You extract messages from the
code, I inject them into the code (and the tests and user documentation).
 
D

Don Y

Hi Shao,
So '__FILE__', '__func__', '__LINE__' would satisfy that?

Ignoring those compilers that don't expand __FILE__ to the full path
of the file, this works. In an earlier implementation, I used a
hash of these three to generate the actual "error code".

The bigger problem is programmer discipline (something that I
obviously don't trust :> )

E.g., the programmer is under no compulsion to invoke MakeErrorCode
in a "meaningful place". So, he could choose to put all of these
"invocations" (for want of a better word) at the very top of the
file. In which case, the __LINE__ and _func_ don't evaluate to
anything "useful".

Similarly, there is nothing to force a developer to differentiate
between conditionals that result in a particular error code being
returned.

E.g.,

MakeErrorCode(foo...)
// Note that the FILE/LINE/func point to the above line, which may
// not be *any* of the following conditionals!
...
...
if (value > 27)
return foo;
...
if (value < 36)
return foo;
...
if (today == tuesday)
return foo;

Of course, there are countless ways of arranging control structures
to result in this same sort of behavior.

I had considered turning MakeErrorCode(error, ...) into a legitimate
*statement* that resolved to:
return error;
This would *ensure* that the error code identified the exact place
where the error was "returned". But, it still doesn't avoid the
above problem:

while (forever) {

if (value > 27)
break;
...
if (value < 36)
break;
...
if (today == tuesday)
break;

}
MakeErrorCode(foo, ...)

There are many other limitations that this imposes on coding style,
as well.

[I've not come up with a way to force developers to "do the right
thing"]
Oh, so a 'const char *' to a message right in the code-base doesn't
satisfy that. You want some other kind of index, then.

Correct. My first approach generated "static const char *" to embed
the error messages in the modules. The sizes grow too quickly. I
then tried parameterizing "MakeErrorCode" so that I could specify
*where* (segment) to store the messages. Same problem only different.

Finally, I decided the messages need not have any relationship to
the code *other* than the "error code" which acts as a handle/index
to tie everything together.

So, I could pull all of this text out of the "executable" and
load it as needed, on demand. Application starts up faster and
has a smaller footprint. "Errors are the exception, not the norm"
I'm not sure that I follow, here. Are you saying that this is what you
want or don't want? Are you saying that ideally, 'MakeErrorCode' would
be a macro or extension that would allow for a translation process to

Exactly ----------------------------------------^^^^^^^^^^^^^^^^^^^
associate 'Err_TooManyDecimalPoints' with the provided text and position
in the code as a one-to-one mapping, and have it possible for the
additional detail to be stored apart from the final executable and not
embedded within it, and for 'Err_TooManyDecimalPoints' to be a unique
value?

Yes. (More follows)

The most important thing is for this to be REALLY painless for the
developer. If he has to write code to manipulate structures he's
just going to take the easy road and have *every* error handled
by a single "error exit".

He still has to implement the functionality to check for those
error conditions (i.e., he can't allow a string of characters
with two decimal points to be accepted as a valid numeric value!).
But, if he has to do "something extra" to report this as a
*different* "error" than "Err_MissingSign" or "Err_MultipleSigns",
he's more likely to just settle on "Err_BadValue" and lump all
of those "situations" into a single error report:

"There's something wrong with the value you typed in"

[I think most people are lazy when it comes to anything beyond
"getting the code to work"]

As regarding "uniqueness", yes, all the error codes should exist
in a single namespace. In something like C++ you could easily
support multiple namespaces -- so each "library" could implement
its own "error code-space". You can do similarly in C (with a bit
of work). But, in each case, you would then have to qualify
Err_<whatever> with an indication of the namespace in which it
was defined.

I thought to tackle this with FILE/LINE/func but how does the
*developer* indicate that he wants the "Err_Overflow" that
is defined by "doubleMath.c/27/dadd" and not the one that is
defined by "networkDriver.c/85/enqueuePacket"?

It was easier to treat file/line/func as *data* that tags along
with the error code instead of making it *part* of the error code.

Stepping ahead to answer your question a few (of your) lines
further along:

In a single namespace, you run the risk of two (independant)
developers picking the same name for an error code. Just
like they could pick the same name for a function, global,
etc.

<someone> sets policy to ensure this doesn't happen. (e.g.,
all my names begin with "don_").

What I want from this error scheme (the question you pose below)
is for the mechanism to tell me if two people have chosen the
same name. I.e., if there are two error codes that I can't
distinguish *solely* based on their names. (since I want to
use *just* the name as the index into the "list of explanations",
etc.).

If you build a table of #defines (or equivalent), then the
compiler can tell you if you have redefined a symbol. But,
you may have split that table into smaller tables so that not
all of the error code "symbols" are in one place! You might
choose to do this to better manage the namespace (so you
can develop separate subsystems/libraries without having to
coordinate your choice of error names *in* a file). Or, as
an efficiency hack (so all files that refer to ANY error code
don't have to parse the entire list of error codes). Or, to
reduce the number of make dependencies (foo.c only depends
on matherrorcodes.h instead of allerrorcodes.h). Etc.

If you've split them, you need a special step to reconcile
them against each other to ferret out potential duplicates.
If you forget to do this, you run the risk of ambiguity in
an error report!
I'm not sure what "pack structs" means, exactly.

Building/populating data structures with explicit values
that can then be reported, etc. like your:
memset(status, 0, sizeof *status);
SetStatusMessage(status, "Bar failed while blargling");
SetStatusSourcePoint(status, __FILE__, __LINE__);
etc. What happens if the developer forgets to take some
or all of these steps? (Or, if he's just lazy!)
I don't follow here, either. Are you saying that, in this example, the
'Err_TooManyDecimalPoints' in this function would not collide with
another function using 'Err_TooManyDecimalPoints' in a similar fashion?

If so, what would a caller use to detect that error?

These were the questions I anticipated above.
He concentrates on writing the code to implement the functionality
desired (as specified in the contract), detecting the exceptions
that are encountered (also in the contract) and DESCRIBING those
exceptions -- *here* (where their nature is most apparent) without
requiring those descriptions to *remain* "here" (in the deployed
code).

Aha! This seems to answer one of the questions I had, above. You wish
for the additional detail associated with the exceptional condition to
be [possibly] stored _outside_ of the final resulting program. This
really seems like you could benefit from some extensions that allow you
to specify sections for things. Then a linker script can omit those
sections from the final resulting program and build something else from
those sections, used for error lookup.

Yes. As I said, I tried that in one incarnation of MakeErrorReport.
I have moved to the point where I now have different tools to
extract different bits of information from the sources. Much like
LP's tangle and weave

(see http://en.wikipedia.org/wiki/Literate_programming)

Since the error *messages* don't usually need to be updated
(i.e., available in the run-time) during debugging, this lets
me skip a fair bit of processing while I am concentrating on
"getting the code right".
Am I following your meaning, here? Are you doing this already?


Do you mean that the programmer uses their convenient "default" language
with 'MakeErrorCode', but the entire blob of additional error code
detail can be swapped with one for another language, etc.?

MakeErrorCode will (eventually... I only speak one language -- and
poorly at that! :> ) allow a developer to add "tags" to each
message. I.e., "This is the en_US version of the short error
message for this error code." "This is the pt_BR version of
the long error message for that same error code."

In theory, you could do this "elsewhere" -- i.e., have someone
browse the "master error message table" and add columns for
each supported language, doing the translation *there*. But,
you would then probably want a mechanism for "back porting"
that information to the source files (so that the source files
become the authoritative reference for ALL versions of the
messages). You'd hate to have to rehire that same translator
*next* project to tell you, AGAIN, what the spanish phrase for
"out of memory" is...

[Note that you could always configure the "translation process(or)"
(your term) to only extract en_US messages (for a US market) or
some *combination* for an international market.]
[Grrrr... I was just distracted by a friend's (banal) question
which caused me to forget what I was going to write! :< ]

As I said, I already *have* all this. Now, I am trying to have it
EFFICIENTLY! :>

Do you mean, you want a rebuild process to rebuild only those things
whose detail/dependencies have changed, and not the entire code-base?

Yes! I'm using this in *really* big projects and can't afford to
wait a day to "make world" each time some new error code is created,
deleted, changed, etc.

Again, people are lazy (I am a people! :> ) If you waste a day
because of some mechanism, you will quickly learn NOT to use it!
This sure reminds me of iPXE's logic for dealing with similar challenges.

But that (speaking from ignorance, here) handled the problem by
partitioning the error code. I.e., math routines could use
errors 0x2340 thru 0x234F; disk I/O routines could use 0x2350 - 0x2357,
etc. (?)

So, an arbitrary source file could #include (or manually synthesize!)
a particular "SUBSYSTEMerrorcode.h" without concern for how or where
the errors were being signaled in the modules that made up that
SUBSYSTEM.
Well I figured you could use macros and other tricks to hide the
details, if you like. I was just trying to demonstrate how it might look
underneath macros.

Understood. But, that would intimidate the developer. He'd quickly
rationalize why he "doesn't need to report error details". If,
instead, all he has to do is *differentiate* individual error
"instances" and give a description of what they signify/indicate,
it gets hard for him to justify NOT doing this. Especially when
it is easy for all of his peers to do so. Reliably!

"Gee, Bob, how come those aspects of the product/program that
you coded all give 'error 27' messages and nothing more? Alice's
part gives all sorts of detail explaining the exact nature of
each DIFFERENT problem! People are having a hard time using
*your* features..."
So what are you saying, that that's what you want to be able to type,
without using the 'MakeErrorCode' at all? Or are you saying that the
'MakeErrorCode' inclusion is about as far away from this type of pattern
as you're willing to get?

I'm saying that the above is the bare minimum that a developer
implementing a "parse string for numeric value" function would
have to code (well, he could technically treat ALL errors as
BAD_VALUE -- which might be a *tiny* bit less complex).

Then, I'm saying "compare this BARE MINIMUM to what I am proposing".
What I am *imposing* on the developer really doesn't look like a
whole helluvalot!
"You walked all the way to the grocery store and you
couldn't bring back a LOTTERY TICKET for me???"

The less I impose, the harder it is for folks to rationalize
not complying.

I.e., if "Alice's" (above) parts of the project are slicker'n snot
BUT TOOK HER MONTHS LONGER THAN NECESSARY to add that extra
reporting functionality, then Bob can point this out as yet
another justification for NOT doing things that way! That
"lottery ticket" doesn't really take up much space in your pocket
as you walk back from the grocery store! :>

[your code elided as I fear I am already reaching the post-length
limit :< ]
Would it be difficult to wrap this up with a macro and call that macro
'MakeErrorCode'? Or is that what you're doing?

MakeErrorCode just shows the "translation processor" where the
information regarding the error code resides in the source file.
(I.e., it makes my life -- in writing that processor -- easier).
It doesn't generate *any* code (in the examples I've shown so
far). All it does (for the source file) is ensure that
"Err_Whatever" can be resolved by the compiler.

It provides information to the translation processor(s) that
allow them to harvest the various error messages, file/line/func
markers, error code name, etc. for use elsewhere (wherever I
opt to store the error messages... maybe a file full of
const char *'s!)
Sorry, is that LISP?

Yes, sorry. cons glues the two arguments together (into a tuple,
of sorts). car extracts the first element from the tuple.
cdr extracts the second element.

I.e., result is a *list*. The list can be seen as an element
followed by a (shorter) list... which can be seen as another
element followed by a (even shorter!) list...
So taking the example of 'Err_TooManyDecimalPlaces':

#1 Before that is ever used in any code anywhere, you build your code.
Then one day you come along and realize it's an exceptional condition
that you'd like to be able to document and catch. So are you saying
you'd like to be able to make a minimal change to 'whatever.c' where you
introduce the exception and associate additional detail (documentation)
with it, and not have to recompile any of the other translation units
(roughly: .c files) files but simply be able to re-link and have the
"additional detail database" updated accordingly?

Correct. Before that "one day", you can grep the sources and
never turn up a reference to Err_TooManyDecimalPlaces (I should
have picked a shorter name! :< ).

On that "one day", you add a MakeErrorCode(Err_TooManyDecimalPlaces,...)
to whatever.c. Of course, whatever.c has now changed so *it* has to
be recompiled (presumably, you also referred to Err_TooManyDecimalPlaces
somewhere inside whatever.c -- in addition to "defining" it.

But, since no one else references Err_TooMany..., no one else should
NEED to be recompiled!

Yet, any functions that call the function in whatever.c that can
*return* Err_TooMany... will now recognize that this is STILL an
error code (i.e., whateverFunction() has returned yet another
particular error code!) AND, if asked to expound on the nature
of that error, will be able to lookup the message associated
with Err_TooMany... and provide it to the user.
#2 You realize that a function in 'caller.c' that calls the function in
'whatever.c' ought to catch the 'Err_TooManyDecimalPlaces' condition and
add some valuable detail to how that condition is relevant. You want to
be able to test the return-value (or some result) of the called function
with 'Err_TooManyDecimalPlaces' without having to add
'Err_TooManyDecimalPlaces' to a header where every other TU #including
that header will require recompilation?

Correct. caller.c should just be able to refer to Err_TooMany...
without a whole lot of fuss.
If so, perhaps you could satisfy
this by having a one-to-one relationship between exceptional condition
and header file.

Did you consider "address constants" at all? This avoids a progressive
ordering of strict numeric codes, while still allowing for a symbolic
identifier and comparison for equality.

EXACTLY!!!!! This is the approach I am now trying.

In essence, the Err_ are treated as extern's. So, the compiler
doesn't care that they aren't (yet) defined. They just have
to be resolvable at linkage time.

Instead, the linkage editor resolves and binds them to their actual
values (I have to fabricate a dummy errorcodes.c that instantiates
all of the Err_'s as "addresses").

But, it (appears to?) satisfy those goals of minimizing recompiles,
etc. Though I still have to "process" the source file to determine
which Err_'s are defined within (or, force the developer to explicitly
declare them in global scope). Duplicate names are detected by
the linker, etc. And, I can get a cross reference map showing what
is referenced, where.

Unfortunately (?) it means sizeof(result_t) is now the same as
that of a void *. I guess I could artificially map them anywhere
in the address space and play pointer math games to ensure
SUCCESS, warn and error are easily derived from the resulting
values. That way I could shrink them to sizeof(short int), for
example...

I'm still chewing on this approach (busy coding something else
at the moment) but I can't see any insurmountable problems so
far...
 
D

Don Y

Hi Ian,

Not at all.

First the user or log messages have to be documented somewhere, for user
documentation and testing.

Yes. I document them IN THE CODE.
Scattering them throughout the code isn't a
great way to do that.

Why? It ensures EVERY error code is documented. And, it keeps that
proximate to the actual code itself. *This* is what the code is
testing for... not something else that you THINK it is enforcing!
Second, the document is the *source* for the messages and error codes,
so it will always be up to date.

What happens when an error code is never referenced? How does
your document ELIDE that error code and its documentation?
In my case, the documentation *is* part of the source code.

Fair enough. You should look at Literate Programming for a
different take on this (see URL later)

<grin> I stared at that for a LONG time trying to figure out what
MS had come up with (I don't use Office). Finally slapped my head
(d'uh!) to realize it had to be a typo! :>
Our approaches appear to be opposite. You extract messages from the
code, I inject them into the code (and the tests and user documentation).

Fair enough. My approach is "inspired" by Literate Programming
(http://en.wikipedia.org/wiki/Literate_programming). In either
approach, you need to have a tool/utility massage a file (or
files) to generate the actual "source" and supporting files.

I bundle it in the source because I can just let MakeErrorCode
be a macro that doesn't alter the source (Literate Programming
requires the source to be "extracted" -- "tangled").
 
D

Don Y

Hi Stefan,

When you say »result«, you must think of value function, i.e.,

T name( parameter )

, where T is/ not/ void, so »name( args )« has a value, which is
the result.

No. I mean "result" as in "product of the computation/activity".
That result might be moving an actuator, computing a value, etc.

A function (apologies to Malcolm if I am playing fast-and-loose
with terms, here) can also return a "result" to the caller.
It can do this by way of having a "type" consistent with that
result (e.g., atof() returns a "double" as its result). Or,
it can have in/out parameters and pass its results back to
the caller that way (e.g., asciitofloat(... &result) ). Or,
it's result can be examined by something shared that both
caller and callee can access (e.g., a global datum manipulated
by one and observed by the other).
In such a case, I deem the function to /always/ produce a result.
This might include a success and a status code, e.g.,

struct result r = parseint( string );
if( r.success )... r.value ... else ... r.status ...

So, the »parseint« function above /never/ fails.
It always returns a result according to its specifications.

See above. "result" is an overloaded term.
And which natural language is used for those textual
descriptions?

Whatever is appropriate for the project/design team.
E.g., if I am making a product for the Spanish market, I
would want those messages to be in spanish!
This is not part of the TCP code, but might be handled in
another layer (usually in a lower layer).

Which means those lower layers communicate *other* information
(that the TCP stack chooses to ignore) to some other daemon
watching for it. In either case, the information HAS TO
exist and has to be reported (if you ever want to know that
the cable is unplugged! Note the goal of the mechanism I
am describing is to *increase* the amount of information
available to the user. It's got to come from *somewhere*!)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top