1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | #include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
/* This is part 3 of a 3-part series of snippets illustrating how to handle
* runtime errors without obscuring the core logic of the function. This is
* aimed at helping you with _check_bmp_header(…).
*/
typedef struct {
int x, y, z; // separate lines is preferable, but this is ok for lecture
char name[255]; // do not declare strings like this in production code (security risk)
} ThreeDNamedPoint;
// _strdup(…) makes a copy of a string on the heap. Caller is responsible for freeing.
//
// You may use or adapt the _strdup(…) function in your code.
char* _strdup(const char* s) { // caller is responsible for freeing memory
char* s_copy = malloc(sizeof(*s_copy)*(strlen(s) + 1));
strcpy(s_copy, s);
return s_copy;
}
// _check(…) reduces duplicate code related to error handling in other functions.
// ∙ condition is something that should be true, unless there was a problem.
// ∙ a_ok is the address of a bool (e.g., declared as bool ok = true; by caller)
// which should be true if execution has been normal so far (i.e., no errors).
// ∙ a_error is the address of a char* (e.g., declared as char* error = NULL by
// caller), which, in case of an error, will be set to a message on the heap.
// ∙ msg_if_fails is the message (e.g., on data segment) that should be reported
// in case condition indicates there was a runtime error (i.e., condition==false).
//
// This is vaguely to assert(…) in that you specify a condition which should be true
// under normal circumstances. However, assert(…) is only to detect bugs in your code,
// while this helper function is only for detecting runtime errors (i.e., things that
// could conceivably happen even if your code is written perfectly).
//
// You may use or adapt the _check(…) function in your code.
bool _check(bool condition, bool* a_ok, char** a_error, const char* msg_if_fails) {
if(*a_ok && !condition) { // If this check failed and no errors until now…
*a_ok = false; // <-- so caller can skip code that depends on this succeeding
*a_error = _strdup(msg_if_fails); // make copy of msg_if_fails on heap
}
return *a_ok;
}
bool write_point(ThreeDNamedPoint p_orig, char* filename, char** error) { // 10 lines
bool ok = true;
FILE* fp = fopen("tdnp.bin", "w");
if( _check(fp != NULL, &ok, error, "cannot open file") ) {
size_t num_written = fwrite(&p_orig, sizeof(p_orig), 1, fp);
_check(num_written == 1, &ok, error, "unable to write to file");
fclose(fp);
}
return ok;
}
///////////////////////////////////////////////////////////////////////
void _print_point(ThreeDNamedPoint p_orig) {
printf("x=%d, y=%d, z=%d, name=%s\n", p_orig.x, p_orig.y, p_orig.z, p_orig.name);
}
int main(int argc, char* argv[]) {
// Create a ThreeDNamedPoint object
ThreeDNamedPoint p_orig = {.x=5, .y=6, .z=7, .name="polyester"};
// Write the point to a binary file.
char* error = NULL;
write_point(p_orig, "tdnp.bin", &error);
// Handle errors -- if any, print the message and exit
if(error != NULL) {
fprintf(stderr, "Error: %s\n", error);
free(error); // free the memory for the error message
return EXIT_FAILURE;
}
// Read the ThreeDNamedPoint from the binary file created above into a new object.
// It would be better to do this in a separate function, similar to write_point(…).
FILE* fp = fopen("tdnp.bin", "r");
ThreeDNamedPoint p_copy; // not initializing because fread(…) will do that for us
fread(&p_copy, sizeof(p_copy), 1, fp); // essentially copies bytes from disk to memory
fclose(fp);
// Print the original object and the copy made when we read it from disk, to visually
// confirm that they are the same.
_print_point(p_orig);
_print_point(p_copy);
return EXIT_SUCCESS;
}
/* vim: set tabstop=4 shiftwidth=4 fileencoding=utf-8 noexpandtab: */
|
© Copyright 2020 Alexander J. Quinn This content is protected and may not be shared, uploaded, or distributed.