Commit 16cbb948 authored by Martin Marinov's avatar Martin Marinov Committed by GitHub Enterprise

TSP-3405 Improve Journal output and return data capabilities (#5)

* Add support for output arguments accessed by * and **
* Add ArgT and ArgQ, the macro JT (journal traits) to provide a generic approach to disambiguate definitions for value, reference and pointer arguments
* Add support for linked arguments
* Recursive Cpp::define() for ArgLinkT<> and ArgT<>
* Reference to pointer arguments now defer processing to the pointer argument implementation, rendering define overloads for pointer to custom types redundant
* Journal non-linked pointers to opaque data now using their actual addresses cast with reinterpret_cast<>
* Remove links to this pointers when emitting object destructor calls
* Remove Data::key
* Add Data::type to allow different types of initialization
* Simplify ArgLinkT
* Add ReturnLinkT and JOURNAL_[METHOD|FUNCTION]_RETURN_LINK
* Remove OutputT
* Add JOURNAL_TYPE_NAME macro
* Add Journal::Cpp casts
* Fix a bug where define(..., float) was outputting invalid float constants
* Improve the comment on Journal::Filename
parent c35d3662
......@@ -15,6 +15,9 @@
#include "JournalFileSystem.hh"
#include "Base/Journal/JournalString.hh"
#include <cctype>
#include <algorithm>
namespace Journal
{
namespace Cpp
......@@ -50,10 +53,23 @@ template <> Data define(Stream&, const unsigned int& _u)
return Data(to_string(_u));
}
template <> Data define(Stream&, const size_t& _s)
{
return Data(to_string(_s));
}
template <> Data define(Stream&, const float& _f)
{
Base::OStringStream os;
os << _f << "f";
os << _f;
auto str_it = os.str.begin();
if (str_it != os.str.end() && *str_it == '-') // negative?
++str_it;
if (std::all_of(str_it, os.str.end(), isdigit)) // all digits?!
os << '.'; // add a dot, as otherwise we will get an int constant
os << 'f'; // now add the 'f' suffix to indicate a float constraint
return Data(os.str);
}
......
......@@ -10,6 +10,7 @@
#define BASE_JOURNALCPPDEFS_HH_INCLUDED
#include <Base/Journal/JournalStream.hh>
#include <Base/Journal/JournalString.hh>
#ifdef JOURNAL_ON
......@@ -17,8 +18,145 @@ namespace Journal
{
namespace Cpp
{
template <typename T>
Data define_cast(Stream&, const String& _rprs, const char* const _cast)
{
Base::OStringStream os;
os << _cast << "_cast<" << type_name<T>() << ">(" << _rprs << ")";
return Data(os.str);
}
template <typename ArgT> Data define(Stream&, const ArgT&);
template <typename T>
Data define_static_cast(Stream& _strm, const String& _rprs)
{
return define_cast<T>(_strm, _rprs, "static");
}
template <typename T>
Data define_dynamic_cast(Stream& _strm, const String& _rprs)
{
return define_cast<T>(_strm, _rprs, "dynamic");
}
template <typename T>
Data define_reinterpret_cast(Stream& _strm, const String& _rprs)
{
return define_cast<T>(_strm, _rprs, "reinterpret");
}
// template <enum EnumT> Data define(Stream&, enum EnumT const& _val)
template <typename T>
typename std::enable_if<std::is_enum<T>::value, Data>::type define(
Stream& _strm, const T& _val)
{
return define_static_cast<T>(_strm, to_string(static_cast<int>(_val)));
}
template <typename T>
typename std::enable_if<!std::is_enum<T>::value, Data>::type define(
Stream&, const T& _val);
template <typename T>
Data define(Stream& _strm, const ArgT<T, ArgQ::VALUE>& _arg)
{ // value output can use the non-traits define
return define(_strm, _arg.vrbl);
}
// Overload for all non-pointer types
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, Data>::type
define_reference(Stream& _strm, const T& _vrbl, const char* const _name)
{
const char* name = _strm.search_unique_name(key(&_vrbl));
if (name != nullptr) // if referenced object has a name already, reuse it
return Data(String("*") + name);
name = stream().book_unique_name(_name);
const auto data = define(_strm, _vrbl); // journal the variable data
JOURNAL_ERROR_if(data.type == Data::Type::INTERNAL,
"Reference to non-linked opaque data " << name);
_strm.os() << type_name<T>() << " " << name;
switch (data.type)
{
case Data::Type::ASSIGN:
_strm.os() << " = " << data.str;
break;
case Data::Type::CONSTRUCT:
if (!data.str.empty())
_strm.os() << "(" << data.str << ")";
break;
default:
break;
}
_strm.os() << "; // pass reference" << ENDL;
return Data(name);
}
// Overload for all pointer types
template <typename T>
typename std::enable_if<std::is_pointer<T>::value, Data>::type
define_reference(Stream& _strm, T _vrbl, const char* const _name)
{
return define(_strm, argument<T>(_vrbl, _name));
}
template <typename T>
Data define(Stream& _strm, const ArgT<T, ArgQ::REFERENCE>& _arg)
{ // reference output has to define a unique variable first and pass it
using rT = typename std::remove_reference<T>::type;
return define_reference<rT>(_strm, _arg.vrbl, _arg.name);
}
template <typename T>
Data define(Stream& _strm, const ArgT<T, ArgQ::POINTER>& _arg)
{ // pointer output has to define a unique variable first and pass its address
if (_arg.vrbl == nullptr)
return define(_strm, nullptr);
const char* name = _strm.search_unique_name(key(_arg.vrbl));
if (name != nullptr)
return Data(name); // a name has been defined for the pointer, reuse it
// a name for the pointed object does not exist yet, so define the object
using rT = typename std::remove_pointer<T>::type;
const auto data = define(_strm, argument<rT>(*_arg.vrbl, _arg.name));
if (data.type != Data::Type::INTERNAL) // data can be defined in the journal?
{// journal a new variable representing the data and return a pointer to it
name = stream().book_unique_name(_arg.name);
_strm.os() << type_name<rT>() << " " << name << " = " << data.str
<< "; // pass pointer" << ENDL;
return Data(String("&") + name);
}
// data is opaque, so journal the pointer value with reinterpret_cast
auto addr = ::Base::format_hex(_arg.vrbl, ::Base::FormatHex::PREFIX_0x);
return define_reinterpret_cast<T>(_strm, addr.buffer());
}
template <typename T>
Data define(Stream& _strm, const ArgLinkT<T, ArgQ::REFERENCE>& _arg)
{ // reference output has to define a unique variable first and pass it
const auto* const name = _arg.link.name;
using rT = typename std::remove_reference<T>::type;
//const auto data = define(_strm, _arg.vrbl);
const auto data = define(_strm, argument<rT>(_arg.vrbl, _arg.name));
_strm.os() << type_name<rT>() << " " << name << " = " << data.str
<< "; // linked pointer" << ENDL;
return Data(name);
}
template <typename T>
Data define(Stream& _strm, const ArgLinkT<T, ArgQ::POINTER>& _arg)
{ // pointer output has to define a unique variable first and pass its address
if (_arg.vrbl == nullptr)
return define(_strm, nullptr);
const auto* const name = _arg.link.name;
//const auto data = define(_strm, *_arg.vrbl);
using rT = typename std::remove_pointer<T>::type;
const auto data = define(_strm, argument<rT>(*_arg.vrbl, _arg.name));
_strm.os() << type_name<rT>() << " " << name << " = " << data.str
<< "; // linked pointer" << ENDL;
return Data(String("&") + name);
}
template <const size_t _bffr_size>
Data define(Stream& _strm, const Base::FormatT<_bffr_size>& _frmt)
......@@ -53,17 +191,14 @@ template <typename T> Data define(Stream& _strm, const CArrayT<T>& _c_arr)
return Data(name);
}
template <class T> Data define(Stream& _strm, const OutputT<T>& _otpt)
template <class T> Data define(Stream&, const ReturnLinkT<T>& _rtrn)
{
_strm.os() << type_name<T>() << " " << _otpt.name /*<< " = " << _otpt.vrbl*/;
_strm.end_line();
return Data(_otpt.name);
return Data(String("auto ") + _rtrn.link.name);
}
} // namespace Scheme
} // namespace Cpp
template <typename ArgT>
Data define_in_language(Stream& _strm, const ArgT& _arg)
template <typename T> Data define_in_language(Stream& _strm, const T& _arg)
{
return Cpp::define(_strm, _arg);
}
......
......@@ -106,7 +106,7 @@ void Stream::emit_constructor(
void Stream::emit_destructor(const char* const _fnct, const Key& _obj)
{
JOURNAL_WRAP(impl_->emit_destructor(_fnct, _obj));
JOURNAL_WRAP(impl_->emit_destructor_and_unlink(_fnct, _obj));
}
void Stream::emit_method_outcome(
......
......@@ -19,6 +19,7 @@
#include <string>
#include <vector>
#include <type_traits>
namespace Journal
{
......@@ -30,17 +31,33 @@ using Base::IOutputStream;
typedef size_t Key;
const Key INVALID_KEY = 0;
template <class T> Key key(const T* const _pntr) { return (Key)_pntr; }
template <class T> Key key(const T* const _pntr)
{
return reinterpret_cast<Key>(_pntr);
}
// Wrapper for data as text from any type, returned from the Journal::define()
struct Data
{
Data(const String& _str, const Key _key = INVALID_KEY)
: str(_str), key(_key)
{}
enum class Type
{
INTERNAL,
ASSIGN,
CONSTRUCT
};
Type type = Type::INTERNAL; // data type
String str; // reference string
Data() {} // opaque data
String str; // reference string
const Key key; // pointer to the variable data, if not a plain type
explicit Data(const String& _str, const Type _type = Type::ASSIGN)
: str(_str), type(_type)
{
}
bool valid() const { return !str.empty(); }
};
typedef std::vector<Data> DataVector;
......@@ -66,7 +83,11 @@ template <class T> String type_name()
#else // JOURNAL_USE_BOOST
//! Override this for any type that is used as output data for C/C++ journals
/*!
Override this for any type that is used as output data for C/C++ journals.
See #define JOURNAL_TYPE_NAME(TYPE) template <> type_name<TYPE>() { return #TYPE; }
*/
template <class T> String type_name();
#endif // JOURNAL_USE_BOOST
......@@ -84,8 +105,7 @@ class Stream;
Include one of the Journal[Cpp|Scheme]Defs.hh files to get an implementation
of this function for the selected output language.
*/
template <typename ArgT>
Data define_in_language(Stream& _strm, const ArgT& _arg);
template <typename T> Data define_in_language(Stream& _strm, const T& _arg);
//! the journal stream class, used when streaming from define() specializations
class Stream
......@@ -352,41 +372,169 @@ CArrayT<T> c_array(
return CArrayT<T>(_name, _size, _pntr);
}
template <typename T> struct OutputT : public Name
/*!
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;
constexpr ArgQ quality() { return Q; }
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)
{
using Arg =
std::conditional<std::is_pointer<T>::value, ArgT<T, ArgQ::POINTER>,
std::conditional<std::is_reference<T>::value,
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>
{
typedef OutputT<T> Self;
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");
OutputT(const char* const _name, T& _vrbl)
: Name(on() ? stream().book_unique_name(_name) : _name), vrbl(_vrbl)
ArgSetT(const Arg& _arg) : Arg(_arg) {}
template <typename ValueT> void assign(ValueT _val) { vrbl = _val; }
void init() { assign(nullptr); }
void make_link(const char* const _name)
{
if (on() && Arg::vrbl != nullptr)
stream().link_unique_name(key(Arg::vrbl), _name);
}
};
T& vrbl;
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) {}
T& operator=(const T& _val)
template <typename ValueT> void assign(ValueT _val) { *Arg::vrbl = _val; }
void init()
{
vrbl = _val;
if (on()) // register the value to the variable name
stream().link_unique_name((Key)_val, name);
return vrbl;
if (Arg::vrbl != nullptr)
assign(nullptr);
}
Self& operator=(const Self&) = delete;
void make_link(const char* const _name)
{
if (on() && Arg::vrbl != nullptr && *Arg::vrbl != nullptr)
stream().link_unique_name(key(*Arg::vrbl), _name);
}
};
template <typename T>
OutputT<T> output(const char* const _name, T& _vrbl)
{
return OutputT<T>(_name, _vrbl);
}
/*!
Enumerates the types of argument links, public to allow access without using
the template arguments.
*/
enum class LinkMode
{
MANUAL,
AUTO
};
//! Contains a plain filename w/o a path to be loaded during replay
struct Filename
//! Link an argument to a variable to allow reuse in the journal
template <typename T, ArgQ Q> struct ArgLinkT : public ArgSetT<T, Q>
{
Filename(const String& _str) : str(_str) {}
String str;
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)
make_link(link.name);
}
};
template <typename T> struct ReturnLinkT
{
static_assert(std::is_pointer<T>::value, "Return value should be a pointer");
Name link;
explicit ReturnLinkT(const char* const _name)
: link(on() ? stream().book_unique_name(_name) : _name)
{
}
T operator=(T pntr)
{
if (on() && pntr != nullptr)
stream().link_unique_name(key(pntr), link.name);
return pntr;
}
};
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);
}
} // namespace Journal
#if defined(_MSC_VER)
......@@ -418,20 +566,32 @@ struct Filename
#define JOURNAL_CONSTRUCTOR(...) { JOURNAL_CHECK \
{ JOURNAL_INCLUDE_THIS; JOURNAL_CALL(constructor, this, ##__VA_ARGS__); } }
#define JOURNAL_DESTRUCTOR JOURNAL(destructor, this)
#define JOURNAL_METHOD(VRBL, ...) JOURNAL(method, VRBL, this, ##__VA_ARGS__)
#define JOURNAL_METHOD_OUTCOME(...) JOURNAL(method_outcome, this, ##__VA_ARGS__)
#define JOURNAL_METHOD_VOID(...) JOURNAL(method_void, this, ##__VA_ARGS__)
#define JOURNAL_FUNCTION(VRBL, ...) JOURNAL_INC(function, VRBL, ##__VA_ARGS__)
#define JOURNAL_FUNCTION_VOID(...) JOURNAL_INC(function_void, ##__VA_ARGS__)
#define JOURNAL_FUNCTION_OUTCOME(...) \
JOURNAL_INC(function_outcome, ##__VA_ARGS__)
#define JOURNAL_C_ARRAY(SIZE, PNTR) ::Journal::c_array(#PNTR, SIZE, PNTR)
#define JOURNAL_OUTPUT(VRBL) jrnl##VRBL
#define JOURNAL_OUTPUT_WRAP(VRBL) \
auto JOURNAL_OUTPUT(VRBL) = ::Journal::output(#VRBL, VRBL)
#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, ...) \
::Journal::ReturnLinkT<decltype(IMPL)> JL(rtrn)(NAME); \
JOURNAL(method, JL(rtrn), this, ##__VA_ARGS__) \
return JL(rtrn) = IMPL;
#define JOURNAL_FUNCTION_RETURN_LINK(NAME, IMPL, ...) \
::Journal::ReturnLinkT<decltype(IMPL)> JL(rtrn)(NAME); \
JOURNAL(function, JL(rtrn), ##__VA_ARGS__) \
return JL(rtrn) = IMPL;
#define JOURNAL_PATH_TRANSFORM(TRNS) \
::Journal::stream().set_path_transform(TRNS)
......@@ -444,6 +604,11 @@ struct Filename
#define JOURNAL_ERROR_if_do(CND, MSG, ACTN) \
if (CND) { JOURNAL_ERROR(MSG); ACTN; }
#define JT(ARG) ::Journal::argument<decltype(ARG)>(ARG, #ARG)
#define JOURNAL_TYPE_NAME(TYPE) \
template <> String type_name<TYPE>() { return String(#TYPE); }
#else
#define JOURNAL_INCLUDE_THIS
......@@ -454,17 +619,16 @@ struct Filename
#define JOURNAL_CONSTRUCTOR(...)
#define JOURNAL_DESTRUCTOR
#define JOURNAL_METHOD(VRBL, ...)
#define JOURNAL_METHOD_OUTCOME(...)
#define JOURNAL_METHOD_VOID(...)
#define JOURNAL_METHOD_RETURN_LINK(VRBL, ...)
#define JOURNAL_FUNCTION(VRBL, ...)
#define JOURNAL_FUNCTION_VOID(...)
#define JOURNAL_FUNCTION_OUTCOME(...)
#define JOURNAL_C_ARRAY(SIZE, PNTR)
#define JOURNAL_OUTPUT(VRBL) VRBL
#define JOURNAL_OUTPUT_WRAP(VRBL)
#define JOURNAL_LINK(VRBL)
#define JOURNAL_LINK_AUTO(VRBL)
#define JOURNAL_PATH_TRANSFORM(TRNS)
#define JOURNAL_MAIN(MAIN)
......@@ -475,5 +639,7 @@ struct Filename
#define JOURNAL_ERROR_if(CND, MSG)
#define JOURNAL_ERROR_if_do(CND, MSG, ACTN)
#define JOURNAL_TYPE_NAME(TYPE)
#endif // JOURNAL_ON
#endif // BASE_JOURNAL_STREAM_HH_INCLUDED
......@@ -151,6 +151,12 @@ void Stream::Impl::time()
<< System::Environment::time(); // time() already contains ENDL
}
void Stream::Impl::emit_destructor_and_unlink(const char* const _fnct, const Key& _obj)
{
emit_destructor(_fnct, _obj);
key_name_.erase(_obj); // erase the link
}
}//namespace Journal
#endif//JOURNAL_ON
......@@ -66,6 +66,7 @@ public:
virtual void emit_constructor(
const char* const _fnct, const Key& _obj, const DataVector& _args) = 0;
virtual void emit_destructor(const char* const _fnct, const Key& _obj) = 0;
void emit_destructor_and_unlink(const char* const _fnct, const Key& _obj);
virtual void emit_method_call(
const char* const _fnct, const Key& _obj, const DataVector& _args) = 0;
......
// Copyright 2019 Autodesk, Inc. All rights reserved.
// Copyright 2020 Autodesk, Inc. All rights reserved.
//
// This computer source code and related instructions and comments are the
// unpublished confidential and proprietary information of Autodesk, Inc.
......@@ -46,8 +46,7 @@ String function_name(const char* const _fnct);
String class_name(const char* const _fnct);
String method_name(const char* const _fnct);
}
} // namespace Journal
#endif // JOURNAL_ON
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment