ExpandCollapse

+ 1 C pointer

share/lib/std/c/cptr.flx

  
  // move to separate file later.
  open class AbstractPointers
  {
    typefun rptr (T:TYPE) : TYPE =>  (get: 1 -> T);
    typefun wptr (T:TYPE) : TYPE =>  (set : T -> 0);
    typefun rwptr (T:TYPE) : TYPE => (get: 1 -> T, set : T -> 0);
  
    fun mkr[T] (p:&<T) => (get= { *p });
    fun mkw[T] (p:&>T) => (set = proc (v:T) { p<-v; });
    fun mkrw[T] (p:&T) => mkr p + mkw p;
  
    fun deref[T] (p: rptr T) => p.get ();
    proc storeat[T] (p: wptr T, v: T) { p.set v; }
  }
  
  open class MachinePointers
  {
    // ordinary pointers
    proc storeat[T:LINEAR] ( p: &>T, v: T) = { _storeat (p,v); }
  
    Dereference a Felx pointer.
    //fun _deref[T:LINEAR]: &<T -> T = "*$t";
    //fun _deref[T:LINEAR]: &<<T -> T = "*$t";
    // NOTE: at present we need all these cases because overload resolution
    // does not "rank" kinds .. hmm .. but even this doesn't work!
    // adding the RW case changes the ambiguity from the view and read only case
    // to the view and read/write case
    // looks like a problem in unification not ranking the modes
    fun deref[T:LINEAR] (p:&<T) => /* { println$ "deref R"; return */ _deref p; /* }(); */
    fun deref[T:LINEAR] (p:&<<T) => /* { println$ "deref V"; return */ _deref p; /* }(); */
  }
  
  open class CompactLinearPointers 
  {
    // concrete compact linear type pointers
    proc storeat[D,C] ( p:_wpclt< D, C >, v: C) = { _storeat (p,v); }
  
    // deref a pointer to compact linear component
    //fun _deref[mach,clv]: _rpclt<mach,clv> -> clv = "::flx::rtl::clt_deref($t)";
    //fun _deref[mach,clv]: _rpclt<mach,clv> -> clv = "*($t)"; // operator * added to C++
    fun deref[mach,clv] (p: _rpclt<mach,clv>) => _deref p;
  }
  
  
  Felix and C pointers.
  Felix pointer ptr[T] = &T.
  C pointer cptr[T] = &T.
  See also carray for incrementable pointers carray[T] = +T.
  open class Cptr 
  {
    Type of a Felix pointer.
    Always points to an object.
    Cannot be NULL.
    Cannot be incremented.
    typedef ptr[T] = &T;
  
    Type of a C pointer.
    Either pointes to an object or is NULL.
    Cannot be incremented.
    variant cptr[T] = | nullptr | Ptr of &T;
  
    Demote a Felix pointer to a C pointer. Safe.
    ctor[T] cptr[T]: &T = "$t";
  
    Promote a C pointer to a Felix pointer.
    Conversion is checked.
    Aborts with match failure if NULL.
    ctor[T] ptr[T](px:cptr[T]) => 
      let Ptr p = px in p
     ; // match failure if null
  
    Checked dereference of C pointer.
    fun deref[T] (px:cptr[T])=> *(px.ptr);
  
    Test if a C pointer is NULL.
    fun is_nullptr[T] (px:cptr[T])=> match px with | #nullptr => true | _ => false endmatch;
    
    instance[T] Eq[cptr[T]] {
      Equality of C pointers.
      fun == : cptr[T] * cptr[T] -> bool = "$1==$2";
    }
    instance[T] Tord[cptr[T]] {
      Total ordering of C pointer.
      NULL is the least element.
      fun < : cptr[T] * cptr[T] -> bool = "$1<$2";
    }
  
    Allocate unmanaged C++ object on the heap and return pointer.
    Felix does not check the argument type, but C++ does.
    The argument must select a suitable C++ constructor.
    gen cnew[T,A] : A -> &T = "new (?1)($a)";
  
    Delete unmanaged C++ object from heap
    proc delete[T] : &T = "delete $1;";
  
    Allocate managed C++ object directly on heap.
    Felix does not check the argument type, but C++ does.
    The argument must select a suitable constructor.
    gen gcnew[T,A] : A -> &T = "new (*ptf->gcp, @?1,true) (?1)($a)";
  
  }
  
  open[T] Eq[cptr[T]];
  open[T] Tord[cptr[T]];
  
  Special notation @T for  type of a C pointer.
  typefun n"@" (T:TYPE) : TYPE => cptr[T]; 
  

+ 2 C Arrays

A carray[T], with more suggestive shorthand notation +T, is an incrementable, non-NULL pointer to a contiguous store.

share/lib/std/c/carray.flx

  
  
  // For some reason this functor must be in global scope
  Define prefix + notation.
  typefun tprefix_plus(T:TYPE) : TYPE => Carray::carray[T]; 
  
  A carray[T] = +T is an incrementable, non-NULL, pointer.
  open class Carray
  {
    requires Cxx_headers::cstdlib;
    open C_hack;
  
    The carray type.
    type carray[T] = new &T;
  

+ 2.1 Allocation

These allocators use raw malloc/calloc/free and therefore provide store of which the garbage collector is unaware. It is best to reserve such carrays for C datatypes.

share/lib/std/c/carray.flx

  
    Allocate a C array on the C heap (malloc).
    Unsafe: Not tracked by GC.
    fun array_alloc[T]: !ints -> carray[T] = '(?1*)::std::malloc(sizeof(?1)*$1)';
  
    Allocate a C array on the C heap with 0 fill (cmalloc).
    Unsafe: Not tracked by GC.
    fun array_calloc[T]: !ints -> carray[T] = '(?1*)::std::calloc(sizeof(?1),$1)';
  
    Free a C array (free).
    Must point to C heap allocated storage. Unsafe.
    proc free[T]: carray[T] = "::std::free($1);";
  

+ 2.2 Dereference

share/lib/std/c/carray.flx

  
    Functional get by index.
    fun get[T]: carray[T] * !ints -> T = '$1[$2]';
  
    Store value in array at index position.
    proc set[T] : carray[T] * !ints * T = "$1[$2]=$3;";
    proc set[T] : carray[T] *  T = "$1[0]=$2;";
  
    proc storeat[T] (lhs: carray[T], rhs: T) { set (lhs, 0, rhs); }
  
    Get by index using application.
    i x = x . i = get (x,i)
    fun apply [T,I in ints] (i:I, x:carray[T]) => get (x,i);
  

+ 2.3 Lvalue dereferences

Note that lvalue operators are for convenience of those familiar with C notation. Felix does not support the notion of lvalues in general: this is a very special case.

share/lib/std/c/carray.flx

    Lvalue reference to element by index position. Unsafe.
    //lvalue fun subscript[T]: carray[T] * !ints -> T = '$1[$2]';
    fun subscript[T]: carray[T] * !ints -> T = '$1[$2]';
  
    Lvalue reference to element by pointer.
    //lvalue fun deref[T]: carray[T] -> T = '*$1';
    fun deref[T]: carray[T] -> T = '*$1';
  

+ 2.4 Pointer operators

share/lib/std/c/carray.flx

    Advance carray to next element.
    fun + [T]: carray[T] * !ints -> carray[T]= '$1+$2';
  
    Backup carray to previous element.
    fun - [T]: carray[T] * !ints -> carray[T] = '$1-$2';
  
    Calculate the offset in elements between
    two overlapping carrays.
    fun - [T]: carray[T] * carray[T]-> ptrdiff = '$1-$2';
  

+ 2.5 Mutators

share/lib/std/c/carray.flx

  
    Mutable pre-increment ++p.
    proc pre_incr[T]: &carray[T] = '++*$1;';
  
    Mutable post-increment p++.
    proc post_incr[T]: &carray[T] = '(*$1)++;';
  
    Mutable pre-decarement --p.
    proc pre_decr[T]: &carray[T] = '--*$1;';
  
    Mutable post-decarement p--.
    proc post_decr[T]: &carray[T] = '(*$1)--;';
  
    Mutable advance by offset amount.
    proc += [T]: &carray[T] * !ints = '*$1+=$2;';
  
    Mutable backup by offset amount.
    proc -= [T]: &carray[T] * !ints = '*$1-=$2;';
  

+ 2.6 Comparisons

share/lib/std/c/carray.flx

  
    Pointer equality.
    instance[T] Eq[carray[T]] {
      fun == : carray[T] * carray[T] -> bool = '$1==$2';
      fun != : carray[T] * carray[T] -> bool = '$1!=$2';
    }
  
    Pointer total ordering.
    instance[T] Tord[carray[T]] {
      fun < : carray[T] * carray[T] -> bool = '$1<$2';
      fun <= : carray[T] * carray[T] -> bool = '$1<=$2';
      fun > : carray[T] * carray[T] -> bool = '$1>$2';
      fun >= : carray[T] * carray[T] -> bool = '$1>=$2';
    }
  

+ 2.7 Conversions

share/lib/std/c/carray.flx

    Get carray of an array.
    fun stl_begin[T,N:COMPACTLINEAR]: carray[array[T,N]] -> carray[T] = "(?1*)&($1->data)";
  
    Unsafe conversion of Felix pointer to carray.
    fun prefix_plus [T]:&T -> carray[T] = "$t"; // unsafe
  
    Demote carray to Felix pointer (safe unless off the end).
    fun neg [T]: carray[T] -> &T = "$t"; // safe (unless we allow +T to be NULL later ..)
  
    Unsafe conversion of Felix pointer to carray.
    ctor[T] carray[T] : &T = "$t";
  
    Get a carray from a Felix array object.
    ctor[T,N:COMPACTLINEAR] carray[T]: &array[T,N] = "($1)->data";
  
  
    Convert C array to Felix array.
    fun array_of[T,N:COMPACTLINEAR]: carray[T] -> &array[T,N] = "*(#0*)(void*)$1";
  }
  
  open[T] Eq[carray[T]];
  open[T] Tord[carray[T]];

test/regress/rt/carray_test.flx

  // carray test
  
  var a : +int = array_alloc[int] 10;
  for var i in 0 upto 9 do
    set(a, i, i * i);
    set(a,i,get(a,i)+1);
  done
  for i in 0 upto 9 do
    println$  a.[i], *(a+i), a.i;
  done
  free a;

test/regress/rt/carray_test.expect

(1, 1, 1)
(2, 2, 2)
(5, 5, 5)
(10, 10, 10)
(17, 17, 17)
(26, 26, 26)
(37, 37, 37)
(50, 50, 50)
(65, 65, 65)
(82, 82, 82)

+ 3 Array sort

Sort an array using STL sort.

share/lib/std/datatype/sort.flx

  
  Utility class to leverage STL sort.
  class Sort
  {
    STL compliant comparator object built from
    a closure of a Felix function.
    private header stl_comparator_def = 
    """
    template<class CT, class FT2, class FFT>
    struct comparator {
      FFT cmp;
      comparator() : cmp(0) {}
      comparator(FFT cmp_a) : cmp(cmp_a) {}
      bool operator ()(CT x, CT y){
        ::std::pair<CT,CT> z(x,y); 
        return cmp->apply(*(FT2*)(void*)&z);
      }
    };
    """ requires Cxx_headers::utility;
  
    private type _comparator[CT,FT2,FFT] = "comparator<?1,?2,?3>" requires stl_comparator_def;
    type stl_comparator[T] = new _comparator[T,T*T,T*T->bool];
  
    private fun _make_comparator[CT,FT2,FFT]: FFT -> stl_comparator[CT] = 
      "comparator<?1,?2,?3>($1)"
    ;
  
    Make a C++ STL comparator object from a Felix comparison function.
    ctor[T] stl_comparator[T] (cmp:T * T -> bool) => 
      _make_comparator[T, T*T, T*T->bool] (cmp)
    ;
  
    Invoke stl sort with C++ comparator.
    proc stl_sort[T]: stl_comparator[T] * +T * +T = "::std::sort($2, $3, $1);" 
      requires Cxx_headers::algorithm;
  
    Invoke stl sort with Felix comparison function.
    inline proc stl_sort[T] (cmp: T * T -> bool, b: +T,  e:+T) => 
      stl_sort (stl_comparator cmp, b, e)
    ;
  
    Invoke stl sort default comparison function.
    inline proc stl_sort[T with Tord[T]] (b:+T, e:+T) => stl_sort ( (< of (T * T)), b, e);
  
  }
  

+ 4 Reference counting pointer.

share/lib/std/c/shared_ptr.flx

  open class SharedPtr
  {
     type shared_ptr[T] 
       = "::std::shared_ptr<?1>"
       requires Cxx_headers::memory
     ;
  
     ctor[T] shared_ptr[T] : 1 = "::std::shared_ptr<?1>()"; // nullptr
     ctor[T] shared_ptr[T] : &T = "::std::shared_ptr<?1>($1)";
  
     proc reset[T] : &shared_ptr[T] = "$1->reset();";
     proc swap[T] : &shared_ptr[T] * &shared_ptr[T] = "$1->swap(*$2);";
     fun get[T] : shared_ptr[T] -> &T = "$1.get()";
     fun deref[T] : shared_ptr[T] -> T = "*$1";
     fun use_count[T] : shared_ptr[T] -> long = "$1.use_count()";
     fun unique[T] : shared_ptr[T] -> bool = "$1.unique";
     fun is_null[T] : shared_ptr[T] -> bool = "(bool)$1";
  }

+ 5 MMap

Address mapping facility. Note: this is the posix function mmap(). Windows has a similar capability we have not modelled yet.

share/lib/std/posix/mmap.flx

  
  class Mmap
  {
    requires package "mmap";
    header """
      // MAP_ANON is an older form of MAP_ANONYMOUS, and should be compatible
      #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
      #  define MAP_ANONYMOUS MAP_ANON
      #endif
    """;
  
    // Offset into file, should be defined elsewhere
    typedef off_t = ulong;
  
    type mmap_prot = "int";
    instance Eq[mmap_prot]{
       fun == : mmap_prot * mmap_prot -> bool = "$1==$2"; 
    }
    instance Bits[mmap_prot]{}
  
    inherit Eq[mmap_prot];
    inherit Bits[mmap_prot];
  
  
    type mmap_flags = "int";
    instance Eq[mmap_flags]{
       fun == : mmap_flags * mmap_flags -> bool = "$1==$2"; 
    }
    instance Bits[mmap_flags]{}
  
    inherit Eq[mmap_flags];
    inherit Bits[mmap_flags];
  
    // protection options
    const PROT_NONE  : mmap_prot;  // Posix: inaccessible
    const PROT_EXEC  : mmap_prot;  // Posix: allow exec
    const PROT_READ  : mmap_prot;  // Posix: allow read (and perhaps exec)
    const PROT_WRITE : mmap_prot;  // Posix: allow write (and perhaps write and exec)
  
    // Linux only
    const MAP_DENYWRITE: mmap_flags; // Linux only
  
    // flags: mode
    const MAP_FILE: mmap_flags;      // Posix: Default mode: map a file
    const MAP_ANONYMOUS: mmap_flags; // Linux, OSX: Map from VM pool
  
    // flags: map address
    const MAP_FIXED: mmap_flags;     // Posix: Client tries to fix the mapping address, 
                              // must set address argument non-NULL
                              // Implementation dependent
                              // Default: system chooses address is not specified
                              // must set address NULL 
  
    // flags: sharing
    const MAP_SHARED : mmap_flags;   // Posix: write changes to backing store on msync
    const MAP_PRIVATE : mmap_flags;  // Posix: don't write changes ever
  
    // System dependent:
    const MAP_HASSEMAPHORE: mmap_flags;
    const MAP_NORESERVE: mmap_flags;
    const MAP_LOCKED: mmap_flags;
    const MAP_GROWSDOWN: mmap_flags;
    const MAP_32BIT: mmap_flags;
    const MAP_POPULATE: mmap_flags;
    const MAP_NONBLOCK: mmap_flags;
  
    // return value of mmap
    const MAP_FAILED : address;
  
    // size of a page
    const _SC_PAGESIZE : long = "sysconf(_SC_PAGESIZE)";
  
    // establish a mapping
    fun mmap:
      address * //< start address
      size *    //< bytes to map
      mmap_prot *     //< protection
      mmap_flags *     //< flags
      int *     //< file descriptor
      off_t     //< offset into file, multiple of _SC_PAGESIZE
      -> address; //< start of reserved address space
  
    // unmap a region
    fun munmap: address * size -> int;
  
    // save region to backing store (MAP_SHARED only)
    fun msync: address * size * int -> int;
  }