Pages: [1] 2 3   Go Down
Author Topic: Call functions numerically with pointers  (Read 1600 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Jr. Member
**
Karma: 0
Posts: 53
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi. I'm using something similar to the following:

Code:
switch (Command)
{
  case 1:
    Command1();
    break;

  case 2:
    Command2();
    break;

  case 3:
    Command3();
    break;

  // etc...
}

Is there a less long-winded way to achieve this? For example, by using a PROGMEM lookup table where each element contained a pointer to a function?
I notice 'Command1();' can be replaced with '(*Command1)();' without issue as long as the procedure is declared in the code before it is called. However I'm not sure how to implement a method that exploits this.
Logged

New Jersey
Offline Offline
Faraday Member
**
Karma: 67
Posts: 3694
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Have a look at pointers to functions - an array of them will help you significantly reduce that code.
Logged

Offline Offline
Edison Member
*
Karma: 29
Posts: 2451
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Why not have 1  command and pass in the argument?

Command (selectedval);

I guess you'd still need to use case to see what got passed...


a linked list would do it...
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 53
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

To clarify, arrays and linked lists use RAM, which I'd hope could be avoided in this case.

A PROGMEM array would be ideal, but I can't find anything on how to get function pointers in and out of them.
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 614
Posts: 49365
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
but I can't find anything on how to get function pointers in and out of them.
Perhaps because you are thinking that there is something magic about a pointer to a function. There is not. How may functions are you think of? I can't see that moving a few pointers out of SRAM is going to accomplish all that much.
Logged

Helsingborg, Sweden
Offline Offline
Sr. Member
****
Karma: 24
Posts: 499
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
PROGMEM void (*Farr[])() = {func1, func2, func3};

void setup(){
  Serial.begin(115200);
  for(int i = 0; i < 3; i++) Farr[i]();
}//setup()

void loop(){}//loop()

void func1(){
  Serial.println(F("function1"));
}//func1()

void func2(){
  Serial.println(F("function2"));
}//func2()

void func3(){
  Serial.println(F("function3"));
}//func3()
« Last Edit: December 08, 2013, 11:00:02 am by nilton61 » Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 53
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thank you, nilton61! Karma gratefully bestowed!
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 53
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I've encountered a weird problem with the method above. I'm baffled. Can anyone figure out why the code below fails when you uncomment either or both of the marked lines?

Code:
PROGMEM void (*Farr[])() = {FuncA, FuncB, FuncC};

byte Global;

void setup(){
  Serial.begin(9600);
  
  Global = 1;
  byte Local = 1;
  //Local = Global; // THIS SCREWS EVERYTHING UP
  
  // Global and Local display as expected:
  Serial.print("Global = ");
  Serial.println(Global);
  Serial.print("Local = ");
  Serial.println(Local);
  
  // These work fine:
  Farr[0](); // Calls FuncA
  Farr[1](); // Calls FuncB
  Farr[2](); // Calls FuncC

  // This works fine:
  Farr[Local](); // Calls FuncB
  
  //Farr[Global](); // THIS ALSO SCREWS EVERYTHING UP
}

void loop(){}

void FuncA(){
  Serial.println("Function A");
}

void FuncB(){
  Serial.println("Function B");
}

void FuncC(){
  Serial.println("Function C");
}

I wonder if there's some behind-the-scenes optimisation happening and the compiler doesn't realise what the 'Farr[Global]()' line needs to function properly?
« Last Edit: December 12, 2013, 06:37:40 pm by paulrd » Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 614
Posts: 49365
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Perhaps you need to say more than "This screws everything up".

The code does something. You expect it to do something. Presumably those two are not the same thing. But, what either one is is a mystery.
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 53
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Sorry, I assumed people would try it for themselves. To clarify, the code demonstrates the principle of calling functions numerically, provided in nilton61's post above. However, both the commented lines cause the program to go into an infinite loop, spewing characters from the serial port, despite containing nothing obvious to cause such a dramatic change the operation of the code. It seems they cause the function call to jump to the wrong location, disrupting the flow of the program, but I can't see why.
« Last Edit: December 12, 2013, 07:03:28 pm by paulrd » Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 614
Posts: 49365
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You are storing the function pointers in PROGMEM and then accessing them as though they were stored in SRAM. That is not going to work well. Since the 3 pointers use a total of 6 bytes, I really can't understand why they can't live in SRAM.
Logged

Helsingborg, Sweden
Offline Offline
Sr. Member
****
Karma: 24
Posts: 499
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I had no problem compiling and running your program with the suspicious calls uncommented. Are you sure that you have changed the serial speed in the monitor? My example was 115200 you use 9600(why?)
Logged

Global Moderator
Online Online
Brattain Member
*****
Karma: 485
Posts: 18796
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

You are storing the function pointers in PROGMEM and then accessing them as though they were stored in SRAM. That is not going to work well. Since the 3 pointers use a total of 6 bytes, I really can't understand why they can't live in SRAM.

Apart from that, the example shouldn't have worked.

The pointers are in PROGMEM and it was called from non-PROGMEM. However the compiler came to the rescue and optimized the loop away:

Code:
11a: 0e 94 12 01 call 0x224 ; 0x224 <_ZN14HardwareSerial5beginEm>
 11e: 0e 94 80 00 call 0x100 ; 0x100 <_Z5func1v>
 122: 0e 94 79 00 call 0xf2 ; 0xf2 <_Z5func2v>
 126: 0e 94 72 00 call 0xe4 ; 0xe4 <_Z5func3v>

Notice that the loop doesn't exist in the code?

So to make it really do what we want we have to prevent the optimization:

Code:
void (*Farr[])() PROGMEM  = {func1, func2, func3};

volatile int foo = 3;

void setup(){
  Serial.begin(115200);
  for(int i = 0; i < foo; i++) Farr[i]();
}//setup()

Now, the code fails to print anything, which is what you want. smiley-wink
Logged


Global Moderator
Online Online
Brattain Member
*****
Karma: 485
Posts: 18796
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I agree with PaulS that you hardly need to use PROGMEM here, but in case you were envisaging a much larger array, this works:

Code:
typedef void (* myFunction) ();

PROGMEM myFunction Farr []  = {func1, func2, func3};

volatile int foo = 3;

void setup(){
  Serial.begin(115200);
  for(int i = 0; i < foo; i++)
    {
    myFunction f = (myFunction) pgm_read_word (&Farr [i]);
    f ();
    }
}//setup()

void loop(){}//loop()

void func1(){
  Serial.println(F("function1"));
}//func1()

void func2(){
  Serial.println(F("function2"));
}//func2()

void func3(){
  Serial.println(F("function3"));
}//func3()
Logged


Helsingborg, Sweden
Offline Offline
Sr. Member
****
Karma: 24
Posts: 499
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

A little strange. When trying nicks example i get this:
sketch_dec13a.ino:3: warning: only initialized variables can be placed into program memory area

When running this code:
Code:
void (*Farr[])() PROGMEM  = {FuncA, FuncB, FuncC};

volatile int foo = 3;

void setup(){
  union {
  void (*p)();
  int i;
  }cv;
  Serial.begin(115200);
  for(int i = 0; i < foo; i++){
    cv.p = Farr[i];
    Serial.println(cv.i);
  }
}//setup()

void loop(){}

void FuncA(){
  Serial.println("Function A");
}

void FuncB(){
  Serial.println("Function B");
}

void FuncC(){
  Serial.println("Function C");
}
i get this output:
0
514
514

This leads me to believe that despite that the addresses of the functions should be known at compile time the array is not initialized. Why??
Logged

Pages: [1] 2 3   Go Up
Jump to: