Cannot use variable index in a constant list

99 views Asked by At

Using the Device Modeling Language (DML), version 1.4.

​I create a param of lists like

param X = [ [0, 0, 0], [0, 0, 1].......]; 

And I want to access them in a method using variable like

method get_var(uint32 idx) -> (uint32) {
  return X[idx][0];
}

But when I try to compile it says I cannot use variable index in a constant list.

Is there a different data type that I should be using other than param to allow this? Or is there a way to define "idx" as a const so it could be used?

The workaround that I currently have is creating a session variable and copying the param list over during post_init() since then I'm allowed to access that session variable using variable index. I don't know if it's the best way to handle that.

4

There are 4 answers

2
Gustav Wiklander On

Another possibility is to use a session variable. Session variables will take some extra space, and will consume stack space equal to their size during object initialization, so they are not suited for very large arrays. But for reasonable array sizes < 256 it is a valid approach.

session int my_array[3][3] = {{0, 1, 2}, {4, 1, 2}, {1, 2, 3}};

method get_int(int idx) -> (int) {
    return my_array[idx][0];
}
0
Erik Carstensen On

The traditional approach (popular in DML 1.2) is to use #foreach:

method get_var(uint32 idx) -> (uint32) {
  local int i = 0;
  #foreach item in (X) {
    if (i == idx) {
      return item[0];
    }
    ++i;
  }
}

This approach will however unroll the for loop, so it should be used with care if the list can be large.

Also, note that in other #foreach use cases where the loop body is somewhat large, you should break out the body into a separate method. Your get_var method is in fact one good example of how to do that.

0
Erik Carstensen On

One solution is to provide an array value through an independent startup memoized method:

typedef int arr_t[3][3];
independent startup memoized method my_array() -> (arr_t *) {
    local arr_t *ret = new arr_t;
    *ret = {{1,2,3},{4,5,6},{7,8,9}};
    return ret;
}
method get_int(int idx) -> (int) {
    return (*my_array())[idx][0];
}

independent startup memoized means that the my_array method body will be called implicitly when the module is loaded, and the result of that call will be cached and reused in all subsequent explicit calls.

This solution is somewhat similar to the session solution but avoids the per-device memory overhead. Another advantage is that the initializer appears in a context that can run code; This can make it the preferred solution in cases where some of the array values can be inferred from others.

0
Erik Carstensen On

One option is to provide the list as a C initializer in a header %{ %} block:

header %{
static const int my_array[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
%}
// DMLC swallows the header block without parsing; "extern" declares to DMLC
// what it provides
extern const int my_array[3][3];
method get_int(int idx) -> (int) {
  return my_array[idx];
}

This approach is economic, both for memory consumption and compile speed, but the code looks awful, so this approach is mainly useful for auto-generated DML code.