Package: src/packages/pointers.fdoc
Pointers and low level address manipulation.¶
key | file |
---|---|
carray.flx | share/lib/std/c/carray.flx |
cptr.flx | share/lib/std/c/cptr.flx |
shared_ptr.flx | share/lib/std/c/shared_ptr.flx |
key | file |
---|---|
sort.flx | share/lib/std/datatype/sort.flx |
posix_mmap.flx | share/lib/std/posix/mmap.flx |
carray_test.flx | test/regress/rt/carray_test.flx |
carray_test.expect | test/regress/rt/carray_test.expect |
C pointer¶
//[cptr.flx]
// move to separate file later.
open class AbstractPointers
{
typedef fun rptr (T:TYPE) : TYPE => (get: 1 -> T);
typedef fun wptr (T:TYPE) : TYPE => (set : T -> 0);
typedef fun 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] ( p: &>T, v: T) = { _storeat (p,v); }
//$ Dereference a Felx pointer.
//lvalue fun deref[T]: &T -> T = "*$1";
fun _deref[T]: &<T -> T = "*$t";
fun deref[T] (p:&<T) => _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] (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.
typedef fun n"@" (T:TYPE) : TYPE => cptr[T];
C Arrays¶
A carray[T]
, with more suggestive shorthand notation +T
,
is an incrementable, non-NULL pointer to a contiguous store.
//[carray.flx]
// For some reason this functor must be in global scope
//$ Define prefix + notation.
typedef fun prefix_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;
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.
//[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);";
Dereference¶
//[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;";
//$ 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);
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.
//[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';
Pointer operators¶
//[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';
Mutators¶
//[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;';
Comparisons¶
//[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';
}
Conversions¶
//[carray.flx]
//$ Get carray of an array.
fun stl_begin[T,N]: 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] carray[T]: &array[T,N] = "($1)->data";
//$ Convert C array to Felix array.
fun array_of[T,N]: carray[T] -> &array[T,N] = "*(#0*)(void*)$1";
}
open[T] Eq[carray[T]];
open[T] Tord[carray[T]];
//[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;
(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)
Array sort¶
Sort an array using STL sort.
//[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);
}
Reference counting pointer.¶
//[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";
}
MMap¶
Address mapping facility. Note: this is the posix function mmap(). Windows has a similar capability we have not modelled yet.
//[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;
}