0

I have this code for some proprietary logger:

#define LOG GetStream()

Where GetStream returns an std::ostream. User will do:

LOG << "text";

I need this to be thread safe but would like to avoid this:

#define END Unlock();
#define LOG Lock(); GetStream() << "text" << END;

Since user will need to add the "END":

LOG << "Text" << END;

Any ideas?

Remark: I handle the carriage return using something like this.

Community
  • 1
  • 1
grunt
  • 662
  • 1
  • 8
  • 24

3 Answers3

2

One way to solve this is to use function-style macros, where you incorporate the locking/unlocking by using a C++ block and scoping:

#define LOG(output)               \
    do                            \
    {                             \
        LockingClass lock;        \
        GetStream() << output;    \
    } while (0)

The LockingClass (or whatever you want to name it) is a scoped lock, which locks the stream on construction, and unlocks it in on destruction.

Could be used like e.g.

LOG("hello variable is " << variable);

Can't use it with expressions containing comma though, the preprocessor will interpret commas as argument separator for the macro. Could probably be solved with variadic macros.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
1

Just though of this:

#define LOG for (int i = 0 ;i < 1 ;i++,(i == 1 ? Unlock())) LockAndGetStream()
grunt
  • 662
  • 1
  • 8
  • 24
  • 1
    Although the code looks so hideous, this does exactly what grunt asked for. +1 And then I realized its himself. – user2883715 Jun 25 '15 at 13:10
0

TL;TD: you can't make the whole x<<text<<end work only by macros.

in order to put many expressions with a semicolon (;) with a macro , the only valid way is using the do{..} while(0) statement , you can read about it more here : do { ... } while (0) — what is it good for?

now, all good until you want to use some operators like <<, then you can't use the do{..} while(0) costruct.

my suggestion : wrap the std::ostream in a class where you overload the << operator. inside the overload , call Lock and Unlock.

struct MyLogger{
  std::ostream& operator << (Text text){
    Lock();
    stream <<text;
    Unlock();
    return stream;
  }
}

where stream is the stream you stream the text into.

now, you can create an object from this class and use regular, non-macro <<:

NyLogger Log;
Log << text;
Community
  • 1
  • 1
David Haim
  • 25,446
  • 3
  • 44
  • 78
  • I edited this, to correct for too many `;`. However, I think this is still incorrect to say that `<<` won't work with `do{ ... } while(0)`. Why do you say this? Joachim's answer works well with `do{ ... } while(0)` – Aaron McDaid Jun 25 '15 at 12:56
  • 1
    he uses `<<` inside the do.. while. he doesn't take some expression with already `<<` and make it work with macro. he basically works with X(Y) macro, not X< – David Haim Jun 25 '15 at 12:59
  • Ah yes I see. The questioner wants to be able to use the "macro" as `LOG << stuff;`, but we can only implement `LOG(stuff);` within macros. Agreed. Yours is the only solution I think that allows `LOG << stuff;` (where `stuff` is of type `Text`) – Aaron McDaid Jun 25 '15 at 13:09
  • May I suggest a template, so that the argument can be of any type? Just change it to `template operator << (T&& text)` (Or `const T&` if pre-C++11) – Aaron McDaid Jun 25 '15 at 13:12