JournalStream.hh 24 KB
Newer Older
1
// Copyright 2020 Autodesk, Inc. All rights reserved.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//
// This computer source code and related instructions and comments are the
// unpublished confidential and proprietary information of Autodesk, Inc.
// and are protected under applicable copyright and trade secret law. They
// may not be disclosed to, copied or used by any third party without the
// prior written consent of Autodesk, Inc.

#ifndef BASE_JOURNAL_STREAM_HH_INCLUDED
#define BASE_JOURNAL_STREAM_HH_INCLUDED

#ifdef JOURNAL_ON 

#include <Base/Utils/IOutputStream.hh>

16
#include <limits>
17
18
#include <string>
#include <vector>
19
#include <type_traits>
20

21
22
namespace Journal 
{
23
24
typedef std::string String;

25
using Base::ENDL;
26
27
using Base::IOutputStream;

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*
Represent a handle, which is a generalization of a pointer and an index.

Handles are used to uniquely identify an object or pseudo-object that you wish
to track in your journal. If you have an object with a stable memory address (a
pointer), then you don't need this--use the key() function directly instead.

Examples of a handle are an element in a vector, a POSIX file handle, or a
socket. In the case of file handles or sockets, these values are "actually"
integers, but should not be journaled as integers, because the value may be
different when the journal is run; using the hard-coded integer value will
result in a non-functioning journal. In the case of vector elements, your
item has a stable base pointer and a stable index, but its actual address is
unstable, and the index value may be unstable the next time the journal is
run; tracking it as a handle solves this problem as well.

You would then pass the Handle to key() to generate a
Key, and that Key can be used for define() calls or to use any of the
name linking functions in the Journal::Stream class.
*/
template <typename PointerT, typename IndexT>
struct HandleT
{
  PointerT pntr;
  IndexT indx;

  HandleT() : pntr(nullptr) {}

  HandleT(const PointerT _pntr, const IndexT _indx) : pntr(_pntr), indx(_indx)
  {
  }

  bool valid() const { return pntr != nullptr; }

  bool operator<(const HandleT<PointerT, IndexT>& _othr) const
  {
    if (pntr == _othr.pntr)
      return indx < _othr.indx;
    return pntr < _othr.pntr;
  }
};

/*! Key is a pair of two size_t as it may need to represent composite data such
as handles and and index in a vector. */
typedef std::pair<size_t, size_t> Key;

/*!
Convert any pointer into a Key that can be used for looking up
names in the journal stream's name book.
*/
template <typename T>
typename std::enable_if<std::is_pointer<T>::value, Key>::type key(const T _pntr)
{
  return Key(
      reinterpret_cast<size_t>(_pntr), std::numeric_limits<size_t>::max());
}
84

85
86
87
88
89
90
91
/*!
Convert any HandleT<> into a Key that can be used for looking up names in the
journal stream's name book. IndexT should be able to static_cast to size_t,
i.e., be a flavor of integer.
*/
template <typename PointerT, typename IndexT>
Key key(const HandleT<PointerT, IndexT>& _hndl)
92
{
93
94
  return Key(
      reinterpret_cast<size_t>(_hndl.pntr), static_cast<size_t>(_hndl.indx));
95
}
96
97
98
99

// Wrapper for data as text from any type, returned from the Journal::define()
struct Data
{
100
101
  String str; // reference string

102
103
104
105
106
107
108
109
110
111
  enum class Type
  {
    INTERNAL,
    ASSIGN,
    CONSTRUCT
  };

  Type type = Type::INTERNAL; // data type 

  Data() {} // opaque data
112

113
114
115
116
117
118
  explicit Data(const String& _str, const Type _type = Type::ASSIGN)
      : str(_str), type(_type)
  {
  }

  bool valid() const { return !str.empty(); }
119
120
121
122
};

typedef std::vector<Data> DataVector;

123
124
125
126
127
128
129
130
131
132
133
134
135
//! Enumerate the Languages supported by Journal
enum class Language
{
  CPP,
  SCHEME
};

class Stream;

/*!
Include one of the Journal[Cpp|Scheme]Defs.hh files to get an implementation
of this function for the selected output language.
*/
136
template <typename T> Data define_in_language(Stream& _strm, const T& _arg);
137

138
139
140
141
//! the journal stream class, used when streaming from define() specializations
class Stream
{
public:
142
143
144
  // do not call constructor/destructor directly, use \ref set_on() instead
  Stream(const Language _lngg, const char* const _cmpn_name,
      const char* const _base_path);
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
  ~Stream();

  // disable copy & assignment
  Stream(const Stream&) = delete;
  Stream& operator=(const Stream&) = delete;

  //! Get the journal output path
  String output_path() const;

  //! Get the next available filename based on _name to use by the journal
  String next_filename(const char* const _name);

  //! Put the "end line" combination for the journal, e.g., ";\n" for C/C++
  void end_line();

160
  //! Make an unique journal name which can be linked to a pointer or key later 
161
162
  String make_unique_name(const char* const _name);

163
  //! Book an unique journal name which can be linked to a pointer or key later 
164
165
166
167
168
  const char* book_unique_name(const char* const _name);

  //! Link to a pointer/key to a pre-booked unique journal name
  void link_unique_name(const Key _key, const char* const _name);

169
  /*!
170
171
172
173
174
175
176
177
178
179
  Given an existing booked key, this unlinks the key from its booked name.
  This ensures that if the key recurs in future, it will be given a new
  name instead of reusing its potentially inappropriate earlier name. Returns
  true if the key was linked to a name, or false if it was missing.
  */
  void unlink_unique_name(const Key _key);

  /*!
  Search for the unique name corresponding to a key, return nullptr if
  the key has not been linked.
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
  */
  const char* search_unique_name(const Key _key) const;

  /*!
  Same as \ref search_unique_name(), but emits an error if no name is found and
  returns a pointer to an invalid journal name. Useful when expecting the key to
  be linked already and signals a journal or client error.
  */
  const char* retrieve_unique_name(const Key _key);

  /*!
  Same as \ref search_unique_name(), but allocates a new unique name based on
  _name and links it to the key if no name has been linked to this key already.
  \return true if the name has been added or false otherwise.
  */
  bool obtain_unique_name(
      const Key _key, const char* const _name_base, const char*& _name);
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233

  //! Journal a constructor
  template <class ObjectT, typename... ArgT> 
  void constructor(const char* const _fnct, const ObjectT* _obj,
    const ArgT&... _args)
  {
    emit_constructor(_fnct, object(_obj), arguments(_args...));
  }

  //! Journal a destructor
  template <class ObjectT> 
  void destructor(const char* const _fnct, const ObjectT* _obj)
  {
    emit_destructor(_fnct, object(_obj));
  }

  //! Journal a method returning outcome
  template <class ObjectT, typename... ArgT> 
  void method_outcome(const char* const _fnct, const ObjectT* _obj, 
    const ArgT&... _args)
  {
    emit_method_outcome(_fnct, object(_obj), arguments(_args...));
  }

  //! Journal a void method
  template <class ObjectT, typename... ArgT> 
  void method_void(const char* const _fnct, const ObjectT* _obj, 
    const ArgT&... _args)
  {
    emit_method_void(_fnct, object(_obj), arguments(_args...));
  }

  //! Journal a method returning any type
  template <typename ReturnT, class ObjectT, typename... ArgT>
  void method(const char* const _fnct, const ReturnT& _rtrn,
      const ObjectT* _obj, const ArgT&... _args)
  {
234
    emit_method(_fnct, dispatch_define(_rtrn), object(_obj),
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
        arguments(_args...));
  }

  //! Journal a function returning outcome
  template <typename... ArgT> 
  void function_outcome(const char* const _fnct, const ArgT&... _args)
  {
    emit_function_outcome(_fnct, arguments(_args...));
  }

  //! Journal a void function
  template <typename... ArgT> 
  void function_void(const char* const _fnct, const ArgT&... _args)
  {
    emit_function_void(_fnct, arguments(_args...));
  }

  //! Journal a function returning any type
  template <typename ReturnT, typename... ArgT>
  void function(
      const char* const _fnct, const ReturnT& _rtrn, const ArgT&... _args)
  {
257
    emit_function(_fnct, dispatch_define(_rtrn), arguments(_args...));
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
  }

  //! Retrieve the key for any data 
  template <class ObjectT> 
  Key object(const ObjectT* _obj) const
  {// don't need a type name here as we can extract this from the key
    return key(_obj);
  }

  //! Journal a comment start, and return os() to complete it
  Base::IOutputStream& comment_os();

  //! Journal an include file
  void include(const char* const _flnm);

  //! Journal a blank line
  void blank_line();

  /*! 
  Journal a path transform function which allows all filenames to be
  transformed before passed as arguments to journaled functions.
  This allows journal data stored as files to be loaded from other locations.
  
  In C/C++ journals the transform is #define PATH(FLNM) _path_trns.
  If no path transform is journaled, the transform is identity.
  */
  void set_path_transform(const char* const _trns);

  /*!
  Transforms the path with a previously set path transform, or leaves it as
  it is if no transform is set.
  */
  void transform_path(String& _path) const;

292
293
294
295
  /*!
  Journal the start of the main() function (C++ specific). The signature can be
  customized or if left blank is set to int main(). 
  \optionally specify if the main function is void, or returns int (default). 
296
  */
297
298
  void main(
      const char* const _main_fnct = nullptr, const bool _rtrn_int = true);
299
300
301
302
303
304

  //! Journal a custom code line.
  void code_line(const char* const _line);

  //! Direct access to the underlying stream, use with caution
  Base::IOutputStream& os();
305
  
306
307
private:
  class Impl;
308
309
  class CppImpl;
  class SchemeImpl;
310
311
312
  Impl* impl_;

private:
313
  template <typename... ArgT> DataVector arguments(const ArgT&... _args)
314
  {
315
316
317
    return {dispatch_define(_args)...};
  }

318
  template <typename T> Data dispatch_define(const T& _arg)
319
320
  {
    return define_in_language(*this, _arg);
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
  }

  void emit_constructor(
      const char* const _fnct, const Key& _obj, const DataVector& _args);
  void emit_destructor(const char* const _fnct, const Key& _obj);
  void emit_method_outcome(
      const char* const _fnct, const Key& _obj, const DataVector& _args);
  void emit_method_void(
      const char* const _fnct, const Key& _obj, const DataVector& _args);
  void emit_method(const char* const _fnct, const Data& _rtrn, const Key& _obj,
      const DataVector& _args);
  void emit_function_outcome(const char* const _fnct, const DataVector& _args);
  void emit_function_void(const char* const _fnct, const DataVector& _args);
  void emit_function(
      const char* const _fnct, const Data& _rtrn, const DataVector& _args);
};

338
339
340
341
342
343
344
345
346
347
/*! 
Customized pointer that ensures only a single object is ever retained and 
prevents overrides when enabling the journal on application startup within the
static data initialization phase.
\note Exposed for efficiency only (inline access), please do *not* use it.
*/
class StreamPtr 
{ 
public:
  ~StreamPtr();
348

349
350
351
352
353
354
355
  static bool valid() { return ptr_ != nullptr; }
  static Stream& stream() { return *ptr_; }
  static void reset(Stream* _ptr);

private:
  static Stream* ptr_;
};
356
357
358
359

/*!
Turn on/off the journal, set the base path optionally. If no base path is
provided, the base path will be composed from the component name as follows:
360
  * from the environment variable toupper(cmpn_nam e)_JOURNAL_BASE if defined,
361
362
363
364
365
366
  * from the local home data directory of the current user/cmpn_name, e.g.,
    %LOCALAPP_DATA%/cmpn_name on Windows, or $HOME/.cmpn_name on Linux and OS X,
  * the current folder sub-folder ./cmpn_name.
\note If _on == true and for some reason the journal output folder and files
cannot be made, the journaling is disabled and this function returns false.
*/
367
368
369
370
bool set_on(const Language _lngg, //!< output journal language
    const bool _on,                    //!< turn on or off
    const char* const _cmpn_name, //!< component name in desired capitalization
    const char* const _base_path  //!< base path
371
372
373
374
375
376
);

/*!
Check if the journal is on or off, as efficient as possible as it is the only
function which is called when the journal is turned off!
*/
377
inline bool on() { return StreamPtr::valid(); }
378
379

//! Access the stream directly, this is not recommended in general
380
inline Stream& stream() { return StreamPtr::stream(); }
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406

//! Get the journal output path, or an empty string if the journal is off
inline String output_path() { return on() ? stream().output_path() : String(); }

// Basic types define()
struct Name
{
  Name(const char* const _name) : name(_name) {}

  const char* const name;
};

// Wrapper for an C-style array of known length
template <typename T> class CArrayT : public Name
{
public:
  const size_t size;
  const T* const pntr;

  CArrayT(const char* const _name, const size_t _size, const T* const _pntr)
      : Name(_name), size(_size), pntr(_pntr)
  {
  }
};

template <typename T>
407
408
CArrayT<T> c_array(
    const char* const _name, const size_t _size, const T* const _pntr)
409
{
410
  return CArrayT<T>(_name, _size, _pntr);
411
412
}

413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
/*!
Contains a plain filename w/o a path to be loaded during replay, allows for
a specialized define() implementation.
*/
struct Filename
{
  Filename(const String& _str) : str(_str) {}
  String str;
};

enum class ArgQ
{
  VALUE,
  REFERENCE,
  POINTER
};

//! Contains a reference to a function/method argument and its type from ArgQ
template <typename T, ArgQ Q> struct ArgT
{
  using Type = T;

Martin Marinov's avatar
Martin Marinov committed
435
  constexpr static ArgQ quality() { return Q; }
436
437
438
439
440
441
442
443
444
445
446
  typedef ArgT<T, Q> Self;

  T& vrbl;
  const char* const name;

  ArgT(T& _vrbl, const char* const _name) : vrbl(_vrbl), name(_name) {}
  Self& operator=(const Self&) = delete;
};

template <typename T> auto argument(T& _vrbl, const char* const _name)
{
Martin Marinov's avatar
Martin Marinov committed
447
  using Arg = typename
448
      std::conditional<std::is_pointer<T>::value, ArgT<T, ArgQ::POINTER>,
Martin Marinov's avatar
Martin Marinov committed
449
          typename std::conditional<std::is_reference<T>::value,
450
451
452
453
454
455
456
457
458
459
460
461
              ArgT<T, ArgQ::REFERENCE>, ArgT<T, ArgQ::VALUE>>::type>::type;
  return Arg(_vrbl, _name);
}

/*!
Helper to allow generic modification of ArgT.vrbl by ArgLinkT, cannot be made
private due to partial specialization rules.
 */
template <typename T, ArgQ Q> struct ArgSetT;

template <typename T>
struct ArgSetT<T, ArgQ::REFERENCE> : public ArgT<T, ArgQ::REFERENCE>
462
{
463
464
465
466
protected:
  using Arg = ArgT<T, ArgQ::REFERENCE>;
  using rT = typename std::remove_reference<T>::type;
  static_assert(std::is_pointer<rT>::value, "Base type should be a pointer");
467

468
469
  ArgSetT(const Arg& _arg) : Arg(_arg) {}

Martin Marinov's avatar
Martin Marinov committed
470
  template <typename ValueT> void assign(ValueT _val) { Arg::vrbl = _val; }
471
472
473
474

  void init() { assign(nullptr); }

  void make_link(const char* const _name)
475
  {
476
477
    if (on() && Arg::vrbl != nullptr)
      stream().link_unique_name(key(Arg::vrbl), _name);
478
  }
479
};
480

481
482
483
484
485
486
487
488
489
template <typename T>
struct ArgSetT<T, ArgQ::POINTER> : public ArgT<T, ArgQ::POINTER>
{
protected:
  using Arg = ArgT<T, ArgQ::POINTER>;
  using rT = typename std::remove_pointer<T>::type;
  static_assert(std::is_pointer<rT>::value, "Base type should be a pointer");

  ArgSetT(const Arg& _arg) : Arg(_arg) {}
490

491
492
493
  template <typename ValueT> void assign(ValueT _val) { *Arg::vrbl = _val; }

  void init()
494
  {
495
496
    if (Arg::vrbl != nullptr)
      assign(nullptr);
497
498
  }

499
500
501
502
503
  void make_link(const char* const _name)
  {
    if (on() && Arg::vrbl != nullptr && *Arg::vrbl != nullptr)
      stream().link_unique_name(key(*Arg::vrbl), _name);
  }
504
505
};

506
507
508
509
510
511
512
513
514
/*!
Enumerates the types of argument links, public to allow access without using 
the template arguments.
*/ 
enum class LinkMode
{
  MANUAL,
  AUTO
};
515

516
517
//! Link an argument to a variable to allow reuse in the journal 
template <typename T, ArgQ Q> struct ArgLinkT : public ArgSetT<T, Q>
518
{
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
  using Arg = ArgT<T, Q>;
  using ArgSet = ArgSetT<T, Q>;
  
  Name link;
  LinkMode mode;

  explicit ArgLinkT(const Arg& _arg, const LinkMode _mode = LinkMode::MANUAL)
      : ArgSet(_arg),
        link(on() ? stream().book_unique_name(Arg::name) : Arg::name),
        mode(_mode)
  {
    if (mode == LinkMode::AUTO) // if AUTO mode?
      ArgSet::init();           // always set the underlying pointer to nullptr
  }

  // output arguments need explicit linking on successful assignment only
  template <typename ValueT> ValueT operator=(ValueT _val)
  {
    ArgSet::assign(_val);
    ArgSet::make_link(link.name);
    mode = LinkMode::MANUAL; // explicitly assigned value, switch the mode
    return _val;
  }

  ~ArgLinkT()
  {
    if (mode == LinkMode::AUTO)
Martin Marinov's avatar
Martin Marinov committed
546
      ArgSet::make_link(link.name);
547
548
549
  }
};

550
551
552
553
554
555
556
557
558
559
template <typename T>
auto argument_link(
    T& _vrbl, const char* const _name, const LinkMode _mode = LinkMode::MANUAL)
{
  auto arg = argument<T>(_vrbl, _name);
  return ArgLinkT<T, arg.quality()>(arg, _mode);
}

// Partial specialization of ReturnLinkT for pointers
struct ReturnLink
560
561
562
{
  Name link;

563
  explicit ReturnLink(const char* const _name)
564
565
566
567
      : link(on() ? stream().book_unique_name(_name) : _name)
  {
  }

568
569
570
  // overload of assignment if the linked type T is a pointer
  template <typename T>
  typename std::enable_if<std::is_pointer<T>::value, T>::type operator=(T pntr)
571
572
573
574
575
  {
    if (on() && pntr != nullptr)
      stream().link_unique_name(key(pntr), link.name);
    return pntr;
  }
576

577
578
579
580
581
582
583
584
585
  // overload of assignment if the linked type T is a handle
  template <typename PointerT, typename IndexT>
  IndexT operator=(const HandleT<PointerT, IndexT>& _hndl)
  {
    if (on() && _hndl.valid())
      stream().link_unique_name(key(_hndl), link.name);
    return _hndl.indx;
  }
};
586

587
} // namespace Journal
588

589
590
591
#if defined(_MSC_VER)
#define __JOURNAL_FUNCTION__ __FUNCTION__ // works in VC well
#else
592
#define __JOURNAL_FUNCTION__ __PRETTY_FUNCTION__ // needed for gcc & xcode
593
#endif                                           // _MSC_VER
594
595
596

#define JOURNAL_CHECK if (::Journal::on()) 

597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
/*!
If you define the macro JOURNAL_PRE_CALL, then the function:

void ::Journal::on_pre_call(::Journal::Stream&, const char* const)

will be called immediately prior to each function that you
journal. If you're using JOURNAL_PRE_CALL, then you must define
an implementation for that function, or you will get a link
error. See also JOURNAL_POST_CALL. Note that you will need to
define the macro prior to including JournalStream.hh.
*/
#ifdef JOURNAL_PRE_CALL
namespace Journal
{
void on_pre_call(Stream&, const char* const);
}
#define JOURNAL_PRE_CALL_IMPL \
  ::Journal::on_pre_call(::Journal::stream(), __JOURNAL_FUNCTION__)
#else
#define JOURNAL_PRE_CALL_IMPL
#endif

/*!
If you define JOURNAL_POST_CALL, then the journal calls:

void ::Journal::on_post_call(::Journal::Stream&, const char* const)

immediately after each function that you journal. See JOURNAL_PRE_CALL
for more discussion.
*/
#ifdef JOURNAL_POST_CALL
namespace Journal
{
void on_post_call(Stream&, const char* const);
}
#define JOURNAL_POST_CALL_IMPL \
  ::Journal::on_post_call(::Journal::stream(), __JOURNAL_FUNCTION__)
#else
#define JOURNAL_POST_CALL_IMPL
#endif

/*!
The base macro for routing various types of journaled events to the
JournalStream. The JOURNAL_PRE_CALL_IMPL and JOURNAL_POST_CALL_IMPL
macros are blank by default, but if you define JOURNAL_PRE_CALL or
JOURNAL_POST_CALL, then you can implement additional handlers that
are called immediately before and/or after each journal event.
*/
645
#define JOURNAL_CALL(CALL, ...) \
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
  { \
    JOURNAL_PRE_CALL_IMPL; \
    ::Journal::stream().CALL(__JOURNAL_FUNCTION__, ##__VA_ARGS__); \
    JOURNAL_POST_CALL_IMPL; \
  }

/*!
If you define JOURNAL_AUTO_INCLUDE somewhere prior to including
JournalStream.hh then the journal will automatically generate a header file
that includes necessary headers from your library. 

It works by assuming that if a function is journaled in component/y/foo.cc, then
it should include component/y/foo.hh. It doesn't support other header extensions
(like .h), and the 'component' needs to match whatever you defined as the
COMPONENT_NAME_STRING prior to including JournalConfig.inc.cc.
661

662
663
\todo customize this option
*/
664
#ifdef JOURNAL_AUTO_INCLUDE
665
#define JOURNAL_INCLUDE_THIS ::Journal::stream().include(__FILE__)
666
667
668
#else
#define JOURNAL_INCLUDE_THIS
#endif//JOURNAL_AUTO_INCLUDE
669

670
671
672
/*!
Emits code into the journal to include the given header file.
*/
673
674
675
#define JOURNAL_INCLUDE(FILE) \
  { JOURNAL_CHECK ::Journal::stream().include(FILE); }

676
677
678
679
/*!
Emits a comment into the header file. Useful for providing context
or error messages to someone reading the journal file.
*/
680
681
682
683
684
685
686
687
688
689
#define JOURNAL_COMMENT(CMNT) \
  { JOURNAL_CHECK { ::Journal::stream().comment_os() << CMNT << Base::ENDL; } }

#define JOURNAL_AS_COMMENT(EXPR) \
  { JOURNAL_CHECK{ ::Journal::stream().comment_os(); EXPR; } }
  
#define JOURNAL(CALL, ...) { JOURNAL_CHECK JOURNAL_CALL(CALL, ##__VA_ARGS__); }
#define JOURNAL_INC(CALL, ...) { JOURNAL_CHECK \
  { JOURNAL_INCLUDE_THIS; JOURNAL_CALL(CALL, ##__VA_ARGS__); } }

690
#define JOURNAL_CONSTRUCTOR(...) JOURNAL_INC(constructor, this, ##__VA_ARGS__)
691
692
693
694
695
696
#define JOURNAL_DESTRUCTOR JOURNAL(destructor, this)
#define JOURNAL_METHOD_OUTCOME(...) JOURNAL(method_outcome, this, ##__VA_ARGS__)
#define JOURNAL_METHOD_VOID(...) JOURNAL(method_void, this, ##__VA_ARGS__)

#define JOURNAL_FUNCTION_VOID(...) JOURNAL_INC(function_void, ##__VA_ARGS__)
#define JOURNAL_FUNCTION_OUTCOME(...) \
697
  JOURNAL_INC(function_outcome, ##__VA_ARGS__)
698
699
700

#define JOURNAL_C_ARRAY(SIZE, PNTR) ::Journal::c_array(#PNTR, SIZE, PNTR)

701
702
703
704
705
706
707
708
709
#define JL(ARG) _jl_##ARG
#define JOURNAL_LINK_MANUAL(ARG) \
  auto JL(ARG) = ::Journal::argument_link<decltype(ARG)>( \
      ARG, #ARG, ::Journal::LinkMode::MANUAL)
#define JOURNAL_LINK_AUTO(ARG) \
  auto JL(ARG) = ::Journal::argument_link<decltype(ARG)>( \
      ARG, #ARG, ::Journal::LinkMode::AUTO)

#define JOURNAL_METHOD_RETURN_LINK(NAME, IMPL, ...) \
710
  ::Journal::ReturnLink JL(rtrn)(NAME); \
711
  JOURNAL(method, JL(rtrn), this, ##__VA_ARGS__); \
712
  return JL(rtrn) = IMPL;
713

714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
/*!
This macro is used for journaling functions where the return value needs
to be tracked. NAME is a string that lets you specify a human-readable
name for the variable which will hold the return value. For example, if
name is "foo" then something like "auto foo_123 = func(...);" will be generated.

The IMPL is the "body" of your function. Even if journaling is turned off,
the IMPL will always be evaluated. If it's a short expression, you can
embed it directly in the macro; if it's a long expression, it may be
better to put it in a lambda, and then invoke the lambda in the macro.
The remaining variable macro arguments ... are the parameters passed to the
journaled function. 

For example:

@code
Widget* get_widget(int a, Point3d b)
{
  JOURNAL_FUNCTION_RETURN_LINK(
      "widget", widgetProvider->getWidget(a, b), a, b);
}
@endcode

The resulting journal output might look something like this:

@code
  auto widget_1 = get_widget( 50, Point3d(2,2,0) );
@endcode

IMPL can return any type which can be cast to a key, i.e., a pointer or a
HandleT<>. Other types are not supported currently.
*/
746
#define JOURNAL_FUNCTION_RETURN_LINK(NAME, IMPL, ...) \
747
  ::Journal::ReturnLink JL(rtrn)(NAME); \
748
749
  JOURNAL(function, JL(rtrn), ##__VA_ARGS__); \
  return JL(rtrn) = IMPL
750
751
752
753

#define JOURNAL_PATH_TRANSFORM(TRNS) \
  ::Journal::stream().set_path_transform(TRNS)
#define JOURNAL_MAIN(MAIN) ::Journal::stream().main(MAIN)
754
#define JOURNAL_MAIN_VOID(MAIN) ::Journal::stream().main(MAIN, false)
755
756
757
758
759
760
761
762
#define JOURNAL_CODE_LINE(CODE) ::Journal::stream().code_line(CODE)
#define JOURNAL_BLANK_LINE ::Journal::stream().blank_line()

#define JOURNAL_ERROR(MSG) JOURNAL_COMMENT("Journal ERROR: " << MSG)
#define JOURNAL_ERROR_if(CND, MSG) if (CND) JOURNAL_ERROR(MSG)
#define JOURNAL_ERROR_if_do(CND, MSG, ACTN) \
  if (CND) { JOURNAL_ERROR(MSG); ACTN; }

763
#define JT(ARG) ::Journal::argument<decltype(ARG)>(ARG, #ARG)
764
765
#define JH(PNTR, INDX) \
  ::Journal::HandleT<decltype(PNTR), decltype(INDX)>(PNTR, INDX)
766

767
#else
768

769
770
771
772
773
774
775
776
777
778
779
780
781
782
#define JOURNAL_INCLUDE_THIS
#define JOURNAL_INCLUDE(FILE)
#define JOURNAL_COMMENT(CMNT)

#define JOURNAL(CMNT, ...)

#define JOURNAL_CONSTRUCTOR(...)
#define JOURNAL_DESTRUCTOR
#define JOURNAL_METHOD_OUTCOME(...)
#define JOURNAL_METHOD_VOID(...)
#define JOURNAL_FUNCTION_VOID(...)
#define JOURNAL_FUNCTION_OUTCOME(...)

#define JOURNAL_C_ARRAY(SIZE, PNTR)
783
784
#define JOURNAL_LINK(VRBL)
#define JOURNAL_LINK_AUTO(VRBL)
785
786
787

#define JOURNAL_PATH_TRANSFORM(TRNS)
#define JOURNAL_MAIN(MAIN)
788
#define JOURNAL_MAIN_VOID(MAIN)
789
790
791
792
793
794
795
#define JOURNAL_CODE_LINE(CODE)
#define JOURNAL_BLANK_LINE

#define JOURNAL_ERROR(MSG)
#define JOURNAL_ERROR_if(CND, MSG)
#define JOURNAL_ERROR_if_do(CND, MSG, ACTN)

796
797
#define JOURNAL_TYPE_NAME(TYPE)

798
799
800
#define JOURNAL_METHOD_RETURN_LINK(NAME, IMPL, ...)  return IMPL
#define JOURNAL_FUNCTION_RETURN_LINK(NAME, IMPL, ...) return IMPL

801
802
#define JH(PNTR, INDX) INDX

803
804
#endif // JOURNAL_ON
#endif // BASE_JOURNAL_STREAM_HH_INCLUDED