CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Array of pointers to functions in struct

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
ysern



Joined: 01 Nov 2012
Posts: 4

View user's profile Send private message

Array of pointers to functions in struct
PostPosted: Wed Nov 28, 2012 4:26 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Nov 28, 2012 5:10 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Thu Nov 29, 2012 3:47 am     Reply with quote

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

Code:

func1(cs, 3);


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

View user's profile Send private message

PostPosted: Thu Nov 29, 2012 1:24 pm     Reply with quote

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: 19520

View user's profile Send private message

PostPosted: Sun Dec 02, 2012 6:16 am     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Dec 03, 2012 3:52 pm     Reply with quote

Hi Ttelmah,

i have checked your code. It is really working. thanks!
ysern



Joined: 01 Nov 2012
Posts: 4

View user's profile Send private message

PostPosted: Mon Dec 03, 2012 3:59 pm     Reply with quote

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: 19520

View user's profile Send private message

PostPosted: Tue Dec 04, 2012 10:24 am     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Jan 28, 2013 4:25 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Jan 28, 2013 10:36 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Jan 29, 2013 3:34 pm     Reply with quote

Great tip, thanks!
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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