implementation details

M

ma740988

How would I modify the TEST_CMD function to essentially return the
appropriate status?

Simply put. TEST_CMD could return either status1 or status2. The
return value will be passed to the Write function inside main. The
prototype for the Write function is highlighted below.

#define TRUE 1
#define FALSE 0

typedef struct
{
unsigned int field1 : 16;
unsigned int field2 : 10;
unsigned int field3 : 6;
} HEADER;

typedef struct
{
HEADER header;
unsigned int cmd_field1 : 1;
unsigned int cmd_field2 : 8;
unsigned int cmd_field3 : 16;
} CMD;

typedef struct
{
unsigned int status1_field1 : 1;
unsigned int status1_field2 : 32;
unsigned int status1_field3 : 5;
unsigned int status1_field4 : 27;
} STATUS1;

typedef struct
{
unsigned int status2_field1 : 1;
unsigned int status2_field2 : 31;
unsigned int status2_field3 : 16;
unsigned int status2_field4 : 16;
} STATUS2;

// 4 more. ie STATUS3, STATUS4, STATUS5, STATUS6

CMD cmd_msg;
STATUS1 status1;
STATUS2 status2;
//STATUS3 status3;
//STATUS4 status4;

void TEST_CMD ( const CMD *ptr_cmd ) // return type on this obviously
cant be 'void'
{
if ( ptr_cmd->header.field1 == 0 )
{
status1->status1_field1 = TRUE;
// return status1;
}
else if ( ptr_cmd->header_field2 != 0 )
{
status2->status2_field1 = FALSE;
// return status2;
}
// 4 more
}


int main ()
{
int channel;

//TEST_CMD ( &cmd_msg);

// The prototype for Write_Msg looks like
// Write_Msg ( int chan,
// void* src // pointer to the appropriate
data
// int len );

// So now assume channel 3.
channel = 3;
// Write_Msg ( channel, // return value from TEST_CMD,
// sizeof(return value from TEST_CMD) );

}

For a single STATUS object I could easily return a copy inside
TEST_CMD or perhaps pass a pointer of the object to TEST_CMD and
'write' to that pointer inside TEST_CMD.
NOTE: I cant modify any of the structs (STATUS/CMD/HEADER struct) to
add an addition 'bit field'. The bit fields form a part of a message
interface defined in a document which makes this interesting
 
A

Arthur J. O'Dwyer

How would I modify the TEST_CMD function to essentially return the
appropriate status?

Using a pointer to void, or a struct type. See details below
my little nitpicky comments.

#define TRUE 1
#define FALSE 0

Evil evil evil! The *only* reason to #define TRUE is so you can
use it instead of 1; but to the average human C programmer, and to
the compiler too, the concept of "TRUE" does *not* mean "1"; it means
"anything non-zero"! Compare

if (my_boolean_value) { ... }

if (my_boolean_value == TRUE) { ... }

These two statements are different in their effects! That's just
a bug waiting to happen. My advice (which of course you're within
your rights to reject on the ground that you're just the maintainer :)
is to ditch the #defines.

typedef struct
{
unsigned int status1_field1 : 1;
unsigned int status1_field2 : 32;

And just a heads-up here: Are you *sure* you know what you're
doing here? Most compilers I know, I think, would put 'field1' in
one byte and 'field2' in the next, not pack them right up together
as you seem to be assuming. But if it ain't broke, and you aren't
complaining, I certainly won't try to fix it. Fixing it would be
hard, anyway.
unsigned int status1_field3 : 5;
unsigned int status1_field4 : 27;
} STATUS1;

typedef struct
{
unsigned int status2_field1 : 1;
unsigned int status2_field2 : 31;
unsigned int status2_field3 : 16;
unsigned int status2_field4 : 16;
} STATUS2;

// 4 more. ie STATUS3, STATUS4, STATUS5, STATUS6

This kind of comment (// foo) is valid only in C++ and post-1999 C.
If you're writing C++, maybe you should try comp.lang.c++ just in
case they know a clever way of doing this in that language.
CMD cmd_msg;
STATUS1 status1;
STATUS2 status2;
//STATUS3 status3;
//STATUS4 status4;

void TEST_CMD ( const CMD *ptr_cmd ) // return type on this obviously
cant be 'void'

Right. Three -- no, four -- options immediately suggest themselves:

1. Return an 'int' or an 'enum'. This is almost certainly the most
straightforward solution to your stated problem, but there's bound to
be some complication you didn't mention. ;-)

int TEST_CMD ( const CMD *ptr_cmd );
/* return 1 if it's status1, 2 if it's status2, ... */

int rc = TEST_CMD(&cmd_msg);
switch (rc) {
case 1: Write_Msg(channel, &status1, sizeof status1); break;
case 2: Write_Msg(channel, &status2, sizeof status2); break;
case 3: Write_Msg(channel, &status3, sizeof status3); break;
case 4: Write_Msg(channel, &status4, sizeof status4); break;
case 5: Write_Msg(channel, &status5, sizeof status5); break;
case 6: Write_Msg(channel, &status6, sizeof status6); break;
};

2. Return a pointer to the appropriate object. But we must
assume that all the objects are the same size, to really get any
advantage out of this option.

#define STATUS_SZ 8 /* or whatever */

void const *TEST_CMD ( const CMD *ptr_cmd );
/* return &status1 if it's status1, &status2 if it's status2, ... */

void const *p = TEST_CMD(&cmd_msg);
Write_Msg(channel, p, STATUS_SZ);

3. Return a special 'struct' type consisting of size and value.
This has the advantage of being extensible more easily than #1 or #2,
but the disadvantage of being slow and ugly. :) Passing and returning
a struct by value can be slow. You could, however, return a pointer
to a local 'static' object of type 'TEST_RC', and access it through
that pointer, if you minded your pointers carefully.

typedef struct {
void *status;
size_t size;
} TEST_RC;

TEST_RC TEST_CMD ( const CMD *ptr_cmd );
/* return a struct TEST_RC, properly filled out, by value */

TEST_RC rc = TEST_CMD(&cmd_msg);
Write_Msg(channel, rc.status, rc.size);

/* or, using the static object... */

TEST_RC *TEST_CMD ( const CMD *ptr_cmd );

TEST_RC *rc = TEST_CMD(&cmd_msg);
Write_Msg(channel, rc->status, rc->size);
/* *rc will be overwritten on the next call to 'TEST_CMD' */
/* so take precautions now */
rc = NULL;

4. Return the data in special parameters.

void TEST_CMD ( const CMD *ptr_cmd, void **result, size_t *size );
/* *result = status1, *size = sizeof status1; et cetera */

void *p;
size_t sz;
TEST_CMD(&cmd_msg, &p, &sz);
Write_Msg(channel, p, sz);

NOTE: I cant modify any of the structs (STATUS/CMD/HEADER struct) to
add an addition 'bit field'. The bit fields form a part of a message
interface defined in a document which makes this interesting

Well, I hope this gives you some ideas to think about.

-Arthur
 
M

ma740988

4. Return the data in special parameters.

void TEST_CMD ( const CMD *ptr_cmd, void **result, size_t *size );
/* *result = status1, *size = sizeof status1; et cetera */

void *p;
size_t sz;
TEST_CMD(&cmd_msg, &p, &sz);
Write_Msg(channel, p, sz);

I'll try to exercise option 4, but first I should preface this by
stating i'm not close to a compiler right now. So in an attempt to
ensure i have the 'cast' right on the void pointer, for completeness
we have

void TEST_CMD ( const CMD *ptr_cmd, void **result, size_t *size )
{
if ( ptr_cmd->header.field1 == 0 )
{
status1->status1_field1 = TRUE;
// we got a STATUS1 now write it to result
(STATUS1)*result = status1; // this is goodness??
*size = sizeof ( status1 );

}
else if ( ptr_cmd->header_field2 != 0 )
{
status2->status2_field1 = FALSE;
// return status2;
} // 4 more
}


I truly appreaciate your assistance.
 
A

Arthur J. O'Dwyer

I'll try to exercise option 4, but first I should preface this by
stating i'm not close to a compiler right now. So in an attempt to
ensure i have the 'cast' right on the void pointer, for completeness
we have

void TEST_CMD ( const CMD *ptr_cmd, void **result, size_t *size )
{
if ( ptr_cmd->header.field1 == 0 )
{
status1->status1_field1 = TRUE;
// we got a STATUS1 now write it to result
(STATUS1)*result = status1; // this is goodness??

Nope. Much simpler, and no cast. Casts are EVIL and should
be avoided, even when you're mucking around with void pointers
and really *want* to cast things, I know... ;-)

*result = &status1;

*result is of type (void*); let's just assign &status1 [which is
of type (STATUS1*)] directly to it.
*size = sizeof ( status1 );

}
else if ( ptr_cmd->header_field2 != 0 )
{
status2->status2_field1 = FALSE;
// return status2;

*result = &status2; /* simple! */
} // 4 more
}


I truly appreciate your assistance.

You're welcome, I'm sure.

-Arthur
 
M

ma740988

4. Return the data in special parameters.
void TEST_CMD ( const CMD *ptr_cmd, void **result, size_t *size );
/* *result = status1, *size = sizeof status1; et cetera */

void *p;
size_t sz;
TEST_CMD(&cmd_msg, &p, &sz);
Write_Msg(channel, p, sz);

Let me expand on option 4 for a minute. Questions intersperced below.

void TEST_CMD ( const CMD *ptr_cmd, void **result, size_t *size )
{
if ( ptr_cmd->header.field1 == 0 )
{
status1->status1_field1 = TRUE;
// we have a STATUS1 struct
*result = &status1; // This should be the address?
*size = sizeof (status1);

// return status1;
}
else if ( ptr_cmd->header_field2 != 0 )
{
status2->status2_field1 = FALSE;
*result = &status2; // This should be the address?
*size = sizeof (status1);

// return status2;

}

// later as u've indicated
void *p;
size_t sz;
TEST_CMD(&cmd_msg, &p, &sz);
Write_Msg(channel, p, sz);

The fact that the second argument to Write_Msg expects a void* is what
makes the address of status1, status2, status3 ... etc seamless. In
other words, we didnt have to do any 'casting' and things of the like
inside TEST_CMD.

Any portability implications with this approach? I cant see it but i
recalled vaguely readign about void ** as it relates to portability.

I truly appreaciate your assistance
 
M

ma740988

Arthur J. O'Dwyer said:
I'll try to exercise option 4, but first I should preface this by
stating i'm not close to a compiler right now. So in an attempt to
ensure i have the 'cast' right on the void pointer, for completeness
we have

void TEST_CMD ( const CMD *ptr_cmd, void **result, size_t *size )
{
if ( ptr_cmd->header.field1 == 0 )
{
status1->status1_field1 = TRUE;
// we got a STATUS1 now write it to result
(STATUS1)*result = status1; // this is goodness??

Nope. Much simpler, and no cast. Casts are EVIL and should
be avoided, even when you're mucking around with void pointers
and really *want* to cast things, I know... ;-)

*result = &status1;

*result is of type (void*); let's just assign &status1 [which is
of type (STATUS1*)] directly to it.
*size = sizeof ( status1 );

}
else if ( ptr_cmd->header_field2 != 0 )
{
status2->status2_field1 = FALSE;
// return status2;

*result = &status2; /* simple! */
} // 4 more
}


I truly appreciate your assistance.

You're welcome, I'm sure.

-Arthur
I have a assigning data 'out' when parameter is a pointer to pointer.

Consider
struct STATUS1
{
unsigned int status1_field1 : 1;
unsigned int status1_field2 : 32;
unsigned int status1_field3 : 5;
unsigned int status1_field4 : 27;
} ;

struct MSG
{
HEADER header; // header defined above
STATUS1 test_msg;
};

MSG msg;
STATUS1 status1;


// second param is a STATUS1*
void TEST::TEST_CMD ( const CMD *ptr_cmd, STATUS1*ptr, int* length)
{
if ( ptr_cmd->header->field1 == 0 )
{
status1.status1_field1 = true;
*ptr = status1; // write data out to a special param
*length = sizeof status1;
}
}

int idx;
TEST_CMD ( &cmd_msg, &msg.test_msg, &idx);

In essence, when I pass in &msg.test_msg, then later look at test_msg
the message is what I expect since we wrote to test_msg inside TEST_CMD

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Now consider

------------------

struct MSG
{
HEADER header; // header defined above
STATUS1* test_msg;
// now a STATUS1 pointer .. IN real code this will be void*
};


// second param is now a STATUS1**
void TEST::TEST_CMD ( const CMD *ptr_cmd, STATUS1**ptr, int* length)
{
if ( ptr_cmd->header->field1 == 0 )
{
status1.status1_field1 = true;
*ptr = &status1; // write data out to a special param This doesnt work.

*length = sizeof status1;
}
}

Same story
//later

int idx;
TEST_CMD ( &cmd_msg, &msg.test_msg, &idx);

Now when I pass in &msg.test_msg, then later look at test_msg the
message is garbage. How do I write the contents out when i have a
pointer to a pointer?
 
A

Arthur J. O'Dwyer

I just noticed this message from a while back; if it's still
relevant, here's the answer... :)

Let me expand on option 4 for a minute. Questions intersperced below.

void TEST_CMD ( const CMD *ptr_cmd, void **result, size_t *size )
{
if ( ptr_cmd->header.field1 == 0 )
{
status1->status1_field1 = TRUE;
// we have a STATUS1 struct
*result = &status1; // This should be the address?
Yes.

*size = sizeof (status1);

// return status1;

No. Look at the function prototype: 'void TEST_CMD(....)'. It returns
'void', which is C-speak for not returning anything of interest at all.
So 'return status1;' would be a constraint violation (a compiler error),
and even 'return;' would not be needed. Falling off the end of the
function will cause the function to 'return' implicitly.
}
else if ( ptr_cmd->header_field2 != 0 )
{
status2->status2_field1 = FALSE;
*result = &status2; // This should be the address?
*size = sizeof (status1);

This should be 'sizeof (status2)', because we're returning a pointer
to 'status2'. And in both cases, the parentheses around the variable
name are unnecessary and thus frowned upon. I consider

*size = sizeof status2;

to be a little bit clearer in most cases.

// later as u've indicated

Watch that laziness! "...as you've indicated..." If you start
messing up your English, who knows what your *code* is going to look
like?
void *p;
size_t sz;
TEST_CMD(&cmd_msg, &p, &sz);
Write_Msg(channel, p, sz);

The fact that the second argument to Write_Msg expects a void* is what
makes the address of status1, status2, status3 ... etc seamless. In
other words, we didnt have to do any 'casting' and things of the like
inside TEST_CMD.

Even if Write_Msg expected something of a different type, you still
wouldn't need to cast -- (void*) can be implicitly converted to
(anythingelse*).
Any portability implications with this approach? I cant see it but i
recalled vaguely readign about void ** as it relates to portability.

This design should work fine. The concern with (void **) is that
some newbies want to write

void getmem(void **to_alloc) { *to_alloc = malloc(1024); }

int *foo;
getmem(&foo);

as if (void **) was a "generic pointer to pointer" the same way (void*)
is sometimes called a "generic pointer to object." But it's not; and
the above won't type-check because (int**) and (void**) are two different
pointer types that can't be implicitly converted back and forth. [One
points to an object of 'int*' type, and the other points to an object
of 'void*' type.]

Passing a (void**) to a function is fine, AS LONG AS you only use it
to store the address of a (void*) object [or NULL]. Which is exactly
what we've done in #4.

HTH,
-Arthur
 
A

Arthur J. O'Dwyer

I have a [problem?]
assigning data 'out' when parameter is a pointer to pointer.

Consider
struct STATUS1
{
unsigned int status1_field1 : 1;
unsigned int status1_field2 : 32;
unsigned int status1_field3 : 5;
unsigned int status1_field4 : 27;
} ;

struct MSG
{
HEADER header; // header defined above
STATUS1 test_msg;
};

MSG msg;
STATUS1 status1;


// second param is a STATUS1*
void TEST::TEST_CMD ( const CMD *ptr_cmd, STATUS1*ptr, int* length)
{
if ( ptr_cmd->header->field1 == 0 )
{
status1.status1_field1 = true;
*ptr = status1; // write data out to a special param
*length = sizeof status1;

To be absolutely portable and correct, you should make 'length' an
object of type 'size_t *'. The 'sizeof' operator returns a value of
type 'size_t'. The only change that need be made is:

void TEST::TEST_CMD(const CMD *ptr_cmd, STATUS1 *ptr, size_t *length)

Oh, and I guess this is really C++ code pretending to be C code, so
you might need a 'std::' in front of the 'size_t' to make it correct.
But visit comp.lang.c++ for C++ programming questions.
}
}

int idx;
TEST_CMD ( &cmd_msg, &msg.test_msg, &idx);

In essence, when I pass in &msg.test_msg, then later look at test_msg
the message is what I expect since we wrote to test_msg inside TEST_CMD

Correct, I guess.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Now consider

------------------

struct MSG
{
HEADER header; // header defined above
STATUS1* test_msg;
// now a STATUS1 pointer .. IN real code this will be void*
};


// second param is now a STATUS1**
void TEST::TEST_CMD ( const CMD *ptr_cmd, STATUS1**ptr, int* length)
{
if ( ptr_cmd->header->field1 == 0 )
{
status1.status1_field1 = true;
*ptr = &status1; // write data out to a special param This doesnt work.

What do you mean, it doesn't work? It looks like it should.
*length = sizeof status1;
}
}

Same story
//later

int idx;
TEST_CMD ( &cmd_msg, &msg.test_msg, &idx);

Now when I pass in &msg.test_msg, then later look at test_msg the
message is garbage. How do I write the contents out when i have a
pointer to a pointer?

Well, obviously you'll need to change your call to Write_Msg() or
whatever it was, so that you'll be printing out *msg.test_msg (the
structure) and not msg.test_msg (the pointer value). If you're
seeing garbage, I bet that's the cause. Remove an '&' or add an '*'
in the appropriate place.

-Arthur
 
M

ma740988

// I think I'm with you (i'm re-reading) but here's a specific test case ..

#ifndef _TEST_B
#define _TEST_B

struct HEADER
{
unsigned int dest :8;
unsigned int msg :8;
unsigned int msg_length :12;
unsigned int counting :4;
};

struct STATUS1
{
unsigned int status1_field1 : 1;
unsigned int status1_field2 : 32;
unsigned int status1_field3 : 5;
unsigned int status1_field4 : 27;
};

struct STATUS2
{
unsigned int status2_field1 : 1;
unsigned int status2_field2 : 32;
unsigned int status2_field3 : 5;
unsigned int status2_field4 : 27;

};
struct MSG
{
HEADER header;
STATUS1* status;
};

struct MSG2
{
HEADER header;
STATUS1 status;
};

struct MSG3
{
HEADER header;
void* status;
};

#endif


#include <iostream>
#include "test_b.h"

#if 0
// As a test for a specific STATUS1 .. This works.
void TEST_CMD ( const HEADER* ptr_header, STATUS1* ptr, int* length)
{
STATUS1 status1 = { 0 };

if ( ptr_header->dest == 0 )
{
status1.status1_field1 = true;
*ptr = status1; // write data out to a special param
*length = sizeof status1;
}
}
#endif

// Now the void*
void TEST_CMD ( const HEADER* ptr_header, void* ptr, int* length)
{
STATUS1 status1 = { 0 };
STATUS2 status2 = { 0 };

if ( ptr_header->dest == 0 )
{
status1.status1_field1 = true;
*(STATUS1 *)ptr = status1; // write data out to a special param
*length = sizeof status1; // ditto!! no problems here
}
else if ( ptr_header->dest == 1 )
{
status2.status2_field1 = true;
*(STATUS2 *)ptr = status2; // write data out to a special param
*length = sizeof status2; // ditto!! no problems here
}
else
{
// more
}
}

// later
int main ()
{
std::cout << " sizeof MSG = " << sizeof (MSG) << std::endl;
std::cout << " sizeof MSG2 = " << sizeof (MSG2) << std::endl;
std::cout << " sizeof MSG3 = " << sizeof (MSG3) << std::endl;

HEADER header = { 0, 1, 2, 3 };
MSG ptr_msg = { { 0, 1, 2, 3 }, { 0 } };
MSG3 ptr_msg3 = { { 0, 1, 2, 3 }, { 0 } };

int len;

// These two lines in tandem works for the 'first' TEST_CMD
//ptr_msg.status = (STATUS1 *)malloc (sizeof (STATUS1));
// TEST_CMD (&header, ptr_msg.status, &len);


// But we'd like to be generic. ie. dont care who's getting updated.
// In just the same context as len.. write data out to ptr_msg3 status.
// So now my dilema.

// TEST_CMD (&header, ptr_msg3.status, &len); ???

std::cout << " length = " << len << std::endl;

return EXIT_SUCCESS;
}

// Ultimately I'll call the Write_Msg function ..
 
A

Arthur J. O'Dwyer

// I think I'm with you (i'm re-reading) but here's a specific test case ..

#ifndef _TEST_B
#define _TEST_B

Nitpick: undefined behavior. Don't use identifiers that start
with an underscore followed by an uppercase letter (or another
underscore); those are reserved for the compiler's own use, or
for future extensions to the language. The compiler is perfectly
free to rap your knuckles for this. Use TEST_B instead, or
H_TEST_B, to indicate a *H*eader file, or whatever.


/* Removed unnecessary details */
struct HEADER;
struct STATUS1;
struct STATUS2;
struct MSG
{
HEADER header;
STATUS1* status;
};

struct MSG2
{
HEADER header;
STATUS1 status;
};

struct MSG3
{
HEADER header;
void* status;
};
Okay...




#include <iostream>
#include "test_b.h"

#if 0
// As a test for a specific STATUS1 .. This works.

As a courtesy to your reader (i.e., me), please don't put in
unnecessary #ifs. If this function is important to your point,
then you'd better leave it in (with no obfuscatory #ifs). If it's
not relevant, then just take it out -- I don't need to see it!
Also, it would be nice if you used regular C90-style comments,
with /* and */, which would help to discourage obfuscatory lines
like the following:
// These two lines in tandem works for the 'first' TEST_CMD
//ptr_msg.status = (STATUS1 *)malloc (sizeof (STATUS1));
// TEST_CMD (&header, ptr_msg.status, &len);

Not only is that indentation messed up, but it's not immediately
clear (as it should be) that those three lines are *all* superfluous
and should have been removed before you posted. If you want to know
why a piece of code isn't working, post the code. Don't post code
that (1) is irrelevant, or (2) is commented out anyway.

Now, back to the code...
void TEST_CMD ( const HEADER* ptr_header, STATUS1* ptr, int* length)
{
STATUS1 status1 = { 0 };

if ( ptr_header->dest == 0 )
{
status1.status1_field1 = true;
*ptr = status1; // write data out to a special param
*length = sizeof status1;
}
}
#endif

// Now the void*

[Note that you haven't just changed to "the void*"; you've also
added some complexity to the function itself, with a second STATUSn
object.]
void TEST_CMD ( const HEADER* ptr_header, void* ptr, int* length)
{
STATUS1 status1 = { 0 };
STATUS2 status2 = { 0 };

if ( ptr_header->dest == 0 )
{
status1.status1_field1 = true;
*(STATUS1 *)ptr = status1; // write data out to a special param
*length = sizeof status1; // ditto!! no problems here
}
else if ( ptr_header->dest == 1 )
{
status2.status2_field1 = true;
*(STATUS2 *)ptr = status2; // write data out to a special param
*length = sizeof status2; // ditto!! no problems here
}
else
{
// more
}
}

// later
int main ()
{
std::cout << " sizeof MSG = " << sizeof (MSG) << std::endl;
std::cout << " sizeof MSG2 = " << sizeof (MSG2) << std::endl;
std::cout << " sizeof MSG3 = " << sizeof (MSG3) << std::endl;

HEADER header = { 0, 1, 2, 3 };
MSG ptr_msg = { { 0, 1, 2, 3 }, { 0 } };
MSG3 ptr_msg3 = { { 0, 1, 2, 3 }, { 0 } };

int len;

// These two lines in tandem works for the 'first' TEST_CMD
//ptr_msg.status = (STATUS1 *)malloc (sizeof (STATUS1));

Or equivalently,

ptr_msg.status = malloc(sizeof *ptr_msg.status);
// TEST_CMD (&header, ptr_msg.status, &len);

// But we'd like to be generic. ie. dont care who's getting updated.
// In just the same context as len.. write data out to ptr_msg3 status.

Huh? What?
// So now my dilema.

// TEST_CMD (&header, ptr_msg3.status, &len); ???

To make this work, change the casts in TEST_CMD from (STATUS1 *) and
(STATUS2 *) to (void **), and assign the addresses of 'status1', etc.,
instead of the their values. That might be enough. Post some
compilable code [in C, obviously].
// Ultimately I'll call the Write_Msg function ..

Then ultimately you'll need to figure out how you're going to
do that. Start thinking now.

-Arthur
 

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,768
Messages
2,569,575
Members
45,052
Latest member
KetoBeez

Latest Threads

Top