views:

212

answers:

2

Hi all,

I have a code that runs on an embedded system and it has to run really fast. I know C and macros, and this particular project is coded mostly in C but it also uses C++ templates [increasingly more]. There is an inline function:

inline my_t read_memory(uint32 addr) {
  #if (CURRENT_STATE & OPTIMIZE_BITMAP)
    return readOptimized(addr);
  #else
    return MEMORY[addr];
  #endif
}

This function reads from memory in an optimized fashion or conventional fashion based on the current state and a bitmap that tells whether to use optimization in a specific state or not.

#define STATE_A 0x0001
#define STATE_B 0x0010
#define STATE_C 0x0100
#define STATE_D 0x1000

#define OPTIMIZE_BITMAP 0x1010 // optimize states d and b

and during the course of execution (ok, compilation), I tried to redefine CURRENT_STATE like so:

int main(){
  #define CURRENT_STATE STATE_A
  do_a();
  #undef CURRENT_STATE
  #define CURRENT_STATE STATE_B
  do_b();
  ....
}

All do_X() functions make read_memory() calls. I could not make this approach work. The value of current state is always STATE_A as I can see when I use #warning statements. This is not my question, although if you can help me with this I'll be double happy. So, my question is, is there a way to do this kind of thing using templates instead of macros?

Some more info: I have to use an inline function because I cannot export MEMORY[] and this is a library function. I really prefer not to modify the function prototype (like read_memory()...) but it will do. Also, pardon my obscurity.

many thanks,

+4  A: 

I think you may be misunderstanding what the compiler (or rather, the preprocessor) does with #defines.

Your example (quoted below) is not useful, because CURRENT_STATE is not used between the #define and the #undef. The preprocessor isn't 'executing' your code, or expanding do_a() inline at this point. #defines and macro expansion can only occur EXACTLY IN THE ORDER OF THE LINES IN YOUR SOURCE.

  #define CURRENT_STATE STATE_A
  do_a();
  #undef CURRENT_STATE

Here's a preprocessor-based solution, if templates fill you with horror. Let's assume that do_a() should use the optimised version.

inline my_t read_memory(uint32 addr) 
{
  return MEMORY[addr];
}

inline my_t read_memory_optimized(uint32 addr) 
{
  return readOptimized(addr);
}

Now, create DO_CONFIG.H

#if defined(DO_A) || (defined(DO_C) || ...)
  #define READ_MEMORY read_memory_optimized
#else
  #define READ_MEMORY read_memory

In DO_A.C, add this at the top

#define DO_A
#include DO_CONFIG.H

...and use x=READ_MEMORY(addr) instead of x=read_memory(addr). To switch from optimised to non-optimised, just change DO_CONFIG.H

Roddy
I'm dumb obviously, thanks
perreal
@perreal. You're asking questions here. That's SMART!
Roddy
so, maybe something like#define CURRENT_STATE STATE_A#include "usr_do_a.c"do_a();#undef CURRENT_STATE...will work? lot's of files...
perreal
@per: Please don't, that will get quite ugly. Can't the `do_X()` functions be templated instead? That would be much cleaner.
Georg Fritzsche
@perreal - that still won't work unless read_memory() is defined as static in each of your do_x.c files
Roddy
@Roddy, @Georg: I see your point, my point being if user knows that he has to call different versions according to his needs then he might as well explicitly call a different version. My intension is to make it totally transparent (yes the bitmap is there but it's different).
perreal
+9  A: 

The inline function will be parsed once, at the point in the translation unit where it is declared, and the state of the macros at that point will be used. Calling the function multiple times with the macros defined differently will not change the definition of the function.

You can do this with a template though --- if you pass the "current state" as a template parameter then you can use a different instantiation at each call point:

template<unsigned state>
inline my_t read_memory(uint32 addr) {
  if(state & OPTIMIZE_BITMAP)
    return readOptimized(addr);
  else
    return MEMORY[addr];
}

int main(){
    read_memory<STATE_A>(some_addr);
    read_memory<STATE_B>(some_addr);
    ....
}

The compiler will realise that state & OPTIMIZE_BITMAP is a constant and optimize out one or other branch of the if for each template instantiation.

Anthony Williams
+1, Definitely cleaner looking than specialization :)
Georg Fritzsche
And since `state` and `OPTIMIZE_BITMAP` are constants, it's very probably the compiler will eliminate the test and the deadcode altogether, thus achieving the same effect.
Matthieu M.