[Problem description] generic is a special type that delays the work of a specified type until the client Code declares and instantiates a class or method. Generics are designed to solve function name conflicts. Generic is generally considered to be the capability of advanced languages. The implementation of generic is generally based on the template concept of advanced languages. Can the C language implement generics? The answer is yes, but it is much more difficult than advanced languages. The following two generic implementation methods are summarized.
[Resolution]
Compiling environment:
Fedora 10, gcc version gcc4.3.2
1. Use function pointers to implement generics
[Code list]
Printtest. c
[Html]
# Include <stdio. h>
# Include <stdlib. h>
# Define DEBUG 0
# Define PRINTSTAT (FORMAT, STAT )\
Printf ("****************" FORMAT "Test ****************\ n "\
, STAT );
# Define DEBUG_PRINT (FORMAT, VALUE) printf ("File % s line % d :"\
# VALUE "=" FORMAT "\ n "\
,__ FILE __, _ LINE __, VALUE \
);
Enum {
RET_ OK,
RET_FAIL
};
Typedef int (* FXPrintFun) (void * data );
Int print (FXPrintFun _ print, void * data)
{
Return _ print (data );
}
Static int print_int (void * data)
{
Printf ("% d", (int) data );
Return RET_ OK;
}
Static int print_int2 (void * data)
{
Printf ("% d", * (int *) data );
Return RET_ OK;
}
Static int print_float (void * data)
{
Printf ("% f", * (float *) data );
Return RET_ OK;
}
Static int print_str (void * data)
{
Printf ("% s", (char *) data );
Return RET_ OK;
}
Int main (void)
{
Int I = 0;
Float f = 2.6;
Char * test = "Generics test! ";
Int * pi = & I;
Float * pf = & f;
# If DEBUG
DEBUG_PRINT ("% f", f)
# Endif
PRINTSTAT ("% s", "Integer ")
For (; I <10; I ++)
Print (print_int, (void *) I );
Printf ("\ n ");
PRINTSTAT ("% s", "Integer ")
For (I = 0; I <10; I ++)
Print (print_int2, (void *) pi );
Printf ("\ n ");
PRINTSTAT ("% s", "Float ")
Print (print_float, (void *) pf );
Printf ("\ n ");
PRINTSTAT ("% s", "String ")
Print (print_str, (void *) test );
Printf ("\ n ");
Return RET_ OK;
}
# Include <stdio. h>
# Include <stdlib. h>
# Define DEBUG 0
# Define PRINTSTAT (FORMAT, STAT )\
Printf ("****************" FORMAT "Test ****************\ n "\
, STAT );
# Define DEBUG_PRINT (FORMAT, VALUE) printf ("File % s line % d :"\
# VALUE "=" FORMAT "\ n "\
,__ FILE __, _ LINE __, VALUE \
);
Enum {
RET_ OK,
RET_FAIL
};
Typedef int (* FXPrintFun) (void * data );
Int print (FXPrintFun _ print, void * data)
{
Return _ print (data );
}
Static int print_int (void * data)
{
Printf ("% d", (int) data );
Return RET_ OK;
}
Static int print_int2 (void * data)
{
Printf ("% d", * (int *) data );
Return RET_ OK;
}
Static int print_float (void * data)
{
Printf ("% f", * (float *) data );
Return RET_ OK;
}
Static int print_str (void * data)
{
Printf ("% s", (char *) data );
Return RET_ OK;
}
Int main (void)
{
Int I = 0;
Float f = 2.6;
Char * test = "Generics test! ";
Int * pi = & I;
Float * pf = & f;
# If DEBUG
DEBUG_PRINT ("% f", f)
# Endif
PRINTSTAT ("% s", "Integer ")
For (; I <10; I ++)
Print (print_int, (void *) I );
Printf ("\ n ");
PRINTSTAT ("% s", "Integer ")
For (I = 0; I <10; I ++)
Print (print_int2, (void *) pi );
Printf ("\ n ");
PRINTSTAT ("% s", "Float ")
Print (print_float, (void *) pf );
Printf ("\ n ");
PRINTSTAT ("% s", "String ")
Print (print_str, (void *) test );
Printf ("\ n ");
Return RET_ OK;
}
Makefile
[Html]
OBJS = printtest. o
TARGET = printtest
SRC = printtest. c
All: $ (OBJS)
Gcc-g $ (OBJS)-o $ (TARGET)
$ (OBJS): printtest. s
Gcc-g-c printtest. s-o $ (OBJS)
Printtest. s: printtest. I
Gcc-g-S printtest. I-o printtest. s
Printtest. I: $ (SRC)
Gcc-g-E $ (SRC)-o printtest. I
Clean:
Rm *~ *. O *. s *. I $ (TARGET)
OBJS = printtest. o
TARGET = printtest
SRC = printtest. c
All: $ (OBJS)
Gcc-g $ (OBJS)-o $ (TARGET)
$ (OBJS): printtest. s
Gcc-g-c printtest. s-o $ (OBJS)
Printtest. s: printtest. I
Gcc-g-S printtest. I-o printtest. s
Printtest. I: $ (SRC)
Gcc-g-E $ (SRC)-o printtest. I
Clean:
Rm *~ *. O *. s *. I $ (TARGET)
* To observe the code compilation, compilation, and link processes, Makefile is slightly complicated and can be compiled directly using the following commands:
[Html] view plaincopyprint? Gcc printtest. c-o printtest
Gcc printtest. c-o printtest
[Running result]
[Html]
* ************** Integer Test ****************
0123456789
* ************** Integer Test ****************
0123456789
* Float Test ****************
2.600000
* ************** String Test ****************
Generics test!
* ************** Integer Test ****************
0123456789
* ************** Integer Test ****************
0123456789
* Float Test ****************
2.600000
* ************** String Test ****************
Generics test!
The above code is worth noting that two types of print_int functions are provided during definition:
[Html]
Static int print_int (void * data)
{
Printf ("% d", (int) data );
Return RET_ OK;
}
Static int print_int2 (void * data)
{
Printf ("% d", * (int *) data );
Return RET_ OK;
}
Static int print_int (void * data)
{
Printf ("% d", (int) data );
Return RET_ OK;
}
Static int print_int2 (void * data)
{
Printf ("% d", * (int *) data );
Return RET_ OK;
} Call time:
[Html]
Int I = 0;
Int * pi = & I;
PRINTSTAT ("% s", "Integer ")
For (; I <10; I ++)
Print (print_int, (void *) I );
Printf ("\ n ");
PRINTSTAT ("% s", "Integer ")
For (I = 0; I <10; I ++)
Print (print_int2, (void *) pi );
Printf ("\ n ");
Int I = 0;
Int * pi = & I;
PRINTSTAT ("% s", "Integer ")
For (; I <10; I ++)
Print (print_int, (void *) I );
Printf ("\ n ");
PRINTSTAT ("% s", "Integer ")
For (I = 0; I <10; I ++)
Print (print_int2, (void *) pi );
Printf ("\ n ");
When print_int is called, the int type is forcibly converted to the void * type. When print_int2 is called, the int * type is forcibly converted to the void * type. Surprisingly, both methods can output the correct results. In fact, the method provided by print_int is just a coincidence, because int Is 4 bytes, and void * is also 4 bytes, there is no error in forced conversion! However, if it is float type, it will not work if you use a method similar to print_int, and gcc prompts that conversion cannot be performed. The safe method is to convert int * to void *, convert void * to int * in the print_int processing function, and then unreference it.
2. Use "#"
The Code provided by method 1 shows the power. Next we will look at another Implementation of generics:
[Code list]
Printtest. h
[Html]
# Ifndef _ PRINTTEST_H __
# Define _ PRINTTEST_H __
# Define GNERIC_PRINT (TYPE, FORMAT, SUFFIX )\
Int print # SUFFIX (TYPE data )\
{\
Printf (FORMAT, data );\
Return 0 ;\
}
# Endif
# Ifndef _ PRINTTEST_H __
# Define _ PRINTTEST_H __
# Define GNERIC_PRINT (TYPE, FORMAT, SUFFIX )\
Int print # SUFFIX (TYPE data )\
{\
Printf (FORMAT, data );\
Return 0 ;\
}
# Endif
Printtest. c
[Html]
# Include <stdio. h>
# Include <stdlib. h>
# Include "printtest. h"
# Define DEBUG 0
# Define PRINTSTAT (FORMAT, STAT )\
Printf ("****************" FORMAT "Test ****************\ n "\
, STAT );
# Define DEBUG_PRINT (FORMAT, VALUE) printf ("File % s line % d :"\
# VALUE "=" FORMAT "\ n "\
,__ FILE __, _ LINE __, VALUE \
);
Enum {
RET_ OK,
RET_FAIL
};
GNERIC_PRINT (int, "% d", _ int)
GNERIC_PRINT (float, "% f", _ float)
GNERIC_PRINT (char *, "% s", _ str)
Int main (void)
{
Int I = 0;
Float f = 2.6;
Char * test = "Generics test! ";
# If DEBUG
DEBUG_PRINT ("% f", f)
# Endif
PRINTSTAT ("% s", "Integer ")
For (; I <10; I ++)
Print_int (I );
Printf ("\ n ");
PRINTSTAT ("% s", "Float ")
Print_float (f );
Printf ("\ n ");
PRINTSTAT ("% s", "String ")
Print_str (test );
Printf ("\ n ");
Return RET_ OK;
}
# Include <stdio. h>
# Include <stdlib. h>
# Include "printtest. h"
# Define DEBUG 0
# Define PRINTSTAT (FORMAT, STAT )\
Printf ("****************" FORMAT "Test ****************\ n "\
, STAT );
# Define DEBUG_PRINT (FORMAT, VALUE) printf ("File % s line % d :"\
# VALUE "=" FORMAT "\ n "\
,__ FILE __, _ LINE __, VALUE \
);
Enum {
RET_ OK,
RET_FAIL
};
GNERIC_PRINT (int, "% d", _ int)
GNERIC_PRINT (float, "% f", _ float)
GNERIC_PRINT (char *, "% s", _ str)
Int main (void)
{
Int I = 0;
Float f = 2.6;
Char * test = "Generics test! ";
# If DEBUG
DEBUG_PRINT ("% f", f)
# Endif
PRINTSTAT ("% s", "Integer ")
For (; I <10; I ++)
Print_int (I );
Printf ("\ n ");
PRINTSTAT ("% s", "Float ")
Print_float (f );
Printf ("\ n ");
PRINTSTAT ("% s", "String ")
Print_str (test );
Printf ("\ n ");
Return RET_ OK;
}
Makefile
[Html]
OBJS = printtest. o
TARGET = printtest
SRC = printtest. c
All: $ (OBJS)
Gcc-g $ (OBJS)-o $ (TARGET)
$ (OBJS): printtest. s
Gcc-g-c printtest. s-o $ (OBJS)
Printtest. s: printtest. I
Gcc-g-S printtest. I-o printtest. s
Printtest. I: $ (SRC)
Gcc-g-E $ (SRC)-o printtest. I
Clean:
Rm *~ *. O *. s *. I $ (TARGET)
OBJS = printtest. o
TARGET = printtest
SRC = printtest. c
All: $ (OBJS)
Gcc-g $ (OBJS)-o $ (TARGET)
$ (OBJS): printtest. s
Gcc-g-c printtest. s-o $ (OBJS)
Printtest. s: printtest. I
Gcc-g-S printtest. I-o printtest. s
Printtest. I: $ (SRC)
Gcc-g-E $ (SRC)-o printtest. I
Clean:
Rm *~ *. O *. s *. I $ (TARGET)
* SRC = printtest. c, instead of SRC = printtest. c printtest. h. The latter cannot properly complete preprocessing, which is confusing here. Can someone explain the reason?
Or use
[Html]
Gcc printtest. h printtest. c-o printtest
Gcc printtest. h printtest. c-o printtest
Compile
[Running result]
[Html]
* ************** Integer Test ****************
0123456789
* Float Test ****************
2.600000
* ************** String Test ****************
Generics test!
* ************** Integer Test ****************
0123456789
* Float Test ****************
2.600000
* ************** String Test ****************
Generics test! Www.2cto.com
This method seems very dazzling, but it is very prone to errors when the amount of code is huge. Therefore, we recommend that you use function pointers to implement generics.
Author; tandesir