写了一段时间C++后,真心感觉STL里的容器是个好东西。一个容器可以容纳任意类型,容器对外的接口可以操作任意类型的数据,甚至包括自定义类型的数据。这种泛型编程的思想,对于大型项目而言是非常有好处的。
对于C而言,想实现泛型编程并非易事,甚至可以说非常繁琐,一大堆坑。最主要也没有现成的轮子可用。当然也有一些通过宏实现了泛型的基础功能,但是可读性,可调试性太差了。
于是就想自己造一个轮子,实现基于C对窗口(顺序表)的泛化,目标就是实现不同类型下,规范接口的一致性。抛砖引玉。
轮子用法int main( void )
{
// 1、创建一个窗口,并初始化它,大小为10,类型为double
ValueWindowSquential tmp;
InitValueWindow( &tmp, kValueTypeList[ DOUBLE ], 10 );
double insert_data = 0;
for ( int i = 0; i < tmp.max_size; i++ )
{
// 2、填充这个窗口,直到窗口填满
insert_data = i * 10;
if ( kWindowAlreadyFull == ValueWindowFixedInsert( &tmp, &insert_data ) )
{
// 3、打印整个窗口
printf( "start sort rn" );
ShowTheWindow( &tmp );
// 4、整个窗口排序
ValueWindowSelectSort( &tmp );
// 5、打印排序后的窗口
printf( "end sort rn" );
ShowTheWindow( &tmp );
break;
}
}
printf( "test generics rn" );
return 0;
}
打印log如下:
这时想换成创建一个uint8_t类型的串口,只需要改两个地方,这两个地方在C++里也避免不了。
int main( void )
{
ValueWindowSquential tmp;
InitValueWindow( &tmp, kValueTypeList[ UINT8 ], 10 );
uint8_t insert_data = 0;
for ( int i = 0; i < tmp.max_size; i++ )
{
insert_data = ( tmp.max_size - i ) * 1;
if ( kWindowAlreadyFull == ValueWindowFixedInsert( &tmp, &insert_data ) )
{
printf( "start sort rn" );
ShowTheWindow( &tmp );
ValueWindowSelectSort( &tmp );
printf( "end sort rn" );
ShowTheWindow( &tmp );
break;
}
}
printf( "test generics rn" );
return 0;
}
大体流程
- 首先初始化一个空窗口对象,然后调用 InitValueWindow 传入窗口类型,大小,然后初始化它。
- 调用 ValueWindowFixedInsert 往窗口中插入值,直到窗口满后反馈状态。
- 打印整个窗口
- 对窗口排序
- 打印整个窗口
这里的泛型主要通过查表实现了,将希望包含的类型加入表中,然后初始化时传入其类型和大小。
插入数据的时候,需要保证数据类型和窗口类型统一,这算是个局限性了。
窗口被填充完毕后,会有反馈窗口状态,这时可以调用 ShowTheWindow 将原始窗口打印。
在调用 ValueWindowSelectSort 将窗口排序。排序完后再次打印。
可以看到除了初始化的时候,需要设定窗口的类型,这和 std::vector< double > 没什么两样,插入数据时需要调用者确保数据类型与窗口统一。
部分源码#ifndef __TEST_GENERICS_h #define __TEST_GENERICS_h #include#include #include #include #include typedef signed char int8_t; typedef unsigned char uint8_t; typedef signed short int16_t; typedef unsigned short uint16_t; typedef signed int int32_t; typedef unsigned int uint32_t; typedef enum { UINT8 = 0, INT, FLOAT, DOUBLE, ERROR } TypeName; const char* kValueTypeList[ ERROR + 1 ] = { "uint8_t", "int", "float", "double", "error", }; TypeName ChangeStringToEnum( const char* tmp ); typedef struct ValueWindowSquential { char* type; void* data; uint32_t max_size; uint32_t sequence; } ValueWindowSquential; void InitValueWindow( ValueWindowSquential* tmp, const char* type, uint32_t max_size ); void ResetValueWindow( ValueWindowSquential* tmp ); typedef enum { kWindowIsNotFull = ( 0 ), kWindowIsSliding, kWindowCanNotInsert, kWindowInputFail, } SlideWindowState; SlideWindowState ValueWindowSlideInsert( ValueWindowSquential* tmp, void* data ); typedef enum { kWindowNotFull = ( 0 ), kWindowAlreadyFull, kFixWindowCanNotInsert, kFixWindowInputFail, } FixedWindowState; FixedWindowState ValueWindowFixedInsert( ValueWindowSquential* tmp, void* data ); void ShowTheWindow( ValueWindowSquential* tmp ); #endif // __TEST_GENERICS_h
#include "test_generics.h"
#include "generics_impl.h"
TypeName ChangeStringToEnum( const char* tmp )
{
assert( tmp != NULL );
TypeName return_tmp = ERROR;
if ( strcmp( tmp, kValueTypeList[ UINT8 ] ) == 0 )
{
return_tmp = UINT8;
}
else if ( strcmp( tmp, kValueTypeList[ FLOAT ] ) == 0 )
{
return_tmp = FLOAT;
}
else if ( strcmp( tmp, kValueTypeList[ DOUBLE ] ) == 0 )
{
return_tmp = DOUBLE;
}
else if ( strcmp( tmp, kValueTypeList[ INT ] ) == 0 )
{
return_tmp = INT;
}
else
{
printf( "error char* input !!!" );
assert( 0 );
}
return return_tmp;
}
// 初始化窗口
// Initialize window
void InitValueWindow( ValueWindowSquential* tmp, const char* type, uint32_t max_size )
{
assert( tmp != NULL );
tmp->type = ( char* ) malloc( strlen( type ) * sizeof( char ) );
strncpy( tmp->type, type, strlen( type ) );
tmp->max_size = max_size;
tmp->sequence = 0;
switch ( ChangeStringToEnum( tmp->type ) )
{
case UINT8: {
tmp->data = ( uint8_t* ) malloc( max_size * sizeof( uint8_t ) );
memset( tmp->data, 0, tmp->max_size );
}
break;
case INT: {
tmp->data = ( int* ) malloc( max_size * sizeof( int ) );
memset( tmp->data, 0, tmp->max_size );
}
break;
case FLOAT: {
tmp->data = ( float* ) malloc( max_size * sizeof( float ) );
memset( tmp->data, 0, tmp->max_size );
}
break;
case DOUBLE: {
tmp->data = ( double* ) malloc( max_size * sizeof( double ) );
memset( tmp->data, 0, tmp->max_size );
}
break;
default: {
printf( "error tmp->type input !!!" );
assert( 0 );
}
break;
}
printf( "type is : %s , number is : %d rn", tmp->type, max_size );
}
// 重置/销毁窗口
void ResetValueWindow( ValueWindowSquential* tmp )
{
tmp->sequence = 0;
tmp->max_size = 0;
if ( tmp->data != NULL )
{
free( tmp->data );
tmp->data = NULL;
}
if ( tmp->type != NULL )
{
free( tmp->type );
tmp->type = NULL;
}
}
// 滑动往窗口插入数据
SlideWindowState ValueWindowSlideInsert( ValueWindowSquential* tmp, void* data )
{
SlideWindowState return_tmp = kWindowIsNotFull;
switch ( ChangeStringToEnum( tmp->type ) )
{
case UINT8: {
uint8_t* tmp_buffer = ( uint8_t* ) tmp->data;
for ( int i = 1; i < tmp->max_size; i++ )
{
tmp_buffer[ i - 1 ] = tmp_buffer[ i ];
}
uint8_t* res = ( uint8_t* ) data;
tmp_buffer[ tmp->max_size - 1 ] = *res;
}
break;
case INT: {
int* tmp_buffer = ( int* ) tmp->data;
for ( int i = 1; i < tmp->max_size; i++ )
{
tmp_buffer[ i - 1 ] = tmp_buffer[ i ];
}
int* res = ( int* ) data;
tmp_buffer[ tmp->max_size - 1 ] = *res;
}
break;
case FLOAT: {
float* tmp_buffer = ( float* ) tmp->data;
for ( int i = 1; i < tmp->max_size; i++ )
{
tmp_buffer[ i - 1 ] = tmp_buffer[ i ];
}
float* res = ( float* ) data;
tmp_buffer[ tmp->max_size - 1 ] = *res;
}
break;
case DOUBLE: {
double* tmp_buffer = ( double* ) tmp->data;
for ( int i = 1; i < tmp->max_size; i++ )
{
tmp_buffer[ i - 1 ] = tmp_buffer[ i ];
}
double* res = ( double* ) data;
tmp_buffer[ tmp->max_size - 1 ] = *res;
}
break;
default: {
printf( "error tmp->type input !!!" );
assert( 0 );
}
break;
}
if ( ++tmp->sequence > tmp->max_size )
{
return_tmp = kWindowIsSliding;
tmp->sequence = tmp->max_size;
}
return return_tmp;
}
// 插入数据直到填满整个窗口
FixedWindowState ValueWindowFixedInsert( ValueWindowSquential* tmp, void* data )
{
FixedWindowState return_tmp = kWindowNotFull;
switch ( ChangeStringToEnum( tmp->type ) )
{
case UINT8: {
uint8_t* tmp_buffer = ( uint8_t* ) tmp->data;
uint8_t* res = ( uint8_t* ) data;
tmp_buffer[ tmp->sequence ] = *res;
}
break;
case INT: {
int* tmp_buffer = ( int* ) tmp->data;
int* res = ( int* ) data;
tmp_buffer[ tmp->sequence ] = *res;
}
break;
case FLOAT: {
float* tmp_buffer = ( float* ) tmp->data;
float* res = ( float* ) data;
tmp_buffer[ tmp->sequence ] = *res;
}
break;
case DOUBLE: {
double* tmp_buffer = ( double* ) tmp->data;
double* res = ( double* ) data;
tmp_buffer[ tmp->sequence ] = *res;
}
break;
default: {
printf( "error tmp->type input !!!" );
assert( 0 );
}
break;
}
if ( ++tmp->sequence >= tmp->max_size )
{
tmp->sequence = 0;
return_tmp = kWindowAlreadyFull;
}
return return_tmp;
}
// 打印窗口内全部值
void ShowTheWindow( ValueWindowSquential* tmp )
{
// printf("current_type:{%d}", ChangeStringToEnum(tmp->type));
switch ( ChangeStringToEnum( tmp->type ) )
{
case UINT8: {
uint8_t* msg = ( uint8_t* ) tmp->data;
for ( int i = 0; i < tmp->max_size; ++i )
{
printf( "i : {%d} , %d rn", i, msg[ i ] );
}
}
break;
case INT: {
int* msg = ( int* ) tmp->data;
for ( int i = 0; i < tmp->max_size; ++i )
{
printf( "i : {%d} , %d rn", i, msg[ i ] );
}
}
break;
case FLOAT: {
float* msg = ( float* ) tmp->data;
for ( int i = 0; i < tmp->max_size; ++i )
{
printf( "i : {%d} , %f rn", i, msg[ i ] );
}
}
break;
case DOUBLE: {
double* msg = ( double* ) tmp->data;
for ( int i = 0; i < tmp->max_size; ++i )
{
printf( "i : {%d} , %f rn", i, msg[ i ] );
}
}
break;
default: {
printf( "error tmp->type input !!!" );
assert( 0 );
}
break;
}
}
int main( void )
{
ValueWindowSquential tmp;
InitValueWindow( &tmp, kValueTypeList[ DOUBLE ], 10 );
double insert_data = 0;
for ( int i = 0; i < tmp.max_size; i++ )
{
insert_data = ( tmp.max_size - i ) * 10;
if ( kWindowAlreadyFull == ValueWindowFixedInsert( &tmp, &insert_data ) )
{
printf( "start sort rn" );
ShowTheWindow( &tmp );
ValueWindowSelectSort( &tmp );
printf( "end sort rn" );
ShowTheWindow( &tmp );
break;
}
}
ResetValueWindow(&tmp);
printf( "test generics rn" );
return 0;
}
这是最开始的一版源码,基本的思路是基于 void* 实现对窗口的泛化,把窗口的地址,大小,类型 在初始化时设定好,以后所有的结构便基于这些信息,实现接口一致性。
目前实现了两种窗口类型, ValueWindowSlideInsert (滑动窗) 和 ValueWindowFixedInsert(固定窗) 。 两者不同之处只是插入数据时的处理不同。滑动窗遵循FIFO模型,即先入先出,窗口状态有未满和开始滑动,一般开始滑动后再对窗口进行操作。
固定窗有未满和已满两种状态,已满后会清空窗口,重新开始填充,这也是两种常见的窗口模型。
在STL里,当有一些底层数据结构去存储数据时,要有一些容器的方法(算法),比如排序等,这里先实现了一些基础的泛型算法接口:
#ifndef GENERICS_IMPL_H #define GENERICS_IMPL_H #include#include "test_generics.h" static void swap( ValueWindowSquential* tmp, uint32_t i, uint32_t j ) { assert( tmp != NULL ); // assert( i > tmp->max_size || j > tmp->max_size ); // assert( i >= tmp->max_size || j >= tmp->max_size ); switch ( ChangeStringToEnum( tmp->type ) ) { case UINT8: { uint8_t* tmp_buffer = ( uint8_t* ) tmp->data; uint8_t res = tmp_buffer[ i ]; tmp_buffer[ i ] = tmp_buffer[ j ]; tmp_buffer[ j ] = res; } break; case INT: { int* tmp_buffer = ( int* ) tmp->data; int res = tmp_buffer[ i ]; tmp_buffer[ i ] = tmp_buffer[ j ]; tmp_buffer[ j ] = res; } break; case FLOAT: { float* tmp_buffer = ( float* ) tmp->data; float res = tmp_buffer[ i ]; tmp_buffer[ i ] = tmp_buffer[ j ]; tmp_buffer[ j ] = res; } break; case DOUBLE: { double* tmp_buffer = ( double* ) tmp->data; double res = tmp_buffer[ i ]; tmp_buffer[ i ] = tmp_buffer[ j ]; tmp_buffer[ j ] = res; } break; default: { printf( "error tmp->type input !!!" ); assert( 0 ); } break; } } static inline void ValueWindowBubbleSort( ValueWindowSquential* tmp ) { switch ( ChangeStringToEnum( tmp->type ) ) { case UINT8: { uint8_t* tmp_buffer = ( uint8_t* ) tmp->data; bool is_end_loop = true; for ( int i = 0; i < tmp->max_size && is_end_loop; i++ ) { is_end_loop = false; for ( int j = tmp->max_size - 1; j >= i; j-- ) { if ( tmp_buffer[ j - 1 ] > tmp_buffer[ j ] ) { swap( tmp, j - 1, j ); is_end_loop = true; } } } } break; case INT: { int* tmp_buffer = ( int* ) tmp->data; bool is_end_loop = true; for ( int i = 0; i < tmp->max_size && is_end_loop; i++ ) { is_end_loop = false; for ( int j = tmp->max_size - 1; j >= i; j-- ) { if ( tmp_buffer[ j - 1 ] > tmp_buffer[ j ] ) { swap( tmp, j - 1, j ); is_end_loop = true; } } } } break; case FLOAT: { float* tmp_buffer = ( float* ) tmp->data; bool is_end_loop = true; for ( int i = 0; i < tmp->max_size && is_end_loop; i++ ) { is_end_loop = false; for ( int j = tmp->max_size - 1; j >= i; j-- ) { if ( tmp_buffer[ j - 1 ] > tmp_buffer[ j ] ) { swap( tmp, j - 1, j ); is_end_loop = true; } } } } break; case DOUBLE: { double* tmp_buffer = ( double* ) tmp->data; bool is_end_loop = true; for ( int i = 0; i < tmp->max_size && is_end_loop; i++ ) { is_end_loop = false; for ( int j = tmp->max_size - 1; j >= i; j-- ) { if ( tmp_buffer[ j - 1 ] > tmp_buffer[ j ] ) { swap( tmp, j - 1, j ); is_end_loop = true; } } } } break; default: { printf( "error tmp->type input !!!" ); assert( 0 ); } break; } } static inline void ValueWindowSelectSort( ValueWindowSquential* tmp ) { switch ( ChangeStringToEnum( tmp->type ) ) { case UINT8: { uint8_t* tmp_buffer = ( uint8_t* ) tmp->data; int tmp_data = 0; for ( int i = 0; i < tmp->max_size; i++ ) { tmp_data = i; for ( int j = i; j < tmp->max_size; j++ ) { if ( tmp_buffer[ tmp_data ] > tmp_buffer[ j ] ) { tmp_data = j; } } if ( tmp_data != i ) { swap( tmp, i, tmp_data ); } } } break; case INT: { int* tmp_buffer = ( int* ) tmp->data; int tmp_data = 0; for ( int i = 0; i < tmp->max_size; i++ ) { tmp_data = i; for ( int j = i; j < tmp->max_size; j++ ) { if ( tmp_buffer[ tmp_data ] > tmp_buffer[ j ] ) { tmp_data = j; } } if ( tmp_data != i ) { swap( tmp, i, tmp_data ); } } } break; case FLOAT: { float* tmp_buffer = ( float* ) tmp->data; int tmp_data = 0; for ( int i = 0; i < tmp->max_size; i++ ) { tmp_data = i; for ( int j = i; j < tmp->max_size; j++ ) { if ( tmp_buffer[ tmp_data ] > tmp_buffer[ j ] ) { tmp_data = j; } } if ( tmp_data != i ) { swap( tmp, i, tmp_data ); } } } break; case DOUBLE: { double* tmp_buffer = ( double* ) tmp->data; int tmp_data = 0; for ( int i = 0; i < tmp->max_size; i++ ) { tmp_data = i; for ( int j = i; j < tmp->max_size; j++ ) { if ( tmp_buffer[ tmp_data ] > tmp_buffer[ j ] ) { tmp_data = j; } } if ( tmp_data != i ) { swap( tmp, i, tmp_data ); } } } break; default: { printf( "error tmp->type input !!!" ); assert( 0 ); } break; } } static inline void ValueWindowInsertSort( ValueWindowSquential* tmp ) { switch ( ChangeStringToEnum( tmp->type ) ) { case UINT8: { uint8_t* tmp_buffer = ( uint8_t* ) tmp->data; uint8_t tmp_data = 0; int j = 0; for ( int i = 1; i < tmp->max_size; i++ ) { if ( tmp_buffer[ i ] < tmp_buffer[ i - 1 ] ) { tmp_data = tmp_buffer[ i ]; // TAG : 数据整体向后迁移,寻找数值更大的成员 for ( j = i - 1; tmp_buffer[ j ] > tmp_data && j >= 0; j-- ) { tmp_buffer[ j + 1 ] = tmp_buffer[ j ]; } tmp_buffer[ j + 1 ] = tmp_data; } } } break; case INT: { int* tmp_buffer = ( int* ) tmp->data; int tmp_data = 0; int j = 0; for ( int i = 1; i < tmp->max_size; i++ ) { if ( tmp_buffer[ i ] < tmp_buffer[ i - 1 ] ) { tmp_data = tmp_buffer[ i ]; // TAG : 数据整体向后迁移,寻找数值更大的成员 for ( j = i - 1; tmp_buffer[ j ] > tmp_data && j >= 0; j-- ) { tmp_buffer[ j + 1 ] = tmp_buffer[ j ]; } tmp_buffer[ j + 1 ] = tmp_data; } } } break; case FLOAT: { float* tmp_buffer = ( float* ) tmp->data; float tmp_data = 0; int j = 0; for ( int i = 1; i < tmp->max_size; i++ ) { if ( tmp_buffer[ i ] < tmp_buffer[ i - 1 ] ) { tmp_data = tmp_buffer[ i ]; // TAG : 数据整体向后迁移,寻找数值更大的成员 for ( j = i - 1; tmp_buffer[ j ] > tmp_data && j >= 0; j-- ) { tmp_buffer[ j + 1 ] = tmp_buffer[ j ]; } tmp_buffer[ j + 1 ] = tmp_data; } } } break; case DOUBLE: { double* tmp_buffer = ( double* ) tmp->data; double tmp_data = 0; int j = 0; for ( int i = 1; i < tmp->max_size; i++ ) { if ( tmp_buffer[ i ] < tmp_buffer[ i - 1 ] ) { tmp_data = tmp_buffer[ i ]; // TAG : 数据整体向后迁移,寻找数值更大的成员 for ( j = i - 1; tmp_buffer[ j ] > tmp_data && j >= 0; j-- ) { tmp_buffer[ j + 1 ] = tmp_buffer[ j ]; } tmp_buffer[ j + 1 ] = tmp_data; } } } break; default: { printf( "error tmp->type input !!!" ); assert( 0 ); } break; } } #endif // GENERICS_IMPL_H



