0

Here's a riddle.

Imagine I have the following C++ function:

template<uint8_t MASK>
uint8_t Foo(uint8_t val)
{
    uint8_t step = 0;
    uint8_t result = 0;
    if(MASK & 0x01) {result |= (val & 0x01) >> step; ++step;}
    if(MASK & 0x02) {result |= (val & 0x02) >> step; ++step;}
    //...etc...
    if(MASK & 0x80) {result |= (val & 0x80) >> step; ++step;}

    return result;
}

When I instantiate this function as such (all values below are just examples values):

uint8_t someval = Foo<0xAA>(44);

The compiler optimizes out the if statements in Foo() because it knows at compile time what the result of said if() statement are.

This is nice and well, but trying to do the same in C is problematic because of the creation of the local variable step.

If step wasn't there, you could do a large #define like this:

#define Foo(MASK, val) (\
    ((MASK & 0x01) ? (val & 0x01) : 0) | \
    ((MASK & 0x02) ? (val & 0x02) : 0) | \
    ...etc...
    ((MASK & 0x80) ? (val & 0x80) : 0) | \
    )

But with step, I'm kind of at an impasse. What can I do to obtain the same functionality of C++ template in C for C++ template function with local variables?

Note that using inline C functions is not an answer, as the compiler will not know at compile time the value of MASK, and thus all the comparisons will not be optimized and will accordingly be part of the final compiled output.

Also note that changing the #define to include the result value is also not an answer, since this changes the "function's" signature.

And lastly, I am fully aware that there may be no answer to this riddle.

11
  • One does not instantiate functions. Commented Aug 17, 2012 at 19:48
  • Why macro instead of function ? Also i think in C you can use { } code block - there you can use variables just like in a function. Commented Aug 17, 2012 at 19:50
  • If the compiler knows the value of MASK at compile time in C++ (it has to if it's a template parameter), why doesn't it in C? Commented Aug 17, 2012 at 19:50
  • Also, couldn't you just pass it as a parameter? a la Foo(0xAA, 44) Commented Aug 17, 2012 at 19:51
  • 1
    Of course the compiler knows the value of Foo(0xAA, 44). Commented Aug 17, 2012 at 19:51

3 Answers 3

5

Have your macro try to do what the template does; create an inline function--

#define Foo(MASK, val) inline uint8_t Foo_##MASK(uint8_t val) \
{ \
   uint8_t step = 0; \
    uint8_t result = 0; \
    if(MASK & 0x01) {result |= (val & 0x01) >> step; ++step;} \
    if(MASK & 0x02) {result |= (val & 0x02) >> step; ++step;} \
    //...etc...
    if(MASK & 0x80) {result |= (val & 0x80) >> step; ++step;} \
\
    return result;\
}
Sign up to request clarification or add additional context in comments.

6 Comments

Nice one! (Although for some reason OP wanted something which would change the macro signature.)
Of course! Hind sight is always 20/20 I suppose. Thanks for the great suggestion!
Actually, I'm not sure that this would work. If you have the following: int main(){uint8_t x = Foo(0xAA, 44); return 0;} you will generate a compiler error because the function body will be defined in main, when in fact the routine needs to be defined outside.
@TRISAbits: you could split into two macros, one #define InstantiateFoo(MASK) inline uint8_t Foo_##MASK etc, which defines the function and you call it at file scope and a second #define Foo(MASK, val) Foo_##MASK(val) to call the appropriately defined function. You have an extra line to remember to put in manually but at least there will be a linker error if you forget it.
Yes you could, but it's not the same as using the C++ template version. Plus, you have to make sure that InstantiateFoo(MASK) is only used once, multiple invocations with the same MASK value will result in a compiler error. This doesn't sound like much of a limitation, but since MASK is a user generated value and could be used in multiple modules across the software, the possibility exists. Some form of inclusion guards for InstantiateFoo(MASK) could ensure that multiple calls to InstantiateFoo(MASK) do not cause compiler errors, but I'm not sure how this would work. Is there a way?
|
2

How about (GCCism):

#define Foo(MASK, val) ({ \
    uint8_t step = 0; \
    uint8_t result = 0; \
    if(MASK & 0x01) {result |= (val & 0x01) >> step; ++step;} \
    if(MASK & 0x02) {result |= (val & 0x02) >> step; ++step;} \
    //...etc...
    if(MASK & 0x80) {result |= (val & 0x80) >> step; ++step;} \
    result;})

3 Comments

Note that this is a non-standard GCC extension called a statement expression.
This is a good idea, but not all compilers support this feature. I'm also not sure if this mechanism is part of ANSI C (I'm sure I'll be corrected on the matter shortly).
@TRISAbits, no, it's not and no, not all will. That's what GCCism means ;)
1

You are just making a false assumption

Note that using inline C functions is not an answer, as the compiler will not know at compile time the value of MASK, and thus all the comparisons will not be optimized and will accordingly be part of the final compiled output.

I just tested, gcc is able to inline everything without problem:

static inline
unsigned Foo(unsigned MASK, unsigned char val)
{
    unsigned char step = 0;
    unsigned char result = 0;
    if(MASK & 0x01) {result |= (val & 0x01) >> step; ++step;}
    if(MASK & 0x02) {result |= (val & 0x02) >> step; ++step;}
    //...etc...
    if(MASK & 0x80) {result |= (val & 0x80) >> step; ++step;}

    return result;
}

int main(int argc, char *argv[]) {
  return Foo(0x02, argc);

}

results in the following assembler for main:

main:
.LFB1:
    .cfi_startproc
    andl    $2, %edi
    movzbl  %dil, %eax
    ret
    .cfi_endproc
.LFE1:
    .size   main, .-main

and clang does functionally the same.

1 Comment

Interesting. I do assume too much. Thanks for the inventigation! Too bad I can't use GCC on the target I am programming, since my compiler does not produce the same excellent out, even after I increase the inline code size threshold (the threshold where the compiler changes the inline function into a normal routine).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.