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 2019 Alexander J. Quinn         This content is protected and may not be shared, uploaded, or distributed.