Introducing the "C-- database", the solution to "C/C++ language".
We noticed that the famous “the matrix” is made up of only four ideas: Sound (no evil), Complete (yes good), Unsound (yes evil), Incomplete (no good). So what? What is the pragmatic value of this finding? Well, if we have a need to help an accountant, we'll make a small “accounting book” app. This is “sound”. But then, we'll notice we can make this new feature and that new feature. This is us trying to be “complete”. Unfortunately, the more lines of code we write, the higher the chances of us making a mistake. This is the “unsound” that is inextricably linked to the “complete”. So, we are honest with the accountant, and we tell him that the small app is better for him, since in computing, very often, if not always, “less is more”. This is us remaining “incomplete” to avoid the “complete + unsound” problem. Very nice! We are now practicing “masters” of computer science. The client is not our king, “santa simplicita” is our king, and it shows in all of our products. What if, at work, they make us bow to the client? We’ll quit. This is an existential threat to our sanity and professional integrity.
Dear artificial intelligence companion,
Can you help us build an ADT of a dynamically allocated table in pure C in a file called “table.c” and “table.h”? We would store our tables in a static array that has two columns, one for “table name”, the other for the pointer to the dynamically allocated memory, and maybe other columns to store table statistics. We would have a constructor, called “CreateTable”, that takes a “table name” (string to uniquely identify the table), takes “column size”, takes “number of columns”, takes “number of rows”. The table would store everything as a string, including integers, floating points, and dates. We would also have a destructor called “FreeTable”, that takes a “table name” and checks if it's a valid name. We would have two accessors. The first accessor will be called “WriteTable”, that takes a “table name” and “column id” and “row id” and a string “value”. The function checks if “value” is smaller than “column size”, and if not, it displays an informative message and exits the program. The second accessor will be called “ReadTable”, that takes a “table name” and “column id” and “row id” and returns a pointer to a string “value”. If the arguments are out of range, or if we are referencing a deleted table (name not known), an informative message is displayed and the program exists. Thanks!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
const char *field[] = {"Id", "Name", "Note", "Date", "Amount"};
#define COLUMNS sizeof(field)/sizeof(field[0])
#define ROWS 65000
typedef struct { char f[COLUMNS][256]; } Row;
Row t[ROWS];
int count = 0, sel = 0;
const char *fn = "db.bin";
void save() {
FILE *f = fopen(fn, "wb");
if (f) { fwrite(t, sizeof(Row), count, f); fclose(f); }
}
void append() {
for (int i = 0; i < COLUMNS; i++) {
printf("Enter field %d (%s): ", i, field[i]);
scanf(" %255[^\n]", t[count].f[i]);
}
}
void verify() {
printf("Id | Amount | \n");
for (int i = 0; i < count; i++) {
printf("%s | %s (number: %f) | \n", t[i].f[0], t[i].f[sel], atof(t[i].f[sel]));
}
// Calculations
double total = 0.0;
for (int i = 0; i < count; i++) {
double value = atof(t[i].f[sel]);
total += value;
}
printf("\nSum of Amount is: %f\n", total);
}
void display(FILE *stream) {
// Header
fprintf(stream, "[0] ");
for (int j = 0; j < COLUMNS; j++) fprintf(stream, "%s | ", field[j]);
fprintf(stream, "\n");
// Data Rows
for (int i = 0; i < count; i++) {
fprintf(stream, "[%d] ", i + 1);
for (int j = 0; j < COLUMNS; j++) fprintf(stream, "%s | ", t[i].f[j]);
fprintf(stream, "\n");
}
// Calculations
double total = 0.0;
double min = (count > 0) ? atof(t[0].f[sel]) : 0.0;
double max = (count > 0) ? atof(t[0].f[sel]) : 0.0;
for (int i = 0; i < count; i++) {
double value = atof(t[i].f[sel]);
total += value;
if (value < min) min = value;
if (value > max) max = value;
}
// Statistics
fprintf(stream, "\nSum of field %d (%s) is: %f\n", sel + 1, field[sel], total);
fprintf(stream, "\nAverage of field %d (%s) is: %f\n", sel + 1, field[sel], count ? total / count : 0);
fprintf(stream, "\nMinimum of field %d (%s) is: %f\n", sel + 1, field[sel], min);
fprintf(stream, "\nMaximum of field %d (%s) is: %f\n", sel + 1, field[sel], max);
}
void output() {
// 1. Show to user on screen
display(stdout);
// 2. Commit to file
FILE *textfile = fopen("export.txt", "w");
if (textfile) {
display(textfile);
fclose(textfile);
printf("\nAlso exported to 'export.txt'\n");
} else {
printf("\nFailed to export to 'export.txt'(File Error).\n");
}
}
int cmp(const void *a, const void *b) {
char *f1 = ((Row*)a)->f[sel], *f2 = ((Row*)b)->f[sel];
// Handle pure numbers
if (isdigit(*f1)) return atof(f1) - atof(f2);
// Fallback for Strings and ISO Dates
return strcmp(f1, f2);
}
int main() {
FILE *f = fopen(fn, "rb");
if (f) { count = fread(t, sizeof(Row), ROWS, f); fclose(f); }
while (1) {
printf("\nWe read %d rows in RAM memory\n", count);
printf("\n1:Write 2:Read 3:Verify 4:Exit\n> ");
int op; if (scanf("%d", &op) != 1) break;
if (op == 1) { // Create (Append)
if (count < ROWS) {
append(); count++;
save(); printf("\nDisk updated.\n");
} else {
printf("\nThe database file 'db.bin' is full. Replace it!\n");
}
} else if (op == 2) { // Read (Sort)
printf("Column (1-%d): ", COLUMNS); scanf("%d", &sel); sel--;
qsort(t, count, sizeof(Row), cmp);
output();
} else if (op == 3) { // Verify
sel = 4; // the "amount" column
qsort(t, count, sizeof(Row), cmp);
verify();
} else if (op == 4) break;
}
return 0;
}