An open API service indexing awesome lists of open source software.

https://github.com/elcritch/cdecl

Nim helper for using C Macros
https://github.com/elcritch/cdecl

Last synced: 5 months ago
JSON representation

Nim helper for using C Macros

Awesome Lists containing this project

README

          

C.D.E.C.L.: Commonly Desired Edge Case Library

See full docs at docs or source on github at elcritch/cdecl.


Small library for macros to handle various edge cases for Nim syntax. These are mostly edge case syntax handlers or tricky C Macro interfacings. The goal is to implement them as generically and well unit tested as possible.


Current macros includes:




  • cdecls: Macros to help using C macros that declare variables

    • cdeclmacro




  • applies: Macros that unpack arguments from various forms and calls functions


    • unpackObjectArgs: macro to splat an object to position arguments


    • unpackObjectArgFields: macro to splat an object to keyword arguments


    • unpackLabelsAsArgs: turn labels to named arguments




  • bitfields: Macros for making bitfield style accessor


    • bitfields: create bitfield accessors for hardware registers using any int type




You can see various usages in the tests folder.

Macros


unpackObjectArgs

Helper to apply all fields of an object as named paramters.


type AddObj = object

a*: int
b*: int

proc add(a, b: int): int =
result = a + b

let args = AddObj(a: 1, b: 2)
let res = unpackObjectArgs(add, args)
assert res == 3

unpackLabelsAsArgs

Helper to transform labels as named arguments to a function. Labels are regular Nim syntax for calling procs but are transformed to parameter names:


proc foo(name: string = "buzz", a, b: int) =

echo name, ":", " a: ", $a, " b: ", $b

template Foo(blk: varargs[untyped]) =
## create a new template to act YAML like API
unpackLabelsAsArgs(foo, blk)

Foo:
name: "buzz"
a: 11
b: 22


Will call foo(name="buzz",a=11,b=22) and print:



buzz: a: 11 b: 22

cdeclmacro

Macro helper for wrapping a C macro that declares a new C variable.


It handles emitting the appropriate C code for calling the macro. Additionally it defines a new Nim variable using importc which imports the declared variable.

Basic Example

import cdecl/cdecls

import cdecl/cdeclapi
export cdeclapi # this is needed clients to use the declared apis

proc CDefineVar*(name: CToken, size: static[int]) {.
cdeclmacro: "C_MACRO_VARIABLE_DECLARER", cdeclsVar(name -> array[size, int32]).}

CMacroDeclare(myVar, 128, someExternalCVariable) # creates myVar


macro cdeclmacro(name: string; def: untyped)

CRawStr Example

import macros

import cdecl

proc CDefineVarStackRaw*(name: CToken, size: static[int], otherRaw: CRawStr): array[size, int32] {.
cdeclmacro: "C_DEFINE_VAR_ADDITION".}

# Pass a raw string to the C macro:
proc runCDefineVarStackRaw() =
CDefineVarStackRaw(myVarStackRaw, 5, CRawStr("40+2"))
assert myVarStackRaw[0] == 42


## **macro** cdeclmacro

Macro helper for wrapping a C macro that declares a new C variable.


It handles emitting the appropriate C code for calling the macro.


It can define Nim variables using importc to wrap the generated variable. This is done using varName: CToken in the argument list and adding a cdeclsVar(varName -> varType) pragma. The cdeclsVar tells the macro which CToken argument to use and its type.


The macro will pass any extra pragmas to the variable. If the global pragma is passed in the emitted C code will be put in the /*VARSECTION*/ section.

```nim
macro cdeclmacro(name: string; def: untyped)
```

## **macro** cmacrowrapper

pragma for making a c macro wrapper

```nim
macro cmacrowrapper(name: string; def: untyped)
```
## **type** CRawStr

Represents a raw string that gets interpolated into generated C ouput

```nim
CRawStr = distinct string
```

## **type** CLabel

used to represent a C macro "label", an alias for CRawStr

```nim
CLabel = CRawStr
```

## **type** CRawToken

Represents a C token derived from a Nim expression

```nim
CRawToken = distinct static[CRawStr]
```

## **type** CToken

Represents a C token derived from a Nim expression

```nim
CToken = distinct static[CRawStr]
```

## **macro** symbolName

Get a string representation of a Nim symbol

```nim
macro symbolName(x: untyped): string
```

## **template** symbolVal

Turns a CRawStr into a normal string

```nim
template symbolVal(x: CRawStr): string
```

## **template** symbolVal

Turns a CRawStr into a normal string

```nim
template symbolVal(x: string): string
```
## **macro** unpackObjectArgs

Calls callee with fields form object args unpacked as individual arguments.


This is similar to unpackVarargs in std/macros but for call a function using the values from an object

```nim
macro unpackObjectArgs(callee: untyped; arg: typed; extras: varargs[untyped]): untyped
```

## **macro** unpackObjectArgFields

Similar to unpackObjectArgs but with named parameters based on field names.

```nim
macro unpackObjectArgFields(callee: untyped; arg: typed;
extras: varargs[untyped]): untyped
```

## **macro** unpackLabelsAsArgs

unpacks labels as named arguments.

```nim
macro unpackLabelsAsArgs(callee: typed; args: varargs[untyped]): untyped
```
## **macro** bitfields

Create a new distinct integer type with accessors for bitfields that set and get bits for each field. This is more stable than C-style bitfields (see below).


The basic syntax for a bitfield declarations is:

fieldname: uint8[4..5]

- fieldName is the name of the accessors and produces both

a getter (fieldName) and setter (fieldName=)

- the range 4..5 is the target bit indexes. The ranges are

inclusive meaning 6 ... 6 is 1 bit. Ranges are sorted so you can also use 5 .. 4 to match hardware documentation.

* The type uint8 is the type that the bits are converted to/from.

Signed types like int8 are supported and do signed shifts to



properly extend the sign. For example:

speed: int8[7..4]


The accessors generated are very simple and what you would generally produce by hand. For example:




bitfields RegConfig(uint16):

speed: int8[4..2]



Generates code similar too:

type

RegChannel = distinct uint16

proc speed*(reg: RegChannel): uint8 =
result = uint8(bitsliced(uint16(reg), 4 .. 9))
proc speed=*(reg: var RegChannel; x: uint8) =
setBitsSlice(uint16(reg), 4 .. 9, x)


This is often preferable to C-style bitfields which Nim does support. C-style bitfields are compiler and architecture dependent and prone to breaking on field alignement, endiannes, and other issues. See https://lwn.net/Articles/478657/

```nim
macro bitfields(name, def: untyped)
```