1
\$\begingroup\$

I have been implementing a control software in C programming language. One of the software modules represents a communication table for some proprietary communication protocol. Each record in this table has one item (among others) consisting of 8 bits. This item represents properties associated to this record. Each record can has below given properties:

reported, event_logged, time_logged, archived, global

I need to define the communication table and for sake of readibility I don't want to fill the properties of each record with values 0-31. My idea was to at first define below given bit masks for individual bits in the properties byte:

#define REPORTED       0x10
#define EVENT_LOGGED   0x08
#define TIME_LOGGED    0x04
#define ARCHIVED       0x02
#define GLOBAL         0x01

Then I wanted to define a macro with parameters which will prepare the content of the properties based on human readable values

#define Create_properties(reported, event_logged, time_logged, archived, global){ \
  (uint8_t)reported | (uint8_t)event_logged | (uint8_t)time_logged |              \
  (uint8_t)global                                                                 \
}

The planned usage is following. For example one of the records in the communication table will be event logged and time logged so I will write the macro in this manner

Create_properties(0, EVENT_LOGGED, TIME_LOGGED, 0, 0, 0)

and the expected result is 01100. I have been facing a problem that I am not able to compile this source code. I always receive compiler error "braces around scalar initializer". I have been using the gcc compiler. Does anybody know why does this error occur? Thanks for any ideas.

\$\endgroup\$
  • 4
    \$\begingroup\$ In general, I would advise to ask pure programming questions like this on stackoverflow.com. Your question is on-topic here & just fine, but you'll get far better quality if you ask this to C gurus at SO. \$\endgroup\$ – Lundin Oct 22 '18 at 14:32
  • 2
    \$\begingroup\$ I'm voting to close this question as off-topic because this is pure programming, and best suited to a different exchange. \$\endgroup\$ – Scott Seidman Oct 22 '18 at 15:03
  • 1
    \$\begingroup\$ @Lundin Your comment proves that at least one of those C gurus have a presence at "our" site as well. ;) \$\endgroup\$ – pipe Oct 22 '18 at 15:18
1
\$\begingroup\$

Curly braces {} are not used for macro definitions. Try removing them and see if it works:

#define Create_properties(reported, event_logged, time_logged, archived, global) \
  (uint8_t)reported | (uint8_t)event_logged | (uint8_t)time_logged |              \
  (uint8_t)global    
\$\endgroup\$
  • \$\begingroup\$ It works without curly brackets. Thank you, Justin. \$\endgroup\$ – Steve Oct 22 '18 at 14:05
  • \$\begingroup\$ @Steve Then be sure to select the answer (the check button) so that future searches will reveal an answered question. \$\endgroup\$ – a concerned citizen Oct 22 '18 at 14:10
  • 2
    \$\begingroup\$ This is a dangerously-written macro. As a bare minimum, you need to surround it with parenthesis. The casts are superfluous and can be removed, or you can cast the end result to the intended type. \$\endgroup\$ – Lundin Oct 22 '18 at 14:29
5
\$\begingroup\$

Since these are only 8 bit numbers, you could safely use an enum. Pick some code prefix that makes sense - I don't know what your code is supposed to do so I used STUFF_ as place-holder, to be replaced by a better name.

enum
{
  STUFF_REPORTED     = 0x10,
  STUFF_EVENT_LOGGED = 0x08,
  STUFF_TIME_LOGGED  = 0x04,
  STUFF_ARCHIVED     = 0x02,
  STUFF_GLOBAL       = 0x01,
};

Then simply create a macro for each case:

#define STUFF_EVENT_TIME_LOGGED ( (uint8_t)(STUFF_EVENT_LOGGED | STUFF_TIME_LOGGED) )

Making a function-like macro such as Create_properties gives no benefit, since it might be confused for a function and will have zero type-safety, making it hard to read and dangerous both. So I wouldn't recommended it, function-like macros should be avoided if possible. But of course it can be done too:

#define Create_properties(reported, event_logged, time_logged, archived, global) \
  (uint8_t) ((reported) | (event_logged) | (time_logged) | (archived) | (global))

Notably, your casts to uint8_t were completely pointless, because each operand of | will get integer promoted to int anyway. If you should cast to something before the | takes place, it would need to be uint32_. Or you can just cast to uint8_t after the operation, as in my macro above.
See Implicit type promotion rules to learn more.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.