Defining function using a Macro?

Started by
6 comments, last by a light breeze 4 years, 6 months ago

Hi. I have no idea what's going on here.
I understand that macros are used to paste code where the macro is located, and that they can take parameters in their use, but this exceeds me.

What I understand is that using macros first sets the header or signature of a function (GetTransitionTable), and inside, defines a static variable TRANSITION_TABLE, which is an array. With the following macro, TRANSITION_ENTRY, fill in the array, I suppose doing an implicit construction of objects, and converting the parameters into the objects expected by the potential object of each element of the array. Finally add a null element at the end.

Then, in the use of macros, pass the states they expect to fill in the array.

Do you basically use the macro to save writing much more repeated text than you would have to write if you didn't use it?

Is this use correct? In The C ++ Programming Language, Bjarne Stroustrup, apart from practically not addressing the issue, seems very contrary to the use of macros.

Thanks.

 

06483c5f8ec5f2cd99df8fc91b6c8a47.png

Used as:

0d4ede23cfb8d4a28dc604370ee356f6.png

Advertisement

I don't know where you got that piece of code but that kind of macro meta-programming just horrifies me.

Macros aren't even part of the language, that's why Stroustrup don't want anything to do with it.

[Prototype replied while I was writing this, but I'll go ahead and post it just in case you find something here useful ?]

Some IDEs that support C/C++ include a feature that allows you to examine the preprocessor output. If you have such a feature available, you might find it informative to examine the preprocessor output for your 'Used as' example. I think your description of what's going on is probably accurate or close to it, but you might find actually seeing the preprocessor output to be illuminating.

Quote

Do you basically use the macro to save writing much more repeated text than you would have to write if you didn't use it?

I would guess that's the motivation in this particular case, yes.

Quote

Is this use correct? In The C ++ Programming Language, Bjarne Stroustrup, apart from practically not addressing the issue, seems very contrary to the use of macros.

Preprocessor macros have some disadvantages and associated pitfalls that are discussed frequently. For example, they don't obey scope, which can cause issues in some circumstances.

My casual observation is that over time the conventional wisdom has increasingly become to favor non-preprocessor features over using the preprocessor when it's possible or at least reasonable to do so. This has become easier to do as new features have been added to C/C++. That doesn't necessarily mean you should never use macros - just that other options should arguably be considered first.

There may be cases where for whatever reason (e.g. there being syntactical elements involved, as in your example) it's not obvious how to reduce boilerplate without using the preprocessor. As for the particular example you posted, without knowing more about the code I can't really say if there are better solutions (although some might argue just writing it out manually each time would be preferable). Looking at the code, some possible alternative solutions spring to mind, but without more context I can't say if they'd actually work or not.

The last thing I'll say is:

Quote

What I understand is that using macros first sets the header or signature of a function (GetTransitionTable), and inside, defines a static variable TRANSITION_TABLE, which is an array. With the following macro, TRANSITION_ENTRY, fill in the array, I suppose doing an implicit construction of objects, and converting the parameters into the objects expected by the potential object of each element of the array. Finally add a null element at the end.

You may already understand this, but just for clarity I'll point out that the macro doesn't really 'do' anything itself. That is, it's not defining or constructing or converting arrays or object or anything like that. All the preprocessor does (to the best of my knowledge at least) is manipulate text.

I'm less bothered by the use of macros here as by the bad practices in the generated code.  Macros are a useful tool of last resort for eliminating redundancies in code, but the sizeof(ARRAY)/sizeof(ARRAY[0]) trick for finding the length of an array has no place in C++.

4 hours ago, a light breeze said:

... the sizeof(ARRAY)/sizeof(ARRAY[0]) trick for finding the length of an array has no place in C++.

Maybe this is off-topic, but I'm curious as to what the alternative would be? I'm not aware of any standard language feature that has replaced this particular trick.

Arguably in the original it seems weird to have both a NULL_TRANSITION array entry and a count of the number of elements, but for a statically sized array what is a better option is there for having a compile-time constant for a compile-time array?

The only one that I can think of would be never use static arrays and only ever use std::vector (or whatever dynamic array is available in your engine, like TArray in UE or whatever vector replacement EA STL has) and that just seems misguided. While I usually use the dynamic array that I have available, there are cases where that's just overkill or doesn't meet the compile-time requirements required.

I've mostly used this in combination with static_assert to validate (at compile time) that some static array size matches the length of an enumeration.

The old static assert trick was to define an array that had size 0 or 1 based on a compile-time boolean condition. If the condition was false, the array would be sized to 0 which is illegal and caused a compile error. That trick no longer has a place in modern C++ precisely because static_assert is now a language feature that directly provides the feature.

--Russell Aasland
--Lead Gameplay Engineer
--Firaxis Games

Something I didn't take note of previously and therefore didn't mention in my earlier comment is the SET_STATE macro. The other macros seem to at least have an ostensible purpose - to reduce repeated boilerplate code (although as I mentioned earlier there may be other solutions for that as well).

SET_STATE, on the other hand, just seems to be an old-school 'macro in place of a function'. From what you posted it's not immediately obvious (to me at least) why a macro would be preferred over a function for setting the state, so I'm a little skeptical of that particular usage of the preprocessor. At first look at least, that seems like the sort of thing one is usually advised not to use the preprocessor for in modern C/C++.

4 hours ago, MagForceSeven said:

Maybe this is off-topic, but I'm curious as to what the alternative would be? I'm not aware of any standard language feature that has replaced this particular trick.


template<class T, std::size_t N> constexpr std::size_t array_size(T(&)[N]) noexcept {
  return N;
}

Without the constexpr and noexcept, this has worked for as long as C++ has had templates.

This topic is closed to new replies.

Advertisement