@Araq A section pragma sounds like a comprehensive solution, but also it sounds 
like it would be really hard to get right. Would it be like codegenDecl but 
with an awareness of what the sections actually mean? Different toolchains 
might have different section names with different restrictions. Would it just 
recognise common ones like .text, .bss, .rodata?

For read-only sections, you would have to ensure that the variable is fully 
initialised at the top level. This initialisation might contain pointers to 
valid things such as procedures or other read-only variables, but I guess it 
should disallow (or at least not be expected to work with) regular runtime 
variables.

But looking at generated C code, it seems like this goes against how top-level 
var/let in Nim generally works (where initialisations are mostly done at 
runtime in the module's 'Init' function)

Ideally it should be possible to make a read-only linked list of pointers to 
procedures:
    
    
    type Action = object
      body: proc () {.nimcall.}
      next: ptr Action
    
    proc fn1() = echo "first"
    proc fn2() = echo "second"
    
    let a2 {.section:rodata.} = Action(body: fn2, next: nil)
    let a1 {.section:rodata.} = Action(body: fn1, next: unsafeAddr a2)
    
    var current = unsafeAddr a1
    while current != nil:
      current.body()
      current = current.next
    
    Run

Which would be equivalent to the following C code:
    
    
    typedef struct Action {
      void (*body)(void);
      const struct Action *next;
    } Action;
    
    void fn1(void) {
      printf("first\n");
    }
    void fn2(void) {
      printf("second\n");
    }
    
    static const Action a2 __attribute__((section("rodata"))) = { .body = fn2, 
.next = NULL };
    static const Action a1 __attribute__((section("rodata"))) = { .body = fn1, 
.next = &a2 };
    
    int main() {
      const Action *current = &a1;
      while (current) {
        current->body();
        current = current->next;
      }
    }
    
    Run

Do you think this kind of output would be possible? Would an {.addressable.} 
pragma for consts be easier? And if so would it still be possible to make it 
that work with pointers to procs?

-

@mratsim Unfortunately that doesn't do what I need. Yes, a C const is 
generated, but it gets copied into a mutable container. So it's still taking up 
RAM. For example, this code currently works when it shouldn't, if arr was truly 
constant:
    
    
    let arr = [1,2,3,4,5]
    let a = unsafeAddr(arr)
    a[2] = 100
    echo arr   # outputs [1, 2, 100, 4, 5]
    
    Run

Besides that, I often need more than just simple arrays. Here are some more 
concrete examples of things I want to be constant in the underlying C code:

Animation data:
    
    
    type AnimData = object
      loop: bool
      frames: ptr UncheckedArray[int]
      len: int
    
    let walkFrames = [4,5,6,7,8,9]
    
    let walkAnimation = AnimData(
      loop: true,
      frames: cast[ptr UncheckedArray[int]](unsafeAddr walkFrames),
      len: walkFrames.len
    )
    
    # pointer to the currently playing animation
    var currentAnim = unsafeAddr(walkAnimation)
    
    Run

Dispatch tables for scenes, entity behaviors, etc. :
    
    
    type Scene = object
      show: proc () {.nimcall.}
      hide: proc () {.nimcall.}
      update: proc () {.nimcall.}
      draw: proc () {.nimcall.}
    
    proc onShow() =
      echo "enter main menu"
    proc onHide() =
      echo "leave main menu"
    proc onUpdate() =
      echo "updating main menu"
    proc onDraw() =
      echo "drawing main menu"
    
    let mainMenu = Scene(
      show: onShow,
      hide: onHide,
      update: onUpdate,
      draw: onDraw,
    )
    
    # pointer to the current scene
    var currentScene = unsafeAddr(mainMenu)
    
    Run

Reply via email to