2

I have some Arduino code that looks like:

char packet = ser.read();
switch(packet){
    case 'a':
         someobj.somefunc();
         break;
    case 'b':
         otherobj.otherfunc();
         break;
    ...
}

I've added a few large libraries that are now causing my sketch to exceed the Arduino Uno's memory. I've done all the usual tricks to reduce memory usage, like using F(), removing unused variables and functions, but I'm still 5% over the memory limit.

I tried commenting out various parts of my code to see what was taking up so much space, and I found removing this switch statement freed up a whopping 20% of memory.

Since it acts like glorified array index, I was wondering if it could in fact be replaced with an array of function pointers, with the packet variable serving as the array index. e.g.

uint packet = ser.read();
func_array[packet]();

Although the class instance types are arbitrary, the methods all return the same type and take no arguments.

How would you write an array of pointers to arbitrary class instance methods in Arduino C/C++?

  • Are you talking about flash or RAM? F() helps with RAM, not with flash. – Edgar Bonet Jul 11 '16 at 8:41
6

This all gets very much trickier than just an array of function pointers.

Your functions are methods in unknown objects. As such you can't have one single type that stores pointers to the functions in different objects.

The simple reason is that C++ changes the function prototype for you. For instance, it changes:

class MyClass {
    public:
        int myFunction(int size) { ... }
};

into:

int myFunction(MyClass *this, int size) { ... }

For another class you end up with something like:

int myFunction(MyOtherClass *this, int size) { ... }

And that is a problem, because although you started with functions that, to you, look the same, they are actually very different. So you can't have one function pointer array with one type that can store either.

One possible method is to use polymorphism, where you have one interface class that defines the interface your other classes use:

class MyBaseClass {
    public:
        virtual int myFunction(int size) = 0;
};

class MyClass : public MyBaseClass {
    public:
        int myFunction(int size) { ... }
};

class MyOtherClass : public MyBaseClass {
    public:
        int myFunction(int size) { ... }
};

You can then access myFunction() in either object as if it were in the class MyBaseClass. Instead of then storing pointers to the functions within the classes you can then store pointers to the classes themselves:

MyBaseClass *classes[2];

classes[0] = (MyBaseClass *)&myClassInstance;
classes[1] = (MyBaseClass *)&myOtherClassInstance;

And then access the functions:

x = classes[i]->myFunction(z);

However, this is all probably very much a moot point. The switch statement in your code is most likely not what is using all the memory, but all the classes are. By removing the switch you are most likely then not actually doing anything with the classes you have included, and so the linker is then stripping out all that now unreferenced code. A single switch, unless it is an absolutely mammoth bit of code, won't take 20% of the Uno's flash.

  • Thank you, I had been trying to use a base class, but I wasn't sure of the exact syntax. You nailed it perfectly. – Cerin Jul 11 '16 at 15:38
5

This is not a direct answer to your question, as I have no such answer, and I believe the answer you are seeking would be of no use to you.

The problem with your question is that your method of testing memory usage is heavily flawed. By removing the switch statement, you most likely have allowed the linker to remove lots of methods referenced by it, leading to huge memory savings. Those savings do not mean the culprit is the switch statement itself.

The correct way to test static memory usage is to use either avr-objdump or avr-nm. You have to find where the IDE puts the compiled elf file, then you can issue the command:

avr-nm --size-sort -Crtd my_program.elf

This will give you a list of all symbols in your program sorted by size. Symbols of type 't', 'w' and 'd' (upper or lower case) eat flash. Symbols of type 'd', 'b' and 'v' eat RAM.

  • You're right, this is arguably really what I'm looking to do. I had Googled for ways to analyse Arduino memory usage but I didn't find anything. I'm glad there's a simple method after all. – Cerin Jul 11 '16 at 15:40
4

I'm not sure if it will not blow your memory limit as every pointer takes up 16 bits. But in C you usually do it this way:

void func1() {
    puts("Hello");
}

void func2() {
    puts("World!");
}

void func3() {
    puts("Array of Size 4");
}

void func4() {
    puts("with Pointer");
}

void func5() {
    puts("to a Function with no arguments");
}

void func6() {
   puts("with return type void");
}

void (* myFunc[6])() = { func1, func2, func3, func4, func5, func6 };

int main(int argc, const char * argv[]) {
    for (int i = 0; i < 6; i++) {
        myFunc[i]();
    }

    return 0;
}

For explanation of

void (* myFunc[6])()

You read form inside to outside looking first to the right then to the left

The variable is named myFunc.

myFunc

My func is an array of size 6.

myFunc[6]

Each of those entries is a Pointer.

* myFunc[6]

Now we need parentheses to make clear, that we have a Pointer to a function with no arguments.

(* myFunc[6])()

And its return type is void.

void (* myFunc[6])()

Hope this helps a bit.

  • 2
    The OP is talking about pointers-to-member-functions - but you've given a VERY good breakdown of pointers-to-functions in general. However, to make it easier do this: declare a typedef as follows: typedef void Fn();. That's a declaration of a type-of function. You can now declare variables of pointers-to-that-type as follows: Fn *fn_p; Which means that you can declare your array (not the OP's array) as Fn *myFunc[6] = { func1, func2, ... }; Neat, huh! – John Burger Jul 11 '16 at 11:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.