Programación procedimental (C)

Entrada y salida

Replicador de la entrada



int main()
{
	int ch = 0;

	while ( ( ch = fgetc(stdin) ) != EOF )
	{
		putchar('[');
		putchar(ch);
		putchar(']');
	}

	return 0;
}
]]>
Lee caracteres de la entrada estándar y los replica en la salida encerrados entre corchetes. Obtener código fuente.

Expresiones y condicionales

Validador de ensayos

El siguiente código surgió de aplicar el ciclo de desarrollo para resolver el problema. La solución es modular, escalable, documentada y aplica otras buenas prácticas de programación. Soluciones con menos instrucciones son posibles, pero el código es menos escalable. Programas "hechos a la carrera" son convenientes para situaciones de emergencia o programación competitiva. No es la norma en el ejercicio de la disciplina.



int main()
{
	// Leer el numero minimo de palabras
	word_count_t min_words = 0; // 0llu
	if ( scanf(WORD_COUNT_FORMAT "\n", &min_words) != 1 )
		return fprintf(stderr, "error: wrong min words\n"), ERROR_WRONG_MIN_WORDS;

	// Leer el ensayo contando palabras y parrafos
	read_and_count_words_and_paragraphs();
  #if 0
	printf("paragraphs: " WORD_COUNT_FORMAT "\n", found_paragraph_count);
  #endif

	// Calcular el minimo de parrafos
	word_count_t min_paragraphs = calculate_min_paragraphs(min_words);

	// Revisar si cumple requisitos
	bool requirements_met = are_requirements_met(min_words, min_paragraphs);
	//(void)requirements_met;

	// Imprimir las estadisticas
	print_statistics(requirements_met, min_words, min_paragraphs);

	return 0;
}
]]>
main.c: Usa las subrutinas del validador de ensayos para crear un programa en línea de comandos. Obtener código fuente.



/* // stdbool.h
#ifndef __cplusplus
	typedef char bool;
	#define true 1
	#define false 0
#endif
*/

#define ERROR_WRONG_MIN_WORDS 1

// Function prototype or function declaration
void read_and_count_words_and_paragraphs();
word_count_t calculate_min_paragraphs(word_count_t min_words);
bool are_requirements_met(word_count_t min_words, word_count_t min_paragraphs);
void print_statistics(bool requirements_met, word_count_t min_words, word_count_t min_paragraphs);

#endif // ESSAY_VALIDATOR_H
]]>
essay_validator.h: Encabezado común para los dos archivos fuente. Obtener código fuente.

#include 
#include 
#include 

#include "essay_validator.h"

// Global variables
// Sea la cantidad de palabras encontradas cero
word_count_t found_word_count = 0;
// Sea la cantidad de parrafos encontrados cero
word_count_t found_paragraph_count = 0;


// Leer el ensayo contando palabras y parrafos:
// Function definition
void read_and_count_words_and_paragraphs()
{
	// Sea longitud de la palabra cero
	word_count_t word_length = 0; // 0ull

	// Repetir mientras hayan letras
	int letter = 0;
	while ( ( letter = fgetc(stdin) ) != EOF )
	{
		// Si la letra es un cambio de linea o un espacio en blanco
		if ( isspace(letter) )
		{
			// Si la longitud de la palabra es 3 o mas
			if ( word_length >= 3 )
			{
				// Incremente la cantidad de palabras encontradas
				++found_word_count;
			}
			// Asignar la longitud de la palabra en cero
			word_length = 0;
			// Si la letra es un cambio de linea
			if ( letter == '\n' )
			{
				// Incrementar la cantidad de parrafos encontrados
				++found_paragraph_count;
			}
		}
		// De lo contrario
		else
		{
			// Incremente la longitud de la palabra
			++word_length;
		}
	}
}

// Imprimir las estadisticas:
void print_statistics(bool requirements_met, word_count_t min_words, word_count_t min_paragraphs)
{
	// Imprimir el _numero minimo de palabras_
	printf("Required words     : " WORD_COUNT_FORMAT "\n", min_words);
	// Imprimir la _cantidad de palabras encontradas_
	printf("Words found        : " WORD_COUNT_FORMAT "\n", found_word_count);
	// Imprimir el _minimo de parrafos_
	printf("Required paragraphs: " WORD_COUNT_FORMAT "\n", min_paragraphs);
	// Imprimir la _cantidad de parrafos encontrados_
	printf("Paragraphs found   : " WORD_COUNT_FORMAT "\n", found_paragraph_count);
	// Si _cumple requisitos_ Imprimir ensayo completo De lo contrario Imprimir ensayo incompleto
	printf("Essay result       : %s\n", requirements_met ? "complete" : "incomplete");
}

// Calcular el minimo de parrafos:
word_count_t calculate_min_paragraphs(word_count_t min_words)
{
	// Retornar el 1.5% de _numero minimo de palabras_
	return 1.5 / 100.0 * min_words;
}

// Revisar si cumple requisitos:
bool are_requirements_met(word_count_t min_words, word_count_t min_paragraphs)
{
	// Retornar si _cantidad de palabras encontradas_ es mayor o igual que _numero minimo de palabras_
		// y la _cantidad de parrafos encontrados_ es mayor o igual que el _minimo de parrafos_
	return found_word_count >= min_words && found_paragraph_count >= min_paragraphs;
}
]]>
essay_validator.c: Valida si ensayos presentados por estudiantes cumplen los requisitos impuestos por el profesor. Obtener código fuente.
build.sh: Guión de comandos (shell script) que compila los archivos anteriores y genera un ejecutable usando GCC/Clang. Obtener código fuente.

Aunque el ejemplo se ubica en la sección de expresiones y condicionales, se agregan ciclos y subrutinas, dado que son conceptos conocidos del curso Programación I.

Indirección, arreglos y matrices

Imprimir rango de números

Recibe los valores de un rango e imprime los valores separados por un delimitador. Si el rango está invertido, intercambia los valores con punteros.




void print_sequence ( long long starting_number, long long final_number, char separator);
void swap (long long * num1, long long * num2);


int main ()
{
	// Pedir los datos inicial y final
	// Leer el dato inicial y final
	printf("Para escribir la secuencia numérica, digite el número inicial: ");
	long long starting_number = 0;
	scanf("%lld", &starting_number);
	
	long long final_number = 0;
	printf("Digite el número final: ");
	scanf("%lld", &final_number);
	
	// Pedir el separador
	// Leer el separador
	if(starting_number > final_number)
		swap(&starting_number, &final_number);
	print_sequence(starting_number, final_number, ' ');
	
}

// Imprimir los números entre el inicial y el final con el separador dado
void print_sequence ( long long starting_number, long long final_number, char separator)
{
	for (long long counter = starting_number; counter <= final_number; ++ counter)
	{
		printf("%lld", counter);
		// Imprimir 
		if(counter != final_number)
			printf("%c", separator);
		else
			printf("\n");
	}
}

void swap (long long * num1, long long * num2)
{
	long long temp = *num2;
	*num2 = *num1;
	*num1 = temp;
}



]]>
Imprime valores en un rango. Obtener código fuente.

Mediana estadística (arreglo automático)

Enunciado del problema en Markdown. Obtener código fuente.

El siguiente programa almacena el arreglo en el segmento de pila y para números grandes, el arreglo desborda este segmento.


#include 
#include 

// Call-back function
int less_than(const void* first, const void* second)
{
	return * (const double*) first - * (const double*) second;
}

double average(double first, double second)
{
	return (first + second) / 2.0;
}

int read_array_arr(size_t value_count, double values[value_count])
{
//	fprintf(stderr, "sizeof(values) = %zu\n", sizeof(values));

	// Por cada valor
	for ( size_t index = 0; index < value_count; ++index )
	{
		// Leer el valor en el arreglo
		// Si el valor no es valido reportar un error y terminar
		if ( scanf("%lg", &values[index] ) != 1 || errno )
			return fprintf(stderr, "median: error: invalid value at position %zu\n", index + 1), 2;
	}
	// Exito
	return 0;
}

int read_array_ptr(const size_t value_count, double* const values)
{
	fprintf(stderr, "sizeof(values) = %zu\n", sizeof(values));

	// Por cada valor
	for ( size_t index = 0; index < value_count; ++index )
	{
		// Leer el valor en el arreglo
		// Si el valor no es valido reportar un error y terminar
		if ( scanf("%lg", &values[index] ) != 1 || errno )
		if ( scanf("%lg", values + index ) != 1 || errno )
		if ( scanf("%lg", &*(values + index) ) != 1 || errno )
			return fprintf(stderr, "median: error: invalid value at position %zu\n", index + 1), 2;
	}
	// Exito
	return 0;
}

void print_median(const size_t value_count, const double values[])
{
	// Si la cantidad de valores es impar
	if ( value_count % 2 )
	{
		// La mediana es el valor que esta en la mitad de la cantidad de valores
		printf("%.2lf\n", values[value_count / 2]);
	}
	// De lo contrario
	else
	{
		// La mediana es el promedio de los dos valores que estan en el centro
		printf("%.2lf\n", average(values[value_count / 2], values[value_count / 2 - 1]) );
	}
}

int main()
{
	// Leer la cantidad de valores
	size_t value_count = 0;

	// Si la cantidad no es valida imprimir un mensaje de error y terminar
	if ( scanf("%zu\n\n", &value_count) != 1 || errno )
		return fprintf(stderr, "%s\n", "median: error: invalid value count"), 1;

//	fprintf(stderr, "value count = %zu\n", value_count);

	// Crear un arreglo de esa cantidad de valores
	double values[value_count];

	int result = read_array_ptr(value_count, values);
	if ( result )
		return result;

	// Ordenar los valores en el arreglo
	qsort(values, value_count, sizeof(double), less_than);

//	for ( size_t index = 0; index < value_count; ++index )
//		fprintf(stderr, "%zu: %lf\n", index, values[index]);

	print_median(value_count, values);

	return 0;
}
]]>
Calcula la mediana con un arreglo automático. Obtener código fuente.

Mediana estadística (arreglo estático)

En la siguiente versión el arreglo se almacena en el segmento de datos, pero se puede acceder sólo dentro de la función main().


#include 
#include 

#define ARRAY_SIZE 20000000

// Call-back function
int less_than(const void* first, const void* second)
{
	return * (const double*) first - * (const double*) second;
}

double average(double first, double second)
{
	return (first + second) / 2.0;
}

int main()
{
	// Leer la cantidad de valores
	size_t value_count = 0;

	// Si la cantidad no es valida imprimir un mensaje de error y terminar
	if ( scanf("%zu\n\n", &value_count) != 1 || errno )
		return fprintf(stdout, "%s\n", "median: error: invalid value count"), 1;

//	fprintf(stderr, "value count = %zu\n", value_count);

	// Crear un arreglo de esa cantidad de valores en segmento de datos
	static double values[ARRAY_SIZE];

	// Por cada valor
	for ( size_t index = 0; index < value_count; ++index )
	{
		// Leer el valor en el arreglo
		// Si el valor no es valido reportar un error y terminar
		if ( scanf("%lg", &values[index] ) != 1 || errno )
			return fprintf(stderr, "median: error: invalid value at position %zu\n", index + 1), 2;
	}

	// Ordenar los valores en el arreglo
	qsort(values, value_count, sizeof(double), less_than);

//	for ( size_t index = 0; index < value_count; ++index )
//		fprintf(stderr, "%zu: %lf\n", index, values[index]);

	// Si la cantidad de valores es impar
	if ( value_count % 2 )
	{
		// La mediana es el valor que esta en la mitad de la cantidad de valores
		printf("%.2lf\n", values[value_count / 2]);
	}
	// De lo contrario
	else
	{
		// La mediana es el promedio de los dos valores que estan en el centro
		printf("%.2lf\n", average(values[value_count / 2], values[value_count / 2 - 1]) );
	}

	return 0;
}
]]>
Calcula la mediana con un arreglo estático. Obtener código fuente.

En la siguiente versión el arreglo también se almacena en el segmento de datos, pero se puede acceder dentro de varias funciones por ser una variable global.


#include 
#include 

#define ARRAY_SIZE 20000000

// Call-back function
int less_than(const void* first, const void* second)
{
	return * (const double*) first - * (const double*) second;
}

double average(double first, double second)
{
	return (first + second) / 2.0;
}

// Crear un arreglo de esa cantidad de valores
/*static*/ double values[ARRAY_SIZE] = {0};

int main()
{
	// Leer la cantidad de valores
	size_t value_count = 0;

	// Si la cantidad no es valida imprimir un mensaje de error y terminar
	if ( scanf("%zu\n\n", &value_count) != 1 || errno )
		return fprintf(stdout, "%s\n", "median: error: invalid value count"), 1;

//	fprintf(stderr, "value count = %zu\n", value_count);

	// Por cada valor
	for ( size_t index = 0; index < value_count; ++index )
	{
		// Leer el valor en el arreglo
		// Si el valor no es valido reportar un error y terminar
		if ( scanf("%lg", &values[index] ) != 1 || errno )
			return fprintf(stderr, "median: error: invalid value at position %zu\n", index + 1), 2;
	}

	// Ordenar los valores en el arreglo
	qsort(values, value_count, sizeof(double), less_than);

//	for ( size_t index = 0; index < value_count; ++index )
//		fprintf(stderr, "%zu: %lf\n", index, values[index]);

	// Si la cantidad de valores es impar
	if ( value_count % 2 )
	{
		// La mediana es el valor que esta en la mitad de la cantidad de valores
		printf("%.2lf\n", values[value_count / 2]);
	}
	// De lo contrario
	else
	{
		// La mediana es el promedio de los dos valores que estan en el centro
		printf("%.2lf\n", average(values[value_count / 2], values[value_count / 2 - 1]) );
	}

	return 0;
}
]]>
Calcula la mediana con un arreglo global. Obtener código fuente.

Mediana estadística (arreglo con alojamiento dinámico)

En la siguiente versión el arreglo se almacena en memoria dinámica. Esta programa es menos propenso a fallar que las versiones anteriores. Puede fallar para grandes volúmenes de datos que no pueden alojarse en la máquina donde corre.


#include 
#include 

// Call-back function
int less_than(const void* first, const void* second)
{
	return * (const double*) first - * (const double*) second;
}

double average(double first, double second)
{
	return (first + second) / 2.0;
}

int main()
{
	// Leer la cantidad de valores
	size_t value_count = 0;

	// Si la cantidad no es valida imprimir un mensaje de error y terminar
	if ( scanf("%zu\n\n", &value_count) != 1 || errno )
		return fprintf(stdout, "%s\n", "median: error: invalid value count"), 1;

//	fprintf(stderr, "value count = %zu\n", value_count);

	// Crear un arreglo de esa cantidad de valores
	double* const values = (double*) malloc( value_count * sizeof(double) );
	if ( values == NULL )
		return fprintf(stderr, "median: error: insufficient memory for size %zu\n", value_count), 2;

	// Por cada valor
	for ( size_t index = 0; index < value_count; ++index )
	{
		// Leer el valor en el arreglo
		// Si el valor no es valido reportar un error y terminar
		if ( scanf("%lg", &values[index] ) != 1 || errno )
			return fprintf(stderr, "median: error: invalid value at position %zu\n", index + 1), 3;
	}

	// Ordenar los valores en el arreglo
	qsort(values, value_count, sizeof(double), less_than);

//	for ( size_t index = 0; index < value_count; ++index )
//		fprintf(stderr, "%zu: %lf\n", index, values[index]);

	// Si la cantidad de valores es impar
	if ( value_count % 2 )
	{
		// La mediana es el valor que esta en la mitad de la cantidad de valores
		printf("%.2lf\n", values[value_count / 2]);
	}
	// De lo contrario
	else
	{
		// La mediana es el promedio de los dos valores que estan en el centro
		printf("%.2lf\n", average(values[value_count / 2], values[value_count / 2 - 1]) );
	}

	// Importantisimo: liberar la memoria apenas no se use mas, C no tiene recolector de basura
	free(values);

	return 0;
}
]]>
Calcula la mediana usando memoria dinámica. Obtener código fuente.

Tipos de arreglos en C


#include 
#include 

void arrays();
void matrixes();
void multidimensional_arrays();

int main()
{
	srand( time(NULL) );

	arrays();
	matrixes();
	multidimensional_arrays();

	return 0;
}

void print_array(long double array[], size_t size)
{
	printf("\t{");
	for ( size_t index = 0; index < size; ++index )
		printf("%s %Lg", index ? "," : "", array[index] );
	printf(" }\n");
}

void arrays()
{
	// 1. Literal array (continuous)
	long double literal_array[] = {-1.0L, -0.5L, 0.0L, 0.5L, 1.0L};
	size_t literal_size = sizeof(literal_array) / sizeof(literal_array[0]);

	printf("literal_array:\n");
	print_array(literal_array, literal_size);
	printf("\tcount: %zu elements\n", literal_size);
	printf("\tsizeof: %zu bytes\n", sizeof(literal_array));


	// 2. Automatic array (continuous)
	size_t automatic_size = 3 + rand() % 8;
	long double automatic_array[automatic_size];
	for ( size_t index = 0; index < automatic_size; ++index )
		automatic_array[index] = index;

	printf("\nautomatic_array:\n");
	print_array(automatic_array, automatic_size);
	printf("\tcount: %zu elements\n", automatic_size);
	printf("\tsizeof: %zu bytes\n", sizeof(automatic_array));


	// 3. Dynamic allocated array (not Dynamic array)
	size_t dynamic_alloc_size = 3 + rand() % 8;
	long double* dynamic_alloc_array = (long double*)malloc(dynamic_alloc_size * sizeof(long double));
	for ( size_t index = 0; index < dynamic_alloc_size; ++index )
		dynamic_alloc_array[index] = index;

	printf("\ndynamic_alloc_array:\n");
	print_array(dynamic_alloc_array, dynamic_alloc_size);
	printf("\tcount: %zu elements\n", dynamic_alloc_size);
	printf("\tsizeof: %zu bytes (long double*)\n", sizeof(dynamic_alloc_array));
	free(dynamic_alloc_array);
}

void print_literal_matrix(size_t rows, size_t cols, long double matrix[rows][cols])
{
	for ( size_t row_index = 0; row_index < rows; ++row_index )
	{
		putchar('\t');
		for ( size_t col_index = 0; col_index < cols; ++col_index )
			printf("%5Lg ", matrix[row_index][col_index] );
		putchar('\n');
	}
}

void print_continous_matrix(long double* matrix, size_t rows, size_t cols)
{
	for ( size_t row_index = 0; row_index < rows; ++row_index )
	{
		putchar('\t');
		for ( size_t col_index = 0; col_index < cols; ++col_index )
			printf("%5Lg ", matrix[row_index * cols + col_index] );
		putchar('\n');
	}
}

void print_uncontinous_matrix(long double** matrix, size_t rows, size_t cols)
{
	for ( size_t row_index = 0; row_index < rows; ++row_index )
	{
		printf("\t{");
		for ( size_t col_index = 0; col_index < cols; ++col_index )
			printf("%5Lg ", matrix[row_index][col_index] );
		printf(" }\n");

	}
}

void literal_matrix()
{
	long double matrix[][3] =
	{
		{1.1L, 1.2L, 1.3L},
		{2.1L, 2.2L, 2.3L},
	};
	size_t cols = 3;
	size_t rows = sizeof(matrix) / cols / sizeof(matrix[0][0]);
	printf("\nliteral_matrix:\n");
	print_literal_matrix(rows, cols, matrix);
	printf("\tcount: %zu rows x %zu cols\n", rows, cols);
	printf("\tsizeof: %zu bytes\n", sizeof(matrix));
}

void automatic_matrix()
{
	// 2. Automatic matrix (continuous)
	size_t rows = 3 + rand() % 8;
	size_t cols = 3 + rand() % 8;
	long double matrix[rows][cols];
	for ( size_t row_index = 0; row_index < rows; ++row_index )
		for ( size_t col_index = 0; col_index < cols; ++col_index )
			matrix[row_index][col_index] = (row_index + 1) + (col_index + 1) / 10.0;

	printf("\nautomatic_matrix:\n");
	print_literal_matrix(rows, cols, matrix);
	printf("\tcount: %zu rows x %zu cols\n", rows, cols);
	printf("\tsizeof: %zu bytes\n", sizeof(matrix));
}

void continous_dynamic_allocated_matrix()
{
	// 3. Dynamic allocated matrix (continous) (not Dynamic matrix)
	size_t rows = 3 + rand() % 8;
	size_t cols = 3 + rand() % 8;
	long double* matrix = (long double*)malloc(rows * cols * sizeof(long double));
	for ( size_t row_index = 0; row_index < rows; ++row_index )
		for ( size_t col_index = 0; col_index < cols; ++col_index )
			matrix[row_index * cols + col_index] = (row_index + 1) + (col_index + 1) / 10.0;

	printf("\ndynamic_alloc_matrix:\n");
	print_continous_matrix(matrix, rows, cols);
	printf("\tcount: %zu rows x %zu cols\n", rows, cols);
	printf("\tsizeof: %zu bytes (long double*)\n", sizeof(matrix));
	free(matrix);
}

void uncontinous_dynamic_allocated_matrix()
{
	// 4. Dynamic allocated matrix (uncontinous) (not Dynamic matrix)
	size_t rows = 3 + rand() % 8;
	size_t cols = 3 + rand() % 8;

	// Allocate a vector of vectors
	long double** matrix = (long double**)malloc(rows * sizeof(long double*));
	for ( size_t row_index = 0; row_index < rows; ++row_index )
		matrix[row_index] = (long double*)malloc(cols * sizeof(long double));

	// Initialize values
	for ( size_t row_index = 0; row_index < rows; ++row_index )
		for ( size_t col_index = 0; col_index < cols; ++col_index )
			matrix[row_index][col_index] = (row_index + 1) + (col_index + 1) / 10.0;

	printf("\nuncontinous_dynamic_alloc_matrix:\n");
	print_uncontinous_matrix(matrix, rows, cols);
	printf("\tcount: %zu rows x %zu cols\n", rows, cols);
	printf("\tsizeof: %zu bytes (long double**)\n", sizeof(matrix));

	// Free each row first, then the vector of vectors
	for ( size_t row_index = 0; row_index < rows; ++row_index )
		free(matrix[row_index]);
	free(matrix);
}

void matrixes()
{
	literal_matrix();
	automatic_matrix();
	continous_dynamic_allocated_matrix();
	uncontinous_dynamic_allocated_matrix();
}


void multidimensional_arrays()
{
	// 3. multidimensional array
	long double multidimensional_array[2][3][4] =
	{
		{
			{11.1L, 11.2L, 11.3L, 11.4L},
			{12.1L, 12.2L, 12.3L, 12.4L},
			{13.1L, 13.2L, 13.3L, 13.4L},
		},
		{
			{21.1L, 21.2L, 21.3L, 21.4L},
			{22.1L, 22.2L, 22.3L, 22.4L},
			{23.1L, 23.2L, 23.3L, 23.4L},
		},
	};

	printf("\nmultidimensional_array:\n");
	printf("\tsizeof: %zu\n", sizeof(multidimensional_array));

	// To be done...
}
]]>
Muestra como se declaran, acceden, destruyen y pasan por parámetro diferentes tipos de arreglos en C. Obtener código fuente.

Cadenas de caracteres y registros

chext: cambiar la extensión

chext es un comando en C para renombrar archivos, en especial para cambiarles la extensión. Para descargar el proyecto completo en un archivo comprimido:

Proyecto para renombrar extensiones
chext.zip


#include "args.h"
#include "chext.h"

int print_help()
{
	printf("Usage: chext EXTENSION FILES\n");
	return 0;
}


int main(int argc, char* argv[])
{
	arguments_t arguments = analyze_arguments(argc, argv);
	if ( arguments.help_asked )
		return print_help();
//	printf("sizeof(arguments_t)==%zd\n", sizeof(arguments));

#if 0
	for ( int index = 0; index < argc; ++index )
		printf("%d=[%s]\n", index, argv[index]);
	const char input_filename[] = "path_to/data_a";
#endif

	if ( argc > 1 )
	{
		const char* target_extension = argv[1];
		long target_extension_length = string_length(target_extension);

		for ( int index = 2; index < argc; ++index )
		{
			const char* const source_filename = argv[index];
			long pos = find_last_pos(source_filename, '.');

			//char output_filename[ string_length(input_filename) + 2 + 1 ];
			char target_filename[pos + 1 + target_extension_length + 1];
			*target_filename = '\0';
			//output_filename = input_filename;

			if ( pos != -1 )
			{
				string_copy_ptr(pos + 1, source_filename, target_filename);
				string_copy_ptr(target_extension_length, target_extension, target_filename + pos + 1);
			}
			printf("%d=[%s]%zu\n", index, target_filename, sizeof(target_filename));
		}
	}
	else
	{
		return fprintf(stderr, "chext: error: no arguments given\n"), 1;
	}

	return 0;
}
]]>
cli/main.c: Comando para cambiar la extensión de archivos. Obtener código fuente.


/**
	@brief Record that stores the value of parsed command line arguments
	@details A structure (record) resembles a class in object-oriented paradigm
*/
typedef struct
{
	/// true if given arguments by user are valid
	bool is_valid;
	/// true if `--help` was asked
	bool help_asked;
	/// true if `--version` was asked
	bool version_asked;
	/// true if simulation mode was asked with `-n` argument
	bool simulation;
	/// Point to the target extension wanted by user
	char* target_extension;
} arguments_t;

/**
	@brief Initializes a @a arguments_t record
	@details This procedure resembles the constructor in object-oriented paradigm
	@param arguments Pointer to the record to be initialized or reseted
*/
void init_arguments(arguments_t* arguments);

/**
	@brief Parses all command-line arguments and returns a record with the results.
	@details This function traverses all given command line arguments by user. Each time
	a valid argument is identified, its respective field in the record @a arguments_t is
	set. A copy of the record is returned.
	@param argc Count of arguments. It must be the same from main(argc, argv)
	@param argv Argument vector. It must be the same from main(argc, argv)
	@return A copy of the structure that contains the analyzed information
*/
arguments_t analyze_arguments(int argc, char* argv[]);

#endif // ARGS_H
]]>
args.h: Declaraciones para analizar los argumentos. Obtener código fuente.


void init_arguments(arguments_t* arguments)
{
	(*arguments).is_valid = true;
	arguments->help_asked = false;
	arguments->version_asked = false;
	arguments->target_extension = NULL;
}

arguments_t analyze_arguments(int argc, char* argv[])
{
	arguments_t arguments;

	init_arguments(&arguments);

	for ( int index = 1; index < argc; ++index )
	{
		if ( strcmp(argv[index], "--help") == 0 )
			arguments.help_asked = true;
	}

	return arguments;
}

]]>
args.c: Implementación de subrutinas para analizar los argumentos. Obtener código fuente.
chext.h: Declaraciones de la biblioteca para renombrar extensiones. Obtener código fuente.


long find_last_pos(const char* where, char what)
{
	long last_found_pos = -1;
	long index = 0;

	while ( where[index] != '\0' )
	{
		if ( where[index] == what )
		{
			last_found_pos = index;
		}
		++index;
	}
	return last_found_pos;
}

const char* find_last(const char* where, char what)
{
	const char* last_found = NULL;

	for( ; *where; ++where )
	{
		if( *where == what )
			last_found = where;
	}
	return last_found;
}

long string_length(const char* text)
{
	long length = 0;

	while ( text[length++] )
		;

	return length - 1;
}

void string_copy(long length, const char* source, char* target)
{
	for ( long index = 0; index < length && source[index]; ++index )
		target[index] = source[index];

	target[length] = '\0';
}

void string_copy_ptr(long length, const char* source, char* target)
{
	while ( length-- && *source )
		*target++ = *source++;

	*target = '\0';
}
]]>
chext.c: Implementación de la biblioteca para renombrar extensiones. Obtener código fuente.
test.h: Nano-biblioteca para facilitar pruebas unitarias. Obtener código fuente.


#include "chext.h"
#include "test.h"

BEGIN_TEST_CASE(test_find_last_pos)
	TEST( find_last_pos("", '.') == -1 );
	TEST( find_last_pos(".", '.') == 0 );
	TEST( find_last_pos("...", '.') == 2 );
	TEST( find_last_pos("path_to/data_a", '.') == -1 );
	TEST( find_last_pos("path_to/data.a", '.') == 12 );
	TEST( find_last_pos("path.to/data_a", '.') ==  4 );
	TEST( find_last_pos("path.to/data.a", '.') == 12 );
END_TEST_CASE()

TEST_CASE(test_find_last)
{
	{ const char* const str = "";    TEST( find_last(str, '.') == NULL ); }
	{ const char* const str = ".";   TEST( find_last(str, '.') == str ); }
	{ const char* const str = ".";   TEST( find_last(str, '.') == str ); }
	{ const char* const str = "..."; TEST( find_last(str, '.') == str + 2 ); }
	{ const char* const str = "path_to/data_a"; TEST( find_last(str, '.') == NULL ); }
	{ const char* const str = "path_to/data.a"; TEST( find_last(str, '.') == str + 12 ); }
	{ const char* const str = "path.to/data_a"; TEST( find_last(str, '.') == str + 4 ); }
	{ const char* const str = "path.to/data.a"; TEST( find_last(str, '.') == str + 12 ); }
}

int main()
{
	test_find_last_pos();
	test_find_last();

	return 0;
}
]]>
test/main.c: Prueba dos subrutinas de la biblioteca contra algunos pocos valores. Falta crear más pruebas y probar todas las funciones de la biblioteca libchext. Obtener código fuente.
Makefile para compilar el proyecto chext. Obtener código fuente.

El archivo de configuración de Doxygen se generó con el comando doxygen -g. Se le hicieron los siguientes cambios para configurarlo como un proyecto de C:

PROJECT_NAME           = "My Project" "chext"
PROJECT_NUMBER         =    1.0.0
PROJECT_BRIEF          =    "An extension renamer"
OUTPUT_DIRECTORY       =    doc
OPTIMIZE_OUTPUT_FOR_C  = NO YES
EXTRACT_ALL            = NO YES
EXTRACT_PRIVATE        = NO YES
EXTRACT_PACKAGE        = NO YES
EXTRACT_STATIC         = NO YES
EXTRACT_LOCAL_METHODS  = NO YES
INPUT                  =    src
RECURSIVE              = NO YES
Cambios hechos al archivo de configuración de Doxygen. Obtener código fuente.

Programación orientada a objetos (C++)

Clases y objetos

Espacios de nombres (namespaces)



const char* const cout = "free cout";

namespace ecci
{

// Esta variable global esta en el espacio de nombres (namespace) global
unsigned long long int cout = 1237348004712893;

} // namespace ecci

using std::endl; // mejor no

using namespace std; // no recomendado

int main()
{
	long cout = -21321712;
	std::cout << "cout = " << cout << std::endl
	 		  << "::cout = " << ::cout << endl
	 		  << "ecci::cout = " << ecci::cout << std::endl;
}
]]>
Espacios de nombres evitan que los identificadores colisionen. Obtener código fuente.

Calculadora fraccional (versión 1)



typedef long long integer;

class Fraction
{
  private:
	integer numerator = 0; // esto es tambien es inicializacion a partir de C++11
	integer denominator = 1;

  public:
	Fraction(integer numerator = 0, integer denominator = 1)
		: numerator{numerator} // esto SI es inicializacion en C++
		, denominator{denominator ? denominator : 1}
	{
		//numerator = 0; // Esto NO es inicializacion
		//denominator = 1; // es asignacion
	}

	inline integer getNumerator() const
	{
		return this->numerator;
	}

	inline void setNumerator(integer numerator)
	{
		Fraction::numerator = numerator;
	}

	inline integer getDenominator() const
	{
		return this->denominator;
	}

	inline void setDenominator(integer denominator)
	{
		if ( denominator )
			this->denominator = denominator;
	}

	void print() const
	{
		std::cout << this->getNumerator() << '/' << this->denominator;
	}
};

// Funcion libre
int main()
{
	Fraction fr1;
	fr1.setDenominator(-3);
	fr1.setDenominator(0);
	std::cout << "fr1 = ";
	fr1.print();
	std::cout << std::endl;

	Fraction fr2(2, 9);
	std::cout << "fr2 = ";
	fr2.print();
	std::cout << std::endl;

	Fraction fr3(5);
	std::cout << "fr3 = ";
	fr3.print();
	std::cout << std::endl;

	Fraction* fr0 = new Fraction();
	delete fr0;
}
]]>
Una clase Fraction en C++. Obtener código fuente.

#include 

typedef long long integer;

typedef struct
{
	integer numerator;
	integer denominator;
} Fraction;

void Fraction_i_i(Fraction* this, integer numerator, integer denominator)
{
	this->numerator = numerator;
	this->denominator = denominator ? denominator : 1;
}

void Fraction_i(Fraction* this, integer numerator)
{
	this->numerator = numerator;
	this->denominator = 1;
}

void Fraction_(Fraction* this)
{
	this->numerator = 0;
	this->denominator = 1;
}

integer Fraction_getNumerator(const Fraction* this)
{
	return this->numerator;
}

void Fraction_setNumerator(Fraction* this, integer numerator)
{
	this->numerator = numerator;
}

integer Fraction_getDenominator(const Fraction* this)
{
	return this->denominator;
}

void Fraction_setDenominator(Fraction* this, integer denominator)
{
	if ( denominator )
		this->denominator = denominator;
}

void Fraction_print(const Fraction* this)
{
	printf("%lld/%lld", Fraction_getNumerator(this), this->denominator);
}

// Funcion libre
int main()
{
	Fraction fr1;
	Fraction_( &fr1 );
	Fraction_setDenominator(&fr1, 3);
	Fraction_setDenominator(&fr1, 0);
	printf("fr1 = ");
	Fraction_print(&fr1);
	putchar('\n');

	Fraction fr2;
	Fraction_i_i(&fr2, 2, 9);
	printf("fr2 = ");
	Fraction_print(&fr2);
	putchar('\n');

	Fraction fr3;
	Fraction_i(&fr3, 5);
	printf("fr3 = ");
	Fraction_print(&fr3);
	putchar('\n');

	Fraction* fr0 = (Fraction*) malloc( sizeof(Fraction) );
	Fraction_(fr0);
	free(fr0);

	return 0;
}
]]>
La clase Fraction en C++ convertida a código procedimental en C. Obtener código fuente.

Sobrecarga de operadores

Calculadora fraccional (versión 2)

Ejemplo de una aplicación con un controlador principal: el "main" lo instancia e inicia la ejecución. Obtener código fuente.
Encabezado de la clase controladora. Obtener código fuente.
 ";
   if ( ! (std::cin >> other) )
	  return false;

   ecci::Fraction previousResult = result;

   switch ( operador )
   {
	  case '+': result = result + other; break;
//      case '-': resultado = resultado - otra; break;
//	    case '*': result = result * other; break;
//      case '/': resultado = resultado / otra; break;

	  case 'c':
	  case 'C':
	  case ' ':
		 result = other;
		 std::cout << result << std::endl;
		 return true;

	  case '=':
		 return true;

	  default:
		 std::cout << "Err" << std::endl;
		 return true;
   }

   std::cout << previousResult << " " << operador << " " << other << " = " << result << std::endl;
   return true;
}

bool Calculator::readOperation()
{
   std::cout << "op> ";
   std::cin >> operador;

   switch ( operador )
   {
	  case '=':
		 std::cout << result << std::endl;
		 break;

	  case 'c':
	  case 'C':
		 result = 0;
		 std::cout << result << std::endl;
		 break;

	  case 'q':
	  case 'Q':
		 return false;
   }

   return std::cin.good();
}
]]>
Implementación de la clase controladora. Obtener código fuente.


namespace ecci
{

#define implicit

typedef long long integer;

class Fraction
{
  private:
	integer numerator = 0; // esto es tambien es inicializacion a partir de C++11
	integer denominator = 1;

  public:
	// Default constructor
	// Conversion constructor
	/*explicit*/ implicit Fraction(integer numerator = 0, integer denominator = 1);

	/**
	 * [getNumerator description]
	 * @return [description]
	 */
	inline integer getNumerator() const
	{
		return this->numerator;
	}

	inline void setNumerator(integer numerator)
	{
		Fraction::numerator = numerator;
		this->simplify();
	}

	inline integer getDenominator() const
	{
		return this->denominator;
	}

	inline void setDenominator(integer denominator)
	{
		if ( denominator )
		{
			this->denominator = denominator;
			this->simplify();
		}
	}

	void print() const;

	void simplify();

	// Metodo de clase
	static integer gcd(integer a, integer b);

	Fraction add(Fraction other) const;

//	Fraction operator+(Fraction other) const;

	friend Fraction operator+(const Fraction& one, const Fraction& two)
	{
		return Fraction( one.numerator * two.denominator + one.denominator * two.numerator
			, one.denominator * two.denominator );
	}

	friend std::istream& operator>>(std::istream& in, Fraction& fraction)
	{
		integer denominator;
		in >> fraction.numerator;
		in.ignore(std::numeric_limits::max(), '/');
		in >> denominator;
		fraction.setDenominator(denominator);
		return in;
	}

	friend std::ostream& operator<<(std::ostream& out, const Fraction& fraction)
	{
		return out << fraction.getNumerator() << '/' << fraction.denominator;
	}
};


} // namespace ecci

#endif // FRACTION_H
]]>
Encabezado de la clase Fraction. Obtener código fuente.
simplify();
}

void /*ecci::*/Fraction::print() const
{
	std::cout << this->getNumerator() << '/' << this->denominator;
}

void Fraction::simplify()
{
	integer gcd = Fraction::gcd(this->numerator, this->denominator);
	this->numerator /= gcd;
	this->denominator /= gcd;

	if ( this->denominator < 0 )
	{
		this->numerator *= -1;
		this->denominator *= -1;
	}
}

integer Fraction::gcd(integer a, integer b)
{
	a = a >= 0 ? a : -a;
	b = b >= 0 ? b : -b;

	while ( b > 0 )
	{
		integer t = b;
		b = a % b;
		a = t;
	}
	return a;
}

Fraction Fraction::add(Fraction other) const
{
	Fraction result;
	result.numerator = this->numerator * other.denominator + 
		this->denominator * other.numerator;
	result.denominator = this->denominator * other.denominator;
	result.simplify();
	return result;
}

/*
Fraction Fraction::operator+(Fraction other) const
{
	return Fraction( this->numerator * other.denominator + this->denominator * other.numerator
		, this->denominator * other.denominator );
}
*/

} // namespace ecci
]]>
Implementación de la clase Fraction. Obtener código fuente.
print();
	std::cout << std::endl;
	delete fr0;

	//Fraction sum1 = fr2.add(fr3); // Java
//	Fraction sum1 = fr2 + fr3; // C++
//	Fraction sum1 = fr2.operator+(fr3); // C++
	Fraction sum1 = operator+(fr2, fr3); // C++
	std::cout << "sum1 = fr2 + fr3 = ";
	sum1.print();
	std::cout << std::endl;

	Fraction sum2 = operator+(fr2, 7); // C++
//	Fraction sum2 = fr2.operator+(7); // C++
//	Fraction sum2 = fr2.operator+( Fraction(7) ); // C++
	std::cout << "sum2 = fr2 + 7 = ";
	sum2.print();
	std::cout << std::endl;

	Fraction sum3 = fr2 + fr3 + 7 + sum1;
//	Fraction sum3 = fr2.add(fr3).add(Fraction(7)).add(sum1);
	std::cout << "sum3 = fr2 + fr3 + 7 + sum1 = ";
	sum3.print();
	std::cout << std::endl;

	Fraction fr4;
	std::cin >> fr4;
	std::cout << "fr4 = " << fr4 << std::endl;
	std::cout << "fr4 + fr3 + 1 == " << fr4 + fr3 + 1 << std::endl;
}
]]>
Programa para probar la clase Fraction. Obtener código fuente.

Clase String



int main(int argc, char* argv[])
{
	ecci::String s0;
	std::cout << "s0=[" << s0 << "]" << std::endl;
	ecci::String s1 = "hola";
	std::cout << "s1=[" << s1 << "]" << std::endl;
	ecci::String s2(s1);
	std::cout << "s2=[" << s2 << "]" << std::endl;
	ecci::String s3{":) !!!", 2};
	std::cout << "s3=[" << s3 << "]" << std::endl;
	s0 = s3;
	std::cout << "s0=[" << s0 << "]" << std::endl;

	std::cerr << "\nString s4 = test2()\n";
	ecci::String s4 = test2();
	std::cout << "s4=[" << s4 << "]\n" << std::endl;

	std::cerr << "\ns3 = test2()\n";
	s3 = test2();
	std::cout << "s3=[" << s3 << "]" << std::endl;

	printLetter(s1, 3);
	std::cerr << "s1[0] = 'b'\n";
	s1[0] = 'b';
	std::cout << "s1=[" << s1 << "]\n" << std::endl;

	ecci::String s5 = /*'>' +*/ s1 + ' ' + "mundo?";
	std::cout << s5 << std::endl;

	(void)argc;
	ecci::String filename = argv[1];
	//filename.replace(".csv", ".md");
	//FILE* inputFile = fopen( filename, "r");
	FILE* inputFile = fopen( filename.c_str(), "r");
	if ( ! inputFile )
		std::cerr << "could not open " << filename << std::endl;
	fclose(inputFile);

  #ifdef TESTING
	std::cout << "\nInstance count: " << ecci::String::getInstanceCount() << std::endl;
  #endif
}
]]>
Programa para probar la clase String. Obtener código fuente.


namespace ecci
{


class String
{
  public:
	/// Indicates a non valid length or index
	static const size_t npos = (size_t)-1;

  private:
	/// Length of this string
	size_t length;
	/// Pointer to the characters of this string in heap
	char* text;

  #ifdef TESTING
	static size_t instanceCount;
  #endif

  public:
	/// Default constructor (ctor)
	/// Conversion constructor
	String(const char* text = "", size_t len = npos);
	/// Conversion constructor
	String(char ch);
	/// Copy constructor
	String(const String& other);
	/// Move constructor
	String(String&& other);
	/// Destructor (dtor)
	~String();
	/// Copy assignment operator
	String& operator=(const String& other);
	/// Move assignment operator
	String& operator=(String&& other);

  #ifdef TESTING
	inline static size_t getInstanceCount() { return instanceCount; }
  #endif

  public:
	/// Get the length of this string
	inline size_t getLength() const { return this->length; }
	/// Get read-only access to the internal null-terminated string
	inline const char* c_str() const { return this->text; }
	/// Get read-only access to the internal null-terminated string
//	inline operator const char*() const { return this->text; }
	/// Get read & write access to the char at position @a index
	inline char& operator[](size_t index) { return text[index]; }
	/// Get read-only access to the char at position @a index
	inline const char& operator[](size_t index) const { return text[index]; }
	/// Allows cout << str1 << ...
	friend inline std::ostream& operator<<(std::ostream& out, const String& str)
	{
		return out << str.text;
	}
	/// Concatenates this string with other string
	const String operator+(const String& other) const;

  private:
	/// Capacity constructor
	explicit String(size_t capacity);
};


} // namespace ecci

#endif // STRING_H
]]>
Encabezado de la clase String. Obtener código fuente.

#include 
#include 

namespace ecci
{

#ifdef TESTING
/*static*/ size_t String::instanceCount = 0;
#endif

String::String(const char* text, size_t len)
	: length{ len != npos ? len : strlen(text) }
	, text{ (char*) malloc( (length + 1) * sizeof(char) ) }
{
	assert(text);
	strncpy(this->text, text, this->length);
	std::cerr << "String(" << text << ", " << length << ")\n";
  #ifdef TESTING
	++instanceCount;
  #endif
}

String::String(char ch)
	: length{ ch ? static_cast(1) : static_cast(0) }
	, text{ (char*) malloc( (length + 1) * sizeof(char) ) }
{
	assert(text);
	this->text[0] = ch;
	this->text[ this->length ] = '\0';
	std::cerr << "StringCh('" << text << "', " << length << ")\n";
  #ifdef TESTING
	++instanceCount;
  #endif
}

String::String(size_t capacity)
	: length{ 0 }
	, text{ (char*) malloc( (capacity + 1) * sizeof(char) ) }
{
	assert(text);
	this->text[0] = '\0';
	std::cerr << "StringSz(" << capacity << ", " << length << ")\n";
  #ifdef TESTING
	++instanceCount;
  #endif
}



String::String(const String& other)
	: length{ other.length }
	, text{ (char*) malloc( (length + 1) * sizeof(char) ) }
{
	strcpy(this->text, other.text);
	std::cerr << "StringCp(" << text << ", " << length << ")\n";
  #ifdef TESTING
	++instanceCount;
  #endif
}

String::String(String&& other)
	: length{ other.length }
	, text{ other.text }
{
	other.length = 0;
	other.text = nullptr;
	std::cerr << "StringMv(" << text << ", " << length << ")\n";
  #ifdef TESTING
	++instanceCount;
  #endif
}



String::~String()
{
	std::cerr << "~String(" << (text ? text : "") << ", " << length << ")\n";
	free(text);
  #ifdef TESTING
	--instanceCount;
  #endif
}

String& String::operator=(const String& other)
{
	if ( this != &other )
	{
		this->length = other.length;
		this->text = (char*) realloc( this->text, (this->length + 1) * sizeof(char) );
		assert(this->text);
		strcpy(this->text, other.text);
	}
	std::cerr << "operator=(" << text << ", " << length << ")\n";
	return *this;
}

String& String::operator=(String&& other)
{
	if ( this != &other )
	{
		free(text);

		this->length = other.length;
		this->text = other.text;

		other.length = 0;
		other.text = nullptr;
	}

	std::cerr << "operator=Mv(" << text << ", " << length << ")\n";
	return *this;
}


const String String::operator+(const String& other) const
{
	size_t len = this->length + other.length;
	String result{ len };
	strcpy( result.text, this->text );
	strcpy( result.text + this->length, other.text );
	result.length = len;
	return result;
}


} // namespace ecci

]]>
Implementación de la clase String. Obtener código fuente.

Programación genérica

Arreglo dinámico


#include 
#include 

#include "Fraction.h"

#include "Array.h"
// #include "Array.cpp"

template 
DataType average(const DataType& first, const DataType& second)
{
	return (first + second) / 2;
}

template 
void printMedian(ecci::Array& values)
{
	std::cerr << values.getCount() << " values\n";
	values.sort();

	// Si la cantidad de valores es impar
	if ( values.getCount() % 2 )
	{
		// La mediana es el valor que esta en la mitad de la cantidad de valores
		std::cout << values[values.getCount() / 2] << std::endl;
	}
	// De lo contrario
	else
	{
		// La mediana es el promedio de los dos valores que estan en el centro
		std::cout << average(values[values.getCount() / 2], values[values.getCount() / 2 - 1]) << std::endl;
	}
}

int main(int argc, char* argv[])
{
  #if 0
	size_t size = 0;
	if ( ! (std::cin >> size) )
		return (std::cerr << "invalid data\n"), 1;

	ecci::Array values(size);
  #endif
  #if 0
	// Si el constructor de conversion es implicito, C++ permitira
	// operaciones peligrosas como la siguiente
	values = 0;
  #endif

	if ( argc <= 1 || strcmp(argv[1], "double") == 0 )
	{
		ecci::Array values;

		double value = 0.0;
		while ( std::cin >> value )
			values.append(value);

		std::cout << std::fixed << std::setprecision(2);

		printMedian(values);
	}
	else if ( strcmp(argv[1], "fraction") == 0 )
	{
		ecci::Array values;

		ecci::Fraction fraction;
		while ( std::cin >> fraction )
			values.append(fraction);

		printMedian(values);
	}
	else
	{
		std::cerr << "median: unsupported data type: " << argv[1] << std::endl;
	}

	ecci::Array fractions2;
}
]]>
Programa de la mediana que usa un arreglo dinámico dado que no conoce la cantidad de elementos. Obtener código fuente.


namespace ecci
{
const size_t initialCapacity = 10; const size_t increaseFactor = 10;
template
class Array
{
  private:
	size_t count;
	size_t capacity;
	DataType* elements;

  public:
	explicit Array(size_t capacity = initialCapacity);
	Array(const Array& other);
	Array(Array&& other);
	~Array();
	Array& operator=(const Array& other);
	Array& operator=(Array&& other);
	inline size_t getCount() const { return count; }
	inline DataType& operator[](size_t index) { return this->elements[index]; }
	inline const DataType& operator[](size_t index) const { return this->elements[index]; }
	void append(const DataType& value);
	void sort();

  private:
	void increaseCapacity();
};

} // namespace ecci


#include 

namespace ecci
{

template 
Array::Array( size_t capacity )
	: count{ 0 }
	, capacity{ capacity ? capacity : initialCapacity }
	, elements{ new DataType[this->capacity]() }
{
}

template 
Array::Array(const Array& other)
	: count{ other.count }
	, capacity{ other.capacity }
	, elements{ new DataType[this->capacity]() }
{
	for ( size_t index = 0; index < this->count; ++index )
		this->elements[index] = other.elements[index];
}

template 
Array::Array(Array&& other)
	: count{ other.count }
	, capacity{ other.capacity }
	, elements{ other.elements }
{
	other.count = other.capacity = 0;
	other.elements = nullptr;
}

template 
Array::~Array()
{
	delete [] this->elements;
}

template
Array& Array::operator=(const Array& other)
{
	if ( this != &other )
	{
		if ( this->capacity != other.capacity )
		{
			delete [] this->elements;
			this->capacity = other.capacity;
			this->elements = new DataType[ this->capacity ]();
		}
		this->count = other.count;
		for ( size_t index = 0; index < this->count; ++index )
			this->elements[index] = other.elements[index];
	}

	return *this;
}

template
Array& Array::operator=(Array&& other)
{
	if ( this != &other )
	{
		delete [] this->elements;

		this->count = other.count;
		this->capacity = other.capacity;
		this->elements = other.elements;

		other.count = other.capacity = 0;
		other.elements = nullptr;
	}

	return *this;
}

template 
void Array::append(const DataType& value)
{
	if ( this->count == this->capacity )
		increaseCapacity();
	this->elements[this->count++] = value;
}

template 
void Array::increaseCapacity()
{
	this->capacity *= increaseFactor;
	DataType* newElements = new DataType[this->capacity];
//	if ( newElements == nullptr )
//		throw Exception("could not increase array");
	for ( size_t index = 0; index < this->count; ++index )
		newElements[index] = this->elements[index];
	delete [] this->elements;
	this->elements = newElements;
}

template 
void Array::sort()
{
	std::sort( this->elements, this->elements + this->count );
}


} // namespace ecci

#endif // ARRAY_H
]]>
Encabezado del arreglo dinámico. Obtener código fuente.
Implementación del arreglo dinámico. Obtener código fuente.

Clase fracción para mediana

Permite comparar fracciones con el operador < para poder ordenarlas y dividir fracciones para poder calcular el promedio.



namespace ecci
{

#define implicit

typedef long long integer;

class Fraction
{
  private:
	integer numerator = 0; // esto es tambien es inicializacion a partir de C++11
	integer denominator = 1;

  public:
	// Default constructor
	// Conversion constructor
	/*explicit*/ implicit Fraction(integer numerator = 0, integer denominator = 1);

	/**
	 * [getNumerator description]
	 * @return [description]
	 */
	inline integer getNumerator() const
	{
		return this->numerator;
	}

	inline void setNumerator(integer numerator)
	{
		Fraction::numerator = numerator;
		this->simplify();
	}

	inline integer getDenominator() const
	{
		return this->denominator;
	}

	inline void setDenominator(integer denominator)
	{
		if ( denominator )
		{
			this->denominator = denominator;
			this->simplify();
		}
	}

	void print() const;

	void simplify();

	// Metodo de clase
	static integer gcd(integer a, integer b);

	Fraction add(Fraction other) const;

//	Fraction operator+(Fraction other) const;

	friend Fraction operator+(const Fraction& one, const Fraction& two)
	{
		return Fraction( one.numerator * two.denominator + one.denominator * two.numerator
			, one.denominator * two.denominator );
	}

	inline Fraction operator/(const Fraction& other) const
	{
		return Fraction( this->numerator * other.denominator, this->denominator * other.numerator);
	}

	// fr1 < fr2
	inline bool operator<(const Fraction& other) const
	{
		return this->numerator * other.denominator < this->denominator * other.numerator;
	}

	friend std::istream& operator>>(std::istream& in, Fraction& fraction)
	{
		integer denominator;
		in >> fraction.numerator;
		in.ignore(std::numeric_limits::max(), '/');
		in >> denominator;
		fraction.setDenominator(denominator);
		return in;
	}

	friend std::ostream& operator<<(std::ostream& out, const Fraction& fraction)
	{
		return out << fraction.getNumerator() << '/' << fraction.denominator;
	}
};


} // namespace ecci

#endif // FRACTION_H
]]>
Encabezado de la clase fracción. Obtener código fuente.
simplify();
}

void /*ecci::*/Fraction::print() const
{
	std::cout << this->getNumerator() << '/' << this->denominator;
}

void Fraction::simplify()
{
	integer gcd = Fraction::gcd(this->numerator, this->denominator);
	this->numerator /= gcd;
	this->denominator /= gcd;

	if ( this->denominator < 0 )
	{
		this->numerator *= -1;
		this->denominator *= -1;
	}
}

integer Fraction::gcd(integer a, integer b)
{
	a = a >= 0 ? a : -a;
	b = b >= 0 ? b : -b;

	while ( b > 0 )
	{
		integer t = b;
		b = a % b;
		a = t;
	}
	return a;
}

Fraction Fraction::add(Fraction other) const
{
	Fraction result;
	result.numerator = this->numerator * other.denominator + 
		this->denominator * other.numerator;
	result.denominator = this->denominator * other.denominator;
	result.simplify();
	return result;
}

/*
Fraction Fraction::operator+(Fraction other) const
{
	return Fraction( this->numerator * other.denominator + this->denominator * other.numerator
		, this->denominator * other.denominator );
}
*/

} // namespace ecci
]]>
Implementación de la clase fracción. Obtener código fuente.

Lista doblemente enlazada


#include "List.h"
#include "Fraction.h"

template 
void printList(/*const*/ ecci::List& list)
{
	std::cout << list.getCount() << " elements read\n";

//	for ( ecci::List::Iterator itr = list.begin(); itr != list.end(); itr.operator++(0) )
	for ( typename ecci::List::Iterator itr = list.begin(); itr != list.end(); itr++ )
		std::cout << *itr << std::endl;
}

void testDouble()
{
	ecci::List list;
	double value = 0.0;
	while ( std::cin >> value )
		list.append(value);

	printList(list);
}

void testFraction()
{
	ecci::List list;
	ecci::Fraction value;
	while ( std::cin >> value )
		list.append(value);

	printList(list);
}

int main()
{
//	testDouble();
	testFraction();
/*
	int x = 0;
	++x = 20;
*/
}
]]>
Lee varios elementos en una lista y los imprime. Obtener código fuente.


namespace ecci
{

template 
class List
{
  private:
	struct Node
	{
	  public:
		Node* previous;
		DataType data;
		Node* next;

	  public:
		Node(const DataType& data, Node* previous = nullptr, Node* next = nullptr)
			: previous(previous)
			, data{data}
			, next{next}
		{
		}
	};

  private:
	size_t count;
	Node* first;
	Node* last;

  public:
	List()
		: count{0}
		, first{nullptr}
		, last{nullptr}
	{
	}

	~List()
	{
		clear();
	}

	inline bool isEmpty() const
	{
		return this->first == nullptr;
	}

	inline size_t getCount() const
	{
		return this->count;
	}

	void append(const DataType& element)
	{
		if ( isEmpty() )
			this->first = this->last = new Node(element);
		else
			this->last = this->last->next = new Node(element, this->last);

		++this->count;
	}

	void clear()
	{
		while ( ! this->isEmpty() )
			this->removeFirst();
	}

	void removeFirst()
	{
		Node* temp = this->first;
		this->first = this->first->next;
		delete temp;
		if ( this->first )
			this->first->previous = nullptr;
		else
			this->last = nullptr;

		--this->count;
	}

  public:
	class Iterator
	{
	  private:
		Node* current;

	  public:
		explicit Iterator(Node* current = nullptr)
			: current(current)
		{
		}

		// itr1 != itr2
		inline bool operator!=(const Iterator& other) const
		{
			return this->current != other.current;
		}

		// *itr
		inline DataType& operator*()
		{
			return this->current->data;
		}

		// ++itr
		inline Iterator& operator++()
		{
			this->current = this->current->next;
			return *this;
		}

		// itr++
		inline Iterator operator++(int)
		{
			Iterator before( *this );
			this->current = this->current->next;
			return before;
		}

		//operator--
	};

  public:
	inline Iterator begin()
	{
		return Iterator(this->first);
	}

	inline Iterator end()
	{
		return Iterator(nullptr);
	}
};

} // namespace ecci

#endif // LIST_H
]]>
Encabezado de la lista doblemente enlazada. Obtener código fuente.
Implementación de la lista doblemente enlazada. Obtener código fuente.

Mapa (diccionario/arreglo asociativo)


#include 

#include 
#include 

#include "Map.h"

bool compString(const std::string& s1, const std::string& s2)
{
	return s1 > s2;
}

//#include 

int main()
{
//	std::map jiji;
	typedef ecci::Map WordCount;
	WordCount wordCount;
	std::string word;
	while ( std::cin >> word )
		++wordCount[word];

//	for ( WordCount::ConstIterator itr = wordCount.constBegin(); itr != wordCount.constEnd(); ++itr )
//		std::cout << itr.getKey() << ": " << itr.getValue() << std::endl;

	typedef std::map< long, std::list< std::string >, std::greater > TopWords;
	TopWords topWords;
	for ( WordCount::ConstIterator itr = wordCount.constBegin(); itr != wordCount.constEnd(); ++itr )
		topWords[ itr.getValue() ].push_back( itr.getKey() );

	for ( TopWords::const_iterator pairItr = topWords.cbegin(); pairItr != topWords.cend(); ++pairItr )
	{
		std::cout << (*pairItr).first << "\t";
		const std::list& words = (*pairItr).second;

		for ( std::list::const_iterator wordItr = words.cbegin(); wordItr != words.cend(); ++wordItr )
			std::cout << *wordItr << ' ';

		std::cout << std::endl;
	}
}
]]>
Cuenta palabras de la entrada estándar. Obtener código fuente.

#include 

#define DISABLE_COPY(Class) \
	Class(const Class& other) = delete; \
	Class(Class&& other) = delete; \
	Class& operator=(const Class& other) = delete; \
	Class& operator=(Class&& other) = delete


namespace ecci
{

template 
struct LessThan
{
	inline bool operator()(const KeyType& key1, const KeyType& key2) const
	{
		return key1 < key2;
	}
};

template 
struct GreaterThan
{
	inline bool operator()(const KeyType& key1, const KeyType& key2) const
	{
		return key1 > key2;
	}
};

template >
class Map
{
  private:
	struct Node
	{
	  public:
		KeyType key;
		ValueType value;
		Node* parent;
		Node* left;
		Node* right;
		// ecci::Array children; // n-ary tree

	  public:
		explicit Node(const KeyType& key = KeyType(), const ValueType& value = ValueType(),
			Node* parent = nullptr, Node* left = nullptr, Node* right = nullptr )
		: key{key}
		, value{value}
		, parent{parent}
		, left{left}
		, right{right}
		{
		}

		DISABLE_COPY(Node);
//		Node(const Node& other) = delete; // = default
//		Node(Node&& other) = delete;
//		Node& operator=(const Node& other) = delete;
//		Node& operator=(Node&& other) = delete;

		~Node()
		{
			delete left;
			delete right;
		}
	};

  private:
	size_t count;
	Node* root;

  public:
	Map()
		: count{0}
		, root{nullptr}
	{
	}

	DISABLE_COPY(Map);

	~Map()
	{
		delete root;
	}

	inline bool isEmpty() const
	{
		return this->root == nullptr;
	}

	inline size_t getCount() const
	{
		return this->count;
	}

	ValueType& operator[](const KeyType& key)
	{
//		Node* node = this->find(key);
//		if ( node == nullptr )
		Node* node = insert(key, ValueType());
		assert(node);
		return node->value;
	}
/*
	const ValueType& operator[](const KeyType& key) const
	{
		Node* node = this->find(key);
		assert(node);
		return node->value;
	}
*/
  public:
	class ConstIterator
	{
	  private:
		Node* current;

	  public:
		explicit ConstIterator(Node* current = nullptr)
			: current{current}
		{
		}

		ConstIterator(const ConstIterator& other) = default;
		ConstIterator(ConstIterator&& other) = default;
		~ConstIterator() = default;
		ConstIterator& operator=(const ConstIterator& other) = default;
		ConstIterator& operator=(ConstIterator&& other) = default;

		inline bool operator!=(const ConstIterator& other) const
		{
			return this->current != other.current;
		}

		ConstIterator& operator++()
		{
			this->current = Map::findNextNode(this->current);
			return *this;
		}

		inline const KeyType& getKey() const
		{
			return this->current->key;
		}

		inline const ValueType& getValue() const
		{
			return this->current->value;
		}
	};

  public:
	ConstIterator constBegin() const
	{
		return ConstIterator( this->minimum(this->root) );
	}

	ConstIterator constEnd() const
	{
		return ConstIterator(nullptr);
	}

  private:
	Node* insert(const KeyType& key, const ValueType& value)
	{
		++this->count;
		if ( this->isEmpty() )
			return this->root = new Node(key, value);

		Node* current = this->root;
		while ( true )
		{
			Comparator comp;
			if ( comp(key, current->key) )
			{
				if ( current->left )
					current = current->left;
				else
					return current->left = new Node(key, value, current);
			}
			else if ( comp(current->key, key) )
			{
				if ( current->right )
					current = current->right;
				else
					return current->right = new Node(key, value, current);
			}
			else
			{
				--this->count;
				return current;
			}
		}

		assert(false);
		return nullptr;
	}

	static Node* minimum(Node* current)
	{
		while ( current && current->left )
			current = current->left;
		return current;
	}

	static Node* findNextNode(Node* current)
	{
//		std::cerr << "findNextNode(Node* current)\n";
		Node* original = current;

		if ( current->right )
			return minimum(current->right);

		while ( current->parent && current == current->parent->right )
		{
			current = current->parent;
			if ( current->parent == nullptr )
				return nullptr;
		}

		if ( current->parent && current == current->parent->left )
			current = current->parent;

		return current == original ? nullptr : current;
	}
};

}


#endif // MAP_H
]]>
Encabezado de un mapa (arreglo asociativo). Obtener código fuente.

Herencia y polimorfismo

Trivia

trivia3.txt

Juego de Trivia en línea de comandos que carga las preguntas de este archivo:

Archivo de preguntas. Obtener código fuente.

main.cpp

main.cpp. Obtener código fuente.

Common.h

Common.h. Obtener código fuente.

Clase Trivia (controlador)



#include "Common.h"

//#include "Question.h"

class Question; // Forward declaration

class Trivia
{
	DISABLE_COPY_CLASS(Trivia);

  private:
	std::vector questions;
	long score = 0;

  public:
	Trivia();
	~Trivia();
	int run();
	static Question* createQuestion(const std::string& type);

  private:
	int loadQuestions();
	bool askQuestion();
	void printStatistics();
};

#endif // TRIVIA_H
]]>
Trivia.h. Obtener código fuente.

#include 
#include 
#include 
#include 

#include "Trivia.h"
#include "Question.h"

Trivia::Trivia()
{

}

Trivia::~Trivia()
{
	for ( size_t index = 0; index < this->questions.size(); ++index )
		delete this->questions[index];
}

int Trivia::run()
{
	if ( int result = this->loadQuestions() )
		return result;

	std::srand( std::time(nullptr) + std::clock() );
	while ( this->askQuestion() )
		;

	this->printStatistics();
	return 0;
}

int Trivia::loadQuestions()
{
	const char* const filename = "trivia3.txt";
	std::ifstream input(filename);
	if ( ! input )
		return std::cerr << "trivia: could not open " << filename << std::endl, 2;

	std::string type;
	while ( std::getline( input, type ) )
	{
		Question* question = createQuestion(type);
		if ( question )
		{
			input >> *question;
			this->questions.push_back( question );
		}
		else
			return std::cerr << "trivia: invalid question type " << type << std::endl, 3;
	}

	return 0;
}

#if 0
	for ( size_t index = 0; index < questions.size(); ++index )
		std::cout << questions[index] << std::endl;
	std::cerr << questions.size() << " questions loaded\n";

	long seed = std::time(nullptr) + std::clock();
	std::default_random_engine generator(seed);
	std::uniform_int_distribution distribution( 0, this->questions.size() );
	auto dice = std::bind(distribution, generator);
	//size_t index = dice();
	for (int i = 0; i < 10; ++i)
		std::cout << dice() << std::endl;
	return false;
#endif

bool Trivia::askQuestion()
{
	size_t index = std::rand() % this->questions.size();
	if ( (*this->questions[index]).ask() )
		++this->score;
	return std::cin.good();
}

void Trivia::printStatistics()
{
	std::cout << "You won " << this->score << " points\n";
}

#include "NumericQuestion.h"
#include "TextualQuestion.h"
#include "SingleChoiceQuestion.h"

// Factory method
Question* Trivia::createQuestion(const std::string& type)
{
	// new Question(); // abstract classes could not be instanced
	if ( type == "numeric" ) return new NumericQuestion();
	if ( type == "textual" ) return new TextualQuestion();
	if ( type == "single_choice" ) return new SingleChoiceQuestion();

	return nullptr;
}
]]>
Trivia.cpp. Obtener código fuente.

Clase Question (model)


#include 

class Question
{
  protected:
	std::string text;
	std::string answer;

  public:
	Question();
	virtual ~Question() { }

	friend std::istream& operator>>(std::istream& in, Question& question)
	{
		return question.read(in, true);
	}

	virtual std::istream& read(std::istream& in, bool skipEmptyLine)
	{
		std::getline(in, this->text);
		std::getline(in, this->answer);

		if ( skipEmptyLine )
			in.ignore(std::numeric_limits::max(), '\n');
		//std::string dummy;
		//std::getline(in, dummy);
		return in;
	}

	friend std::ostream& operator<<(std::ostream& out, const Question& question)
	{
		return out << '{' << question.text << "}[" << question.answer << ']';
	}

	virtual void print(std::ostream& out) const;

	bool ask();
	virtual bool isRightAnswer(const std::string& playerAnswer) const = 0;
};

#endif // QUESTION_H
]]>
Question.h. Obtener código fuente.


#include "Question.h"

Question::Question()
{

}

void Question::print(std::ostream& out) const
{
	out << this->text << std::endl;
}

bool Question::ask()
{
	this->print(std::cout);
	std::string playerAnswer;
	std::getline(std::cin, playerAnswer);
	//bool correct = this->vtable[0](playerAnswer);
	bool correct = this->isRightAnswer(playerAnswer);
	std::cout << (correct ? "Right!\n\n" : "Wrong!\n\n");
	return correct;
}

]]>
Question.cpp. Obtener código fuente.

Clase NumericQuestion

NumericQuestion.h. Obtener código fuente.


NumericQuestion::NumericQuestion()
{

}

bool NumericQuestion::isRightAnswer(const std::string& playerAnswer) const
{
	return std::strtod(playerAnswer.c_str(), nullptr)
		== std::strtod(this->answer.c_str(), nullptr);
}
]]>
NumericQuestion.cpp. Obtener código fuente.

Clase TextualQuestion

TextualQuestion.h. Obtener código fuente.


TextualQuestion::TextualQuestion()
{

}

bool TextualQuestion::isRightAnswer(const std::string& playerAnswer) const
{
	// ToDo: trim both answers

	if ( playerAnswer.length() != this->answer.length() )
		return false;

	for ( size_t index = 0; index < playerAnswer.length(); ++index )
		if ( std::tolower(playerAnswer[index]) != std::tolower(this->answer[index]) )
			return false;

	return true;
}
]]>
TextualQuestion.cpp. Obtener código fuente.

Clase SingleChoiceQuestion



#include "Question.h"

class SingleChoiceQuestion : public Question
{
  protected:
	std::vector choices;

  public:
	SingleChoiceQuestion();
	virtual bool isRightAnswer(const std::string& playerAnswer) const override;
	virtual std::istream& read(std::istream& in, bool skipEmptyLine) override;
	virtual void print(std::ostream& out) const override;
};

#endif // SINGLECHOICEQUESTION_H
]]>
SingleChoiceQuestion.h. Obtener código fuente.


#include "SingleChoiceQuestion.h"

SingleChoiceQuestion::SingleChoiceQuestion()
{

}

bool SingleChoiceQuestion::isRightAnswer(const std::string& playerAnswer) const
{
	return std::strtol(playerAnswer.c_str(), nullptr, 10)
			== std::strtol(this->answer.c_str(), nullptr, 10);
}

std::istream& SingleChoiceQuestion::read(std::istream& in, bool skipEmptyLine)
{
	(void)skipEmptyLine;
	Question::read(in, false);

	std::string choice;
	while ( std::getline(in, choice) && choice.length() > 0 )
		this->choices.push_back(choice);

	return in;
}

void SingleChoiceQuestion::print(std::ostream& out) const
{
	Question::print(out);
	for ( size_t index = 0; index < this->choices.size(); ++index )
		out << (index + 1) << ". " << this->choices[index] << std::endl;
}
]]>
SingleChoiceQuestion.cpp. Obtener código fuente.