Reputation: 694
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.
Upvotes: 4
Views: 2795
Reputation: 679
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.
Upvotes: 2
Reputation: 18751
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.
Upvotes: 3
Reputation: 137930
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.)
)
Upvotes: 0
Reputation: 63481
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.
Upvotes: 2