4

Is this even possible? I would like to write a macro that makes it easier to use some of my classes functionality.

Lets say I have 2 member functions in my class, setup() and cleanup(), where setup() sets up parameters for some operation that needs to be executed in its own scope, and cleanup() preforms cleanup (similar to a constructor and destructor concept).

Currently, I do this:

myClassInstance.setup(); //call the setup function
{ //start scope
    //CREATE LOCAL VARS
    //DO STUFF IN THIS SCOPE
    myClassInstance.cleanup(); //cleanup
} //end scope, destroy locals

But would like to do something like this instead:

NEWSCOPE(myClassInstance) //calls setup()
{
    //CREATE LOCAL VARS
    //DO STUFF IN THIS SCOPE
} // calls cleanup() and destroys locals

My thought was to write a macro class that can be instantiated when the macro is used and setup() and cleanup() could be implemented in the constructor/destructor... or something like that...

Is this the right way to think about this or is there another way to write a macro that can essentially wrap around code written by the user?

* EDIT * I fixed the naming convention as the function names were causing come confusion.

radensb
  • 664
  • 1
  • 7
  • 25
  • This kind of thing is done fairly often in OpenSceneGraph using just aaronman's answer – tamato Aug 14 '13 at 23:26
  • But I dont want to have to explicitly call the startScope() and endScope() functions and eliminate the possibility of the user forgetting the { } all together. – radensb Aug 14 '13 at 23:29
  • If all code was designed to prevent the possibility of a lazy programmer using a library incorrectly, this planet would grind to a halt. – paddy Aug 14 '13 at 23:48
  • @radensb take a look at my updated answer and if nothing clicks then I guess I'll just delete my answer – aaronman Aug 15 '13 at 01:06
  • I guess you are into making code difficult to read and maintain. This will hurt you or future employees in the long term – Ed Heal Nov 05 '15 at 20:37

4 Answers4

3

To create a new scope just use an anonymous block.

{ 
    Obj obj;
    /* 
    teh codez
    */
}//obj is deallocated

So you don't need a macro

It also sounds like you startScope and endScope should actually be constructor and destructor but once again it's hard to know without knowing what they actually do

UPDATE: I tried to give you an answer but instead I'll just rant.

similar to a constructor and destructor concept

To me that sounds like they are constructors and destructors, when you have the constructor and destructor doing the setup and cleanup the operations will be performed naturally and readably with RAII.

Another thing, you say your first solution (which I sort of accidentally gave back to you) is working, why workaround with a macro, in C macros were needed to simulate features (like templates, and objects) that C++ provides. For almost every situation, especially with C++11, macros will only make things worse and harder to debug, also in your case it seems like you actually have to type more when you do the macro?

My suggestion is rethink why you need to have a macro and why setup and cleanup can't be a constructor and destructor.

Alexey
  • 5,898
  • 9
  • 44
  • 81
aaronman
  • 18,343
  • 7
  • 63
  • 78
  • That is what I am doing already, but then I have to call both startScope and endScope() at the beginning and end. I am writing a library and wanted to make it as simple as possible to use. Using a Macro could force a new scope (i hope) and simplify the usage. – radensb Aug 14 '13 at 23:27
  • @radensb this does force a new scope, allocate the objects on the stack and they will be deallocated at the end of this scope, I don't see how you could hope for anything simpler than this – aaronman Aug 14 '13 at 23:29
  • @radensb it's possible I am misunderstanding your problem because I don't know what `startscope` and `endscope` functions do but the truth is macro's should not really be used in c++ – aaronman Aug 14 '13 at 23:34
  • @radensb Perhaps using the terminology 'scope' for your API is misleading. Maybe there's a better name, to avoid confusion with the normal meaning of 'scope'. – paddy Aug 14 '13 at 23:35
  • @paddy agreed I find it hard to believe that RAII can't easily solve this problem – aaronman Aug 14 '13 at 23:36
  • @aaronman Yes, you are correct, and if you look at my first example, that is exactly what I am doing. The issue is that I need to do some setup before the scope and cleanup after the scope, which is what the startScope() and endScope() functions do. After I add those functions, Im back where I started. – radensb Aug 14 '13 at 23:36
  • @radensb see my update and try to explain better what is happening in your code – aaronman Aug 14 '13 at 23:38
  • You're right...Bad names, I will fix that. Sorry guys. How about this instead: setup() and clean() instead of startScope() and endScope(). – radensb Aug 14 '13 at 23:39
  • "setup" *what*? "cleanup" *what*? Better names make more intuitive code. – paddy Aug 14 '13 at 23:43
  • 1
    This still seems like a job for RAII. "setup" and "cleanup" are pretty much exactly what a constructor and destructor are made for, respectively. Were your constructor and destructor actually doing their job, you could say like `{ MyClass myClassInstance; /* do your stuff here */ }`, and it'd all get cleaned up just as you're trying to do. – cHao Aug 14 '13 at 23:43
  • -1. This answer is so trivial that it should probably be a comment. It doesn't even attempt to provide a solution. – Potatoswatter Aug 15 '13 at 00:05
  • @Potatoswatter sorry I was going to improve it but the OP wasn't expressing what he wanted clearly so i left to go play tennis – aaronman Aug 15 '13 at 00:36
  • @cHao that's essentially what I've been trying to tell him the whole time but the OP refuses to elaborate why things have to work the way he has them and what any of the functions actually do – aaronman Aug 15 '13 at 00:49
  • @Potatoswatter I don't even get dignified with a response, at least tell me why my answer is still terrible – aaronman Aug 15 '13 at 06:51
  • Well, I never saw the update since it was posted after your reply comment. But it still doesn't really clearly answer anything. The downside to destructors is that throwing exceptions is fatal. His use case is essentially that of a ScopeGuard, which is what all the answers are clustering around. It's a very well-studied problem so there's no reason to be shooting in the dark. – Potatoswatter Aug 15 '13 at 07:00
  • @Potatoswatter I guess my point of view is that he is only assuming he needs scope guards, but doesn't actually need them – aaronman Aug 15 '13 at 07:06
  • @Potatoswatter also if he needs a scopeguard someone should have linked to this [article](http://www.drdobbs.com/cpp/generic-change-the-way-you-write-excepti/184403758?pgno=2) – aaronman Aug 15 '13 at 07:11
  • @Potatoswatter it could be made better with C++11 features but i'm sure it's still valid, I can't say I'm that familiar with scope guard features – aaronman Aug 15 '13 at 17:22
2

You might treat this in the same way as you would acquire a mutex lock with RAII. Something like this:

class MyClassScopeBlock
{
  public:
    MyClassScopeBlock( MyClass & c )
        : obj(c)
    {
        obj.startScope();
    }

    ~MyClassScopeBlock()
    {
        obj.endScope();
    }

  private:
    MyClass & obj;
};

Then instantiate that as a local variable inside a scope block:

{
    MyClassScopeBlock block( myClassInstance );
    //CREATE LOCAL VARS
    //DO STUFF IN THIS SCOPE
}

And if you really want, you can define a macro for it, to be used inside the scope block:

#define NEWSCOPE(inst) MyClassScopeBlock block(inst)

Personally, I prefer to stay away from macros whenever possible.

paddy
  • 60,864
  • 6
  • 61
  • 103
  • This is really close to what I was thinking about, but I would like to to look like my second example if possible. If not, this could work. I have been looking at macro tricks for a while and some of them are really ingenious. Im hoping that there is someway of doing this. – radensb Aug 14 '13 at 23:47
  • In that case, look at KugBuBu's answer. By all means, play with macros. What you will find is that the more you do so, the more you will want to avoid them. The RAII paradigm I've described is extremely common, and intuitive for people who know C++. At some point, you need to pass responsibility to the person using your code and stop trying to nanny them. =) – paddy Aug 14 '13 at 23:54
  • @paddy cmon KugBuBu's answer has a macro that its only purpose is to become an end bracket ( `}` ), if he accepts that I give up on stackoverflow – aaronman Aug 15 '13 at 01:10
2

I spent hours trying to figure out how to make a Macro control a scope after seeing the BOOST_FOREACH Macro. In the process of figuring it out I ran across this question hoping it held the answer! But, not quite. So, I read through all of the code for the BOOST_FOREACH and the original design for BOOST_FOREACH. Then I felt kind of dumb... A Macro essentially inserts the code directly where it is placed. This means that we can have a Macro:

#define LOOP_3() \
    for(int i = 0; i < 3; ++i)

Now, let us test it out!

LOOP_3() std::cout << "Hello World!" << std::endl;
/* === Output ===
Hello World!
Hello World!
Hello World!
*/

Yay! But, how is this useful? Well, at the end of the loop what happens to i? The destructor is called which for i is not too fancy, but the idea is there.

All we need now is a class to handle this:

class SCOPE_CONTROL {
public:
    SCOPE_CONTROL(): run(1) { std::cout << "Starting Scope!" << std::endl; }
    ~SCOPE_CONTROL() { std::cout << "Ending Scope!" << std::endl; }
    bool run;
}

Let us put that sucker to use!

#define NEWSCOPE() \
    for(SCOPE_CONTROL sc = SCOPE_CONTROL(); sc.run; sc.run = 0)

...
NEWSCOPE()
    std::cout << "    In the Body!" << std::endl;
std::cout << "Not in body..." << std::endl;
...

/* === Output ===
Starting Scope!
    In the Body!
Ending Scope!
Not in body...
*/

To use the setup and cleanup functions, just change a small bit!

class SCOPE_CONTROL {
public:
    SCOPE_CONTROL(MyClass myClassInstance): control(myClassInstance), run(1) { 
        control.setup();
    }
    ~SCOPE_CONTROL() { control.cleanup(); }
    bool run;
    MyClass & control;
}
#define NEWSCOPE(control) \
    for(SCOPE_CONTROL sc = SCOPE_CONTROL(control); sc.run; sc.run = 0)

...
NEWSCOPE(myClassInstance)
{
    // CREATE LOCAL VARS
    // DO STUFF IN THIS SCOPE
}   // end scope, destroy locals
...

To make it even better use the ENCODED_TYPE (how to make in the design for BOOST_FOREACH very simple!) to allow SCOPE_CONTROL to be a template type.

tkellehe
  • 659
  • 5
  • 11
  • The only problem I was unable to figure out is if there was a variable `sc` in the scope area it would throw an error because of the `sc` in the Macro. Also, the `sc` object can be edited in the scope. – tkellehe Nov 05 '15 at 20:32
0

A better alternative to putting the entire scope inside the macro replacement is to use something like a finally block. I've had success encapsulating the linked solution with these macros:

#define FINALLY_NAMED( NAME, ... ) auto && NAME = \
        util::finally( [&]() noexcept { __VA_ARGS__ } );
#define FINALLY( ... ) CPLUS_FINALLY_NAMED( guard, __VA_ARGS__ )
#define DO_FINALLY static_cast< void >( guard );

usage:

{
    myClassInstance.setup(); //call the setup function
    FINALLY ( myClassInstance.cleanup(); ) //call the cleanup function before exit

    // do something

    DO_FINALLY // Explicitly note that cleanup happens here. (Only a note.)
}

This is exception-safe, and cleanup executes if and only if setup completes successfully, just like a constructor/destructor pair. But the the cleanup must not throw exceptions.


But if you want to do it the old-fashioned way…

You can contain the entire scope inside the macro by using variadic macros:

#define NEWSCOPE( INSTANCE, ... ) { \
    (INSTANCE).setup(); /* call the setup function */ \
    { __VA_ARGS__ } /* paste teh codez here */ \
    (INSTANCE).cleanup(); /* call the cleanup function */

I would recommend against putting cleanup inside the internal scope because the point of a scope is to contain declarations and names, but you want to use the name of INSTANCE from the outer scope.

usage:

NEWSCOPE ( myClassInstance,
    // Do stuff.
    // Multiple declarations, anything can go here as if inside braces.
    // (But no #define directives. Down, boy.)
)
Community
  • 1
  • 1
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • I dont think this does what you think it does... Wouldn't the parameter put into the '...' be placed into __VA_ARGS__? I would need the body of code placed there, which is not what this does. – radensb Aug 15 '13 at 00:57
  • @radensb The entire body of code would be accepted into `...` and substituted into the `__VA_ARGS__`. The concept of individual parameters breaks down; really it's one parameter which may contain commas. (Otherwise commas not nested in parens cause problems.) This is actually the typical usage of variadic macros, as there is no macro facility for iterating over items. Anyway, I assure you I know what I'm doing, as I used this idiom to write my own preprocessor :) – Potatoswatter Aug 15 '13 at 01:08
  • @radensb ask and ye shall receive. But I pretty strongly recommend the first example. If nothing else having code inside a macro expansion will make your compiler jump through a hoop when formatting error messages. – Potatoswatter Aug 15 '13 at 01:14
  • Very cool. I had no idea that this was possible with macros. I am beginning too see why they are avoided in C++, but this is neat. Thanks for the suggestions. – radensb Aug 15 '13 at 16:35