2

A while back I had posted this question on SO. In that question I was looking for a way to implement a solution which we had thought of. But from the answers I got, I saw that that solution was unimplementable using the default C preprocessor.

So I've decided to post the problem directly and see if a proper solution can be found.

PROBLEM STATEMENT

We have a custom hardware platform on which we've used the Contiki OS to build the firmware. The hardware is based on the MSP430 micro-controller. Being an embedded and restrained environment, we are looking for ways to optimize the code.

One area were we want to optimize is the logs we are printing to the console. The default printf() function is not working because reasons. So we have our own function to print to the console using the UART.

void ax_log_msg(enum log_msg_id, char* message);

This obviously will only take fixed strings unlike printf() but it serves our purpose.

Now the fixed strings that we give in the function will obviously take up a considerable amount of memory. We want to reduce the memory usage by doing something before the actual compilation that will replace the strings with a fixed integer/number that is unique to every call. It will also generate a table or file where the integers will be mapped to the string they replaced. This table would be used in the user-end application to show the actual message while the device will only print integers.

So what I'm looking for is a way to implement such a solution. Any answer would help: just ideas or pre-existing solutions; anything helps.

Additional Notes

  • The log_msg_id enum and the message the user wants to print are not related to one another. The enum values will mostly be used to group the strings for the table/document.

  • We are using the TI CCS IDE to build our application.

  • Unique values for each unique string instead of each call would be better, but not necessary.

  • The firmware is written in C.

  • The message that will be printed can be anything the user/programmer wants. The system should pick it out from the code.

  • Since the firmware code will span across multiple files written by different people, the system should be able to hold values across the files. This is one of the reasons the basic C- preprocessor was not used.

If any information is missing, please do mention it and I will add it.

Thank You

Community
  • 1
  • 1
Suman Roy
  • 673
  • 5
  • 18
  • To add to [@user694733's answer](http://stackoverflow.com/a/25441555/69809), the thing that you probably need to ensure is that your log message IDs don't change much between firmware versions, because it will make things harder to maintain. This basically means that making this an automatic part of your build process might introduce breaking changes whenever you insert a log statement between previous statements. Otherwise, you could introduce a perl build script (something like [this](http://www.perlmonks.org/?node_id=261545)) into your build process. – vgru Aug 22 '14 at 07:57
  • I wouldn't, however, consider doing that if you don't do continuous integration (i.e. "one-click builds" and CVS integration). If you are going down this road, you really want to ensure that each lookup table is linked to a specific revision, that your device communicates its firmware version towards the app, and that the app has access to all table revisions. – vgru Aug 22 '14 at 08:00
  • @Groo The lookup table will only change between firmware versions and the app will always be aware of the current version. – Suman Roy Aug 22 '14 at 10:06
  • 1
    I still think there is no reason for multiple developers to avoid creating hardcoded resource tables. It's a much better solution than mindlessly using hardcoded strings all over the code (anyone who ever needed to localize an app will tell you it's not the way to do it). It is also much easier to parse these files, like the `log_table.x` in the example below, without the need to modify them and create the code for the client app. – vgru Aug 22 '14 at 10:12

1 Answers1

3

Sound like a job for the X-macros:

log_table.x

/*Symbol             ID   Message           */
X(LOG_ID_BOOTING,     1,  "Booting"          )
X(LOG_ID_OUT_OF_MEM,  2,  "Out of memory"    )
X(LOG_ID_FOO_BAR,     3,  "Foo failed to bar")

log.h

/* Generate list of available ID numbers */
enum LogID {
#define X(a, b, c)   a = b,
#include "log_table.x"
#undef X
};

void do_log(enum LogID id);

some_module.c

p = malloc(sizeof(Foo));
if(!p)
    do_log(LOG_ID_OUT_OF_MEM);

Note that you could add some more columns to log table, like error level, etc.

In this example I ignored the message column completely. This is major advantage. You could generate string table to debug versions, and leave it out from the final release.

Another nice advantage is that .x file is easy to parse, so you could use Python script for example to generate nice PDF file with all error codes and explanations in it.

user694733
  • 15,208
  • 2
  • 42
  • 68
  • What happens with `c` inside the define? – vgru Aug 22 '14 at 07:21
  • @Groo It is ignored. That is the point, you can select what columns you want by defining the macro as you like. – user694733 Aug 22 '14 at 07:22
  • But it doesn't seem like OP wants to manually define each message ID (especially in his [previous question](http://stackoverflow.com/q/25204389/69809)). So this basically shows how to create an enum with hardcoded values, which is what OP probably wants to avoid (although I strongly agree that IDs should be *fixed* if they are going to be interpreted outside the device, to prevent versioning issues). So +1 from me anyway. – vgru Aug 22 '14 at 07:28
  • 1
    @Groo Well I am simply suggesting what OP needs to do, not what OP wants to do :) – user694733 Aug 22 '14 at 07:29
  • @user694733 will the X-macro hold values across files? – Suman Roy Aug 22 '14 at 10:10
  • @Suman: there is no such concept in this example. Log IDs are hardcoded (1, 2, 3). – vgru Aug 22 '14 at 10:17
  • @SumanRoy As Groo pointed out, in each compilation unit that includes the `log.h`, ID numbers will have the same value (for example `LOG_ID_OUT_OF_MEM == 2`). – user694733 Aug 22 '14 at 10:57
  • @user694733 see new note – Suman Roy Aug 22 '14 at 11:11
  • @SumanRoy Saw your edit, but unfortunately I don't have anything new to add. I don't think you can have fixed integers without having a centralized table. – user694733 Aug 22 '14 at 14:32