What does the following definition of an interface mean

cinterfacesobject-oriented-design

I am reading Real-Time UML Workshop for Embedded Systems and I do not understand what they are saying here. I have bolded the parts of the text that I do not fully understand. I am trying to understand this from a C stand point of view.

An interface is a named collection of services. Services come in two
basic flavors. Operations are synchronous services that are invoked,
or called by clients. Event receptions are asynchronous services that
are invoked by sending an asynchronous signal to the object accepting
that event. An interface can contain either or both operations and
event receptions. Both operations and event signals can contain data,
called parameters, passed with the invocation. The set of parameters,
along with the name of the service, is called the signature of the
service. A class that is compliant with an interface provides a
method for every operation and an event reception for every signal
specified in the interface.

Interfaces allow you to separate the specification of services that may be called on a class from the implementation of those services. A
class defines methods (for operations) and event receptions (for
signals, specified on the state machine). Both methods and event
receptions include the lines of code that implement the service. An
operation or signal is a specification only, and does not include such
implementation detail.

Interfaces may not have implementation (either attributes or methods) and are not directly instantiable. A class is said to realize
an interface if it provides a method for every operation specified in
the interface, and those methods have the same names, parameters,
return values, preconditions, and postconditions of the corresponding
operations in the interface.

What is an example (or two!) of implementation of an interface in C ?
From what it seems like, its just a prototype of a specific functions in the header file.

I removed UML and added interface and object-oriented-design because I think the question turns around this subjects rather than UML

Best Answer

OOP languages do it much better, and even in C there are better ways to do this, but here is a crude example. I'll show an interface with methods only - the concept of event receptions is more complex, but orthogonal to the one of interfaces, and also beside methods and event receptions there can be other things an interface's implementation needs to fulfill, and I can't show them all here(definitely not in C!)

#include<stdio.h>

typedef struct {
    /// Prints a key-value pair, where value is an integer
    void (*printIntItem)(char key[], int value);
    /// Prints a key-value pair, where the value is a string
    void (*printStringItem)(char key[], char value[]);
} PrintInterface;


void json_printIntItem(char key[], int value) {
    printf("{\"%s\": %d}\n", key, value);
}
void json_printStringItem(char key[], char value[]) {
    printf("{\"%s\": \"%s\"}\n", key, value);
}
PrintInterface PrintJson() {
    PrintInterface interface;
    interface.printIntItem = json_printIntItem;
    interface.printStringItem = json_printStringItem;
    return interface;
}


void xml_printIntItem(char key[], int value) {
    printf("<%s>%d</%s>\n", key, value, key);
}
void xml_printStringItem(char key[], char value[]) {
    printf("<%s>%s</%s>\n", key, value, key);
}
PrintInterface PrintXml() {
    PrintInterface interface;
    interface.printIntItem = xml_printIntItem;
    interface.printStringItem = xml_printStringItem;
    return interface;
}


void printStuff(PrintInterface implementation) {
    implementation.printIntItem("one", 1);
    implementation.printStringItem("two", "2");
}

int main() {
    PrintInterface jsonImpl = PrintJson();
    printStuff(jsonImpl);

    PrintInterface xmlImpl = PrintXml();
    printStuff(xmlImpl);
    return 0;
}

PrintInterface is the interface. It has two methods - printIntItem and printStringItem. Their contract can be divided into two parts:

  • Explicit contract - can be enforced by the language. That's the signature, what they accept and what they return. Some languages allow adding more things to enforce in this contract - e.g. which exceptions(errors) may be thrown, how the method can interact with other data etc. In this case all that's enforced is that the methods accept two arguments - a string and a int/string - and return nothing.
  • Implicit contract - the documentation, including what can be understood from the methods names and the names of their arguments. The language can not enforce this, so it's up to the programmer to respect. It's almost always a bad idea to ignore that part of the contract - if the author bothers to write something in the documentation it's usually for a reason, and also users of the interface will expect the contract to be respected.

I've also written two implementations - PrintJson and PrintXml. What they do is simply to put functions inside an PrintInterface struct. Proper OOP languages/libraries have much more efficient and convenient ways to do this, but this one is for the sake of being easy to follow. They both return a value of the same type - PrintInterface - and that type can be passed around to users of the interface - like printStuff.

When you run this program, this will be printed:

{"one": 1}
{"two": "2"}
<one>1</one>
<two>2</two>

First printStuff is called with the JSON implementation. It uses the API to print the key-value pair "one"->1 and then the key-value pair "two"->"2", and thus {"one": 1} and {"two": "2"} are printed because it uses a JSON implementation.

Then printStuff is called with the XML implementation. It uses the API to print the key-value pair "one"->1 and then the key-value pair "two"->"2", and thus <one>1</one> and <two>2</two> are printed because it now uses an XML implementation.

Related Topic