9

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++?

Rolle
  • 2,900
  • 5
  • 36
  • 40

9 Answers9

41

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
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 12
    +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 Apr 17 '09 at 08:55
  • 1
    Also it avoids sort of hard-to-debug problems that can occur with macros. – Andy Brice Apr 17 '09 at 08:58
  • +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 Apr 17 '09 at 12:18
  • 1
    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- Apr 18 '09 at 09:21
  • @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 Apr 18 '09 at 16:02
  • 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? –  Apr 18 '09 at 17:26
  • @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 Apr 20 '09 at 12:29
  • @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 Apr 20 '09 at 12:34
14

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
  • 32,009
  • 9
  • 68
  • 103
7

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 Apr 17 '09 at 09:57
  • 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 Apr 17 '09 at 10:04
  • 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& rhs):trash(rhs.trash) { rhs.trash = true; } ~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 Apr 17 '09 at 10:11
  • 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 :) –  Apr 17 '09 at 10:23
  • 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 Apr 17 '09 at 10:26
4
#define startstop(x, y, ...) for( /* use macro args */ )
dirkgently
  • 108,024
  • 16
  • 131
  • 187
  • +1, even if not that many people will understand the dependency injection there... – David Rodríguez - dribeas Apr 17 '09 at 09:26
  • @Iraimbilanja: What is unclear and what is incomplete? The question does not specify anything about start() or stop(). – dirkgently Apr 17 '09 at 09:42
  • 2
    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. –  Apr 17 '09 at 09:51
  • you should mention that "..." syntax is c99 and not yet part of C++ (will be with c++1x) – Johannes Schaub - litb Apr 17 '09 at 09:59
  • @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 Apr 17 '09 at 10:05
  • 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? –  Apr 17 '09 at 10:13
  • @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 Apr 17 '09 at 10:24
  • 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 p(true, RaiiStarter()); p.first; p.first = false) –  Apr 17 '09 at 10:59
  • similar to this for-each loop: http://stackoverflow.com/questions/400951/c-foreach-or-similar/400970#400970 – Johannes Schaub - litb Apr 17 '09 at 11:45
  • 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 Apr 17 '09 at 11:55
  • 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. –  Apr 17 '09 at 11:59
  • 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 Apr 17 '09 at 15:03
2

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
  • 57,943
  • 15
  • 89
  • 102
1

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

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

user103391
  • 41
  • 4
1

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
  • 17,166
  • 1
  • 38
  • 51
1

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
  • 74,600
  • 47
  • 176
  • 233
-1

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
  • 6,509
  • 8
  • 40
  • 70
  • 1
    eh, aside from being off-topic, C++/CLI doesn't even have the using() construct of C#. –  Apr 18 '09 at 17:00