Skip to content

Instantly share code, notes, and snippets.

@MichaelSnowden
Last active July 22, 2020 06:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MichaelSnowden/73eaddeebc689b36ec10856c593ee3cc to your computer and use it in GitHub Desktop.
Save MichaelSnowden/73eaddeebc689b36ec10856c593ee3cc to your computer and use it in GitHub Desktop.
An extremely bad idea for a way to implement continuation passing style via setjmp and longjmp and a not-so-bad idea
#include <string.h>
#include <stdio.h>
#include <setjmp.h>
#include <unistd.h>
typedef struct {
char *data;
size_t offset;
size_t capacity;
} Buffer;
struct Continuation;
typedef struct {
void (*success)(struct Continuation *, void *);
void (*error)(struct Continuation *, void *);
} ContinuationVTable;
typedef struct Continuation {
const ContinuationVTable *vptr;
} Continuation;
void Continuation_success(Continuation *continuation, void *result) {
continuation->vptr->success(continuation, result);
}
void Continuation_error(Continuation *continuation, void *result) {
continuation->vptr->error(continuation, result);
}
void Buffer_memcpy(Buffer *buffer, const void *src, size_t size, Continuation *continuation) {
if (buffer->offset + size > buffer->capacity) {
Continuation_error(continuation, NULL);
return;
}
memcpy(buffer->data + buffer->offset, src, size);
buffer->offset += size;
Continuation_success(continuation, NULL);
}
void Buffer_strcpy(Buffer *buffer, const void *src, Continuation *continuation) {
size_t size = strlen(src);
Buffer_memcpy(buffer, src, size, continuation);
}
typedef struct {
Continuation super;
jmp_buf *reference;
void **result;
} JumpContinuation;
typedef enum {
ContinuationStatus_SUCCESS = 1,
ContinuationStatus_ERROR = 2,
} ContinuationStatus;
void JumpContinuation_success(Continuation *continuation, void *result) {
JumpContinuation *jumpContinuation = (JumpContinuation *) continuation;
if (jumpContinuation->result != NULL) {
*jumpContinuation->result = result;
}
longjmp(*jumpContinuation->reference, ContinuationStatus_SUCCESS);
}
void JumpContinuation_error(Continuation *continuation, void *result) {
JumpContinuation *jumpContinuation = (JumpContinuation *) continuation;
if (jumpContinuation->result != NULL) {
*jumpContinuation->result = result;
}
longjmp(*jumpContinuation->reference, ContinuationStatus_ERROR);
}
const ContinuationVTable jumpContinuationVTable = (ContinuationVTable) {
.success = JumpContinuation_success,
.error = JumpContinuation_error,
};
void JumpContinuation_init(JumpContinuation *continuation, jmp_buf *reference, void **result) {
continuation->super.vptr = &jumpContinuationVTable;
continuation->reference = reference;
continuation->result = result;
}
struct NodeVTable;
typedef struct {
struct NodeVTable *vptr;
} Node;
typedef struct NodeVTable {
void (*toString)(Node *, Buffer *, Continuation *);
} NodeVTable;
void Node_toString(Node *node, Buffer *buffer, Continuation *continuation) {
node->vptr->toString(node, buffer, continuation);
}
typedef struct {
Node super;
char *name;
} LeafNode;
void LeafNode_toString(Node *node, Buffer *buffer, Continuation *continuation) {
LeafNode *leafNode = (LeafNode *) node;
Buffer_strcpy(buffer, leafNode->name, continuation);
}
NodeVTable leafNodeVTable = (NodeVTable) {
.toString = LeafNode_toString,
};
void LeafNode_init(LeafNode *node, char *name) {
node->super.vptr = &leafNodeVTable;
node->name = name;
}
typedef struct {
Node super;
Node *left;
Node *right;
} BinaryNode;
typedef struct {
Continuation super;
jmp_buf *context;
void **result;
int index;
} AndThenContinuation;
void AndThenContinuation_success(Continuation *continuation, void *result) {
AndThenContinuation *andThenContinuation = (AndThenContinuation *) continuation;
if (andThenContinuation->result != NULL) {
*andThenContinuation->result = result;
}
++andThenContinuation->index;
longjmp(*andThenContinuation->context, andThenContinuation->index);
}
void AndThenContinuation_error(Continuation *continuation, void *result) {
AndThenContinuation *andThenContinuation = (AndThenContinuation *) continuation;
if (andThenContinuation->result != NULL) {
*andThenContinuation->result = result;
}
longjmp(*andThenContinuation->context, -1);
}
const ContinuationVTable andThenContinuationVTable = (ContinuationVTable) {
.success = AndThenContinuation_success,
.error = AndThenContinuation_error,
};
void AndThenContinuation_init(AndThenContinuation *continuation, jmp_buf *context, void **result) {
continuation->super.vptr = &andThenContinuationVTable;
continuation->index = 0;
continuation->context = context;
continuation->result = result;
}
void BinaryNode_toStringUsingJumpContinuation(Node *node, Buffer *buffer, Continuation *continuation) {
BinaryNode *binaryNode = (BinaryNode *) node;
JumpContinuation jumpContinuation;
jmp_buf reference;
JumpContinuation_init(&jumpContinuation, &reference, NULL);
switch (setjmp(reference)) {
default:
Buffer_strcpy(buffer, "(", (Continuation *) &jumpContinuation);
case ContinuationStatus_SUCCESS:
break;
case ContinuationStatus_ERROR:
err:
Continuation_error(continuation, NULL);
return;
}
switch (setjmp(reference)) {
default:
Node_toString(binaryNode->left, buffer, (Continuation *) &jumpContinuation);
case ContinuationStatus_SUCCESS:
break;
case ContinuationStatus_ERROR:
goto err;
}
switch (setjmp(reference)) {
default:
Buffer_strcpy(buffer, " ", (Continuation *) &jumpContinuation);
case ContinuationStatus_SUCCESS:
break;
case ContinuationStatus_ERROR:
goto err;
}
switch (setjmp(reference)) {
default:
Node_toString(binaryNode->right, buffer, (Continuation *) &jumpContinuation);
case ContinuationStatus_SUCCESS:
break;
case ContinuationStatus_ERROR:
goto err;
}
switch (setjmp(reference)) {
default:
Buffer_strcpy(buffer, ")", (Continuation *) &jumpContinuation);
case ContinuationStatus_SUCCESS:
break;
case ContinuationStatus_ERROR:
goto err;
}
Continuation_success(continuation, NULL);
}
void BinaryNode_toStringUsingAndThenContinuation(Node *node, Buffer *buffer, Continuation *continuation) {
BinaryNode *binaryNode = (BinaryNode *) node;
AndThenContinuation andThenContinuation;
jmp_buf reference;
AndThenContinuation_init(&andThenContinuation, &reference, NULL);
switch (setjmp(reference)) {
case -1:
Continuation_error(continuation, NULL);
return;
default:
Buffer_strcpy(buffer, "(", (Continuation *) &andThenContinuation);
case 1:
Node_toString(binaryNode->left, buffer, (Continuation *) &andThenContinuation);
case 2:
Buffer_strcpy(buffer, " ", (Continuation *) &andThenContinuation);
case 3:
Node_toString(binaryNode->right, buffer, (Continuation *) &andThenContinuation);
case 4:
Buffer_strcpy(buffer, ")", (Continuation *) &andThenContinuation);
case 5:
Continuation_success(continuation, NULL);
}
}
NodeVTable binaryNodeVTable = (NodeVTable) {
.toString = BinaryNode_toStringUsingAndThenContinuation,
};
void BinaryNode_init(BinaryNode *node, Node *left, Node *right) {
node->super.vptr = &binaryNodeVTable;
node->left = left;
node->right = right;
}
void DebugContinuation_success(Continuation *continuation, void *result) {
printf("\033[32;1msuccess\033[0m\n");
}
void DebugContinuation_error(Continuation *continuation, void *result) {
printf("\033[31;1merror\033[0m\n");
}
const ContinuationVTable debugContinuationVTable = (ContinuationVTable) {
.success = DebugContinuation_success,
.error = DebugContinuation_error,
};
void DebugContinuation_init(Continuation *continuation) {
continuation->vptr = &debugContinuationVTable;
}
int main(int argc, char **argv) {
char data[1024];
Buffer buffer = (Buffer) {.data = data, .offset = 0};
LeafNode a;
LeafNode_init(&a, "a");
Continuation debugContinuation;
DebugContinuation_init(&debugContinuation);
buffer.capacity = 0;
Node_toString((Node *) &a, &buffer, &debugContinuation);
buffer.capacity = 1;
Node_toString((Node *) &a, &buffer, &debugContinuation);
buffer.capacity = sizeof(data);
JumpContinuation jumpContinuation;
jmp_buf reference;
JumpContinuation_init(&jumpContinuation, &reference, NULL);
BinaryNode root;
BinaryNode_init(&root, (Node *) &a, (Node *) &a);
size_t offset = buffer.offset;
switch (setjmp(reference)) {
default:
Node_toString((Node *) &root, &buffer, (Continuation *) &jumpContinuation);
case ContinuationStatus_SUCCESS: {
size_t size = buffer.offset - offset;
printf("success: %ld\n", size);
write(STDOUT_FILENO, buffer.data + offset, size);
putc('\n', stdout);
break;
}
case ContinuationStatus_ERROR:
printf("error\n");
break;
}
LeafNode b, c;
LeafNode_init(&b, "b");
LeafNode_init(&c, "c");
BinaryNode right;
BinaryNode_init(&right, (Node *) &b, (Node *) &c);
root.right = (Node *) &right;
offset = buffer.offset;
switch (setjmp(reference)) {
default:
Node_toString((Node *) &root, &buffer, (Continuation *) &jumpContinuation);
case ContinuationStatus_SUCCESS: {
size_t size = buffer.offset - offset;
printf("success: %ld\n", size);
write(STDOUT_FILENO, buffer.data + offset, size);
putc('\n', stdout);
break;
}
case ContinuationStatus_ERROR:
printf("error\n");
break;
}
return 0;
}
#include <stdio.h>
#include <string.h>
#include <unistd.h>
typedef struct {
char *data;
size_t offset;
size_t capacity;
} Buffer;
int Buffer_memcpy(Buffer *buffer, const void *src, size_t size) {
if (buffer->offset + size > buffer->capacity) {
return -1;
}
memcpy(buffer->data + buffer->offset, src, size);
buffer->offset += size;
return 0;
}
// not exactly strcpy since it doesn't add \0 at the end
int Buffer_strcpy(Buffer *buffer, const char *src) {
return Buffer_memcpy(buffer, src, strlen(src));
}
struct NodeVTable;
typedef struct {
struct NodeVTable *vptr;
} Node;
typedef struct NodeVTable {
int (*toString)(Node *, Buffer *buffer);
} NodeVTable;
int Node_toString(Node *node, Buffer *buffer) {
return node->vptr->toString(node, buffer);
}
typedef struct {
Node super;
char *name;
} LeafNode;
int LeafNode_toString(Node *node, Buffer *buffer) {
LeafNode *leafNode = (LeafNode *) node;
return Buffer_strcpy(buffer, leafNode->name);
}
NodeVTable leafNodeVTable = (NodeVTable) {
.toString = LeafNode_toString,
};
void LeafNode_init(LeafNode *node, char *name) {
node->super.vptr = &leafNodeVTable;
node->name = name;
}
typedef struct {
Node super;
Node *left;
Node *right;
} BinaryNode;
int BinaryNode_toString(Node *node, Buffer *buffer) {
BinaryNode *binaryNode = (BinaryNode *) node;
// This is the ugly part of the imperative code. I'd prefer to give control to the buffer, instead of the client
// optionally checking the error code, they should be forced to check it using a success/fail model. Essentially,
// force the client to add their own if/else statement.
if (Buffer_strcpy(buffer, "(") != 0) {
return -1;
}
if (Node_toString(binaryNode->left, buffer) != 0) {
return -1;
}
if (Buffer_strcpy(buffer, " ") != 0) {
return -1;
}
if (Node_toString(binaryNode->right, buffer) != 0) {
return -1;
}
return Buffer_strcpy(buffer, ")");
}
NodeVTable binaryNodeVTable = (NodeVTable) {
.toString = BinaryNode_toString,
};
void BinaryNode_init(BinaryNode *node, Node *left, Node *right) {
node->super.vptr = &binaryNodeVTable;
node->left = left;
node->right = right;
}
int main(int argc, char **argv) {
LeafNode a, b, c;
LeafNode_init(&a, "a");
LeafNode_init(&b, "b");
LeafNode_init(&c, "c");
BinaryNode root, left;
BinaryNode_init(&left, (Node *) &a, (Node *) &b);
BinaryNode_init(&root, (Node *) &left, (Node *) &c);
char data[1024];
Buffer buffer = (Buffer) {.data = data, .offset = 0, .capacity = sizeof(data)};
Node_toString((Node *) &root, &buffer);
write(STDOUT_FILENO, buffer.data, buffer.offset);
fflush(stdout);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment