Skip to content

Instantly share code, notes, and snippets.

@aschmidt75
Created June 21, 2021 13:54
Show Gist options
  • Save aschmidt75/33821c277ff72f90064ec228f18158c5 to your computer and use it in GitHub Desktop.
Save aschmidt75/33821c277ff72f90064ec228f18158c5 to your computer and use it in GitHub Desktop.
Example of c11 anonymous struct / Introduction of abstract interfaces

Example of c11 anonymous struct / Introduction of abstract interfaces

$  gcc -Wno-missing-declarations -std=c11 -fms-extensions -Wno-microsoft-anon-tag -o ex *.c && ./ex
#ifndef __APP_ERROR_H
#define __APP_ERROR_H
#include <stdlib.h>
typedef struct error {
unsigned int code;
const char *msg;
struct error *wrapped;
} error;
#endif
#include <stdlib.h>
#include <stdio.h>
/**
* gcc -Wno-missing-declarations -std=c11 -fms-extensions -Wno-microsoft-anon-tag -o x *.c && ./x
*/
#include "sample_component_intf.h"
#include "sample_component.h"
/**
* main_usage uses an instance of a sample_component_intf.
* It onlyhas access to the function pointers defined in the
* interface part. It cannot use the internals
*/
void main_usage(sample_interface *sci) {
sample_result_or_err res = sci->sample_function_1(sci, 3);
if (res.ok) {
printf("%d\n", res.sample_result);
} else {
printf("error: %d %s\n", res.err.code, res.err.msg);
}
}
/**
* main owns and creates a sample_component. It passes on
* its interface
*/
int main(int argc, char **argv) {
sample_component* c = sample_component_new();
main_usage((sample_interface*)c);
sample_component_delete(&c);
return 0;
}
#include <stdlib.h>
#include <stdbool.h>
#include "sample_component.h"
/**
* sample_component_private contains private variables
*/
typedef struct sample_component_private {
int a;
} sample_component_private;
sample_result_or_err sample_component_sample_function_1(void* _this, int some_int);
/**
* implementation of sample_component_new
* allocate memory, set public variables, allocate memory for
* private fields, set private fields
*/
sample_component* sample_component_new() {
sample_component* res;
res = (sample_component*)malloc(sizeof(sample_component));
res->sample_function_1 = &sample_component_sample_function_1;
res->counter = 0;
res->private = (sample_component_private*)malloc(sizeof(sample_component_private));
sample_component_private *p = (sample_component_private*)res->private;
p->a = 0;
return res;
}
void sample_component_delete(sample_component** c) {
if (*c != NULL) {
if ( (*c)->private != NULL) {
free((*c)->private);
(*c)->private = NULL;
}
free(*c);
*c = NULL;
}
}
/**
* implementation of sample_component_sample_function_1
*/
sample_result_or_err sample_component_sample_function_1(void* _this, int some_int) {
sample_component* this = (sample_component*)_this;
this->counter += 1;
sample_component_private *p = (sample_component_private*)this->private;
p->a = some_int;
if (p->a %2 == 0) {
sample_result_or_err res = {
.ok = true,
.sample_result = (p->a)+1
};
return res;
}
error e = {
.code = 15,
.msg = "Odd number detected"
};
sample_result_or_err res = {
.ok = false,
.err = e
};
return res;
}
#ifndef __SAMPLE_COMPONENT_H
#define __SAMPLE_COMPONENT_H
#include "sample_component_intf.h"
/**
* Implementation side
* - sample_component implements sample_interface
* - only used by owning code. client code receives a pointer to the interface, not this struct
* - may contain additional fields, considered public
* - should have private variables hidden in the private struct
*/
typedef struct sample_component {
// anonymously extend the interface (requires -fms-extensions)
struct sample_interface;
// public variables go here
int counter;
void *private;
} sample_component;
/**
* sample_component_new creates a new sample_component.
*/
sample_component* sample_component_new();
/**
* sample_component_delete frees memory
*/
void sample_component_delete(sample_component**);
#endif
#ifndef __SAMPLE_COMPONENT_INTF_H
#define __SAMPLE_COMPONENT_INTF_H
#include <stdlib.h>
#include <stdbool.h>
#include "app_error.h"
/**
* A sample result struct, returned by an interface function
* - if ok == false, err contains the error
* - if ok == true, use the result
*/
typedef struct sample_result_or_err {
const bool ok;
union {
const int sample_result;
const error err;
};
} sample_result_or_err;
/**
* Interface part
* - focus on behaviour from perspective of client code
* - all functions as function pointers
* - do not reveal structural internals, use data transfer structs instead
* - return structs that combine a status and the result
*/
typedef struct sample_interface {
/**
* sample_function_1 does sample things with some int
* @returns sample_result_or_err
*/
sample_result_or_err (*sample_function_1)(void* sample_component_intf, int some_int);
} sample_interface;
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment