|
| 1 | +C.scala: A Shallow DSL for C |
| 2 | +============================ |
| 3 | + |
| 4 | +This project defines a Scala library for the core C language and some useful C |
| 5 | +libraries. It also provides implementations of Scala collections such as HashMap |
| 6 | +or List in the C DSL. |
| 7 | + |
| 8 | +Installation |
| 9 | +------------ |
| 10 | + |
| 11 | +The shallow C definitions are accompanied by implementations which use the JNI. |
| 12 | +`sbt compile` will take care of generating and compiling the required C code, |
| 13 | +provided that: |
| 14 | + |
| 15 | + * GLib is installed on your system. You can check this by making sure that |
| 16 | + `pkg-config --list-all | grep glib` prints something. |
| 17 | + |
| 18 | + * clang is installed on your system (`which clang` should not return an error). |
| 19 | + |
| 20 | + * The JNI headers for your system are as specified in `project/Build.scala`. |
| 21 | + |
| 22 | + |
| 23 | +The core C language |
| 24 | +------------------- |
| 25 | + |
| 26 | +The functionality of the C language itself is provided in the `cscala.CLang` package. |
| 27 | +This includes functions such as `sizeof`, address-taking and dereferencing. |
| 28 | + |
| 29 | +This functionality is contained in the `cscala.CLang` package. The following import |
| 30 | +is also needed in order to use it: |
| 31 | + |
| 32 | +```scala |
| 33 | +import cscala.CLangTypes._ |
| 34 | +``` |
| 35 | + |
| 36 | +To use the JNI implementations, the calling code needs to load the library |
| 37 | +generated during compilation: |
| 38 | + |
| 39 | +```scala |
| 40 | +System.loadLibrary("shallow") |
| 41 | +``` |
| 42 | + |
| 43 | +### Pointers |
| 44 | + |
| 45 | +C.scala deals with pointers by providing the Scala type `Pointer[T]`. Pointers |
| 46 | +to values can be obtained using the `&` method and those pointers can be |
| 47 | +dereferenced using the `*` method: |
| 48 | + |
| 49 | +```scala |
| 50 | +val x: Int = 5 |
| 51 | +val px: Pointer[Int] = &(x) |
| 52 | +println(*(x)) |
| 53 | +// => 5 |
| 54 | +``` |
| 55 | + |
| 56 | +Note: currently, calling `&` on the same value twice will return pointers to |
| 57 | +different copies of the value. |
| 58 | + |
| 59 | +It is possible to perform pointer arithmetic using the `pointer_add` and |
| 60 | +`pointer_sub` methods. Assignment is possible using `pointer_assign`. For |
| 61 | +example, using `malloc` from the `cscala.StdLib` pacakge it is possible to write |
| 62 | +array-like operations: |
| 63 | + |
| 64 | +```scala |
| 65 | +val array: Pointer[Int] = malloc[Int](5) // Allocates space for 5 integers |
| 66 | +for (i <- 0 until 5) { |
| 67 | + pointer_assign(pointer_add(array, i), i) |
| 68 | +} |
| 69 | + |
| 70 | +println(*(pointer_add(array, 3))) |
| 71 | +// => 3 |
| 72 | +``` |
| 73 | + |
| 74 | +### Structs |
| 75 | + |
| 76 | +It is possible to declare and use structs with C.scala, but declaration is |
| 77 | +currently cumbersome. A struct is declared by declaring both a subclass of |
| 78 | +`CStruct` and an instance of `CStructInfo[T]` for your new struct `T`. For |
| 79 | +example: |
| 80 | + |
| 81 | +```scala |
| 82 | +class CTimeVal(val bytes: Array[Byte]) extends CStruct { |
| 83 | + def this(sec: Long, usec: Long) = |
| 84 | + this(ByteBuffer.allocate(16).order(ByteOrder.LITTLE_ENDIAN).putLong(sec).putLong(usec).array) |
| 85 | +} |
| 86 | +implicit val CTimeValInfo: CStructInfo[CTimeVal] = new CStructInfo[CTimeVal] { |
| 87 | + val sizes = List( |
| 88 | + 'tv_sec -> sizeof[Long], |
| 89 | + 'tv_usec -> sizeof[Long]) |
| 90 | + def create(bytes: Array[Byte]) = new CTimeVal(bytes) |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +Structs can be created using `malloc`, `pointer_assign` and `pointer_add`, or by |
| 95 | +simply creating a new instance of the `CStruct`. Member selection in pointers to |
| 96 | +structs can be done using the `->` method: |
| 97 | + |
| 98 | +```scala |
| 99 | +val tv = new CTimeVal(12 ,45) |
| 100 | +val p = &(tv) |
| 101 | +println(->[CTimeVal, Long](p, 'tv_sec)) |
| 102 | +// => 12 |
| 103 | +``` |
| 104 | + |
| 105 | +### Casts |
| 106 | + |
| 107 | +C programmers expect to be able to cast between types without runtime errors. |
| 108 | +Since Scala casts will fail at runtime when attempting, for example, a cast from |
| 109 | +a `Pointer[Int]` to a `Int`, C.scala provides an alternative way to cast via |
| 110 | +the `as` method. `as[Int](p: Pointer[Int])` will cast the pointer to an int |
| 111 | +(the underlying address pointed to) without failing at runtime. |
| 112 | + |
| 113 | + |
| 114 | +C libraries |
| 115 | +----------- |
| 116 | + |
| 117 | +A handful of useful C libraries are provided by C.scala. Currently functions in |
| 118 | +the following libraries are provided: |
| 119 | + |
| 120 | + * `stdlib.h` via the `cscala.CStdLib` package (only `free` and `malloc`). |
| 121 | + * `stdio.h` via the `cscala.CStdIO` package. |
| 122 | + * `sys/time.h` via the `cscala.CSysTime` package (only `gettimeofday`, |
| 123 | + `timeval_subtract` and the `timeval` struct |
| 124 | + * `string.h` via the `CString` pacakge. |
| 125 | + |
| 126 | +Some collections from GLib are also provided: |
| 127 | + |
| 128 | + * [Doubly-linked lists](https://developer.gnome.org/glib/stable/glib-Doubly-Linked-Lists.html) |
| 129 | + via the `cscala.GListHeader` package. |
| 130 | + * [Hash tables](https://developer.gnome.org/glib/stable/glib-Hash-Tables.html) |
| 131 | + via the `cscala.GHashTableHeader` package. |
| 132 | + * [Arrays](https://developer.gnome.org/glib/stable/glib-Arrays.html) via the |
| 133 | + * `cscala.GArrayHeader` package. |
| 134 | + * [Balanced binary |
| 135 | + * trees](https://developer.gnome.org/glib/stable/glib-Balanced-Binary-Trees.html) |
| 136 | + * via the `cscala.GTreeHeader` package. |
| 137 | + |
| 138 | +To use the GLib libraries, the following import is needed: |
| 139 | + |
| 140 | +```scala |
| 141 | +import cscala.GLibTypes._ |
| 142 | +``` |
| 143 | + |
| 144 | + |
| 145 | +Scala collections |
| 146 | +----------------- |
| 147 | + |
| 148 | +The libraries presented above can be used to prototype C implementations of |
| 149 | +Scala collections. This has already been done for a few collections: |
| 150 | + |
| 151 | + * HashMaps in `cscala.collections.CHashMap`. |
| 152 | + * ArrayBuffers in `cscala.collections.CArrayBuffer`. |
| 153 | + * Lists in `cscala.collections.CList`. |
| 154 | + * Sets in `cscala.collections.CSet` (partially). |
| 155 | + * TreeSets in `cscala.collections.CTreeSet` (partially). |
| 156 | + |
| 157 | +To use these collections, the following import is needed: |
| 158 | + |
| 159 | +```scala |
| 160 | +import cscala.collections.CCollectionsTypes._ |
| 161 | +``` |
| 162 | + |
| 163 | + |
| 164 | +Issues |
| 165 | +====== |
| 166 | + |
| 167 | + * Struct declaration and selection should be cleaned up so that `->` no longer |
| 168 | + needs type parameters all the time, and declaration can be done more easily |
| 169 | + (e.g. just a case class). |
| 170 | + |
| 171 | + * The `pointer_add` and `pointer_sub` methods, as well as the `->` method |
| 172 | + should be methods on `Pointer[T]`, perhaps via an implicit class. |
| 173 | + |
| 174 | + * Casting is only partly implemented. |
| 175 | + |
| 176 | + * Varargs functions such as printf aren't implemented. |
| 177 | + |
0 commit comments