This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

C51: Null function pointer

I am trying to convert an unmaintainable user interface
driver over to something that uses data structures to define
the menu organization (This example is stripped down to the
bare minimum from my actual app).

Picture an array of screens, selectable by the user's
activation of some arrow keys:

         Col 0                  Col 1
           +-----------+
    Row 0  | Screen 1a |
           +-----------+
                /\ 
                ||
                \/
           +-----------+       +-----------+
    Row 1  | Screen 2a | <===> | Screen 2b |
           +-----------+       +-----------+

I define a function to output each screen (in the code
below, they also change the row & column indices):

  void f1a(void) {

    outputScreen1a();
    if(r > 0)
      r++;
    return;
  }

  /* ============= */

  void f2a(void) {

    outputScreen2a();
    if(c < 1)
      c++;
    return;
  }

  /* ============= */

  void f2b(void) {

    outputScreen2b();
    r = c = 0;
    return;
  }

These are called by the main() function, using a function
pointer table:

  static code const void (* code menuFunc[2][2])(void) = {
    { f1a,    NULL },
    { f2a,    f2b  }
  };

  unsigned char r, c;

  void main(void) {
    unsigned char i;

    SFRPAGE = 0;   /* Go to default page     */
    WDTCN = 0xDE;  /* Disable watchdog timer */
    WDTCN = 0xAD;
    IE   = 0;      /* Disable all interrupts */
    i = 0;
    r = c = 0;

    while (1) {

      if(i < 70) {
        i++;
      } else {
        i = 0;
                  (*menuFunc[r][c])();
      }
    }  /* END while (TRUE) */

  }  /* END main() */

As presented above, the source file will compile without
error. But my application is quite a bit more complex than
what I present here, the table is assymetrical (marked by
the NULL pointer) and I want to guantee I do not
inadvertently dereference it, so I add a qualification to
the main control loop:

      while (1) {

        if(i < 70) {
          i++;
        } else {
          i = 0;
          if((menuFunc[r][c])() != NULL)
                    (*menuFunc[r][c])();
        }
      }  /* END while (TRUE) */

C51 does not like this. It flags the statement where I check
for NULL:

?????.C:(56): error C193: '!=' : illegal conversion from/to 'void'

NULL is defined in stdio.h:

 #define NULL ((void *) 0)

K&R doesn't state it explicitly, but seems to assume that
void is an object type, that (void *) can only point to an
object, and never uses NULL as a pointer to a function.

So the question is, if I can't use NULL to mark an
uncallable function in the table, is there a null function
pointer I could use instead?

============================================================
Gary Lynch           |   To send mail, no$pam in domain name
lynchg@no$pam.com    |   must be changed to stacoenergy.
============================================================

  • Your line:

     if((menuFunc[r][c])() != NULL)
    


    contains a () making it a function call of a function taking void.

    You have to realize that the deferencing * is not needed for function pointers.

    Try:

    if (menuFunc[r][c]) {
        ....
    }
    


    to test your pointer.

  • By George, you've got it! That took care of the problem.

    Thanks for the insight.

    ============================================================
    Gary Lynch           |   To send mail, no$pam in domain name
    lynchg@no$pam.com    |   must be changed to stacoenergy.
    ============================================================
    

  • had you used a static code analyzer, you'd be saying: "By George static code analyzer, you've got it!"

    :-)

  • By 'static code analyzer' I presume you mean PC-Lint, which
    prompts me to take this to the next level.

    I do own a copy of PC-Lint, and have used it in the past to
    solve some hairy problems. I would like to use it
    routinely, but am impeded by the tool being apparently an
    attribute of the project, rather than of uV3 itself.

    By that I mean every time I create a new project, I have to
    go tell the IDE the locations of the lint executable, the
    configuration file that characterizes my micro, and the
    compiler's default include files (all things that generally
    don't change--project to project).

    This is about 75% of the effort of installing a new
    application for EVERY project (and I create a lot of them).
    So I only go through it if it promises me a major benefit.
    But I don't run into problems like the one you just
    witnessed very often (It looks like my last query to this
    body was last April).

    Am I the only one who thinks we ought to be able to just
    install PC-Lint, enable it in uV3, and be able to run it
    from then on?

    ============================================================
    Gary Lynch           |   To send mail, no$pam in domain name
    lynchg@no$pam.com    |   must be changed to stacoenergy.
    ============================================================
    

  • but you can run PC-lint from the command line, or better - using a batch file. Who needs uv3 for that...?
    just create "lint files" files per project, and you are in business.

  • lint-nt -w2 -e40 -e10 -e123 -e63 -e19 -e31 -e534 -e409 -e522 -e553 -e537 -e647 -e525 -e539 -e14 -e602 -e547 -e501 -e140 -e526 CO-RV.lnt lint_files.lnt > results.txt
    

    and lint_files.lnt,

    -I..\STR9_scheduler
    -I"\Keil\ARM\INC\ST\91x"
    -I"\Keil\ARM\RV31\INC"
    
    ..\STR9_scheduler\91x_it.c
    ..\STR9_scheduler\config.c
    ..\STR9_scheduler\LED.c
    ..\STR9_scheduler\main.c
    ..\STR9_scheduler\messaging.c
    ..\STR9_scheduler\priority_queue.c
    ..\STR9_scheduler\queue.c
    ..\STR9_scheduler\scheduler.c
    ..\STR9_scheduler\Serial.c
    ..\STR9_scheduler\swi_functions.c
    ..\STR9_scheduler\synchronization.c
    ..\STR9_scheduler\system_notifications.c
    ..\STR9_scheduler\system_services.c
    ..\STR9_scheduler\timer.c
    ..\STR9_scheduler\trace_buffer.c
    

  • I don't own PC-Lint.

    I wrote a little program that extracts a bit of information from the Keil project files (and also looks at the produced output files).

    Then I use this to create a Makefile that uses a couple of free compilers to compile (but not link) the source code with suitable warning levels. In my case, it is enough to know what source files are part of the project, and what directories the source is in and what directories to scan for include files.

    Not perfect, but a quick way to get a bit of extra static analysis of the code. Spending money on PC-Lint would probably be a better solution, but I did the above once when I was bored and had to wait a couple of hours for a transport. In the end, the solution has produced a couple of intersting warnings that I would have liked support for by the ARM compiler.

    Alas, it can't do cross-module analysis, so I should probably some day look at automatic support for any of the free source-code analyzers that are available. When I originally did the above, I didn't had Internet access, so I had to settle for whatever tools/info I had on the laptop.

    Anyone who have any special favourites?
    en.wikipedia.org/.../List_of_tools_for_static_code_analysis

  • I tried splint yesterday. It is a nice tool, but lacks support for specific compiler extensions besides some GNU stuff.

  • What I forgot to mention is that I do the above for ARM and not C51 projects. This greatly reduces the problems with compiler extensions.

    A bit of creative use of sed or #defines can normally "standardize" the code.

  • On 24-Oct-2008 05:50; Per Westermark wrote:
    >
    > I wrote a little program that extracts a bit of information
    > from the Keil project files (and also looks at the produced
    > output files).
    >
    > Then I use this to create a Makefile that uses a couple of
    > free compilers to compile (but not link) the source code
    > with suitable warning levels.
    >
    I keep digressing, but I have been trying to implement just
    what you describe for years.

    I have batch files that call Perl scripts to extract enough
    info from the *.__i and *.lnp files to create a makefile I
    could archive with my source. It would need to include the
    compile- & link-time directives (which I use for
    configuration management). I can create them for a specific
    project, but have never gotten one to work as a general
    purpose utility.

    - What language do you write in?
    - Did you get yours working for the general case?

    ============================================================
    Gary Lynch           |   To send mail, no$pam in domain name
    lynchg@no$pam.com    |   must be changed to stacoenergy.
    ============================================================
    

  • I really have to check if I used C or C++ ;)

    Not the quickest way to write the string manipulation but I didn't have too much tools installed on the laptop, and I got the idea when I failed to buy a book to read while waiting for a plane after the original plane got cancelled.

    Yes, it worked quite well, but in this case I wasn't originally so very concerned about compilation options since the goal was to test-compile with other compilers. The important thing was to be able to feed the other compilers with define symbols. My aim was to use the correct set of source files, the correct include directories and the correct conditional compilation options. Then I had to do a bit of automatic tweaking of the source to get it through for example the MinGW version of the gcc compiler.

    But yes, it does pick up the command-line options that the ARM compiler prints at the top of the list files, so I can compile the files with the ARM compiler too and with the correct options.

    I basically did this instead of doing cross words, without really knowing what would come out of it.

  • I would like to use it
    routinely, but am impeded by the tool being apparently an
    attribute of the project, rather than of uV3 itself.

    So what's keeping you from copying and modifying a personally adjusted default project (including Lint settings) instead of creating new ones all the time?

  • Let's see if I can apply your advice to my logistics.

    My 'top level BOM' is a system containing 5 MCUs, which
    communicate over several networks. Thus I must maintain 5
    execution modules and around 120 source files to make a
    release.

    The product is offered in 50 different configurations which
    I maintain from a common source deck by changing the values
    of 4 pre-processor constants at build time.

    Four of the 5 applications are based on finite state
    machines, which I implement calling a function pointer
    table, indexed by the state variable. When elements of the
    call tree pass strings as parameters to lower-level
    functions, these strings get stored in the same ?CO? code
    segment as the function pointer table, and L51 is too dense
    to realize this is not a recursive function call, bombarding
    me with a couple dozen warnings and moving all affected
    functions out of the overlayable RAM logic, which eats up so
    much internal RAM I have to write OVERLAY directives, unique
    to each MCU, to get it to fit.

    My work is mostly adding new features, and your average
    feature changes the code in around 2 execution modules.
    Every other change causes a source file or 2 to grow to the
    point where I have to re-factor, so my project file source
    list is NEVER the same between MCUs, and rarely the same
    between revisions of the same MCU.

    So I could possibly create a 'default project file' to copy
    into each directory, for which I would have to delete all
    the file entries, the pre-processor constants, and OVERLAY
    directives, then customize it to the new environment. I had
    heretofore considered this a wash, but with the wish to
    enable lint all the time, it is worth running a check.

    I'll report back when I know.

  • On 25-Oct-2008 06:21; Hans-Bernhard Broeker wrote:
    >
    > So what's keeping you from copying and modifying a
    > personally adjusted default project (including Lint
    > settings) instead of creating new ones all the time?
    >
    I promised to report my findings on the feasibility of that
    suggestion. First I need to explain how I structure
    projects.

    The file containing the main() function has a 3-part naming
    convention:

      - The name of the application (in current project, that
        is the same as what uV3 calls the 'Target',
      - A symbol denoting configuration for products that
        get sliced into many different one-of-a-kind special
        configurations,
      - The revision number.
    

    Thus, the file that started this dialog is named:

                  RectCtlDelta2_0.c
                  -------     ---
                     |   ----- |
                     |     |   |
    The MCU that ----+     |   +---  Rev 2.0
    controls the           |
    rectifier         The designation
    section of        for 'plain, vanilla',
    the UPS           i.e. non-special
    

    If you let everything default, C51 will translate this into
    an object file with the same name and extension .obj, and if
    you specify that module first to BL51, it will create
    absolute object in a file with the same name and no
    extension.

    I create a project file called RectCtlDelta2_0.uv2 to hold
    all the build details, and that is (I hope) what Herr
    Broeker wanted me to clone. It creates a self-documenting
    trail from executable back to source, with the most critical
    parameters visible in the (shared) file name.

    Let's say we move from Rev 2.0 to 2.1, and I copy:

      - RectCtlDelta2_0.c   to RectCtlDelta2_1.c, &
      - RectCtlDelta2_0.uv2 to RectCtlDelta2_1.uv2
    

    Under dialog box Project\Options for Target 'RectifierCtl' I
    discovered most of the parameters on the 'Listing' tab did
    not copy through, reverting to their defaults. I snooped
    around and found file RectCtlDelta2_0.opt, created by uV3
    when I run a build held them, so I tried cloning it as well:

      - RectCtlDelta2_0.opt to RectCtlDelta2_1.opt.
    

    Now I have most of the attributes of the old project
    assigned to the new, but I'm not through yet. As I mentioned
    earlier, tab 'BL51 Misc' contains data for the OVERLAY
    directive, and that references a constant, code segment from
    the module holding the FSM and main(), so I must edit its
    revision number:

      ?PR?onRect?RectCtlDelta2_0 ~ ?CO?RectCtlDelta2_0,
      ?PR?onNoBattery?RectCtlDelta2_0 ~ ?CO?RectCtlDelta2_0
    

    must become:

      ?PR?onRect?RectCtlDelta2_1 ~ ?CO?RectCtlDelta2_1,
      ?PR?onNoBattery?RectCtlDelta2_1 ~ ?CO?RectCtlDelta2_1
    

    I could eliminate that by moving the FSM to another source
    file--one not containing the revision number, but there's
    more.

    The 'BL51 Misc' tab also contains a 'Linker control string'
    field with the name of the target file for my absolute
    object module:

        TO "RectCtlDelta2_0"
    

    I need to change that, but BL51 will not let me edit it in
    the dialog box.

    In the positive column, after I configured PC-lint from the
    Tools\ menu, it remained configured in the new project.

    That's all the investigation I have time for today, but it's
    not looking like a time-saving approach.

    ============================================================
    Gary Lynch           |   To send mail, no$pam in domain name
    lynchg@no$pam.com    |   must be changed to stacoenergy.
    ============================================================
    

  • I create a project file called RectCtlDelta2_0.uv2 to hold all the build details, and that is (I hope) what Herr Broeker wanted me to clone.

    I'm afraid you took this ball and ran it with much further and faster than I meant you to.

    Your original problem was that the PC-Lint configuration has to be re-done after every "Project->New Project". The solution to that issue is to have a default project that is exactly the same as a virgin "New Project", but with PC-Lint configured. You can then copy that around instead of using "New Project".

    That said, I think you need to step back a bit and review your naming strategy a bit. I rather much doubt that you're doing yourself any kind of favour by putting the project version number into the source file names. Version number tags belong into your revision control system, not in file names. File name modifications just create change all over the place (section names, e.g.) that only get in the way instead of being helpful. Whether or not the same applies to the other two parts of the file name is for you to decide, so I'll just say that I don't to see any reason why the file containing main() should not be called main.c.

    But if you really must, I would suggest you try an automated text search-and-replace tool on the copied uV2 and opt files to apply all changes related to the filename change to the project.

    The 'BL51 Misc' tab also contains a 'Linker control string'
    field with the name of the target file for my absolute
    object module:

    TO "RectCtlDelta2_0"

    That one's pretty easy. Find the project options, and go to the "Output" tab. Change the executable's name there. (The text substitution step mentioned before could take care of that automatically).

    And you may want to consider using multiple targets in a single project instead of creating a whole new project for every minor version of your program.

    Generally speaking, it feels like you need to relax and re-read the entire documentation on uVision --- at the moment, you appear to fighting it instead of letting it help you.