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
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

/* This is part 1 of a 3-part series of snippets illustrating how to handle
 * runtime errors without obscuring the core logic of the function.  This
 * is the naïve example.  It works, but is bad because the code for making
 * the newly allocated copy of the error string on the heap is duplicated.
 */
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;

bool write_point(ThreeDNamedPoint p, char* filename, char** a_error) { // 21 lines

    // Open file
    FILE* fp = fopen("tdnp.bin", "w");

    // Error handler #1:  Make sure file was opened successfully.
    if(fp == NULL) {
        const char* temp_error = "cannot open file";
        char* error = malloc(sizeof(*error)*(strlen(temp_error) + 1));
        strcpy(error, temp_error);
        *a_error = error;
        return false; // failure
    }

    // Write object
    size_t num_written = fwrite(&p, sizeof(p), 1, fp);

    // Error handler #2:  Make sure object was written successfully.
    if(num_written != 1) {
        const char* temp_error = "unable to write to file";
        char* error = malloc(sizeof(*error)*(strlen(temp_error) + 1));
        strcpy(error, temp_error);
        *a_error = error;
        fclose(fp);
        return false; // failure
    }

    fclose(fp);
    return true;
}

///////////////////////////////////////////////////////////////////////

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