Author Topic: storing function pointer and calling  (Read 2488 times)

0 Members and 1 Guest are viewing this topic.

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
storing function pointer and calling
« on: June 03, 2021, 07:16:24 am »
Problem in c++ (arduino)  ( my c++ skills are very rusty... )
Simple task : store and retrieve function pointers in a simple table.
Don't focus on what the code does. It is just an example. The part i am interested in is retrieving the address of a function and storing it in a variable, not something defined as pointer or array of pointers. Don't know if that is possible. We may have to make two arrays, one holding the normal integers , one holding the pointers.
Again , don't focus on what the example code does, it is nonsense. i am after how to store the address of a function and then call that function given its address.

Code: [Select]
int functions[10][4];  // an 2 dimensional array that will hold data and function pointers

int somefunction(){return 123;};           // a routine that does something, takes no arguments but returns a value
int someotherfunction (){return 456;}; // a routine that does something else , takes no arguments but returns a value


// a routine that will store the pointer to a given function in a given slot in the array. this is the bit i am fuzzy on. how do i obtain the pointer to the passed function ?
// the routine takes 2 integers and a pointer to a given function
void storefunction (int location , int data1,int data2, int funcpointer)  // <- correct definition needed here so it can take address of function
{
  functions[location][0] = data1;
  functions[location][1] = data2;
  functions[location][2] = funcpointer;
}

// this routine finds a pointer in the array and calls it
int process (int location){
  int result = call functions[location][2];  // <- this bit of code is needed.
  result = result + functions[location][0]
  result = result / functions[location][1]
  return result;
}

storefunction (0,0,1,somefunction);
storefunction (1,123,2,somefunction);
storefunction (2,0,1,someotherfunction);
storefunction (3,0,1,someotherfunction);

int x = process(0);  //  should now contain 123 (we added 0 and divided by 1)
int x = process(1);  //  should now contain 123 (we added 123 and divided by 2)


« Last Edit: June 03, 2021, 07:48:21 am by free_electron »
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 2223
  • Country: nz
  • Formerly SiFive, Samsung R&D
Re: storing function pointer and calling
« Reply #1 on: June 03, 2021, 07:43:30 am »
You're fairly close.

The main problem is your array stores ints not function pointers.

You want

Code: [Select]
typedef int (*func_no_args_returns_int)();

func_no_args_returns_int functions[4];

The main other thing wrong is there is no call keyword. You just put () after the array access: functions[location]().
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #2 on: June 03, 2021, 07:52:33 am »
You're fairly close.

The main problem is your array stores ints not function pointers.

And there is the rub. i want to store this in a multidimensional array that contains other data that are not function pointers.. that is what i am trying to figure out.

Let's say we have an array that is 10 by 3.
Each line contains two integers and a function pointer (which is nothing but an integer).
so:
-  how do i cast such an array ?
- how can i pass both normal values and function pointer to a routine
Code: [Select]

void storefunction (int location_in_array , int integer_data1,int integer_data2, 'functionpointer') {};

storefunction (5,999,111,the_function_that_needs_calling);





« Last Edit: June 03, 2021, 07:57:30 am by free_electron »
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1141
  • Country: se
Re: storing function pointer and calling
« Reply #3 on: June 03, 2021, 07:54:33 am »
The problem (calling a function based on an index) can be probably tackled in different ways in 'real' C++, but I'll give you some hints for a C-style solution, as that seems to be what you are after.

First, the definition of the table:
Code: [Select]
int functions[4];won't do: this is an array of integers, not of function pointers.
The correct declaration is:
Code: [Select]
int (*functions[4])(void);Reading the declaration we have that:
"functions is an array of 4 pointers to a function taking no arguments and returning int"
This site will do that for you, but IMO it's important to learn the skill.

As this kind of declaration often becomes difficult to eye-parse, I often prefer to split it in a typedef and the actual declaration:
Code: [Select]
typedef int (*funcPtr)(void); // funcPtr is a pointer to a function taking no arguments and returning int
funcPtr functions[4]; // Functions is an array [4] of functPtr

Now, with functions in place the rest is a real breeze!
Filling the array is as simple as assigning its elements.
The name of the function, if not followed by (), will give you the pointer. If you want to make it more explicit, &myfunction has exactly he same meaning:
Code: [Select]
int myfunction(void) { return 42; }
...
functions[1] = myfunction; // or &myfunction

Invoking the function is similarly very simple:
Code: [Select]
int result;
result = functions[1](); // calls myfunction()

Standard references C++17:
Function calls [expr.call]
Conversion to function pointer [conv.func]
& with a function name [expr.unary.op] §3
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #4 on: June 03, 2021, 08:04:54 am »
The problem (calling a function based on an index) can be probably tackled in different ways in 'real' C++, but I'll give you some hints for a C-style solution, as that seems to be what you are after.
nope. I have found thousands of examples like that, but that is NOT what i seek !
the examples all assume either a single dimension and one type (pointer)

if have a two dimensional array that contains both normal data and function pointers.
In essence i need to be able to store the 'function pointer' as a plain integer (the address is nothing but a simple integer). and then have a routine that allows me to pass this integer , converting it back to a pointer and invoking the routine.

thinkof it this way : we have two processors running the same binary. they talk over a serial link.
processor 1 says to processor 2 : please execute your function at location 0x01234
this 0x01234 is sent in plain ascii. the receiver converts this back to an integer, loads a pointer and invokes the routine it is now pointing to. basically a remote procedure call  by passing addresses.

the functions don't return anything because they pick up data from global variables and dump their results back into global variables, they only have an exit value returning an error code. not all functions have the same number of arguments
anyway ,we are diverging.

what is seek is how to create a function that can accept two integers and a function pointer and store that in a row of a two dimensional array
Code: [Select]
void storefunction (int location_in_array , int integer_data1,int integer_data2, 'functionpointer') {};
storefunction (5,999,111,the_function_that_needs_calling);

and then how to do the reverse : given the row number in the array : invoke the function
« Last Edit: June 03, 2021, 08:12:29 am by free_electron »
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1141
  • Country: se
Re: storing function pointer and calling
« Reply #5 on: June 03, 2021, 08:05:24 am »
so how do i cast such an array ?
Simple: you do not cast it - that's a different thing.

But, you can build a structure containing a pointer to function and the integers, and have an array of those.

As an example, here's the (largely abridged) code I use in the processing pipeline for an SDR hobby project (C11 - not C++):
Code: [Select]
/**Generic step function pointer
 * buf: points the buffer to be processed
 * len: processing length
 * user: step specific parameter,
 *       uintptr_t can be used to safely pass a void pointer in an uint */
typedef void (*ProcStepFunc)(float complex *buf, uint32_t len, uintptr_t user);

/* A structure with the processing step and its arguments */
typedef struct ProcStep_s
{
    ProcStepFunc func;
    float complex *buf;
    uint32_t     len;
    uintptr_t    user;
} ProcStep;

extern ProcStep  pipeline[];

Not really complicated.
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 610
Re: storing function pointer and calling
« Reply #6 on: June 03, 2021, 08:05:51 am »
You can play with ideas in the online compiler-
https://godbolt.org/z/hsbxosabj


Code: [Select]
template<typename T, unsigned N>
static constexpr auto
arraySize(T (&v)[N]) { return N; }

using funcT = int(*)();

struct FuncArray {
    funcT func;
    int a;
    int b;
};

FuncArray funcArray[10];

void storefunction (int n, funcT func, int a, int b) {
    if( n >= arraySize(funcArray) ) return;
    funcArray[n].func = func;
    funcArray[n].a = a;
    funcArray[n].b = b;
}

int callfunction (int n){
    if( n >= arraySize(funcArray) or not funcArray[n].func ) return 0;
    return funcArray[n].func();
}


int somefunction(){return 123;}
int someotherfunction (){return 456;}



int main()
{
    storefunction (1,somefunction,123,123);
    storefunction (2,somefunction,456,456);
    storefunction (3,someotherfunction,789,789);
    storefunction (0,someotherfunction,452,452);

    int x = callfunction(0);  //  should now contain 456
    x = callfunction(2);  //  should now contain 123

while(1){}
}
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #7 on: June 03, 2021, 08:14:27 am »
so how do i cast such an array ?
Simple: you do not cast it - that's a different thing.

But, you can build a structure containing a pointer to function and the integers, and have an array of those.

As an example, here's the (largely abridged) code I use in the processing pipeline for an SDR hobby project (C11 - not C++):
Code: [Select]
/**Generic step function pointer
 * buf: points the buffer to be processed
 * len: processing length
 * user: step specific parameter,
 *       uintptr_t can be used to safely pass a void pointer in an uint */
typedef void (*ProcStepFunc)(float complex *buf, uint32_t len, uintptr_t user);

/* A structure with the processing step and its arguments */
typedef struct ProcStep_s
{
    ProcStepFunc func;
    float complex *buf;
    uint32_t     len;
    uintptr_t    user;
} ProcStep;

extern ProcStep  pipeline[];

Not really complicated.

aha ! of course. structures ... and then make an array of structures. how come i didn't think of that ... -facepalm-. it;s been too long since i wrote code...
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1141
  • Country: se
Re: storing function pointer and calling
« Reply #8 on: June 03, 2021, 08:17:50 am »
I have found thousands of examples like that, but that is NOT what i seek !
the examples all assume either a single dimension and one type (pointer)

if have a two dimensional array that contains both normal data and function pointers.
In essence i need to be able to store the 'function pointer' as a plain integer (the address is nothing but a simple integer). and then have a routine that allows me to pass this integer , converting it back to a pointer and invoking the routine.
Ok, this was definitely not clear from the first post.

If you are sure of the function pointer implementation and that a function pointer can be safely translated back and forth to an integer, you can simply use a cast (now I see why you mentioned it) with the typedef I proposed.
Code: [Select]
int funcs[4]; // this your int array, but they are really function poointers
typedef int (*FuncPtr)(void); // funcPtr is a pointer to a function taking no arguments and returning int
[...]
int result;
FuncPtr f = (FuncPtr)funcs[1];
result = f();

Two or more dimensions do not matter at all.

[good Stuff]
Yes that is a good way to do it in C++!
Nandemo wa shiranai wa yo, shitteru koto dake.
 
The following users thanked this post: free_electron

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #9 on: June 03, 2021, 08:40:34 am »
rats .. more problems.

Code: [Select]
#define max_daemons 10

int daemoncounter =0;                // keep track of how many we already have

using functionpointer = int(*)();
struct Daemon {
   int counter;              // current counter value
   int interval;             // given interval to reload
   int state;                // runtime state 0 = stop 1 =run
   functionpointer handler;  // function to be called when counter reaches zero
};

Daemon Daemons[max_daemons]; // the array of daemons
 
int CreateDaemon (int interval, functionpointer target_function )
{
   Daemons[daemoncounter].interval = interval;
   Daemons[daemoncounter].handler = target_function;
   daemoncounter++;         
};

void StartDaemon (int daemon)
{
   Daemons[daemon].counter = Daemons[daemon].interval;
   Daemons[daemon].state = 1;
};

void PauseDaemon (int daemon)
{
   Daemons[daemon].state = 0;
};
void ResumeDaemon (int daemon)
{
   Daemons[daemon].state = 1;
};
           
void AbortDaemon (int daemon)
{
   Daemons[daemon].state = 0;
   Daemons[daemon].counter = 0;
};

error 'functionpointer' has not been declared ...
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1141
  • Country: se
Re: storing function pointer and calling
« Reply #10 on: June 03, 2021, 08:50:41 am »
Type aliases with "using" were introduced in C++11.
You might have an old compiler, or wrong compiler flags.
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #11 on: June 03, 2021, 08:51:55 am »
Type aliases with "using" were introduced in C++11.
You might have an old compiler, or wrong compiler flags.

arduino in tinkercad

this is why i hate languages like c and c++. it 's like trying to peel a banana with a hammer. compile -> fix typo's -> compile - > fix flags -> compile -> compiler issues.
i want interpreted basic back. at least i can move forward in that without endless hunting for typos and cryptical error mesages.
i am trying to port a simple basic program to arduino and i've been struggling for hours on this simple bit of code.
all it is is a bit of simple downcounters attached to an interrupt handler , that fire off functions when their count reaches zero.
grrr... frustrated.
« Last Edit: June 03, 2021, 08:57:07 am by free_electron »
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1141
  • Country: se
Re: storing function pointer and calling
« Reply #12 on: June 03, 2021, 09:09:32 am »
Type aliases with "using" were introduced in C++11.
You might have an old compiler, or wrong compiler flags.

arduino in tinkercad

this is why i hate languages like c and c++.
Typos: get a good IDE/programming editor, it's difficult nowadays to slip in typos in variable names or syntax errors. I use Visual Studio and VS Code, but even Eclipse (barf) is  decent at it.
Compiler issue: know your compiler and how you use it - Aduino makes a good job of mangling and hiding what the compiler is actually doing. Good for simple stuff, dreadful for (even slightly) advanced one.
Languages: C and C++ are rich complex languages - especially modern C++ - it takes time and a lot of patience and dedication... :blah:
Nandemo wa shiranai wa yo, shitteru koto dake.
 
The following users thanked this post: agehall

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #13 on: June 03, 2021, 09:11:53 am »
Type aliases with "using" were introduced in C++11.
You might have an old compiler, or wrong compiler flags.

arduino in tinkercad

this is why i hate languages like c and c++.
Typos: get a good IDE/programming editor, it's difficult nowadays to slip in typos in variable names or syntax errors. I use Visual Studio and VS Code, but even Eclipse (barf) is  decent at it.
Compiler issue: know your compiler and how you use it - Aduino makes a good job of mangling and hiding what the compiler is actually doing. Good for simple stuff, dreadful for (even slightly) advanced one.
Languages: C and C++ are rich complex languages - especially modern C++ - it takes time and a lot of patience and dedication... :blah:

ok, but that doesn't solve my problem at hand : how do i get this thing to run in arduino ? it is supposed to be c++
neither typedef nor using works.
i need to get this thing running. proof fo concept. things that take 5 minutes in VB take 5 days in c. i have no patience. i'm trying to help someone write a simple task scheduler.

« Last Edit: June 03, 2021, 09:13:42 am by free_electron »
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1141
  • Country: se
Re: storing function pointer and calling
« Reply #14 on: June 03, 2021, 09:32:41 am »
it is supposed to be c++
neither typedef nor using works.
Arduino is a bastardized version of some C++ (I have no idea which standard and I'm not looking it up).
That said, I can't see any reason the correct typedef should not work.

BTW: you have undefined behaviour: int function that falls off the end. The compiler is allowed to do whatever, including spurious error messages (no, it's 99.9% not the case here).
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #15 on: June 03, 2021, 09:38:42 am »
code compiles and runs in arduino ide. does not compile in tinkercad.  Typical Autocrap product.
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 4070
  • Country: fi
Re: storing function pointer and calling
« Reply #16 on: June 03, 2021, 10:56:48 am »
Arduino is supposed to be used by artists, not programmers, to write code like

blink_led();
delay(100);
blink_led();

It's just not usable in real professional work or more complex projects, get rid of it.

Looking at your preferred function pointer approach, it also looks like you are "a C guy", and there is nothing wrong with it. I guess it would be helpful to limit yourself to C as you are not using (or don't want to use) C++ features. So compile with a C compiler. This way, the language gets approximately two orders of magnitude smaller, with well specified freestanding environment, meaning it is possible for a human being to understand everything going on.
 
The following users thanked this post: newbrain

Offline brucehoult

  • Super Contributor
  • ***
  • Posts: 2223
  • Country: nz
  • Formerly SiFive, Samsung R&D
Re: storing function pointer and calling
« Reply #17 on: June 03, 2021, 11:01:31 am »
i need to get this thing running. proof fo concept. things that take 5 minutes in VB take 5 days in c. i have no patience. i'm trying to help someone write a simple task scheduler.

I find the opposite. C is an extremely simple and rather stable language. I struggle to think of the last time something that didn't have obvious typos failed to work as expected.

There is no way that typedef won't work.
 
The following users thanked this post: newbrain

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 2718
  • Country: fi
    • My home page and email address
Re: storing function pointer and calling
« Reply #18 on: June 03, 2021, 11:39:12 am »
There are two main use cases for use with indirect functions: closures and callbacks.

A typical example of callbacks is how the standard C qsort() function takes the (pointer to the) function used to compare elements as a parameter.

In this case, free_electron has a closure: a function pointer, and an environment (here, parameters) the function is called in.  Unlike some other programming languages, C and C++ do not have a native way to describe arbitrary closures; this is why structures (or classes in C++) are used to describe them.  In C++, subclasses can be used to describe function calls with different types and/or number of arguments.  Unions can be used in both C and C++ (especially in code that compiles as either C or C++ cleanly), but the aliasing rules does impose certain constraints on how this should be done: commonly used code works but may not be portable (see e.g. POSIX struct sockaddr, which only works with compilers and C libraries that implement the stricter POSIX rules, and not just standard C) – and obviously that is also the kinda sorta the situation we have here.

The Arduino environment is a freestanding GNU C/C++ environment, with a custom system library (the base Arduino library).  The standard C or C++ libraries are not available.  Because the compiler used is GNU gcc, the rules it implements do differ a bit from the standard C and C++ specifications.  Language lawyers can discuss whether it conforms to which version of which standard, but basically, C++ freestanding environment leaves a lot for the implementation to decide, whereas the C freestanding environment is a bit better specified; and the freestanding environment GNU C and C++ compilers provide is pretty well described here and piecewise in the run time options.  In particular, there are built in functions that although nominally part of the C or C++ library, are by default implemented by the compiler instead, unless directed otherwise; and that the prototype int func() can actually mean either "function with arbitrary arguments" or "function with no arguments", depending on the exact details.

Wait, keep reading: I do have a point.  You see, because of the above, the solution here is to use the minimum intersection of all of the above, to get it to work in real life in different environments.  (In language lawyer terms, this is about picking the most likely subset of the implementation defined behaviour, plus assumed workarounds for bugs in non-conforming compilers.)
  • Use exact function pointer declaration in the structure, not via a typedef
  • Use explicit function prototypes
  • For different types of functions, use an initial common member in the structure to describe the type via an enum, and an union of the structures containing the parameters passed to each type of function
The first one is arguably a bug in a compiler, but :-//.  The second avoids the issue with ambiguity in a function prototype with empty parameter list (whether that means "arbitrary parameters" or "no parameters" or something else, depends; and we want to avoid any ambiguity to get the code to work).  The third one works in both C and C++ even with strict aliasing rules.  But if you only need closures of one specific function prototype type, you don't need to bother.

Let's say you want to support closures that have a context of two integer parameters, and a third one supplied at call time, so something that is ambiguous wrt. the above terminology just to keep things fun; and returns an int:
Code: [Select]
struct afterthought {
    int  (*callback)(int a, int b, int c);
    int  a;
    int  b;
};

#define  AT_NUM_X  5
#define  AT_NUM_Y  6

static struct afterthought  at_array[AT_NUM_Y][AT_NUM_X];

int set_afterthought(int x, int y, int (*callback)(int a, int b, int c), int a, int b) {
    if (x < 0 || x >= AT_NUM_X || y < 0 || y >= AT_NUM_Y) {
        /* Index [y][x] is out of bounds */
        return -1;
    }

    at_array[y][x].callback = callback;
    at_array[y][x].a = a;
    at_array[y][x].b = b;
    return 0;
}

int run_afterthought(int x, int y, int c) {
    if (x < 0 || x >= AT_NUM_X || y < 0 || y >= AT_NUM_Y) {
        /* Index [y][x] is out of bounds */
        return -1;
    }
    return at_array[y][x].callback(at_array[y][x].a, at_array[y][x].b, c);
}
The index check is not an assert(), because assert() is not available in freestanding C or C++.  We could write a macro, or use #ifndef NDEBUG ... #endif around the index bounds check, so it is only compiled in when NDEBUG is NOT defined, just like assert()s.

When we supply the function to use to set_afterthought(), you may or may not need to use ampersand (address-of operator).  In C, the two are equivalent, but I'm not sure how C-and-C++ compilers (like the Microsoft compiler) treat it.  You could work around this via a macro (like Glib uses G_CALLBACK(function)), so you only need to pick it once based on the compiler used (per macros that each compiler defines).
« Last Edit: June 03, 2021, 11:44:31 am by Nominal Animal »
 

Online langwadt

  • Super Contributor
  • ***
  • Posts: 2478
  • Country: dk
Re: storing function pointer and calling
« Reply #19 on: June 03, 2021, 11:41:50 am »
Type aliases with "using" were introduced in C++11.
You might have an old compiler, or wrong compiler flags.

arduino in tinkercad

this is why i hate languages like c and c++. it 's like trying to peel a banana with a hammer. compile -> fix typo's -> compile - > fix flags -> compile -> compiler issues.
i want interpreted basic back. at least i can move forward in that without endless hunting for typos and cryptical error mesages.
i am trying to port a simple basic program to arduino and i've been struggling for hours on this simple bit of code.
all it is is a bit of simple downcounters attached to an interrupt handler , that fire off functions when their count reaches zero.
grrr... frustrated.

well, if you have no idea what your are doing and just throw random stuff at the compiler hoping it will guess what you mean, ....



 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 2718
  • Country: fi
    • My home page and email address
Re: storing function pointer and calling
« Reply #20 on: June 03, 2021, 11:52:52 am »
i am trying to port a simple basic program to arduino and i've been struggling for hours on this simple bit of code.
That right there is the root cause of your problem.

You are, essentially, trying to bolt on wings to a van, and get it to fly supersonic.

What I mean is that each language – and here, we really need to consider Arduino a separate "language" because basically none of the standard C or C++ library is available, and is a very limited, low-level programming environment – has its own approach to solving the problems.  (I call this the paradigm.  Yes, C, C++, and Basic are all imperative languages, but that's just one aspect of the overall approach to problem solving the languages implement.) "Simple basic" is actually rather high level language environment, with lots of abstractions (like "wings" and "fuselage" and "jet engine" in my above analog).  This means that the design, the structure, of the basic program does not map at all to the design or structure of an Arduino sketch.



It sounds like your code implements timeouts.  I could help you with that.  So, why not ask the underlying question (in the programming section) – describing what you want the code to achieve, instead of asking about a minute implementation detail of your already chosen solution?
« Last Edit: June 03, 2021, 11:55:38 am by Nominal Animal »
 
The following users thanked this post: newbrain

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 610
Re: storing function pointer and calling
« Reply #21 on: June 03, 2021, 12:24:14 pm »
I don't use arduino, but maybe your source file is .c and arduino may be adding an -xc option when it sees a .c file. It may be using g++ for all files but tacking in a -xc for what it thinks are c files. That would be one thing to check.

I use the gcc-avr 7.3.0 toolchain from arduino for avr0/1 and use c++ most of the time. 7.3.0 is c++17 'compatible' so you also get some nice features if you are into c++. I would figure out what commands are going to the toolchain from whatever your environment is so you can see what is wrong.


edit-

and as long you are using c++, may as well use it like c++
https://godbolt.org/z/jT1Mvj4n5
https://godbolt.org/z/MvbhsKjn6  (my 'style' which keeps important names on the left)
Not sure if you have a need to reuse these array members at runtime, and if not I would just populate the array one time as needed and skip dealing with a fixed array size and figuring out if there is any room left, etc. (remove daemoncounter things, change create() to a Daemon constructor instead).

Just like c, you keep things as private as you can and in places they 'belong'. A class/struct is a good place to handle the Daemon functions and vars.
« Last Edit: June 03, 2021, 02:13:01 pm by cv007 »
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #22 on: June 03, 2021, 02:53:36 pm »
Type aliases with "using" were introduced in C++11.
You might have an old compiler, or wrong compiler flags.

arduino in tinkercad

this is why i hate languages like c and c++. it 's like trying to peel a banana with a hammer. compile -> fix typo's -> compile - > fix flags -> compile -> compiler issues.
i want interpreted basic back. at least i can move forward in that without endless hunting for typos and cryptical error mesages.
i am trying to port a simple basic program to arduino and i've been struggling for hours on this simple bit of code.
all it is is a bit of simple downcounters attached to an interrupt handler , that fire off functions when their count reaches zero.
grrr... frustrated.

well, if you have no idea what your are doing and just throw random stuff at the compiler hoping it will guess what you mean, ....
the issue is that the code works in the real arduino ide , but does not work in tinkercad simulator ...
Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 

Online Nominal Animal

  • Super Contributor
  • ***
  • Posts: 2718
  • Country: fi
    • My home page and email address
Re: storing function pointer and calling
« Reply #23 on: June 03, 2021, 03:27:59 pm »
the issue is that the code works in the real arduino ide , but does not work in tinkercad simulator ...
Um.

Tinkercad simulator is based on Code::Blocks simulator, which is an API-level simulator. It simulates based on the function calls, not based on what the hardware register values et cetera are.  As far as I know, it does not support timer interrupts, because the base API does not have such functions; they vary too much between different hardware.

So, let me guess, you're poking at the hardware registers to manipulate the timer, and are not seeing the interrupts fire, ever? Or such code does not compile at all?  Even using structures causes the simulator to b0rk? That is because the simulator does not support that, it's too simple of a simulator for that sort of stuff.

In fact, I don't even believe there is a proper compiler used for the simulation at all.  Just a lexer/parser that converts the code to an abstract syntax model (a directed graph of operations) – that's how it can convert between the different forms, from graphical to text – and a state machine that acts on that abstract syntax model.  It is a toy.
« Last Edit: June 03, 2021, 03:33:14 pm by Nominal Animal »
 

Offline free_electron

  • Super Contributor
  • ***
  • Posts: 7653
  • Country: us
    • SiliconValleyGarage
Re: storing function pointer and calling
« Reply #24 on: June 03, 2021, 03:56:26 pm »
ok guys, all comments aside , Below is the code that compiles in the official arduino ide. it does not compile in tinkercad.
I always assumed arduino was plain c++ , but apparently it is not.
I grabbed the damn thing because i wanted to show proof of concept on how to make a simple cooperative threading application. I have used this mechanism on many applications ( 8051 in pl/m or asm ) and even in other compilers on arm and other machines.
The arduino world is indeed full of code containing blocking 'sleep' statements. So that is why my little microthreader solves : you can set up a process that will fire at a given interval without blocking others. It is not time-deterministic and yu need to make sure you complete all before the next tick but it works.

i normally do the following:
1) i set  up a table containing 4 parameters per line : the current count value, the interval , the run state and the pointer to what needs to be called. Since we run on 8/16 bitters there are all cast as 16 bit integers (we dont have more than 64k code memory)
2) if i need to handle 10 processes the table is  10 lines by 4 words or 80 bytes of memory. So my overhead is very low.
3) i set up a hardware timer to fire an interrupt every 1 second ( or 1 millisecond or 100 milliseconds , play at will here, just make sure to be able to complete all before next tick )
4) at interrupt, the handler decrements all the count values in the table where the run state is set to 1.
    if the count value hits zero :
    we call the function (we have the pointer ). the return value is stored in the state. this allows a function to terminate itself. if it returns 0 it stops itself.
    we reload the count value with the interval

there are a number of housekeeping commands
pause and resume : alter the run state (pause = 0 , resume = 1) without modifying the count value.
stop : set runstate to zero but reset the count value to interval, when start comes it begins from interval
start : set runstate to 1

int createprocess (interval, function to be called ) : loads the table with the data , returns the index the process is stored in in the table

example :
blinky_led1 = createprocess (5, led1handler)    // this fires every 5 seconds led1handler is responsible to do something with led1
blinky_led2 = createprocess (1, led2handler)    // this fires every 1 second
flasher = createprocess (10,flasherhandler)


startprocess (blinky_led1)
startprocess (blinky_led2)
startprocess(flasher)

main {
//i can do whatever here , even sleep. the interrupts will keep running
   if some_event then pause (blinky_led1)
  if some_other event then stop (blinky_led2)

};

The actual handlers can take 1 argument.
the argument tells the individual functions to execute the normal runcode or a different chunk of code. for example :
0 : do the normal process , for example toggle the led pin
1 : set the led pin high
2 : set the led pin low
255 : emergency stop : safe the i/o (whatever that means for your application ) and terminate yourself

a helper function (Rundaemon) allows you to do a force call to the handlers.


Code: [Select]

typedef int (* functionpointer) (int);
#define max_daemons 10
struct Daemon {
   int counter;              // current counter value
   int interval;             // given interval to reload
   int state;                // runtime state 0 = stop 1 =run
   functionpointer handler;  // function to be called when counter reaches zero
};
int daemoncounter =0;                // keep track of how many we already have
Daemon Daemons[max_daemons]; // the array of daemons
 
int CreateDaemon (int interval, functionpointer handler )
{
   Daemons[daemoncounter].interval = interval;
   Daemons[daemoncounter].handler = handler;
   daemoncounter++;         
};

void StartDaemon (int daemon)
{
   Daemons[daemon].counter = Daemons[daemon].interval;
   Daemons[daemon].state = 1;
};

void PauseDaemon (int daemon)
{
   Daemons[daemon].state = 0;
};
void ResumeDaemon (int daemon)
{
   Daemons[daemon].state = 1;
};
           
void AbortDaemon (int daemon)
{
   Daemons[daemon].state = 0;
   Daemons[daemon].counter = 0;
};

void RunDaemon (int daemon, int command)
{
  Daemons[daemon].state = Daemons[daemon].handler(command);
};


// some dummy routines
int led1 (int command){
  static int x;
  int exitcode = 1;
  pinMode(3,OUTPUT);
  if (command == 0){x = ~x;};             // toggle x
  if (command == 1){x = 1 ;};             // make high
  if (command == 2){x = 0 ;};             // make low
  if (command == 255){                    // emergency stop
    x=0;           // turn the led off (safe)
    exitcode =0;   // kill ourselves
  } 
  digitalWrite(3,x);
 
  return exitcode;                        // signal we want to keep running
};

int led2 (int command){
  static int x;
  pinMode(4,OUTPUT);
  x = ~(x);
  digitalWrite(4,x);
  return 1;
};

int led3 (int command){
  static int x;
  pinMode(5,OUTPUT);
  x = ~(x);
  digitalWrite(5,x);
  return 1;
};


// the interrupt handler. this fires at a 1 second interval
ISR(TIMER1_COMPA_vect)
{ for (int x = 0; x <daemoncounter; x++){
    if (Daemons[x].state=1){                       // if in run state
      if (Daemons[x].counter ==0) {                // and we hit zero
       
        //Daemons[x].handler();
        Daemons[x].state = Daemons[x].handler(0);   // -> execute the handler and store return value in state
                                                    //    this allows a function to stop itself by returning 0
                                                    //    we call with argument 0 : normal operation
        Daemons[x].counter = Daemons[x].interval;   // -> reload the counter
       
      }
      else {
        Daemons[x].counter--;                    // else decrement the counter
      };
    };
  };
}

void setup() {
    CreateDaemon (1, led1(0));  // blink once a second
    CreateDaemon (2, led2(0));  // blink every two second
    CreateDaemon (3, led3(0));  // blink every three seconds
     
    // Set up the hardware countern
    // - mode 4 (CTC)
    // - set prescaler to 1024
    // - using TCCR1A and TCCR1B registers
    // - see datasheet starting on page 170
       TCCR1A = 0b00000000; // set WGM11 and WGM10 bits (table 20-6, p171)
       TCCR1B = 0b00001101; // set WGM13, WGM12 (table 20-6), CS12, CS11, and CS10 bits (table 20-7, p 173)
       OCR1A = 15625;       // set the output compare value (timer interrupt will trigger when the timer reaches this value)
                            // one clock tick is 62.5 nanoseconds @ 16MHz
                            // prescaler * OCR1A * 62.5nS = seconds.
       TIMSK1 = 0b00000010; // enable the interrupt - p184
       sei();               // allow interrupts
}

void loop() {
  // do whatever logic here 
}

Professional Electron Wrangler.
Any comments, or points of view expressed, are my own and not endorsed , induced or compensated by my employer(s).
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf