Skip to content

Latest commit

 

History

History
190 lines (155 loc) · 7.72 KB

arc_api.md

File metadata and controls

190 lines (155 loc) · 7.72 KB

STC arc: Atomic Reference Counted Smart Pointer

arc is a smart pointer that retains shared ownership of an object through a pointer. Several arc objects may own the same object. The object is destroyed and its memory deallocated when the last remaining arc owning the object is destroyed with arc_X_drop();

The object is destroyed using arc_X_drop(). A arc may also own no objects, in which case it is called empty. The arc_X_cmp(), arc_X_drop() methods are defined based on the i_cmp and i_keydrop macros specified. Use arc_X_clone(p) when sharing ownership of the pointed-to object.

All arc functions can be called by multiple threads on different instances of arc without additional synchronization even if these instances are copies and share ownership of the same object. arc uses thread-safe atomic reference counting, through the arc_X_clone() and arc_X_drop() methods.

When declaring a container with shared pointers, define i_key_arcbox with the arc type, see example.

See similar c++ class std::shared_ptr for a functional reference, or Rust std::sync::Arc / std::rc::Rc.

Header file and declaration

#define i_TYPE <ct>,<kt>   // shorthand to define i_type,i_key
#define i_type <t>         // arc container type name (default: arc_{i_key})
#define i_key <t>          // element type: REQUIRED. Defines arc_X_value
#define i_cmp <f>          // three-way compareison. REQUIRED IF i_key is a non-integral type
                           // Note that containers of arcs will "inherit" i_cmp
                           // when using arc in containers with i_val_arcbox MyArc - ie. the i_type.
#define i_use_cmp          // may be defined instead of i_cmp when i_key is an integral/native-type.
#define i_keydrop <f>      // destroy element func - defaults to empty destruct
#define i_keyclone <f>     // REQUIRED if i_keydrop is defined, unless 'i_opt c_no_clone' is defined.

#define i_keyraw <t>       // convertion type (lookup): default to {i_key}
#define i_keyto <f>        // convertion func i_key* => i_keyraw: REQUIRED IF i_keyraw defined.
#define i_keyfrom <f>      // from-raw func.

#define i_opt c_no_atomic  // Non-atomic reference counting, like Rust Rc.
#define i_tag <s>          // alternative typename: arc_{i_tag}. i_tag defaults to i_key
#include "stc/arc.h"

X should be replaced by the value of i_tag in all of the following documentation.

Methods

arc_X       arc_X_init();                                   // empty shared pointer
arc_X       arc_X_from(i_keyraw raw);                       // create an arc from raw type (available if i_keyraw defined by user).
arc_X       arc_X_from_ptr(i_key* p);                       // create an arc from raw pointer. Takes ownership of p.
arc_X       arc_X_make(i_key key);                          // create an arc from constructed key object. Faster than from_ptr().

arc_X       arc_X_clone(arc_X other);                       // return other with increased use count
arc_X       arc_X_move(arc_X* self);                        // transfer ownership to receiver; self becomes NULL
void        arc_X_take(arc_X* self, arc_X unowned);         // take ownership of unowned.
void        arc_X_assign(arc_X* self, arc_X other);         // shared assign (increases use count)

void        arc_X_drop(arc_X* self);                        // destruct (decrease use count, free at 0)
long        arc_X_use_count(const arc_X* self);

void        arc_X_reset(arc_X* self);
void        arc_X_reset_to(arc_X* self, i_key* p);          // assign new arc from ptr. Takes ownership of p.

uint64_t    arc_X_hash(const arc_X* x);                     // hash value
int         arc_X_cmp(const arc_X* x, const arc_X* y);      // compares pointer addresses if no `i_cmp` is specified.
                                                            // is defined. Otherwise uses 'i_cmp' or default cmp.
bool        arc_X_eq(const arc_X* x, const arc_X* y);       // arc_X_cmp() == 0

// functions on pointed to objects.

uint64_t    arc_X_value_hash(const i_key* x);
int         arc_X_value_cmp(const i_key* x, const i_key* y);
bool        arc_X_value_eq(const i_key* x, const i_key* y);

Types and constants

Type name Type definition Used to represent...
arc_null {0} Init nullptr const
arc_X struct { arc_X_value* get; long* use_count; } The arc type
arc_X_value i_key The arc element type
arc_X_raw i_keyraw Convertion type

Example

// Create two stacks with arcs to maps.
// Demonstrate sharing and cloning of maps.
// Show elements dropped.
#define i_implement
#include "stc/cstr.h"

#define i_type Map
#define i_key_str // i_key: cstr, i_keydrop: cstr_drop, etc..
#define i_val int // year
// override cstr_drop(p) by defining i_keydrop:
#define i_keydrop(p) (printf("  drop name: %s\n", cstr_str(p)), cstr_drop(p))
#include "stc/smap.h"

#define i_type Arc // (atomic) ref. counted pointer
#define i_key Map
#define i_keydrop(p) (printf("drop Arc:\n"), Map_drop(p))
#include "stc/arc.h"

#define i_type Stack
#define i_key_arcbox Arc // Note: use i_key_arcbox for arc or box value types
#include "stc/stack.h"

int main(void)
{
    Stack s1 = {0}, s2 = {0};
    Map *map;

    // POPULATE s1 with shared pointers to Map:
    map = Stack_push(&s1, Arc_make(Map_init()))->get; // push empty map to s1.
    Map_emplace(map, "Joey", 1990);
    Map_emplace(map, "Mary", 1995);
    Map_emplace(map, "Joanna", 1992);

    map = Stack_push(&s1, Arc_make(Map_init()))->get;
    Map_emplace(map, "Rosanna", 2001);
    Map_emplace(map, "Brad", 1999);
    Map_emplace(map, "Jack", 1980);

    // POPULATE s2:
    map = Stack_push(&s2, Arc_make(Map_init()))->get;
    Map_emplace(map, "Steve", 1979);
    Map_emplace(map, "Rick", 1974);
    Map_emplace(map, "Tracy", 2003);

    // Share two Maps from s1 with s2 by cloning(=sharing) the arcs:
    Stack_push(&s2, Arc_clone(s1.data[0]));
    Stack_push(&s2, Arc_clone(s1.data[1]));

    // Deep-copy (does not share) a Map from s1 to s2.
    // s2 will contain two shared and two unshared maps.
    map = Stack_push(&s2, Arc_from(Map_clone(*s1.data[1].get)))->get;

    // Add one more element to the cloned map:
    Map_emplace_or_assign(map, "Cloned", 2022);

    // Add one more element to the shared map:
    Map_emplace_or_assign(s1.data[1].get, "Shared", 2022);

    puts("S1");
    c_foreach (i, Stack, s1) {
        c_forpair (name, year, Map, *i.ref->get)
            printf("  %s:%d", cstr_str(_.name), *_.year);
        puts("");
    }

    puts("S2");
    c_foreach (i, Stack, s2) {
        c_forpair (name, year, Map, *i.ref->get)
            printf("  %s:%d", cstr_str(_.name), *_.year);
        puts("");
    }
    puts("");

    c_drop(Stack, &s1, &s2);
}

Output:

S1
  Joanna:1992  Joey:1990  Mary:1995
  Brad:1999  Jack:1980  Rosanna:2001  Shared:2022
S2
  Rick:1974  Steve:1979  Tracy:2003
  Joanna:1992  Joey:1990  Mary:1995
  Brad:1999  Jack:1980  Rosanna:2001  Shared:2022
  Brad:1999  Cloned:2022  Jack:1980  Rosanna:2001

drop Arc:
  drop name: Rick
  drop name: Tracy
  drop name: Steve
drop Arc:
  drop name: Cloned
  drop name: Brad
  drop name: Rosanna
  drop name: Jack
drop Arc:
  drop name: Brad
  drop name: Shared
  drop name: Rosanna
  drop name: Jack
drop Arc:
  drop name: Joanna
  drop name: Mary
  drop name: Joey