|
|
View previous topic :: View next topic |
Author |
Message |
ysern
Joined: 01 Nov 2012 Posts: 4
|
Array of pointers to functions in struct |
Posted: Wed Nov 28, 2012 4:26 pm |
|
|
Hello here.
Code: | #include <16F887.h>
#FUSES NOWDT, INTRC, NOPUT, NOPROTECT, /*NODEBUG,*/ BROWNOUT, NOLVP, NOCPD, NOWRT
#use delay(clock=4000000)
typedef void (* callback ) ();
struct structWithCallbacks {
callback * callbacks;
};
void func1(callback * arrCallbacks, int n) {
int i;
for (i = 0; i < n; i++) {
arrCallbacks[i]();
}
}
void callbackHandler () {
// do nothing
}
void main() {
callback * cs = { callbackHandler, callbackHandler, callbackHandler };
/*line:26*/ func1(cs, 3); // <==== All is OK here
struct structWithCallbacks s1;
s1.callbacks = cs;
/*line:30*/ func1(s1.callbacks, 3); // <==== Error#53 Expecting function name
} |
I get an error in line 30 when i try to compile code above: Error#53 Expecting function name. Interesting thing is that the line 26 is almost the same, but no error there.
Please, tell me is there something wrong in code or the problem is in CCS compiler? Thanks. |
|
|
dyeatman
Joined: 06 Sep 2003 Posts: 1934 Location: Norman, OK
|
|
Posted: Wed Nov 28, 2012 5:10 pm |
|
|
As always, before we look at the problem, what version of compiler are you using? _________________ Google and Forum Search are some of your best tools!!!! |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Thu Nov 29, 2012 3:47 am |
|
|
There is a probable clue in the architecture of most PICs. Most PICs have a modified Harvard architecture. That's to say they have two distinct memory spaces and busses: one for data and another quite separate one for instructions.
So what? Well, this split allows a number of optimisations: it allows instruction fetches and data accesses to be done simultaneously, allowing PICs to be highly yet simply pipelined using low cost hardware. It also allows the two memories to be optimised for their respective functions: they are different widths; program memory generally being wider than data memory.
There is a downside. Most high level languages, including C, were developed for and expect a flat single memory space. (Yes I am aware that many PDP11s, on which Unix really took off had separated I & D space, but that was a virtualisation to ease the 64Kbyte limitation of the instruction set on a fundamentally single memory space) C function pointers are an indication of that. C expects that a pointer to code, such as a function pointer, or even to any const "variable" (how can constants be variable exactly?), will be the same - the same width, the same address space etc. - as a pointer to data.
On Harvard machines that is NOT the case. Access to program memory space is very limited, indeed in pure Harvard machines there is no access at all from running code. The "modified" in "modified Harvard architecture" refers to limited, i.e. slow and difficult, access. In PICs and many other processors, such as the early TI DSPs which were also Harvard machines for much the same reasons as PIC were, consts, literal strings, and function pointers are all difficult to implement and may have limitations.
On C18, there are many different versions of the standard string and memory library functions, things like strcmp and memcpy, to cope with this: the different versions are dependant on which memory space or spaces are being dealt with. CCS tries to get round this transparently so that we, the users don't have to worry too much about which memory space is which.
I suspect you've just come up to the limit of what CCS C can do for you. It can, through devious means, implement a function pointer that can be resolved at compile time, such as your
but cannot deal with one that can only be resolved at runtime, such as your:
Code: |
func1(s1.callbacks, 3);
|
Or put another way, it cannot put function pointers in a data memory structure, i.e. RAM, that can be used at run time, whereas it can work out a hardcoded emulation of a function pointer fixed at compile time that doesn't ever get stored in RAM.
This is a direct and fundamental result of the architecture of the PIC. I feel its actually pretty amazing that CCS manages to implement function pointers at all on the PIC. That there is a limitation is only really to be expected.
So, are you trying to port some code to the PIC? Code written for another processor with much greater capability and with conventional Von Neumann architecture. Some PC code perhaps? To use callbacks on the PIC, because they have limited program space as well as the architectural difference, is unusual. Its rarely required, and seldom seen; like dynamic memory, which is arguably to be avoided like the plague on all true embedded projects.
So what are you trying to achieve?
RF Developer |
|
|
ysern
Joined: 01 Nov 2012 Posts: 4
|
|
Posted: Thu Nov 29, 2012 1:24 pm |
|
|
hi dyeatman,
dyeatman wrote: | As always, before we look at the problem, what version of compiler are you using? |
this is my first post, didn't know this. the compiler version is 4.137
hi RF_Developer,
thanks for meaningful answer very much. now i understand the problem.
RF_Developer wrote: | So what are you trying to achieve? |
i try to implement some system controlling algorithm. i use the code that has been wrote as example of algorithm implementation, but not wrote for PIC MCUs. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19515
|
|
Posted: Sun Dec 02, 2012 6:16 am |
|
|
This is one that CCS perpetually seems to have trouble with. It does correctly reference function names, but doesn't then allow them to be stored in more complex types....
What you can do is cheat!. A functions address is just a number. Lo and behold, if you store these numbers as integers (int16 or int32, depending on your chip), and then cast or transfer these to be pointers to functions, they work!...
So:
Code: |
typedef void(*callback)(void);
struct structWithCallbacks {
int32 callbacks;
};
void func1(int32 arrCallbacks[], int n) {
int i;
callback ptr;
for (i = 0; i < n; i++) {
ptr=arrCallbacks[i];
(*ptr)();
}
}
#separate
void callbackHandler (void) {
// do nothing
delay_us(1);
}
#separate
void second (void) {
//do different nothing
delay_us(2);
}
void main() {
struct structWithCallbacks s1;
int32 cs[3];
cs[0]=callbackHandler;
cs[1]=second;
cs[2]=callbackHandler;
func1(cs, 3);
s1.callbacks = cs;
func1(s1.callbacks, 3);
}
|
You seem to have to put the value from the array into a simple variable before you use it, and notice that you have to initialise the array for function names manually.
However this does merrily call the three functions.
It appears to be the act of using more complex types that gets CCS confused, and if you store the numbers as normal integers then put them into a simple variable supporting 'callback', the confusion doesn't happen.....
Best Wishes |
|
|
ysern
Joined: 01 Nov 2012 Posts: 4
|
|
Posted: Mon Dec 03, 2012 3:52 pm |
|
|
Hi Ttelmah,
i have checked your code. It is really working. thanks! |
|
|
ysern
Joined: 01 Nov 2012 Posts: 4
|
|
Posted: Mon Dec 03, 2012 3:59 pm |
|
|
Hello Ttelmah,
You mentioned about int16 or int32.
Ttelmah wrote: | A functions address is just a number. Lo and behold, if you store these numbers as integers (int16 or int32, depending on your chip) |
But I tried to make a code with simple int. It is working too. Can I simply use int? My chip is dsPIC33. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19515
|
|
Posted: Tue Dec 04, 2012 10:24 am |
|
|
Remember on a DsPic, an 'int', is an int16. It'll work, provided the function is in the low part of memory, but if your chip has more than 64K, it'll stop working for addresses above this point.
Better by far to be 'explicit', and make sure you choose a size larger than the amount of ROM on the chip.
Best Wishes |
|
|
DireSpume
Joined: 07 Jan 2013 Posts: 31
|
|
Posted: Mon Jan 28, 2013 4:25 pm |
|
|
So, if your method works, Ttelmah, then I don't see any reason why they can't make the compiler handle it properly. Maybe they just haven't gotten around to implementing it yet. And thanks for the solution. I really wanted to set up a const array of structs that mapped a command ID to a command handler, thus avoiding a huge switch statement, but was surprised when my function pointer did not compile.
This is how I tried to do it:
Code: |
typedef enum { CMD_SENDDATA, CMD_HTR, CMD_CNT } CmdID;
typedef struct
{
CmdID CmdId; // ID of this command
bool DataIn; // Will data be coming in or sent out for this command
uint8 DataLen; // Number of bytes of data associated with this command
void (*CmdHdlr)(uint8); // Pointer to command handler
} CmdInfo;
void Cmd_SendData(uint8 state)
{
}
void Cmd_HeaterCtrl(uint8 state)
{
}
const CmdInfo CmdHdlrs[] =
{
{ CMD_SENDDATA, false, sizeof(DeviceVals), &Cmd_SendData },
{ CMD_HTR, true, sizeof(DeviceVals), &Cmd_HeaterCtrl }
};
|
However, this doesn't work even if I use void* or uint32 instead of a function pointer. Apparently I have to set the CmdHdlr value at run-time, like so:
Code: |
const CmdInfo CmdHdlrs[] =
{
{ CMD_SENDDATA, false, sizeof(DeviceVals), NULL },
{ CMD_HTR, true, sizeof(DeviceVals), NULL }
};
void CmdHdlrInit()
{
CmdHdlrs[0].CmdHdlr = &Cmd_SendData;
CmdHdlrs[1].CmdHdlr = &Cmd_HeaterCtrl;
}
|
So my array cannot be const and I have to split initialization into two blocks of code. Not as clean, but it works. Or at least I assume it does -- I haven't tested it yet, but it compiles. |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1349
|
|
Posted: Mon Jan 28, 2013 10:36 pm |
|
|
The following will compile (4.140). I don't have a way to test functionality:
Code: |
typedef void (*CmdHdlr_t)(uint8);
typedef enum { CMD_SENDDATA, CMD_HTR, CMD_CNT } CmdID;
typedef struct
{
CmdID CmdId; // ID of this command
bool DataIn; // Will data be coming in or sent out for this command
uint8 DataLen; // Number of bytes of data associated with this command
CmdHdlr_t CmdHdlr; // Pointer to command handler
} CmdInfo;
void Cmd_SendData(uint8 state)
{
}
void Cmd_HeaterCtrl(uint8 state)
{
}
const CmdInfo CmdHdlrs[] =
{
{ CMD_SENDDATA, false, sizeof(DeviceVals), &Cmd_SendData },
{ CMD_HTR, true, sizeof(DeviceVals), &Cmd_HeaterCtrl }
};
|
Generally, I have always typedef'ed function pointers in CCS. It's mostly because of how I was taught in school, but it usually works out well. |
|
|
DireSpume
Joined: 07 Jan 2013 Posts: 31
|
|
Posted: Tue Jan 29, 2013 3:34 pm |
|
|
Great tip, thanks! |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|