C Interfaces and Implementations: Techniques for Creating Reusable Software

Category: Programming
Author: David R. Hanson
3.8
All Hacker News 16
This Month Stack Overflow 2

Comments

by anonymous   2019-07-21

First I suggest you take a look at http://www.amazon.com/Interfaces-Implementations-Techniques-Creating-Reusable/dp/0201498413. Second most browsers are asynchronous so you are going to need a event library like libuv or libev. Also most modern websites require javascript to function properly, but adding a javascript engine to your browser would greatly complicate the project. I also don't see any mention of how you plan on parsing the http being sent to and from your browser, I suggest https://github.com/joyent/http-parser.

As for your question on control flow, I would have a function that parse's the response from the server and use's switch() to handle the various types of data being sent to your browser. There is a field in the http header which explains the content type and that way your browser should be able to call different functions based of what the content type is.

Also take a look at function pointers, both here Polymorphism (in C) and here How do function pointers in C work? . Function pointers would/could be a more eloquent way to solve your problem instead having large switch statements through out your code. With function pointers you can have one function that when called in your program behaves differently.

I will try to explain below with a browser as an example.

So lets say your browser just got back a http response from some server. The http response looks something like this in C.

struct http_res
{
    struct http_header *header;
    struct http_body *body

    int (*decode_body)(char **);
};

So first your http parser will parse the http header and figure out if it's a valid response and if there's content, etc, etc. If there is content the parser will check the type and based off, if it's html, javascript, css, or whatever the parser will set the function pointer to point at the right function to decode the http body.

static int decode_javascript(char **body)
{
    /* Whatever it takes to parse javascript from http. */
    return 0;
}

static int decode_html(char **body)
{
    /* Whatever it takes to parse html from http. */
    return 0;
}

static int decode_css(char **body)
{
    /* Whatever it takes to parse css from http. */
    return 0;
}

int parse_http_header(struct http_res *http)
{
    /* ... lots of other code to figure out content type. ... */

    switch(body_content_type)
    {
        case BCT_JAVASCRIPT:
          http->decode_body = &decode_javascript;
          break;

        case BCT_HTML:
          http->decode_body = &decode_html;
          break;

        case BCT_CSS:
          http->decode_body = &decode_css;
          break;

        default:
          printf("Error can't parse body type.\n");
          return -1;
    }
    return 0;
}

Now when we pass the http request to another part of the browser that function can call decode_body() in the http response object and it will end up with a decoded body it can understand, with out knowing what it's decoding.

int next_function(struct http_res * res)
{
    char *decoded_body;
    int rtrn;

    /* Now we can decode the http body with out knowing anything about
    it. We just call decode_body() and end up with a buffer with the
    decoded data in it. */
    rtrn = res->decode_body(&decoded_body);
    if(rtrn < 0)
    {
        printf("Can't decode body.\n");
        return -1;
    }

    return 0;
}

To make your program really modular at least in C, you would stick the various parts of your browser in different shared libraries, like the HTTP parser, event library, Javascript engine, html parser, etc, etc. Then you would create interfaces between each library and you would be able to swap out each library with a different one with having to change your program, you would link a different library at run time. Take a look at Dr Robert martin(uncle bob) he talks about this extensively. This talk is good but it lacks slides https://www.youtube.com/watch?v=asLUTiJJqdE , starts at 8:20. This one is also interesting, and it has slides: https://www.youtube.com/watch?v=WpkDN78P884 .

And finally nothing about C, perl or python means you will have to recode your program logic. You will have to design your program so that each module does not know about each other. The module knows about the interface and if you connect two modules that both "speak" the same interface you will have created a modular system. It's just like how the internet works the various computers on the internet do not need to know what the other computer is, or what it's doing, or it's operating system, all they need to know is TCP/IP and they can communicate with all the other devices on the internet.

by anonymous   2019-07-21

Answering your first question, I recommend to encapsulate your structures using opaque pointers (a.k.a Handles).

For example, you may declare a handle to a linked list (here MS-like naming):

typedef struct linked_list_t* HLINKEDLIST;

We assume that linked_list_t is a generic one (composed of void pointers).

This way you can hide what a "handle" to a linked list is, or in what form is implemented (information hiding):

HLINKEDLIST LinkedListCreate();
LinkedListAdd(LLELEMENT v);
LinkedListCopy(HLINKEDLIST dst, const HLINKEDLIST src);

Handle subtypes are also commonly defined, such as PHLINKEDLIST (pointer to a linked list handle).

Related types can also be defined for convenience (and to use the limited information hiding available in C). e.g: the linked list element type can be defined as

typedef void* LLELEMENT;

There are good books on Data Structures in C to check. This is good: http://www.amazon.com/Interfaces-Implementations-Techniques-Creating-Reusable/dp/0201498413

Also note that LLELEMENT is actually compatible with void* so if you are defining other type def as:

typedef void* SYSTEMDATA;

SYSTEMDATA is compatible with LLELEMENT, so the compiler won't complain on:

int QuerySystemData(SYSTEMDATA* sd);

and calling:

QuerySystemData(lle);

where lle is of type LLELEMENT.

This type checking can be enforced encapsulating simple members in structs. If I don't remember bad, declaring STRICT in a program using windows.h causes handles to be type-safer (incompatible between). Definitions like the following are common:

typedef struct __HWND 
{ 
int __handle; 
} __HWND; 

typedef __HWND* HWND; 

If the simpler definitions were:

typedef int HWND;
typedef int HBITMAP;

The two handles would be type compatible and interchangeable on functions expecting to work with windows and functions expecting to work with bitmaps (potential horrible bugs).

by greenyoda   2017-08-19
"With OOP it's all about classes, abstractions, interfaces etc. and design patterns."

The ability to create abstractions is pretty much a part of any programming language, including assembler. They're a property of the structure of the code, not of specific language features. For example, in the Linux kernel, which is written in C, there's an abstraction called a file descriptor that represents a stream of bytes that your program can read/write, whether it comes from a file on a disk, a serial port, a pipe, etc.

Interfaces also exist in all languages: they're just a well-defined set of calls that one piece of code uses to communicate with another piece of code. For example, the C runtime library (or any library) has an interface (API), and callers of the library don't need to know about the internal structure of the library to be able to call its services. (There's a pretty good book called "C Interfaces and Implementations"[1] that talks about how to write reusable code in C.)

Classes are also possible to implement in C. A class is pretty much a data structure with a bunch of functions (the methods) that manipulate its data. In C, this can be represented by a structure and bunch of function pointers. The "constructor" function calls malloc() to create an instance of the class, sets its member variables from its parameters and returns a pointer to it. (Implementing inheritance is a bit tricky.)

To be able effectively split development tasks across a team, the same rules apply as in any other language: implement functionality behind well-defined APIs so that one part of the code can be used by another without having to know the details of how it works.

[1] http://www.amazon.com/dp/0201498413

by nvoorhies   2017-08-19
C Interfaces and Implementations: Techniques for Creating Reusable Software is the best thing I've seen on the topic, personally. http://www.amazon.com/Interfaces-Implementations-Techniques-...
by defen   2017-08-19
C Interfaces and Implementations is really good: http://www.amazon.com/Interfaces-Implementations-Techniques-...