Way to determine type of variable?

  • Thread starter Walter L. Preuninger II
  • Start date
W

Walter L. Preuninger II

I would like to write a generic procedure that will take string or numeric
variables. I can not think of a way to make this more clear except to show
what I want.

int main(void)
{
int i=7;
char *s="/etc/filesystems";

generic(i);
generic(s);
generic("Hello");
generic(2048);
exit(0);
}

should return:
7
/etc/filesystems
Hello
2048

so...

void generic( ???????)
{
???
}

I have looked at sprintf, varargs etc.

Could someone point me in the right direction?

Thanks,

Walter
 
J

Jens.Toerring

Walter L. Preuninger II said:
I would like to write a generic procedure that will take string or numeric
variables. I can not think of a way to make this more clear except to show
what I want.
int main(void)
{
int i=7;
char *s="/etc/filesystems";

should return:
7
/etc/filesystems
Hello
2048

void generic( ???????)
{
???
}
I have looked at sprintf, varargs etc.
Could someone point me in the right direction?

Sorry, but there's no right direction. Types are a compile
time concept and the type information is only used during the
compilation and is only implicitely contained in the resulting
executable (i.e. why certain machine instructions are used and
not others). So at run-time the type information does not exist
anymore and thus such generic functions are impossible - there
is no additional type information attached to a variable once
the code has been compiled.

All that is left is the question why you need such generic
functions (beside that it sometimes would be nice to have
them) - perhaps there's a different solution to your problem.

Regards, Jens
 
M

Mark A. Odell

I would like to write a generic procedure that will take string or
numeric variables. I can not think of a way to make this more clear
except to show what I want.

int main(void)
{
int i=7;
char *s="/etc/filesystems";

generic(i);
generic(s);
generic("Hello");
generic(2048);
exit(0);
}

should return:
7
/etc/filesystems
Hello
2048

so...

void generic( ???????)
{
???
}

I have looked at sprintf, varargs etc.

Could someone point me in the right direction?

Hate to say it, but C++ is the right direction for this. This is one of
those things, that in C, is rather clumsy to do, error prone, and doesn't
look very nice in the end.
 
V

Victor Nazarov

Walter L. Preuninger II said:
I would like to write a generic procedure that will take string or numeric
variables. I can not think of a way to make this more clear except to show
what I want.

int main(void)
{
int i=7;
char *s="/etc/filesystems";

generic(i);
generic(s);
generic("Hello");
generic(2048);
exit(0);
}

should return:
7
/etc/filesystems
Hello
2048

so...

void generic( ???????)
{
???
}

I have looked at sprintf, varargs etc.

Could someone point me in the right direction?

Thanks,

Walter

You'll need to implement polymorphism or use macrosses. If you really need
this facilities, try using C++ instead. You CAN implement something like
this in C, see http://ldeniau.home.cern.ch/ldeniau/html/oopc/oopc.html
for example
 
E

Eric Sosman

Walter said:
I would like to write a generic procedure that will take string or numeric
variables. I can not think of a way to make this more clear except to show
what I want.

int main(void)
{
int i=7;
char *s="/etc/filesystems";

generic(i);
generic(s);
generic("Hello");
generic(2048);
exit(0);
}

should return:
7
/etc/filesystems
Hello
2048

so...

void generic( ???????)
{
???
}

I have looked at sprintf, varargs etc.

Could someone point me in the right direction?

The best you can get is something along the lines of

enum Type { INT, STRING, /* others ... */ );
generic(INT, i);
generic(STRING, s);
generic(STRING, "Hello");
generic(INT, 2048);

Or with a repackaging of essentially the same idea, you could get

struct { enum Type type;
union { int i; char *s; /* others ... */ } value;
} args;
args.type = INT; args.value.i = i; generic(args);
args.type = STRING; args.value.s = s; generic(args);
args.value.s = "Hello"; generic(args);
args.type = INT; args.value.i = 2048; generic(args);

Neither of these seems especially attractive, nor do any of
the other variations I can think of. They're ugly, they're
clumsy, and they're error-prone -- the compiler will not
detect the mistakes in

/* first form */
generic(STRING, 42);

/* second form */
args.type = INT; args.value.s = "Boom!"; generic(args);

The fundamental problem is that C is a statically-typed
language, meaning that the type of every expression is fixed
at compile time. True, <stdarg.h> (not <varargs.h>, BTW) can
evade this requirement, but only briefly: In order to access
the arguments corresponding to `...', you must use an expression
whose type is known and immutable. C is not Lisp.
 
J

jacob navia

Walter L. Preuninger II said:
I would like to write a generic procedure that will take string or numeric
variables. I can not think of a way to make this more clear except to show
what I want.

int main(void)
{
int i=7;
char *s="/etc/filesystems";

generic(i);
generic(s);
generic("Hello");
generic(2048);
exit(0);
}

should return:
7
/etc/filesystems
Hello
2048

so...

void generic( ???????)
{
???
}

I have looked at sprintf, varargs etc.

Could someone point me in the right direction?

Thanks,

Walter

This is not possible with standard C.
Generic functions aren't part of the standard language.

The lcc-win32 compiler implements generic functions
exactly like you want, but it isn't standard C, i.e. generic
functions are an *extension* of the language.

If you do not mind using extensions go to
http://www.cs.virginia.edu/~lcc-win32.

Within standard C you can write a function like this:

#define INTEGER 1
#define STRING 2
#define DOUBLE 3
union data {
int Type;
int integer;
double doublefloat;
float floatfloat;
char *string;
// add other cases here
};

void generic(union data Data)
{
switch (Data.Type) {
case INTEGER:
printf("%d\n",Data.integer);
break;
case STRING:
printf("%s\n",Data.string);
break;
// Add the other cases here
}
}

int main(void)
{
union data Data;
Data.integer = 7;
Data.Type = INTEGER;
generic(Data);
Data.string = "/etc/path";
Data.Type = STRING;
generic(Data);
Data.Type = DOUBLE;
Data.doublefloat = 67.987;
generic(DOUBLE,Data);
/// etc
}

}
 
D

Default User

Walter L. Preuninger II said:
I would like to write a generic procedure that will take string or numeric
variables. I can not think of a way to make this more clear except to show
what I want.

int main(void)
{
int i=7;
char *s="/etc/filesystems";

generic(i);
generic(s);
generic("Hello");
generic(2048);
exit(0);
}

should return:
7
/etc/filesystems
Hello
2048

so...

void generic( ???????)
{
???
}

I have looked at sprintf, varargs etc.

Could someone point me in the right direction?


Something like sprintf() is about as close as you are going to get. You
can use a variadic function or pass in void pointers, but in any case
you'll need to let the function know about the type.

One way would be a typed data struct:


struct data
{
void *datap;
int type;
};

Then set type when the data is created.




Brian Rodenborn
 
D

Drew MacDonald

jacob said:
Within standard C you can write a function like this:

#define INTEGER 1
#define STRING 2
#define DOUBLE 3
union data {
int Type;
int integer;
double doublefloat;
float floatfloat;
char *string;
// add other cases here
};

Uh, that doesn't look right. Shouldn't it be something
more like:

struct data
{
int Type;
union values
{
int integer;
double doubleFloat;
float floatFloat;
char *string
// add other cases here
};
};

If the Type is in the union, then wouldn't Type and
integer overwrite each other?

Drew
 
W

Walter L. Preuninger II

[snip]

All that is left is the question why you need such generic
functions (beside that it sometimes would be nice to have
them) - perhaps there's a different solution to your problem.

Regards, Jens

I needed a generic error printing routine, better than what perror()
provides.

incomplete code follows

char *filename="X";
char *buffer;
int size=65536;

file=fopen(filename,"rt");
if (file==NULL)
{
xerror("fopen failed",filename);
exit(1);
}
buffer=(char *)malloc(size);
if(buffer == NULL) {
xerror("malloc failed, size=",size);
}

I guess I could use sprintf to fill a buffer and print/pass that, but I
wanted to lessen the amount of code written.

I have written a macro for xerror that does pass argv[0], __FILE__, __LINE__
and errno to produce
../test:test.c:15:fopen failed:X for the fopen example above.

Thanks,

Walter
 
J

jacob navia

AAAAAArg!!!!

Right!

4 eyes see more than just two. Another proof of that old
wisdom.

It must be a structure with the type *outside* the union.

Sorry about this bug!

jacob
 
J

jacob navia

The version I posted contained a serious error:
The type of the union should not be stored in the
union itself!

Here is a corrected version.
Sorry for this error.
Within standard C you can write a function like this:

#define INTEGER 1
#define STRING 2
#define DOUBLE 3
struct data {
int Type; // Type OUTSIDE the union
union u {
 
J

Jens.Toerring

Walter L. Preuninger II said:
I needed a generic error printing routine, better than what perror()
provides.
incomplete code follows
char *filename="X";
char *buffer;
int size=65536;
file=fopen(filename,"rt");
if (file==NULL)
{
xerror("fopen failed",filename);
exit(1);
}
buffer=(char *)malloc(size);
if(buffer == NULL) {
xerror("malloc failed, size=",size);
}
I guess I could use sprintf to fill a buffer and print/pass that, but I
wanted to lessen the amount of code written.

I guess the cleanest way would be to add printf-like format
information to the string you send to xerror(), e.g.

xerror( "fopen failed for:%s", filename );
xerror( "malloc failed: size=%ld", size );

etc. and in xerror() you then would have

#include <stdarg.h>

void xerror( const char *fmt, ... )
{
va_list ap;

/* Put printing argv[0], __FILE__ and __LINE__ in here */

va_start( ap, fmt );
vfprintf( stderr, fmt, ap );
va_end( ap );
}

It doesn't cost you too much in typing and works without lots of
complicated (and error prone) macros or extremely ugly code.

Regards, Jens
 
K

Keith Thompson

Walter L. Preuninger II said:
I would like to write a generic procedure that will take string or numeric
variables. I can not think of a way to make this more clear except to show
what I want.

int main(void)
{
int i=7;
char *s="/etc/filesystems";

generic(i);
generic(s);
generic("Hello");
generic(2048);
exit(0);
}

should return:
7
/etc/filesystems
Hello
2048

By "should return", I think you mean "should print".
so...

void generic( ???????)
{
???
}

I have looked at sprintf, varargs etc.

You're asking about function overloading, which C doesn't support.
<OT>C++ does.</OT>

You can do something similar with variable argument lists
(<stdarg.h>), but you have to have an initial argument specifying the
type(s) of the following argument(s). For example, you could have
something like

generic(INT, i);
generic(STRING, s);
generic(STRING, "Hello");
generic(INT, 2048);

(given appropriate declarations of INT, STRING, etc.)
 
W

Walter L. Preuninger II

[snip]


/* Put printing argv[0], __FILE__ and __LINE__ in here */
only problem here is that __FILE__ and __LINE__ represent the filename and
line number of the source file, in which would be xerror.c and not main.c or
what ever, but thanks for the info and I will see how I can apply it to my
needs


Thanks!

Walter
 
M

Malcolm

Walter L. Preuninger II said:
I would like to write a generic procedure that will take string or numeric
variables. I can not think of a way to make this more clear except to
show what I want.

int main(void)
{
int i=7;
char *s="/etc/filesystems";

generic(i);
generic(s);
generic("Hello");
generic(2048);
exit(0);
}

should return:
7
/etc/filesystems
Hello
2048

so...

void generic( ???????)
{
???
}

I have looked at sprintf, varargs etc.
So you should know that you can write a function

void generic(char *type, ...)

generic("int", 123);
generic("char *", "Hello");
etc.

Then in generic you decode the first argument, which tells you what to pass
to va_arg() to get the argument. If you try to write printf() it is done in
a similar manner, excpet that the format string uses a "%" specifier to tell
you the argument type instead of passing a name.

If you have any problem implementing the variable argument list, just post
back.
 
J

Jens.Toerring

Walter L. Preuninger II said:
/* Put printing argv[0], __FILE__ and __LINE__ in here */
only problem here is that __FILE__ and __LINE__ represent the filename and
line number of the source file, in which would be xerror.c and not main.c or
what ever, but thanks for the info and I will see how I can apply it to my
needs

Unless you have a C99 compliant compiler, allowing macros with a
variable number of arguments, that's going to be more messy. The
simplest approach probably would be to define

#define AFL argv[0], __FILE__, __LINE__

and use it as

xerror( AFL, "File not found: %s", filename );

and declare xerror as

void xerror( const char *prog_name, const char *file_name, int line_number,
const char *fmt, .... );

That way you would get at the file name and line number without too
much hassle (well, it's not beautiful, but it should work). And if
you're too lazy to write that AFL stuff into code 'sed' or something
similar can be quite useful for such mindnumbing tasks;-)

Regards, Jens
 
E

Eric Sosman

Walter said:
[snip]


/* Put printing argv[0], __FILE__ and __LINE__ in here */

only problem here is that __FILE__ and __LINE__ represent the filename and
line number of the source file, in which would be xerror.c and not main.c or
what ever, but thanks for the info and I will see how I can apply it to my
needs

If you have a C99-conforming compiler you can write
macros with variable numbers of arguments, and that eases
the task of adding __FILE__ and __LINE__ to an XERROR macro
that in turn invokes the xerror() function. Failing that,
gcc has its own pre-C99 way of writing "varargs macros."
And if even that isn't acceptable, you can use the dodge
described in Question 10.26 of the FAQ

http://www.eskimo.com/~scs/C-faq/top.html
 
S

Sam Dennis

Walter said:
void xerror( const char *fmt, ... )
{
/* Put printing argv[0], __FILE__ and __LINE__ in here */

only problem here is that __FILE__ and __LINE__ represent the filename and
line number of the source file, in which would be xerror.c and not main.c

That's easily solved with a small macro wrapper for the function, which
would substitute the file and line number for the invocation, e.g.:

#define xerror( fmt, ... ) xerror( "%s:%d - " fmt, __FILE__, __LINE, \
__VA_ARGS__ )

However, this has several limitations (other than requiring C99 macros)
so I'm sure that you'll want to try to make something better. Perhaps,
even, something that doesn't affect the arguments to xerror...
 
E

Eric Sosman

Sam said:
Walter said:
void xerror( const char *fmt, ... )
{
/* Put printing argv[0], __FILE__ and __LINE__ in here */

only problem here is that __FILE__ and __LINE__ represent the filename and
line number of the source file, in which would be xerror.c and not main.c


That's easily solved with a small macro wrapper for the function, which
would substitute the file and line number for the invocation, e.g.:

#define xerror( fmt, ... ) xerror( "%s:%d - " fmt, __FILE__, __LINE, \
__VA_ARGS__ )

However, this has several limitations (other than requiring C99 macros)
so I'm sure that you'll want to try to make something better. Perhaps,
even, something that doesn't affect the arguments to xerror...

Another approach is to print the location information and
the error-specific information with two functions instead of
trying to do it all with one. In addition to Jens' xerror(),
you'd also write

void xwhere(const char *file, int line) {
fprintf (stderr, "%s line %d: ", file, line);
}

Then you'd use

#define XERROR xwhere(__FILE__, __LINE__) , xerror
...
XERROR ("Can't open %s\n", filename);
XERROR ("Supercalifragilisticexpialidocious!\n");
XERROR ("Invalid co-ordinates (%g,%g)\n", x, y);

You could, of course, dispense with xwhere() and call fprintf()
directly from the macro expansion, if you can be sure that
<stdio.h> has been included everwhere XERROR is used. And there
are other variations, too -- but the essential idea here is to
avoid all hassles with variable-length macro arguments by defining
XERROR as an object-like macro with no arguments at all.
 
K

Keith Thompson

Unless you have a C99 compliant compiler, allowing macros with a
variable number of arguments, that's going to be more messy. The
simplest approach probably would be to define

#define AFL argv[0], __FILE__, __LINE__

and use it as

xerror( AFL, "File not found: %s", filename );

If it refers to argv[0], you can only use it within main() (assuming
the usual declarations).

You can have main() save the value of argv[0] to a global variable and
refer to that variable in the AFL macro.
 

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,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top