Created
June 10, 2015 03:49
-
-
Save masterl/b355c779af940cfda058 to your computer and use it in GitHub Desktop.
Struct with generic fields defined on runtime
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The MIT License (MIT) | |
// Copyright (c) 2015 Leonardo de Oliveira Lourenço | |
// Permission is hereby granted, free of charge, to any person obtaining a copy | |
// of this software and associated documentation files (the "Software"), to deal | |
// in the Software without restriction, including without limitation the rights | |
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
// copies of the Software, and to permit persons to whom the Software is | |
// furnished to do so, subject to the following conditions: | |
// The above copyright notice and this permission notice shall be included in all | |
// copies or substantial portions of the Software. | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
// SOFTWARE. | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
typedef enum | |
{ | |
false = 0, | |
true | |
} Bool; | |
typedef enum | |
{ | |
INT = 1, | |
UINT, | |
LONG, | |
ULONG, | |
FLOAT, | |
DOUBLE, | |
STRING | |
} FieldType; | |
typedef union | |
{ | |
int i; | |
unsigned int ui; | |
long l; | |
unsigned long ul; | |
float f; | |
double d; | |
char *s; | |
} GenericField; | |
typedef struct | |
{ | |
int size; | |
GenericField *fields; | |
FieldType *types; | |
char **names; | |
} GenericStruct; | |
void init_gen_struct( GenericStruct *element ); | |
void destroy_gen_struct( GenericStruct *element ); | |
Bool add_field( GenericStruct *element, char *field_name, GenericField value, FieldType type ); | |
void add_fields( GenericStruct *element ); | |
void clean_buffer( void ); | |
char *get_number_format( FieldType type ); | |
Bool read_number( FieldType type, void * ); | |
Bool read_numeric_field( GenericField *field, FieldType field_type ); | |
void read_string( char *str, int size ); | |
void show_field( GenericStruct *element, int index ); | |
void show_fields( GenericStruct *element ); | |
int main( void ) | |
{ | |
GenericStruct example; | |
init_gen_struct( &example ); | |
add_fields( &example ); | |
show_fields( &example ); | |
destroy_gen_struct( &example ); | |
return 0; | |
} | |
void init_gen_struct( GenericStruct *element ) | |
{ | |
if( element ) | |
{ | |
element->size = 0; | |
element->fields = NULL; | |
element->types = NULL; | |
element->names = NULL; | |
} | |
} | |
void destroy_gen_struct( GenericStruct *element ) | |
{ | |
int i; | |
if( !element ) | |
{ | |
return; | |
} | |
if( element->fields ) | |
{ | |
free( element->fields ); | |
element->fields = NULL; | |
} | |
if( element->types ) | |
{ | |
free( element->types ); | |
element->types = NULL; | |
} | |
if( element->names ) | |
{ | |
for( i = 0; i < element->size; ++i ) | |
{ | |
if( element->names[i] ) | |
{ | |
free( element->names[i] ); | |
} | |
} | |
free( element->names ); | |
element->names = NULL; | |
} | |
element->size = 0; | |
} | |
Bool add_field( GenericStruct *element, char *field_name, GenericField value, FieldType type ) | |
{ | |
GenericStruct temp; | |
init_gen_struct( &temp ); | |
if( !element ) | |
{ | |
return false; | |
} | |
temp.size = element->size + 1; | |
temp.fields = (GenericField *)realloc( element->fields, temp.size ); | |
temp.types = (FieldType *)realloc( element->types, temp.size ); | |
temp.names = (char **)realloc( element->names, temp.size ); | |
if( ( !temp.fields ) || ( !temp.types ) || ( !temp.names ) ) | |
{ | |
printf( "\nCouldn't add new field. Memory allocation error\n" ); | |
// Copying any fields that were successfully allocated so the memory can be freed later | |
if( temp.fields ) | |
{ | |
element->fields = temp.fields; | |
} | |
if( temp.types ) | |
{ | |
element->types = temp.types; | |
} | |
if( temp.names ) | |
{ | |
element->names = temp.names; | |
} | |
return false; | |
} | |
temp.names[element->size] = (char *)malloc( ( strlen( field_name ) + 1 ) * sizeof( char ) ); | |
if( temp.names[element->size] == NULL ) | |
{ | |
printf( "\nCouldn't add new field name. Memory allocation error\n" ); | |
return false; | |
} | |
strcpy( temp.names[element->size], field_name ); | |
temp.types[element->size] = type; | |
if( type == STRING ) | |
{ | |
temp.fields[element->size].s = (char *)malloc( ( strlen( value.s ) + 1 ) * sizeof( char ) ); | |
if( temp.fields[element->size].s == NULL ) | |
{ | |
printf( "\nError adding new string field. Memory allocation error\n" ); | |
} | |
else | |
{ | |
strcpy( temp.fields[element->size].s, value.s ); | |
} | |
} | |
else | |
{ | |
temp.fields[element->size] = value; | |
} | |
element->fields = temp.fields; | |
element->types = temp.types; | |
element->names = temp.names; | |
element->size = temp.size; | |
return true; | |
} | |
void add_fields( GenericStruct *element ) | |
{ | |
int option; | |
char field_name[61]; | |
char str_field[512]; | |
GenericField field; | |
FieldType field_type; | |
do | |
{ | |
printf( "\n----- ADD FIELD -----\n" ); | |
printf( "\nWhat's the new value type?" | |
"\n(An invalid choice will cancel the operation)" | |
"\n 1 - int" | |
"\n 2 - unsigned int" | |
"\n 3 - long" | |
"\n 4 - unsigned long" | |
"\n 5 - float" | |
"\n 6 - double" | |
"\n 7 - string" | |
"\n > " ); | |
read_number( INT, &option ); | |
if( ( option < INT ) || ( option > STRING ) ) | |
{ | |
printf( "%d\n", option ); | |
printf( "\nCanceled...\n" ); | |
return; | |
} | |
printf( "\nType in the field name (max size: 60) " ); | |
read_string( field_name, 61 ); | |
field_type = option; | |
if( field_type == STRING ) | |
{ | |
printf( "Type in the field value (string): " ); | |
read_string( str_field, 512 ); | |
if( str_field[0] == '\0' ) | |
{ | |
printf( "Field can't be blank\n" ); | |
continue; | |
} | |
field.s = str_field; | |
} | |
else | |
{ | |
printf( "Type in the field value (numeric): " ); | |
read_numeric_field( &field, field_type ); | |
} | |
add_field( element, field_name, field, field_type ); | |
} while( true ); | |
} | |
void clean_buffer( void ) | |
{ | |
while( getchar() != '\n' ) | |
; | |
} | |
char *get_number_format( FieldType type ) | |
{ | |
int index = type - 1; | |
static char formats[6][4] = {"%d", "%u", "%ld", "%lu", "%f", "%d"}; | |
if( ( index >= 0 ) && ( index < 6 ) ) | |
{ | |
return formats[index]; | |
} | |
return NULL; | |
} | |
Bool read_number( FieldType type, void *ptr ) | |
{ | |
int fields_read; | |
Bool invalid_input = true; | |
char *format = get_number_format( type ); | |
if( !format || !ptr ) | |
{ | |
return false; | |
} | |
do | |
{ | |
printf( "\nReading with format = %s\n", format ); | |
fields_read = scanf( format, ptr ); | |
if( fields_read != 1 ) | |
{ | |
printf( "Please type in a valid integer number...\n" ); | |
} | |
else | |
{ | |
invalid_input = false; | |
} | |
clean_buffer(); | |
} while( invalid_input ); | |
return true; | |
} | |
Bool read_numeric_field( GenericField *field, FieldType field_type ) | |
{ | |
Bool result; | |
switch( field_type ) | |
{ | |
case INT: | |
result = read_number( field_type, &field->i ); | |
break; | |
case UINT: | |
result = read_number( field_type, &field->ui ); | |
break; | |
case LONG: | |
result = read_number( field_type, &field->l ); | |
break; | |
case ULONG: | |
result = read_number( field_type, &field->ul ); | |
break; | |
case FLOAT: | |
result = read_number( field_type, &field->f ); | |
break; | |
case DOUBLE: | |
result = read_number( field_type, &field->d ); | |
break; | |
default: | |
result = false; | |
} | |
return result; | |
} | |
void read_string( char *str, int size ) | |
{ | |
char format[20]; | |
// -1 for \0 | |
snprintf( format, 20, " %%%d[^\n]", size - 1 ); | |
scanf( format, str ); | |
clean_buffer(); | |
} | |
void show_field( GenericStruct *element, int index ) | |
{ | |
if( ( !element ) || ( index < 0 ) || ( index >= element->size ) ) | |
{ | |
return; | |
} | |
printf( " Field name: %s\n", element->names[index] ); | |
switch( element->types[index] ) | |
{ | |
case INT: | |
printf( "Field value: %d\n", element->fields[index].i ); | |
break; | |
case UINT: | |
printf( "Field value: %u\n", element->fields[index].ui ); | |
break; | |
case LONG: | |
printf( "Field value: %ld\n", element->fields[index].l ); | |
break; | |
case ULONG: | |
printf( "Field value: %lu\n", element->fields[index].ul ); | |
break; | |
case FLOAT: | |
printf( "Field value: %f\n", element->fields[index].f ); | |
break; | |
case DOUBLE: | |
printf( "Field value: %lf\n", element->fields[index].d ); | |
break; | |
case STRING: | |
printf( "Field value: %s\n", element->fields[index].s ); | |
break; | |
} | |
} | |
void show_fields( GenericStruct *element ) | |
{ | |
int i; | |
if( !element ) | |
{ | |
return; | |
} | |
if( element->size == 0 ) | |
{ | |
printf( "\nStruct have no fields!\n" ); | |
return; | |
} | |
for( i = 0; i < element->size; ++i ) | |
{ | |
show_field( element, i ); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Naive implementation just for an example