GithubHelp home page GithubHelp logo

turbodt / xre Goto Github PK

View Code? Open in Web Editor NEW
0.0 1.0 0.0 121 KB

This project became a C library for state management.

License: MIT License

Makefile 2.75% C 96.66% Shell 0.58%
c-language c-programming-language library react-like react-like-library state-management

xre's Introduction

XRE Lib

Table of Contents

  1. Description
  2. Summary
  3. Usage
    1. Minimal Example
    2. More Examples
  4. How does it work
    1. Low level: States, context and components
    2. Middle level: References
    3. High level: Hooks
      1. Use state
      2. Use effect
    4. Example
  5. Dependencies
  6. License

Description

This library is a memory state manager for iterative function invocations for the C programming language. The library provide tools to keep memory among function calls, as well as automatize clean ups and updates.

Summary

This project was born as an experiment to determine if it would be possible to bring the ideas of React's hooks to C.

It turned out that yes, it is possible. At least partially. As result this project turned it self into the XRE library.

Usage

In my experience, I found this library especially useful when I develop:

  • Loop-based applications, for example: games using Raylib.
  • Application with multiple components which should keep their state through component switching.

But probably there are other use-cases where this library is also useful (let me know).

On how to use it, for now, we will only provide examples and a the header files.

Minimal example

#include <stdio.h>
#include <xre.h>


void print_acc(XREContext *ctx, va_list args) {
    int partial_value = va_arg(args, int);

    XREStateInt * sum_state = xre_use_int(ctx, 0);

    int sum = xre_state_get_int(sum_state);

    sum += partial_value;
    printf("%d\n", sum);

    xre_state_set_int(sum_state, sum);
}


int main() {
    XREContext * ctx = xre_context_root_alloc();
    if (ctx == NULL) {
        return 1;
    }

    for (int value; scanf("%d", &value) != EOF; ) {
        xre_use_root(ctx, &print_acc, value);
    }

    xre_context_destroy(ctx);
    return 0;
}

This program reads a sequence of integers from stdin and provides the partial sum on the go. Observe that the variable containing the global sum (sum) is defined inside the function and keeps its value among different calls.

More examples

You can find a couple of more complete examples under /examples folder. They haven't been written carefully. Sorry about that. However they provide enough information to use all the features this library provides.

How does it work

The API and its concepts can be categorized into three abstraction levels.

Low level: States, contexts and components

A context state or simply an state is a piece of memory persistent among function calls which is able to clean up itself at context destruction time. The object definition is private.

A context is a sequence states. Because it is a sequence, context states should be accessed in the same exact order (ie. sequentially) at every component call. Contexts relate among them hierachically in a tree structure. Concretely contexts can reference to child contexts by using labels.

A component is the function where a context is running from the beginning to its end. They have the exactly following signature:

typedef void (*XREComponent)(struct XREContext *, va_list);

When a component is invoked it always does throught xre_use (or derivates). That function will create or use the corresponding associated context. During the component execution is when we can sequentialy access to the context's states or invoke child contexts.

To access the context's states we use the low-level API, directly or indirectly.

We can invoke children contexts with xre_use, passing the current context and the child's label as arguments.

Middle level: References

References are a next level of abstraction over the context states. Similarly to them, the function of references is to point to any value, but they have two main differences with respect context states:

  1. They control over the pointed data is more complete. Concretely, they are composed by:

1.1. A constructor, so they can build the value on first component's call. 1.2. A destructor, so they can clean up the memory when the associated context is destroyed. 1.3. A setter, so we can modify the value. 1.4. A comparator, so we can know if the value has changed during the component's call.

  1. They definition is public, so we can extend the struct and use it as a base to create custom structures.

They are mapped one to one with context states, so effectively references behave similar to context states and have the same restrictions. That means that the xre_use_ref functions in a context should be always be called in the same exact order at every component call.

Because the above two points, references are the foundations to the high level elements.

High level: Hooks

Because of the story of this library, we use the same terminology as [[React]] uses for their equivalent in this library.

A hook is any function inside a component that passes the associated context as an argument.

Because hooks could, directly or indirectly, access the context's states, they must follow the Rules of Hooks:

  1. Call hooks at same order at each component call.

  2. Pass the current context as a parameter.

Observe that those rules are direct translations from React's Rules of Hooks.

First rule is the translation of "Only call hooks at the top level". Second rule is the translation of "Only call hooks from React functions".

Not to follow the Rule of Hooks leads the program to an undefined behaviour, and probably to a SEGFAULT. Therefore, from now on we are always assuming every hook follow the Rules of Hooks.

Then, to be a hook is an interesting property, because any regular function containing one or more hooks is a hook.

The converse, however, is not always true. There are atomic hooks which internally use references or event the low-level API to access the context states.

Also, according to that definition, when components aren't called throught xre_use they become hooks.

As a final note, a convention used in this library is that hooks and only hooks are prefixed by xre_use. We recommend to keep that convention at the main application.

Summarizing we say that hook are the high level bricks that can be composed or directly used to endow the application with the main functionality provided by the library.

We finish this section by explaining two hooks built in this library ready to be used.

Use state

The hook xre_use_<type>(ctx, initial_value) uses exactly one context state through a reference to store in memory a variable of type <type>. The returned value allow us to get, modify or detect changes on that value. Allocation and free are automatically handled on context creation and destruction.

The <type> can be one of: int, long int, size_t, char, void * and void const *.

Use effect

The hook xre_use_effect(ctx, effect, dependencies, ...) allows us to perform any side effect and effect's clean up when any reference in the dependencies array changes. It can be also configure to run the effect and the clean up either at each component call or only at context construction and destruction.

Example

All the above concepts appear (directly or indirectly) in the following the partial example:

void app(XREContext *ctx, va_list args) {
    (void) args;

    XREStateInt * fail_counter_state = xre_use_int(ctx, 0);
    XREStatePtr * command_hist_state = xre_use_ptr(ctx, NULL);
    XREStateString * command_state = xre_use_string(ctx, NULL);

    static XRERef const * cmd_hist_deps[] = {NULL};
    xre_use_effect(ctx, &init_command_hist, cmd_hist_deps, command_hist_state);

    char command[256];
    scanf("%s", command);
    xre_state_set_string(command_state, command);

    XRERef const * cmd_append_deps[] = {&command_state->ref, NULL};
    xre_use_effect(
        ctx,
        &append_command,
        cmd_append_deps,
        command_hist_state,
        command
    );

    if (strcmp(command, "back") == 0) {
        xre_state_set_int(fail_counter_state, 0);
        return;
    } else if (strcmp(command, "order") == 0) {
        xre_state_set_int(fail_counter_state, 0);
        xre_use(ctx, "order_screen", &order_screen);
    } else if (strcmp(command, "catalog") == 0) {
        xre_state_set_int(fail_counter_state, 0);
        xre_use(ctx, "catalog_screen", &catalog_screen);
    } else {
        int fail_counter = xre_state_get_int(fail_counter_state);
        xre_state_set_int(fail_counter_state, fail_counter + 1);
    }

    int fail_counter = xre_state_get_int(fail_counter_state);
    if (fail_counter >= 3) {
        print_help();
    }
}

Dependencies

This library only depends on the UTHash library, which is a header file.

The current make file expects this header file to be inside a folder called ./external/uthash.

License

This project is under the MIT license.

xre's People

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.