Commit f90c9415 authored by Martin Marinov's avatar Martin Marinov Committed by GitHub Enterprise

UNWRAP-128 Clean up and improve journal capabilities to support UNWRAP (#6)

* Move type_name<> to JournalCppDefs.hh and namespace Cpp and enable automatic handling on *, & and const types

* Add JOURNAL_ADD_TYPE, JOURNAL_ADD_TYPE_INTERNAL, JOURNAL_TYPE_EXTERNAL 

* Leverage argument<> for CArrayT<> elements

* Add Journal::to_lower() and improve object naming when the class name is all capitals

* Prevent de-referencing of possibly invalid argument pointers

* Add define_consrtuct[_default] to simplify for non-opaque data constructors, e.g., vectors, planes, matrices, transforms, etc.

* Fix a bug where references to a pointer could inadvertently dereference an invalid pointer

* Disable automatic includes by default; enable them by defining JOURNAL_AUTO_INCLUDE.

* Add JOURNAL_MAIN_VOID to prevent return from the main() function in C++ journals.

* Add extract_directory() and replace_filename() in JournalFileSystem.hh/cc
parent a0c5f510
......@@ -23,43 +23,74 @@ namespace Journal
namespace Cpp
{
#ifdef JOURNAL_USE_BOOST
void filter_type_name(String& _type_name)
{
const char PTR64_STR[] = " __ptr64";
const auto pos = _type_name.find(PTR64_STR);
if (pos != String::npos)
_type_name.erase(pos, sizeof(PTR64_STR) - 1);
}
#endif
template <> Data define(Stream&, const std::nullptr_t&)
{
return Data("nullptr");
}
template <> Data define(Stream&, const bool& _b)
JOURNAL_ADD_TYPE_EXTERNAL(bool, _b)
{
_strm; // prevent warnings
return Data(_b ? "true" : "false");
}
template <> Data define(Stream&, const short& _i)
JOURNAL_ADD_TYPE(char, _c)
{// TODO: (unsigned char can be output as a symbol in many cases)
_strm; // prevent warnings
return Data(to_string(_c));
}
JOURNAL_ADD_TYPE_EXTERNAL(unsigned char, _c)
{
_strm; // prevent warnings
return Data(to_string(_c));
}
JOURNAL_ADD_TYPE_EXTERNAL(short, _i)
{
_strm; // prevent warnings
return Data(to_string(_i));
}
template <> Data define(Stream&, const unsigned short& _u)
JOURNAL_ADD_TYPE_EXTERNAL(unsigned short, _u)
{
_strm; // prevent warnings
return Data(to_string(_u));
}
template <> Data define(Stream&, const int& _i)
JOURNAL_ADD_TYPE_EXTERNAL(int, _i)
{
_strm; // prevent warnings
return Data(to_string(_i));
}
template <> Data define(Stream&, const unsigned int& _u)
JOURNAL_ADD_TYPE_EXTERNAL(unsigned int, _u)
{
_strm; // prevent warnings
return Data(to_string(_u));
}
template <> Data define(Stream&, const size_t& _s)
JOURNAL_ADD_TYPE_EXTERNAL(size_t, _s)
{
_strm; // prevent warnings
return Data(to_string(_s));
}
template <> Data define(Stream&, const float& _f)
JOURNAL_ADD_TYPE_EXTERNAL(float, _f)
{
_strm; // prevent warnings
Base::OStringStream os;
os << _f;
......@@ -73,8 +104,9 @@ template <> Data define(Stream&, const float& _f)
return Data(os.str);
}
template <> Data define(Stream&, const double& _d)
JOURNAL_ADD_TYPE_EXTERNAL(double, _d)
{
_strm; // prevent warnings
return Data(to_string(_d));
}
......
......@@ -12,44 +12,105 @@
#include <Base/Journal/JournalStream.hh>
#include <Base/Journal/JournalString.hh>
#ifdef JOURNAL_USE_BOOST
#include <boost/core/typeinfo.hpp>
#endif // JOURNAL_USE_BOOST
#ifdef JOURNAL_ON
namespace Journal
{
namespace Cpp
{
// If Boost is available for use in Journal, we provide a default type_name()
// implementation based on RTTI and Boost demangling. Otherwise type_info()
// has to be specialized for any type that is used as an output argument.
#ifdef JOURNAL_USE_BOOST
//! Correct some known issues with automatic type_name()
void filter_type_name(String& _type_name);
//! Automatic type-name extraction using Boost
template <class T> String type_name()
{
auto str = boost::core::demangled_name(BOOST_CORE_TYPEID(T));
#ifdef _MSC_VER
// on MSVC, pointer types contain __ptr64 which is MSVC only, so filter it out
filter_type_name(str);
#endif // _MSC_VER
return str;
}
#define JOURNAL_TYPE_NAME(TYPE)
#else // JOURNAL_USE_BOOST
template <typename T>
Data define_cast(Stream&, const String& _rprs, const char* const _cast)
typename std::enable_if<std::is_pointer<T>::value, String>::type type_name()
{
Base::OStringStream os;
os << _cast << "_cast<" << type_name<T>() << ">(" << _rprs << ")";
return Data(os.str);
auto str = type_name<typename std::remove_pointer<T>::type>();
str += "*";
return str;
}
template <typename T>
Data define_static_cast(Stream& _strm, const String& _rprs)
typename std::enable_if<std::is_reference<T>::value, String>::type type_name()
{
return define_cast<T>(_strm, _rprs, "static");
auto str = type_name<typename std::remove_reference<T>::type>();
str += "&";
return str;
}
template <typename T>
Data define_dynamic_cast(Stream& _strm, const String& _rprs)
typename std::enable_if<std::is_const<T>::value, String>::type type_name()
{
return define_cast<T>(_strm, _rprs, "dynamic");
auto str = type_name<typename std::remove_const<T>::type>();
return str + " const";
}
/*!
Override this for any type that is used as output data for C/C++ journals, you
can use the JOURNAL_TYPE_NAME() helper macro.
*/
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value &&
!std::is_reference<T>::value && !std::is_const<T>::value, String>::type
type_name();
#define JOURNAL_TYPE_NAME(TYPE) \
template <> String type_name<TYPE>() { return String(#TYPE); }
#endif // JOURNAL_USE_BOOST
template <typename T>
Data define_reinterpret_cast(Stream& _strm, const String& _rprs)
auto define_cast(const String& _rprs, const char* const _cast)
{
return define_cast<T>(_strm, _rprs, "reinterpret");
Base::OStringStream os;
os << _cast << "_cast<" << type_name<T>() << ">(" << _rprs << ")";
return os.str;
}
template <typename T> auto define_static_cast(const String& _rprs)
{
return define_cast<T>(_rprs, "static");
}
template <typename T> auto define_dynamic_cast(const String& _rprs)
{
return define_cast<T>(_rprs, "dynamic");
}
template <typename T> auto define_reinterpret_cast(const String& _rprs)
{
return define_cast<T>(_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)
Stream&, const T& _val)
{
return define_static_cast<T>(_strm, to_string(static_cast<int>(_val)));
return Data(define_static_cast<T>(to_string(static_cast<int>(_val))));
}
template <typename T>
......@@ -71,42 +132,83 @@ define_reference(Stream& _strm, const T& _vrbl, const char* const _name)
if (name != nullptr) // if referenced object has a name already, reuse it
return Data(String("*") + name);
name = stream().book_unique_name(_name);
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;
_strm.os() << type_name<T>() << " " << name << " = " << data.str
<< "; // pass reference" << ENDL;
return Data(name);
case Data::Type::CONSTRUCT:
_strm.os() << type_name<T>() << " " << name;
if (!data.str.empty())
_strm.os() << "(" << data.str << ")";
_strm.os() << "; // pass reference" << ENDL;
return Data(name);
case Data::Type::INTERNAL:
JOURNAL_ERROR("Reference to non-linked opaque data for " << _name);
break;
default:
JOURNAL_ERROR("Reference to unsupported data type for " << _name);
break;
}
_strm.os() << "; // pass reference" << ENDL;
return Data(name);
return data;
}
// 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)
typename std::enable_if<std::is_pointer<T>::value, Data>::type define_reference(
Stream& _strm, T _ptr, const char* const _name)
{
return define(_strm, argument<T>(_vrbl, _name));
const auto name = _strm.search_unique_name(key(_ptr));
if (name != nullptr) // already registered?
return Data(name);
_strm.os() << type_name<T>() << " " << _name << "; // referenced pointer"
<< ENDL;
return Data(_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
const auto name = _strm.make_unique_name(_arg.name);
using rT = typename std::remove_reference<T>::type;
return define_reference<rT>(_strm, _arg.vrbl, _arg.name);
return define_reference<rT>(_strm, _arg.vrbl, name.c_str());
}
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, Data>::type define_pointer(
Stream& _strm, T* const& _vrbl, const char* const /*_name*/)
{
return define(_strm, _vrbl); // call the define for T*
}
template <typename T> // We have a non-null pointer to pointer argument
typename std::enable_if<std::is_pointer<T>::value, Data>::type define_pointer(
Stream& _strm, T* const& _vrbl, const char* const _name)
{
using rT = typename std::remove_pointer<T>::type;
const auto data = define_pointer<rT>(_strm, *_vrbl, _name);
if (data.type == Data::Type::INTERNAL)
{// define data for T* should never be internal, even if define(T) is internal
JOURNAL_ERROR(
type_name<T*>() << " for " << _name << " should not be internal data");
// data is opaque, so journal the pointer value with reinterpret_cast
auto addr = ::Base::format_hex(_vrbl, ::Base::FormatHex::PREFIX_0x);
return Data(define_reinterpret_cast<T>(addr.buffer()));
}
const auto name = _strm.make_unique_name(_name);
_strm.os() << type_name<T*>() << " " << _name << " = &" << data.str;
_strm.end_line();
return Data(name); // return a pointer to the data
}
template <typename T>
......@@ -119,29 +221,14 @@ Data define(Stream& _strm, const ArgT<T, ArgQ::POINTER>& _arg)
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());
return define_pointer<rT>(_strm, _arg.vrbl, _arg.name);
}
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);
return define_reference<rT>(_strm, _arg.vrbl, _arg.link.name);
}
template <typename T>
......@@ -150,11 +237,8 @@ Data define(Stream& _strm, const ArgLinkT<T, ArgQ::POINTER>& _arg)
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;
_strm.os() << type_name<rT>() << " " << name << "; // linked pointer" << ENDL;
return Data(String("&") + name);
}
......@@ -172,13 +256,15 @@ template <typename T> Data define(Stream& _strm, const CArrayT<T>& _c_arr)
std::vector<Data> dfns;
dfns.reserve(_c_arr.size);
for (size_t i = 0; i < _c_arr.size; ++i)
dfns.push_back(define(_strm, _c_arr.pntr[i]));
{
dfns.push_back(define(_strm,
::Journal::argument<const T>(_c_arr.pntr[i], _c_arr.name)));
}
// Make a unique name for this array variable and skip the leading _
const auto name = _strm.make_unique_name(_c_arr.name + 1);
auto& os = _strm.os();
os << "const " << type_name<T>() << " " << name << "[" << _c_arr.size
<< "] = {";
os << type_name<T>() << " " << name << "[" << _c_arr.size << "] = {";
for (size_t i = 0; i < _c_arr.size; ++i)
{
if (i > 0)
......@@ -196,6 +282,35 @@ template <class T> Data define(Stream&, const ReturnLinkT<T>& _rtrn)
return Data(String("auto ") + _rtrn.link.name);
}
inline void define_append_argument(Stream&, String&, const bool) {}
template <typename NextT, typename... RestT>
void define_append_argument(Stream& _strm, String& _str, const bool _frst,
const NextT& _next, const RestT&... _rest)
{
if (!_frst)
_str += ", ";
_str += define(_strm, _next).str;
define_append_argument(_strm, _str, false, _rest...);
}
//! Define a construct expression from the construct arguments
template <typename ObjectT, typename... ArgT>
Data define_construct(Stream& _strm, const ObjectT&, const ArgT&... _args)
{
String str(type_name<ObjectT>());
str += "(";
define_append_argument(_strm, str, true, _args...);
str += ")";
return Data(str, Data::Type::CONSTRUCT);
}
//! Define the default (no arguments) construct expression
template <typename ObjectT> Data define_construct_default(const ObjectT&)
{
return Data(String(), Data::Type::CONSTRUCT);
}
} // namespace Cpp
template <typename T> Data define_in_language(Stream& _strm, const T& _arg)
......@@ -205,5 +320,27 @@ template <typename T> Data define_in_language(Stream& _strm, const T& _arg)
} // namespace Journal
#define JOURNAL_ADD_TYPE(TYPE, VRBL) \
JOURNAL_TYPE_NAME(TYPE); \
template <> Data define(Stream& _strm, TYPE const& VRBL)
#define JOURNAL_ADD_TYPE_INTERNAL(TYPE) \
JOURNAL_TYPE_NAME(TYPE); \
template <> Data define(Stream&, TYPE const&) { return Data(); }
#define JOURNAL_ADD_TYPE_EXTERNAL(TYPE, VRBL) \
JOURNAL_TYPE_NAME(TYPE); \
template <> Data define(Stream& _strm, TYPE* const& VRBL) \
{ \
auto name = _strm.make_unique_name(#VRBL); \
auto data = define_reference(_strm, *VRBL, name.c_str()); \
return Data(String("&") + data.str); \
} \
template <> Data define(Stream& _strm, TYPE const* const& VRBL) \
{ \
return define(_strm, const_cast<TYPE*>(VRBL)); \
} \
template <> Data define(Stream& _strm, TYPE const& VRBL)
#endif // JOURNAL_ON
#endif // BASE_JOURNALCPPDEFS_HH_INCLUDED
......@@ -33,7 +33,8 @@ Stream::CppImpl::CppImpl(const char* const _cmpn_name, const char* const _base_p
Stream::CppImpl::~CppImpl()
{
os() << "return 0;" << ENDL;
if (rtnr_int_)
os() << "return 0;" << ENDL;
close_scope();
}
......@@ -89,7 +90,7 @@ void Stream::CppImpl::include(const char* const _flnm)
const auto& emit_include = [this](const char* const _flnm)
{// TODO: cache emitted includes and don't repeat them
hh_fs_ << "#include <" << _flnm << ">" << std::endl;
hh_fs_ << "#include \"" << _flnm << '"' << std::endl;
};
if (last_cmpn == _flnm)
......@@ -116,13 +117,14 @@ void Stream::CppImpl::include(const char* const _flnm)
emit_include(flnm.c_str());
}
void Stream::CppImpl::main(const char* const _main_fnct)
void Stream::CppImpl::main(const char* const _main_fnct, const bool _rtnr_int)
{
blank_line();
add_includes();
blank_line();
os() << (_main_fnct == nullptr ? "int main()" : _main_fnct) << ENDL;
open_scope();
rtnr_int_ = _rtnr_int;
}
void Stream::CppImpl::code_line(const char* const _line)
......
......@@ -28,7 +28,7 @@ public:
// Language-specific API
Base::IOutputStream& comment_os() override;
void include(const char* const _flnm) override;
void main(const char* const _main_fnct) override;
void main(const char* const _main_fnct, const bool _rtnr_int) override;
void code_line(const char* const _line);
void end_line();
......@@ -71,6 +71,7 @@ private:
std::string path_trns_;
int indt_ = 0;
const int SCOPE_INDENT_SPACE_NMBR = 2;
bool rtnr_int_ = true;
private:
void open_scope();
......
......@@ -177,6 +177,24 @@ String extract_filename(const String& _path)
return path.filename().string();
}
String extract_directory(const String& _path)
{
fs::path path(_path);
return path.parent_path().string();
}
String replace_filename(const String& _path, const String& _flnm)
{
fs::path path(_path);
#ifdef JOURNAL_USE_BOOST
path.remove_filename();
path /= _flnm;
#else
path.replace_filename(_flnm);
#endif//JOURNAL_USE_BOOST
return path.string();
}
}//namespace Journal
#endif//JOURNAL_ON
......@@ -51,6 +51,12 @@ String compose_next_filename(const String& _jrnl_path, const char* const _name);
//! Extract the filename from a path
String extract_filename(const String& _path);
//! Extract the directory of a file from a full filename path
String extract_directory(const String& _path);
//! Replace the filename in the provided path with the given filename
String replace_filename(const String& _path, const String& _flnm);
}//namespace Journal
#endif//JOURNAL_ON
......
......@@ -34,13 +34,13 @@ void Stream::SchemeImpl::line(const String& _str)
scm_fs_.flush();
}
void Stream::SchemeImpl::set_path_transform(const char* const /*_trns*/) {}
void Stream::SchemeImpl::set_path_transform(const char* const) {}
void Stream::SchemeImpl::transform_path(String& /*_path*/) const {}
void Stream::SchemeImpl::transform_path(String&) const {}
void Stream::SchemeImpl::include(const char* const /*_flnm*/) {}
void Stream::SchemeImpl::include(const char* const) {}
void Stream::SchemeImpl::main(const char* const /*_main_fnct*/) {}
void Stream::SchemeImpl::main(const char* const, const bool) {}
void Stream::SchemeImpl::code_line(const char* const _line)
{
......
......@@ -28,7 +28,7 @@ public:
// Language-specific API
Base::IOutputStream& comment_os() override;
void include(const char* const _flnm) override;
void main(const char* const _main_fnct) override;
void main(const char* const _main_fnct, const bool) override;
void code_line(const char* const _line);
void end_line();
......
......@@ -150,9 +150,9 @@ void Stream::include(const char* const _flnm)
JOURNAL_WRAP(impl_->include(_flnm));
}
void Stream::main(const char* const _main_fnct)
void Stream::main(const char* const _main_fnct, const bool _rtnr_int)
{
JOURNAL_WRAP(impl_->main(_main_fnct));
JOURNAL_WRAP(impl_->main(_main_fnct, _rtnr_int));
}
void Stream::end_line() { JOURNAL_WRAP(impl_->end_line()); }
......@@ -218,14 +218,6 @@ bool set_on(const Language _lngg, const bool _on,
return true;
}
void filter_type_name(String& _type_name)
{
const char PTR64_STR[] = " __ptr64";
const auto pos = _type_name.find(PTR64_STR);
if (pos != String::npos)
_type_name.erase(pos, sizeof(PTR64_STR) - 1);
}
} // namespace Journal
#endif // JOURNAL_ON
......@@ -13,10 +13,6 @@
#include <Base/Utils/IOutputStream.hh>
#ifdef JOURNAL_USE_BOOST
#include <boost/core/typeinfo.hpp>
#endif // JOURNAL_USE_BOOST
#include <string>
#include <vector>
#include <type_traits>
......@@ -62,36 +58,6 @@ struct Data
typedef std::vector<Data> DataVector;
// If Boost is available for use in Journal, we provide a default type_name()
// implementation based on RTTI and Boost demangling. Otherwise type_info()
// has to be specialized for any type that is used as an output argument.
#ifdef JOURNAL_USE_BOOST
//! Correct some known issues with automatic type_name()