tags:

views:

924

answers:

9

Hi, Instead of doing the following everytime

start();

// some code here

stop();

I would like to define some sort of macro which makes it possible to write like:

startstop()
{

//code here

}

Is it possible in C++?

+14  A: 

The idiomatic way of doing this in C++ is called Resource Acquisition Is Initialization, or shortly RAII. In addition to providing what you want, it also has the added benefit of being exception safe: the stop function will be called even if your code throws an exception.

Define a guard struct:

struct startstop_guard
{
    startstop_guard()
    {
        start();
    }
    ~startstop_guard()
    {
        stop();
    }
};

and then rewrite your code this way:

{
    startstop_guard g;
    // your code
}

The guard's destructor (and thus the stop function) will be called automatically at the end of the enclosing block.

avakar
+1. RAII! RAII! RAII! :)
j_random_hacker
Hey, no need for finally :)
Skurmedel
+1  A: 

What are you trying to do? I'd recommend checking out RAII as a much more C++ oriented way of doing things than macro hacking, with all its unforeseen consequences.

Pontus Gagge
+30  A: 

You can do something very close using a small C++ helper class.

class StartStopper {
public:
    StartStopper() { start(); }
    ~StartStopper() { stop(); }
};

Then in your code:

{
    StartStopper ss;
    // code here
}

When execution enters the block and constructs the ss variable, the start() function will be called. When execution leaves the block, the StartStopper destructor will be automatically called and will then call stop().

Greg Hewgill
+1. This approach is rock-solid. Even if an exception is thrown, stop() will still be called, because C++ guarantees that destructors are always called on scope exit.
j_random_hacker
Also it avoids sort of hard-to-debug problems that can occur with macros.
Andy Brice
+1. The conversation in dirkgently's response is intriguing, but this is the best solution because it does not create a "secret macro language."
John Dibling
Btw - with MSVC++ compiler I've seen it calling a destructor before the scope was complete. It somehow detected that the variable will not be used anymore and called the destructor immediately, although there were still more things left in the scope.
Vilx-
@Vilx: Interesting. I checked the standard and that is definitely non-conforming behaviour if the destructor has side effects (side-effect-free destruction is allowed earlier than scope exit by 3.7.2.3).
j_random_hacker
j_random_hacker: does side effects mean what I think it means here? like printing to cout, or assigning to a global? is this actually defined anywhere?
@Iraimbilanja: Yes, it means what you think it means. 1.9 para 6 says that reading/writing a volatile object or calling a library I/O function is "observable behaviour" (as is anything additionally specified by the implementation), and the compiler is obliged to perform these in the correct sequence (note that there are cases where more than 1 sequence is permitted).
j_random_hacker
@Iraimbilanja: Also para 7 says any modification of an object, reading of a volatile object, or call of an I/O function is a "side effect" and the compiler must produce observable behaviour "as if" (para 2) all side effects were completed by the next sequence point (meaning it technically could execute them in some other order, but not in such a way that anyone could ever tell).
j_random_hacker
+3  A: 
#define startstop(x, y, ...) for( /* use macro args */ )
dirkgently
+1, even if not that many people will understand the dependency injection there...
David Rodríguez - dribeas
-1 for incomplete and unclear implementation.
@Iraimbilanja: What is unclear and what is incomplete? The question does not specify anything about start() or stop().
dirkgently
The question specifies the signatures of start() and stop(), and requests a macro that makes the "startstop { ... }" syntax call start() at the opening brace and stop() at the closing brace. Your answer does not provide a working implementation of that. It is unclear what the /*use macro args*/ comment should be replaced with, and it's unclear how the macro arguments x and y are related to the question. FWIW it _is_ possible to make a working implementation with 'for' but you haven't done it.
you should mention that "..." syntax is c99 and not yet part of C++ (will be with c++1x)
Johannes Schaub - litb
@litb: I did not intend varargs. @Iraimbilanja: I don't think it is possible without getting to know what start() and stop() really is.
dirkgently
Well, I think it's safe to assume that both start() and stop() are functions taking no arguments and returning a discardable value (or none)... and that they are scoped such that you can call them unqualified, with syntax such as "start();" -- what more do you need?
@Iraimbilanja: What tells you it's a safe assumption? IMO, macros beget macros. So, if either of start() or stop() is a macro, the for loop will not exactly be sugar.
dirkgently
Well there is an implementation using `for` that works for macros too. I'll spare you the details but here's the general form: #define startstop for(pair<bool, RaiiStarter> p(true, RaiiStarter()); p.first; p.first = false)
similar to this for-each loop: http://stackoverflow.com/questions/400951/c-foreach-or-similar/400970#400970
Johannes Schaub - litb
the for-version could work without that mutable thingy. like for(RaiiStarter r; r; ) then you can do struct RaiiStarter { bool e; operator bool() const { return e=!e; } RaiiStarter():e(){ start(); } ~RaiiStarter() { stop(); } }; :)
Johannes Schaub - litb
yeah cool idea :) this is shaping up to be the nicest solution. maybe dirkgently could put it in his answer so it can get accepted.
When I saw the OP's code, my first impression was -- he wants some profiling done. Which is why I discounted any RAII stuff. But looks like you guys are having fun! Cheers.
dirkgently
+1  A: 

Don't use macros. You can use inline functions instead as it provides type checking and other features. You can take a look here: inline functions

Naveen
-1 inline functions are completely irrelevant.
+2  A: 

Generic solution with RAII and boost::function ( std::function ).

class starter
{
    typedef boost::function< void () > action;
    action end_;
public:
    starter(action start, action end):
        end_(end)
    { 
        log("starter start");
        start(); 
    }
    ~starter()
    { 
        log("starter end");
        end_() ; 
    }
};
int main()
{
    {
        starter s(start, stop);
        middle();
    }

    return 0;
}

or to test and check the idea

    void print(const std::string& message)
    {
        std::cout << message << std::endl;
    }
    int main()
    {
        starter s(boost::bind(print, "globalstart"),
                  boost::bind(print, "globalend")); 

        {
            starter s(boost::bind(print, "start"),
                      boost::bind(print, "end")); 
            std::cout << "middle" << std::endl;
        }
        return 0;
    }
Mykola Golubyev
+1 Nice implementation of scoped guards using boost::function, can be combined with dependency injection into a macro for a more natural usage.
David Rodríguez - dribeas
+2  A: 

Other answers have addressed the RAII side of the question well, so I'm going to address the syntax side of it.

#define startstop for(Starter s; s.loop; s.loop = false)
struct Starter {
    bool loop;
    Starter() { start(); loop = true; }
    ~Starter() { stop(); }
};

Used like:

startstop {
    // some code
}

Should be self-explanatory enough.

indeed, i wanted to write exactly that thing too. you could also do #define startstop() if(...) ... , which would have the benefit of not touching things like int startstop = 1; and so on. i.e a function-like macro would need parens to do substitution, which would be safer (and would also directly correspond to his wishes, and would additionally allow pushing arguments to Starter's ctor). but you still get +1 of course :)
Johannes Schaub - litb
Yeah good point.
one other thing: as of c++03, the compiler is allowed to take a copy of Starter() and bind the reference to that copy. that means, you would create two objects (one explicitly, and one created by the compiler for binding the reference to it). you would, similar to alexandrescu's scope_guard's implementation, need to introduce a mutable boolean variable which you set to false (rhs.bool_var) by a cctor and true in the default ctor, and this->bool_var by the cctor. only if it is true call end())
Johannes Schaub - litb
so something like (inverted the _guard meaning, because i think it makes more sense :)) struct Starter { mutable bool trash; Starter() : trash() { start(); } Starter(Starter const } ~Starter() { if(!trash) end(); } operator bool() const { return false; } }. now, end is called only once, for the one that was not copied (the one that was bound to the reference).
Johannes Schaub - litb
whoa does C++ allows a copy here? weird, why would it :) Thanks for the improvement. Now the code is sufficiently complicated that I'm compelled to write an "explanation" section :)
for details, consult this one: http://www.comeaucomputing.com/iso/cwg_defects.html#391 :) that is weird. i'm glad it will change with c++1x :)
Johannes Schaub - litb
.. and now with 200% less suck! thanks litb.
A: 

In c#, you could use the IDisposable pattern, and implement your Stop() functionality in the Dispose() method, but that would would work if you were using a .net variant of c++.

Jason Coyne
eh, aside from being off-topic, C++/CLI doesn't even have the using() construct of C#.
A: 

credit to dirkgently for the idea.. I thought I'd fill the rest in

#define startstop() for(start();isStarted();stop())