Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do I need to #undef a local #define? Is there such a thing as a local define?

Sometimes to make things easier to write and read, I write some local #define macros within functions (for example, #define O_REAL Ogre::Real).

Do I need to #undef the local #define to ensure that it stays within a certain block of code? Or does it automatically #undef when it goes out of scope? Does it even have a concept of scope?

I am unsure on how #define works in this case. Now, I have of course experimented with the code and reached certain conclusions but since I am unsure, I would like some expert opinion/advice.

like image 279
Samaursa Avatar asked Feb 20 '11 09:02

Samaursa


People also ask

Should I or do I need to?

I would say that "should" implies some sort of option, where it is more of a suggestion. Whereas "need to" implies necessity (obviously ;-) which means that any other option leaves something out.

Do I need or will I need?

They are both correct. The would one sounds less direct and is probably a good choice if you want to be somewhat polite. Show activity on this post. Would sounds conditional or hypothetical, so it should be used when expressing a possibility rather than an expected event.

Should I need to meaning?

The phrase should have to is also used in hypotheticals to mean "should find it necessary to"--in other words, it means "(the subject) is in a situation in which it is necessary to." Here's an example: If passengers should have to evacuate, lights leading to the emergency doors will be illuminated.

Will I have to or do I have to?

Both are correct depending on situation. If your meeting was ongoing and your presence was required (or not required) at that time "do I have to" could have been used. On the other hand if meeting was in future or question of your presence was for future then "will I have to" would have been used.


4 Answers

#define does not respect any C++ scope. There is no such thing as a "local" #define. It'll be in effect until it is #undef-ed. The preprocessor's macro mechanism is just like the "find-and-replace" functionality found in most text editors; it has no respect for the contents of the file.

In other words, if you want your #define to be local within a certain block of code, you must #undef it at the end of that block due to the fact that macros do not "understand" scope.

In fact, that's one of the biggest reasons why macros are discouraged unless they are absolutely necessary in C++. It's why macro names are usually typed in UPPER_CASE to indicate that it's in fact a macro.


There are actually quite a few macro-less solutions for your specific situation. Consider the following:

namespace ReallyLongOuterNamespace
{
    namespace ReallyLongInnerNamespace
    {
        class Foo {};
        void Bar() {}
    };
}

void DoThis()
{
    // Too much typing!
    ReallyLongOuterNamespace::ReallyLongInnerNamespace::Foo f;
    ReallyLongOuterNamespace::ReallyLongInnerNamespace::Bar();
}

You can use namespace aliases:

void DoThis()
{
    namespace rlin = ReallyLongOuterNamespace::ReallyLongInnerNamespace;

    rlin::Foo f;
    rlin::Bar();
}

You can also use typedefs:

void DoThis()
{
    typedef ReallyLongOuterNamespace::ReallyLongInnerNamespace::Foo MyFoo;

    MyFoo f;
}

You can also use using declarations:

void DoThis()
{
    using ReallyLongOuterNamespace::ReallyLongInnerNamespace::Foo;
    using ReallyLongOuterNamespace::ReallyLongInnerNamespace::Bar;

    Foo f;
    Bar();
}

You can even use a combination of the above!

void DoThis()
{
    namespace rlin = ReallyLongOuterNamespace::ReallyLongInnerNamespace;
    typedef rlin::Foo MyFoo;
    using rlin::Bar;

    MyFoo f;
    Bar();
}

With respect to Ogre::Real, it appears to be a typedef for a float or a double. You can still use namespace aliases, typedefs and using declarations with typedefs as well:

void UseOgre()
{
    typedef Ogre::Real o_Real; // Yes, you can typedef typedefs.
    using Ogre::Real;
    /* Or, you can use:
    namespace o = Ogre;
    typedef o::Real o_Real;
    using o::Real;
    */

    // All equivalent
    Ogre::Real r1;
    o_Real r2;
    Real r3;
    o::Real r4;
}
like image 80
In silico Avatar answered Sep 17 '22 23:09

In silico


The scope of a macro is part of compilation unit that comes after #define, up until the end of the unit (which is to say, until the end of .cpp file). However Visual C++ has a pair of #pragma push_macro/pop_macro that can be used in a case where macro definitions overlap. You can push previous definition, define your own, use it, and when you decide just pop previous definition.

like image 26
Dialecticus Avatar answered Sep 19 '22 23:09

Dialecticus


Unfortunately, #defines do not respect scoping rules. #defines that are not #undef'd will affect all code after them. Additionally, if code before you defines the same macro name, you'll have problems. In C++ you can usually avoid needing to use such local macros with local typedefs and references. For example, you could do:

void foo() {
    typedef Ogre::Real O_REAL;
    // ... 
}

This will respect scoping rules. For variables you can use references:

void foo() {
    int &BAR = Foo::quux::baz::static_bar;
    // ...
}
like image 23
bdonlan Avatar answered Sep 19 '22 23:09

bdonlan


#define is handled by the preprocessor, which doesn't actually know C syntax at all. A #define ignores scope, and will remain in effect until you #undef it or until the end of the compilation unit.

like image 21
mjfgates Avatar answered Sep 20 '22 23:09

mjfgates