Methods I might use to access variables with a simple interpreter?

I am putting together a project, slowly as I learn, that will need to display text and values in a web page and on an LCD.

I have done quite a bit of reading and playing, listened to a great deal of helpful replies and can now do both.
Thanks all :slight_smile:

I want to use similar methods to populate the web page and LCD in so much as I would like to store the hard code in a file that has place-holders for dynamic data.
I have previously asked about this but in a far more general and ill informed manner, see:-

If you are interested.

I now appreciate that to refer to anything at run time the processor uses pointers, which means I will have to do the same.

I have looked at Bitlash, which I am impressed with, but see that it uses predefined variables in EEPROM which I assume is because it need to knlow where they are in order to calculate the pointer required to access them.

There was a suggestion that I map the variables I want to access but not much in the way of detail about how I might go about that.
I suggested using a big case statement in a function to return values based on a text string argument but was advised that mapping was a better way.

SO ...

I know that I must predefine anything that I want to access, which isn't a problem.
I want my place-holders to be human readable and make logical sense so they need to be text strings.
I know I need a function that takes a place-holder as an argument and returns a value.

Given these requirements I can think of 2 ways to go.

  1. Use a case statement, there would need to be a case for every variable I want to access.
    That seems simple and is probably fast but I doubt its the bast way.

  2. Use a two arrays one hold the place-holder text and a second, with the same ordinal, to hold a pointer to the value.
    I could loop through the first until I found the required name and then get the pointer from the second, using it to return the value.

I appreciate that I would have to 'load' the arrays but would only be doing that once for each variable.
I don't think speed will be a problem so looping through an array of strings isn't going to be an issue but I have no idea how to work with pointers.
I looked about on the web and discovered that * returns a pointer, as opposed to a value, if placed on the left of a variable in an assignment statement.
I couldn't find how to access a value using the pointer, probably because I didn't recognise what I found rather than the info not being available.

How do I go about doing this on an Arduino
a. Getting a pointer to a variable?
b. using the pointer to return the variable value

Do global variables always stay in the same place in memory once they have been created or do I need to do something to ensure that?

Solution 1 is easy, solution 2 isn't - Is there a benefit to using pointers, apart from the learning opportunity, to solve my problem?

Is there anything else I should be looking at?

Sorry that is big, I don't want to make this hard, just right whilst learning whatever I can.
Thanks folks

Al

Dyslexicbloke:

  1. Use a case statement, there would need to be a case for every variable I want to access.
    That seems simple and is probably fast but I doubt its the bast way.

Fast? Probably not. The GCC-AVR compiler that comes with Arduino expands switch/case to a series of if/else.

A simple speed improvement is to put the most likely cases at the top.

  1. Use a two arrays one hold the place-holder text and a second, with the same ordinal, to hold a pointer to the value.
    I could loop through the first until I found the required name and then get the pointer from the second, using it to return the value.

Hashing. Hash function. Hash table.

If you are concerned about look-up performance...
http://burtleburtle.net/bob/hash/perfect.html

How do I go about doing this on an Arduino
a. Getting a pointer to a variable?

& MyVariable;

b. using the pointer to return the variable value

int MyVariable;

void loop( void )
{
  int * MyPointer;

  MyPointer = & MyVariable;

  Serial.println( * MyPointer );
}

Do global variables always stay in the same place in memory once they have been created

Yes.

or do I need to do something to ensure that?

No.

Dyslexicbloke:
I now appreciate that to refer to anything at run time the processor uses pointers, which means I will have to do the same.

I'm not quite sure what you mean by that.

Referring to variables by strings is going to be RAM-intensive. Remember you don't have a lot of it.

I have looked at Bitlash, which I am impressed with, but see that it uses predefined variables in EEPROM which I assume is because it need to knlow where they are in order to calculate the pointer required to access them.

Thank you for the kind words about Bitlash. But I am puzzled here, perhaps just by "predefined". You define functions at the command line; Bitlash stores them in eeprom. It can then find them to use them later. Nothing predefined I'm aware of.

Regarding your question about the two-arrays method, it works fine. It's used in several places in Bitlash, for example in the reservedwords table at line 365 here in the parser: bitlash/src/bitlash-parser.c at master · billroy/bitlash · GitHub

-br

Spot on CB thanks ...
Questions coming but need to review new reply first

Pointers at runtime ...
The code defines variables by name and thereafter refers to them that way.
When the code is compiled the names no longer exist and all references become pointers.

What I originally wanted to do was write a function to do this:-

serial.print( GetVariableByName("varmane"))

Since the variable name doesn't exist in runtime it would be impossible.

8K should be plenty ... I am only planning to map 20-30 vars
Generally I avoid too much nesting and only define global and static where it is absolutely necessary.

Thanks

When faced with similar problems I often use "descriptors". Basically, they are a data structure used to define a piece of data.

typedef struct
{
  char const * const name;
  int * const  asInt;
}
descriptor_t;

static int v1;
static int v2;

static const descriptor_t descriptors[] =
  {
    { "v1", &v1 },
    { "v2", &v2 }
  };

You can easily add arbitrary data like a text description or help. They often end up with validation data...

typedef struct
{
  char const * const name;
  int * const  asInt;
  int minimumValue;
  int maximumValue;
}
descriptor_t;

...or getter / putter calls...

typedef void (*descriptor_put_t)( descriptor_t const * const descriptor, int const newValue );
typedef int (*descriptor_get_t)( descriptor_t const * const descriptor );

typedef struct
{
  char const * const name;
  int * const  asInt;
  descriptor_put_t put;
  descriptor_get_t get;
}
descriptor_t;

Bitlash ...
It looks clever and would do more than I need in many respects but I couldn't see how to get over my primary objective.

The first thing is that in my definition file the one that bitlash is reading I want to be able to get the contents of a variable in the same way a pin value is fetched.
GetVariableByName("varmane") or simmilar.

Varname needs to be descriptive to make the files readable.

I looked through your documentation and thought I understood your concept.
Your vars are always stored in EEPROM, so that bitlash and C both will know where they are.
There are 26 of them which the bitlash interpreter refers to as A to Z, these are predefined
You cant define a new variable, although you can define a new function which is like a macro containing existing functions.
bitlash cant access, by name, a global variable defined in C for the same reason I cant, C variable names don't exist in runtime.

I will be more than happy to be wrong about this because if I am then bitlash will do my bidding.

Back to pointers ...
I have read a few descriptions of Hashing and I don't understand it. I thought at first it was an index like you would use in a database but it appears not.
However all the sites talked about big datasets and mine isn't big so I will leave that for now.

If a Switch/Case compiles to a bunch if IF's and looping through an array of names requires 1 IF for each iteration wouldn't the processing time be similar.
In fact since controlling a loop requires that a counter is incremented and the count checked would that not make the loop approach slower than a bunch of IF's?

Can I define a global variable within a function?
This would allow me to use a function to define the variable based on a name I supply,
Get its pointer after it is defined,
Put the pointer and name into appropriate arrays.
Is a variable defined within setup() in the scope of loop_()

Thanks for the help thus far
Al

Dyslexicbloke:
I have read a few descriptions of Hashing and I don't understand it.

In the computer science sense, the idea is to take lots of information and perfectly reduce it to less information.

In the practical sense, some math is performed on a blob of data (like a string). The math results in an integer value. The idea is that the integer can be used in place of the string.

I thought at first it was an index like you would use in a database but it appears not.

Your first thought is correct. Hashing is one type of index available in most database systems.

However all the sites talked about big datasets and mine isn't big so I will leave that for now.

In your case, it would be used to eliminate all but a final string compare. If look-ups are frequent or the element count is large it can make a huge performance difference. For low element counts calculating the hash value can be more expensive than the string compares.

If a Switch/Case compiles to a bunch if IF's and looping through an array of names requires 1 IF for each iteration wouldn't the processing time be similar.

Very similar.

In fact since controlling a loop requires that a counter is incremented and the count checked would that not make the loop approach slower than a bunch of IF's?

A very tiny bit slower. The string comparison is significantly more expensive which makes the loop overhead essentially insignificant.

Can I define a global variable within a function?

No. But you can declare a "static" variable within a function.

Is a variable defined within setup() in the scope of loop_()

No.

You could get creative with some structs/unions and the preprocessor's stringify (#) operator.
That would keep names consistent.

Your vars are always stored in EEPROM, so that bitlash and C both will know where they are.

This may be the source of some confusion. In Bitlash, the variables a through z are stored in RAM. Functions you define at the command line are stored in EEPROM. Expressions in those functions may reference the a-z variables, the pinvars (D0-D13 and A0-A7 for direct IO pin access), other functions in EEPROM, built-in functions in C, user functions in C, and so on.

bitlash cant access, by name, a global variable defined in C for the same reason I cant, C variable names don't exist in runtime.

When I need to do this for a Bitlash application I write a user function in C to expose the variable in Bitlash as a function. It's quite simple:

// add user function "pos" to Bitlash to expose the encoder position
numvar func_pos(void) {
    return (numvar) encoderPosition;    // return C global
}

void setup(void) {
...
    addBitlashFunction("pos", (bitlash_function) func_pos);    // register the pos() function
...

But you wouldn't find yourself doing that much if you were "thinking in bitlash", and therefore wrote your application to use the a-z variables. At least in my experience. In fact, sometimes it works best the other way around: it's easy to update the a-z vars from C; see the bitlashclock example.

Anyway, I don't think Bitlash solves (or even addresses) the general C introspection problem, but it might be an easy way to put customized text on your LCD, which is where I believe this yak shaving exercise began, yes?

Best,

-br

Thanks CB I like concise and direct, I now fee I have a handle on things.
I think I will use the arrays, pointers and loop approach just because it will be tired code.

You could get creative with some structs/unions and the preprocessor's stringify (#) operator.

What now??

Ok seriously, it sounds interesting but I haven't the faintest idea what you mean or for that matter how to look it up.
I know what a struct is and I believe that a union is a chunk of memory with more than one pointer referencing it but at that point you loose me
How would / could those help?

I would love to know even if I don't go that way just as an exercise.

Al

Your loyal friend

Are pointers always INT's?
I just assumed that they were, given that they are addresses so to speak.

The bitlash code is interesting thanks ... It is defiantly something I will play with.
When I have an application I will have a go at writing it conventionally and then in bitlash to compare the performance and relative complexity.

Are pointers always INT's?

No, they are memory addresses that just happen to look a bit like integers.
If they were just "int"s, then increment and decrement operators would always just add or subtract one from them, instead of how they actually behave.

OoooH feeling seriously ignorant again, and just when I thought I was beginning to understand.

Does that mean that when defining a variable to hold a pointer I would use the same type, like this?

float FloatVar = 1.1;
int IntVar = 10;

float * NewPointerForFloat;
int * NewPointerForInt;

NewPointerForFloat = & FloatVar;
NewPointerForInt = & IntVar;

Serial.println( * FloatVar );
Serial.println( * IntVar );

If so how does that work for char arrays?
How do pointers behave when incremented, do they increment to the next address of the same type?

Can I ask for clarification of post 7

When faced with similar problems I often use "descriptors" ...

I thought I understood but I clearly don't.
Given that I want to group things together indexing through memory, by pointer, or creating an array of types sounds like a plan.
An array of types is far cleaner than two arrays, I assume I can do that.
Am I correct in assuming that a member, probably not the correct terminology, of a type would have a fixed offset from the pointer to the struct its self?
If so how would I calculate that? and if not how does it work?

Sorry to be vague ... It may be age but its probably background, I am beginning to see that I should have learned to program in 'real' language in the first place.
VB was a great plan when I needed to be productive quickly

How do pointers behave when incremented, do they increment to the next address of the same type?

Bingo!

Does that mean that when defining a variable to hold a pointer I would use the same type, like this?

You use the same type (plus the *) as the type of the variable being pointed to. A float pointer points to floats. An int pointer points to ints. A char pointer points to chars.

If so how does that work for char arrays?

Just like it does with any other type.

float val[10] = { // Some initializers // };
float *ptr = val;

Serial.print(ptr[5]);

Change float to char, and it works exactly the same.

Am I correct in assuming that a member, probably not the correct terminology, of a type would have a fixed offset from the pointer to the struct its self?

Yes, but you don't know what that offset is. The exact value depends on how the struct is packed. Don't try to go there.

By the way, it's "of a struct", not "of a type".

Am I correct in assuming that a member, probably not the correct terminology, of a type would have a fixed offset from the pointer to the struct its self?

For that, there's the "offsetof" macro.