arnuld said:
I just got a word from my seniors and was told of these:
(1) A C compiler will definitely be available on the target embedded
platform but a C++ compiler may or may not be available.
(2) Use as much printf()s as you can for debugging purposes but code will
be shipped with no printf()s.
(3) don't use malloc() unless it is absolutely necessary.
(4) The RAM available will be around 64-128 MB.
(5) The final executable should be less than 1 MB
I don't think I have left with any choice here, except to use macros or
void pointers to implement a PQ in C.
Notice that with macros, you get the same code duplications as with
templates (for the small benefit of a theorically better type checking
by the C compiler, as oxymoronic that may sound).
I would advise you to use void* and a single copy of the PQ code.
If you are afraid of not being able to prove statically to yourself
that when you put objects of type T1 in a PQ, you get objects of type
T1 back, you could wrap all your objects in a structure with a type
tag to check dynamically that you've got objects of the right type:
typedef struct {
int typeTag;
union {
int i;
float f;
double d;
char c;
String string;
T1 t1;
T2 t2;
...
} value;
} Object;
With such a structure you can make a PQ of Object* instead of a PQ of
void*, and whatever the type of data you have, you can put it in a PQ,
and when you get it back with the typeTag field you can know what type
of value of really have.
See in the following program how the array o in main contains Objects
of any kind, (an int and a struct T1 for example), and how you can
process them in the loop.
------------------------------------------------------------------------
#include <assert.h>
#include <iso646.h>
#include <stdio.h>
typedef enum { false=0, true } bool;
typedef struct {
int allocated;
int length;
char* data;
} String;
typedef struct {
int i;
int j;
} T1;
typedef struct {
String name;
String email;
} T2;
enum {
type_nil=0,
type_int,
type_float,
type_double,
type_char,
type_String,
type_T1,
type_T2
};
const char* typeLabel(int type){
static const char* typeLabels[]={"null","int","float","double","char",
"String","T1","T2"};
return(typeLabels[type]);}
typedef struct {
int typeTag;
union {
int i;
float f;
double d;
char c;
String string;
T1 t1;
T2 t2;
} value;
} Object;
Object Nil={type_nil};
Object box_int(int i){
Object result;
result.typeTag=type_int;
result.value.i=i;
return result; }
Object make_t1(int i,int j){
Object result;
result.typeTag=type_T1;
result.value.t1.i=i;
result.value.t1.j=j;
return result; }
bool String_equal(String a,String b){
return((a.length==b.length)
and (0==strncmp(a.data,b.data,a.length))); }
bool T1_equal(Object a,Object b){
assert(type_T1==a.typeTag);
assert(type_T1==b.typeTag);
return((a.value.t1.i==b.value.t1.i)
and (a.value.t1.j==b.value.t1.j)); }
bool T2_equal(Object a,Object b){
assert(type_T2==a.typeTag);
assert(type_T2==b.typeTag);
return(String_equal(a.value.t2.name,b.value.t2.name)
and String_equal(a.value.t2.email,b.value.t2.email)); }
void doSomethingWithInt(Object o){
assert(type_int==o.typeTag);
printf("integer %d\n",o.value.i);
}
void doSomethingWithT1(Object o){
assert(type_T1==o.typeTag);
printf("T1 %d,%d\n",o.value.t1.i,o.value.t1.j);
}
bool equal(Object a,Object b){
if(a.typeTag!=b.typeTag){ return false; }
switch(a.typeTag){
case type_nil: return(true);
case type_int: return(a.value.i==b.value.i);
case type_char: return(a.value.c==b.value.c);
case type_float: return(a.value.f==b.value.f);
case type_double: return(a.value.d==b.value.d);
case type_String: return(String_equal(a.value.string,b.value.string));
case type_T1: return(T1_equal(a,b));
case type_T2: return(T2_equal(a,b));
default: return(false);
}
}
int main(){
Object o[]={box_int(42),make_t1(1,2),Nil};
int i;
for(i=0;not equal(o
,Nil);i++){
switch(o.typeTag){
case type_int:
doSomethingWithInt(o);
break;
case type_T1:
doSomethingWithT1(o);
break;
default:
fprintf(stderr,"I cannot deal with objects of type %s"
,typeLabel(o.typeTag));
break;
}}
return(0);
}
/*
-*- mode: compilation; default-directory: "~/src/tests-c++/" -*-
Compilation started at Tue Oct 6 15:22:00
SRC="/home/pjb/src/tests-c++/tt.c" ; EXE="tt" ; gcc -g3 -ggdb3 -o ${EXE} ${SRC} && ./${EXE} && echo status = $?
integer 42
T1 1,2
status = 0
Compilation finished at Tue Oct 6 15:22:00
*/
------------------------------------------------------------------------
Oh, and if you grow tired of all the switches, you can put the
functions appliable on each kind of objects in a table, and have each
object keep a pointer to that table. Then you can write instead of:
switch(o.typeTag){
case type_int:
doSomethingWithInt(o);
break;
case type_T1:
doSomethingWithT1(o);
break;
default:
fprintf(stderr,"I cannot deal with objects of type %s"
,typeLabel(o.typeTag));
break;
}}
something like just:
call(o,"doSomething");
or even, with a macro such as:
#define S(O,M) call(O,#M)
just:
S(o,doSomething)