Last active
July 22, 2020 06:27
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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