What Are Some Tricks I Can Use with MACros

What are some tricks I can use with macros?

In C, it's common to define macros that do some stuff getting the verbatim argument, and at the same time define functions to be able to get the address of it transparently.

// could evaluate at compile time if __builtin_sin gets
// special treatment by the compiler
#define sin(x) __builtin_sin(x)

// parentheses avoid substitution by the macro
double (sin)(double arg) {
return sin(arg); // uses the macro
}

int main() {
// uses the macro
printf("%f\n", sin(3.14));

// uses the function
double (*x)(double) = &sin;

// uses the function
printf("%f\n", (sin)(3.14));
}

What can you do with macros that can't be done with procedures?

Macros are a tricky subject that I've personally reversed my own opinions on no less than a dozen times, so take everything with a grain of salt.

If you are just getting acquainted with macros, you'll find the most helpful to be those macros that clarify or expedite existing expressions.

One such macro that you may have been exposed to is the anaphoric macro, which remains as popular today as the day Paul Graham coined the term:

(define-syntax (aif x)
(syntax-case x ()
[(src-aif test then else)
(syntax-case (datum->syntax-object (syntax src-aif) '_) ()
[_ (syntax (let ([_ test]) (if (and _ (not (null? _))) then else)))])]))

These macros introduce "anaphora" variables, namely it that you can use in the consequent and alternative clauses of an if statement, e.g:

(aif (+ 2 7)
(format nil "~A does not equal NIL." it)
(format nil "~A does equal NIL." it))

This saves you the hassle of typing a let statement -- which can add up in large projects.

More broadly, transformations that change the structure of a program, dynamically generate "templated" code or otherwise "break" rules (that you hopefully know well enough to break!) are the traditional domain of the macro. I could write on and on about how I've simplified countless projects and assignments with macros -- but I think you'll get the idea.

Learning Scheme-style macros is a little daunting, but I assure you that there are excellent guides to syntax-rules-style macros for the merely eccentric, and as you gain more and more experience with macros, you'll probably come to the conclusion that they are a beautiful idea, worthy of the name "Scheme".

If you happen to use Racket (previously known as PLT Scheme) -- It has excellent documentation on macros, even providing a few neat tricks along the way (I'd also guess most of what is written there can be readily written in another Scheme dialect without issue)

What are C macros useful for?

I end up having to remember what the macro is and substitute it in my head as I read.

That seems to reflect poorly on the naming of the macros. I would assume you wouldn't have to emulate the preprocessor if it were a log_function_entry() macro.

The ones that I have encountered that were intuitive and easy to understand were always like little mini functions, so I always wondered why they weren't just functions.

Usually they should be, unless they need to operate on generic parameters.

#define max(a,b) ((a)<(b)?(b):(a))

will work on any type with an < operator.

More that just functions, macros let you perform operations using the symbols in the source file. That means you can create a new variable name, or reference the source file and line number the macro is on.

In C99, macros also allow you to call variadic functions such as printf

#define log_message(guard,format,...) \
if (guard) printf("%s:%d: " format "\n", __FILE__, __LINE__,__VA_ARGS_);

log_message( foo == 7, "x %d", x)

In which the format works like printf. If the guard is true, it outputs the message along with the file and line number that printed the message. If it was a function call, it would not know the file and line you called it from, and using a vaprintf would be a bit more work.

Suggestions for learning about macros in C

You can check out the following links:

  • http://www.delorie.com/gnu/docs/gcc/cpp_16.html
  • http://www.cprogramming.com/tutorial/cpreprocessor.html

Most of the tricks you'll have to pick up on your own. Maybe if you can post some of the macros you don't understand, we can help you out with those.

Can I implement this kind of profiling code with a macro?

I am assuming you do not want to have any dynamic memory management involved? Because otherwise you could simply use a std::vector and do a push_back() for each result...

Otherwise, I do not think this can be achieved easily by just using standard language elements. But MSVC, clang and gcc support __COUNTER__, which is a special macro that is incremented in each use and that can be exploited here. Storing the initial value before the function, then using it in every "LAP", you can compute the number of laps within the function. Moreover, you can declare the result array without needing to specify the first dimension before the function if you use a C-array via extern, and then define it afterwards with the now known number of laps.
You can also simply store the __LINE__ result at the same time when you store the __rdtscp() result.
See the following example. It is all quite fragile and assumes that the macros are used in that order, but depending on the actual code, it might be sufficient (https://godbolt.org/z/crrGY7n4P):

#include <array>
#include <cstdint>
#include <iostream>

#ifndef _MSC_VER
// https://code-examples.net/en/q/e19526
uint64_t __rdtscp( uint32_t * aux )
{
uint64_t rax,rdx;
asm volatile ( "rdtscp\n" : "=a" (rax), "=d" (rdx), "=c" (*aux) : : );
return (rdx << 32) + rax;
}
#else
#include <intrin.h>
#endif

constexpr std::size_t MAX_TRIALS = 3;

struct ResultElem
{
uint64_t timing;
unsigned line;
};

void process_timestamps(ResultElem results[][MAX_TRIALS], std::size_t numResult, char const * const func)
{
std::cout << func << ": Num = " << numResult << std::endl;
for (std::size_t trial = 0; trial < MAX_TRIALS; ++trial) {
std::cout << "\tTrial=" << trial << std::endl;
for (std::size_t i = 0; i < numResult; ++i) {
std::cout << "\t\tLine=" << results[i][trial].line << ", time=" << results[i][trial].timing << std::endl;
}
}
}

#define STOPWATCH_BOILERPLATE_PRE(f) \
extern ResultElem results_ ## f[][MAX_TRIALS]; \
constexpr std::size_t counterStart_ ## f = __COUNTER__; \
std::size_t trial_ ## f = 0;

#define STOPWATCH_BEGIN(f) uint32_t core; STOPWATCH_LAP(f)

#define STOPWATCH_LAP(f) results_ ## f[__COUNTER__ - counterStart_ ## f - 1][trial_ ## f] = {__rdtscp(&core), __LINE__}

#define STOPWATCH_END(f) \
STOPWATCH_LAP(f); \
if(++trial_ ## f == MAX_TRIALS) { \
process_timestamps(results_ ## f, __COUNTER__ - counterStart_ ## f - 1, #f); \
trial_ ## f = 0; \
}

// Needs to be used directly after STOPWATCH_END() because we subtract 2 from __COUNTER__.
#define STOPWATCH_BOILERPLATE_POST(f) \
constexpr std::size_t numResult_ ## f = __COUNTER__ - counterStart_ ## f - 2; \
ResultElem results_ ## f[numResult_ ## f][MAX_TRIALS];

STOPWATCH_BOILERPLATE_PRE(f)

void f()
{
STOPWATCH_BEGIN(f);

// do stuff

STOPWATCH_LAP(f);

// do some more stuff

STOPWATCH_LAP(f);

// do even more stuff

STOPWATCH_END(f);
}

STOPWATCH_BOILERPLATE_POST(f)

Alternatives I could think of:

  • Without dynamic allocations and staying in the standard, you might build something using BOOST_PP_COUNTER. The STOPWATCH_LAP would then probably turn into some form of #include statement.

  • I could also imagine that it might be possible to build something without macros using a weird loophole in C++14, but that gets terribly complicated.

Variadic macro trick

This post Variadic macro to count number of arguments has what you're looking for I believe. Look at the first and second responses.

Any trick for simpler definition of multiline C macros?

Not really recommended in this case, but you can do something like what you want to achieve with X-macros:

#define SUPPORTED_TYPES \
X(int) \
X(double) \
X(char)

#define X(TYPE) \
typedef struct { \
TYPE *head; \
TYPE *tail; \
size_t capacity; \
} Fifo_##TYPE, *pFifo_##TYPE;
SUPPORTED_TYPES
#undef X

#define X(TYPE) \
inline Fifo_##TYPE * Fifo_##TYPE##_init(size_t capacity) \
{ \
Fifo_##TYPE * fifo = calloc(1, sizeof(Fifo_##TYPE)); \
TYPE * data = calloc(capacity, sizeof(TYPE)); \
fifo->head = data; \
fifo->tail = data; \
fifo->capacity = capacity; \
}
SUPPORTED_TYPES
#undef X

But this didn't really improve the situation all that much. It got rid of the need for a single, ugly Fifo_define macro, so you can split up the code in several sections. But the macro mess remains.


I would recommend some completely different approach. Two suggestions:

  • Handle the type-generic things in the classic C way, in run-time. Use callbacks. Keep track of the used type with an enum, if needed.

  • C11 _Generic allows all kinds of type safety tricks and can be used to phase out such messy macros. Example that implements "functors". The macro itself is kept minimal and the different implementations for various types is typed out. (That's usually what you end up doing anyway, when you do type-generic programming.)

define / use a Macro inside a fcuntion - good style/practice?

Alternate solution:

#include <stdbool.h>

void DeviceOn(void)
{
uint8_t DIOPort = ReadDIOPort();

bool PowerIsOK = DIOPort & 1 << SENSOR1;
bool SafetyIsOK = ! (DIOPort & 1 << SENSOR2);
bool LightIsOn = DIOPort & 1 << SENSOR3;
bool CoffeeMugIsFull = DIOPort & 1 << SENSOR4;

if (PowerISOK && SafetyIsOK && LightIsOn && CoffeeMugIsFull)
turnOnDevice();
else
turnOffDevice();
}


Related Topics



Leave a reply



Submit