Commit 86c62dda authored by Adam Helps's avatar Adam Helps Committed by Martin Marinov
Browse files

TSP-3429 TSP-3435 Handles and reference counts (#9)

* Adds optional pre-call and post-call events in the journal to implement reference count abridgement.
* Adds new documentation for many functions, and improves existing.
* Removes "JOURNAL_FUNCTION" from the created macros when journal is turned off, because there is no such macro when journal is turned on.
* Adds the ability to unlink names in the journal manually. This was already being done by destructors, but there wasn't a way to do it for names managed in other ways.
* Add HandleT<> to support handle tracking
* Generalize the key<> function to handles and make key a pair of size_t 
* Simplify ReturnLink and make it work for HandleT<>
* Adds an operator< to the HandleT class so that it can be used as a key in associative containers (T-Splines uses it this way for ref-count collapsing).
parent 5ee99aa8
...@@ -290,11 +290,17 @@ template <typename T> Data define(Stream& _strm, const CArrayT<T>& _c_arr) ...@@ -290,11 +290,17 @@ template <typename T> Data define(Stream& _strm, const CArrayT<T>& _c_arr)
return Data(name); return Data(name);
} }
template <class T> Data define(Stream&, const ReturnLinkT<T>& _rtrn) template <> inline Data define(Stream&, const ReturnLink& _rtrn)
{ {
return Data(String("auto ") + _rtrn.link.name); return Data(String("auto ") + _rtrn.link.name);
} }
template <typename PointerT, typename IndexT>
Data define(Stream& _strm, const HandleT<PointerT, IndexT>& _hndl)
{
return Data(_strm.retrieve_unique_name(key(_hndl)));
}
inline void define_append_argument(Stream&, String&, const bool) {} inline void define_append_argument(Stream&, String&, const bool) {}
template <typename NextT, typename... RestT> template <typename NextT, typename... RestT>
...@@ -341,7 +347,12 @@ template <typename T> Data define_in_language(Stream& _strm, const T& _arg) ...@@ -341,7 +347,12 @@ template <typename T> Data define_in_language(Stream& _strm, const T& _arg)
JOURNAL_TYPE_NAME(TYPE); \ JOURNAL_TYPE_NAME(TYPE); \
template <> Data define(Stream&, TYPE const&) { return Data(); } template <> Data define(Stream&, TYPE const&) { return Data(); }
// an internal type that is tracked by the name book at all time /*!
This generates the appropriate type_name and define functions for
an internal type that is tracked by the name book at all times. This
is appropriate for opaque pointers whose value always first
appears when it is returned from a journaled function.
*/
#define JOURNAL_ADD_TYPE_INTERNAL_TRACKED(TYPE, VRBL) \ #define JOURNAL_ADD_TYPE_INTERNAL_TRACKED(TYPE, VRBL) \
JOURNAL_ADD_TYPE_INTERNAL(TYPE); \ JOURNAL_ADD_TYPE_INTERNAL(TYPE); \
template <> Data define(Stream& _strm, TYPE* const& VRBL) \ template <> Data define(Stream& _strm, TYPE* const& VRBL) \
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
namespace Journal namespace Journal
{ {
#define JOURNAL_WRAP(EXPR) \ #define JOURNAL_WRAP(EXPR) \
try \ try \
{ \ { \
...@@ -82,6 +81,11 @@ void Stream::link_unique_name(const Key _key, const char* const _name) ...@@ -82,6 +81,11 @@ void Stream::link_unique_name(const Key _key, const char* const _name)
JOURNAL_WRAP(impl_->link_unique_name(_key, _name)); JOURNAL_WRAP(impl_->link_unique_name(_key, _name));
} }
void Stream::unlink_unique_name(const Key _key)
{
JOURNAL_WRAP(impl_->unlink_unique_name(_key));
}
const char* Stream::search_unique_name(const Key _key) const const char* Stream::search_unique_name(const Key _key) const
{ {
JOURNAL_WRAP_RETURN(impl_->search_unique_name(_key)); JOURNAL_WRAP_RETURN(impl_->search_unique_name(_key));
...@@ -106,7 +110,8 @@ void Stream::emit_constructor( ...@@ -106,7 +110,8 @@ void Stream::emit_constructor(
void Stream::emit_destructor(const char* const _fnct, const Key& _obj) void Stream::emit_destructor(const char* const _fnct, const Key& _obj)
{ {
JOURNAL_WRAP(impl_->emit_destructor_and_unlink(_fnct, _obj)); JOURNAL_WRAP(impl_->emit_destructor(_fnct, _obj));
unlink_unique_name(_obj);
} }
void Stream::emit_method_outcome( void Stream::emit_method_outcome(
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <Base/Utils/IOutputStream.hh> #include <Base/Utils/IOutputStream.hh>
#include <limits>
#include <string> #include <string>
#include <vector> #include <vector>
#include <type_traits> #include <type_traits>
...@@ -24,12 +25,73 @@ typedef std::string String; ...@@ -24,12 +25,73 @@ typedef std::string String;
using Base::ENDL; using Base::ENDL;
using Base::IOutputStream; using Base::IOutputStream;
typedef size_t Key; /*
const Key INVALID_KEY = 0; 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());
}
template <class T> Key key(const T* const _pntr) /*!
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)
{ {
return reinterpret_cast<Key>(_pntr); return Key(
reinterpret_cast<size_t>(_hndl.pntr), static_cast<size_t>(_hndl.indx));
} }
// Wrapper for data as text from any type, returned from the Journal::define() // Wrapper for data as text from any type, returned from the Journal::define()
...@@ -95,18 +157,26 @@ public: ...@@ -95,18 +157,26 @@ public:
//! Put the "end line" combination for the journal, e.g., ";\n" for C/C++ //! Put the "end line" combination for the journal, e.g., ";\n" for C/C++
void end_line(); void end_line();
//! Make an unique journal name which can be linked to a pointer/key later //! Make an unique journal name which can be linked to a pointer or key later
String make_unique_name(const char* const _name); String make_unique_name(const char* const _name);
//! Book an unique journal name which can be linked to a pointer/key later //! Book an unique journal name which can be linked to a pointer or key later
const char* book_unique_name(const char* const _name); const char* book_unique_name(const char* const _name);
//! Link to a pointer/key to a pre-booked unique journal name //! Link to a pointer/key to a pre-booked unique journal name
void link_unique_name(const Key _key, const char* const _name); void link_unique_name(const Key _key, const char* const _name);
/*! /*!
Search for the unique name corresponding to the key, return nullptr if the key Given an existing booked key, this unlinks the key from its booked name.
has not been linked. 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.
*/ */
const char* search_unique_name(const Key _key) const; const char* search_unique_name(const Key _key) const;
...@@ -287,7 +357,7 @@ private: ...@@ -287,7 +357,7 @@ private:
/*! /*!
Turn on/off the journal, set the base path optionally. If no base path is 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: provided, the base path will be composed from the component name as follows:
* from the environment variable toupper(cmpn_name)_JOURNAL_BASE if defined, * from the environment variable toupper(cmpn_nam e)_JOURNAL_BASE if defined,
* from the local home data directory of the current user/cmpn_name, e.g., * 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, %LOCALAPP_DATA%/cmpn_name on Windows, or $HOME/.cmpn_name on Linux and OS X,
* the current folder sub-folder ./cmpn_name. * the current folder sub-folder ./cmpn_name.
...@@ -477,30 +547,42 @@ template <typename T, ArgQ Q> struct ArgLinkT : public ArgSetT<T, Q> ...@@ -477,30 +547,42 @@ template <typename T, ArgQ Q> struct ArgLinkT : public ArgSetT<T, Q>
} }
}; };
template <typename T> struct ReturnLinkT 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
{ {
static_assert(std::is_pointer<T>::value, "Return value should be a pointer");
Name link; Name link;
explicit ReturnLinkT(const char* const _name) explicit ReturnLink(const char* const _name)
: link(on() ? stream().book_unique_name(_name) : _name) : link(on() ? stream().book_unique_name(_name) : _name)
{ {
} }
T operator=(T pntr) // 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)
{ {
if (on() && pntr != nullptr) if (on() && pntr != nullptr)
stream().link_unique_name(key(pntr), link.name); stream().link_unique_name(key(pntr), link.name);
return pntr; return pntr;
} }
};
template <typename T> auto argument_link(T& _vrbl, const char* const _name, // overload of assignment if the linked type T is a handle
const LinkMode _mode = LinkMode::MANUAL) template <typename PointerT, typename IndexT>
{ IndexT operator=(const HandleT<PointerT, IndexT>& _hndl)
auto arg = argument<T>(_vrbl, _name); {
return ArgLinkT<T, arg.quality()>(arg, _mode); if (on() && _hndl.valid())
} stream().link_unique_name(key(_hndl), link.name);
return _hndl.indx;
}
};
} // namespace Journal } // namespace Journal
...@@ -512,18 +594,89 @@ template <typename T> auto argument_link(T& _vrbl, const char* const _name, ...@@ -512,18 +594,89 @@ template <typename T> auto argument_link(T& _vrbl, const char* const _name,
#define JOURNAL_CHECK if (::Journal::on()) #define JOURNAL_CHECK if (::Journal::on())
/*!
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.
*/
#define JOURNAL_CALL(CALL, ...) \ #define JOURNAL_CALL(CALL, ...) \
::Journal::stream().CALL(__JOURNAL_FUNCTION__, ##__VA_ARGS__) { \
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.
\todo customize this option
*/
#ifdef JOURNAL_AUTO_INCLUDE #ifdef JOURNAL_AUTO_INCLUDE
#define JOURNAL_INCLUDE_THIS ::Journal::stream().include(__FILE__) #define JOURNAL_INCLUDE_THIS ::Journal::stream().include(__FILE__)
#else #else
#define JOURNAL_INCLUDE_THIS #define JOURNAL_INCLUDE_THIS
#endif//JOURNAL_AUTO_INCLUDE #endif//JOURNAL_AUTO_INCLUDE
/*!
Emits code into the journal to include the given header file.
*/
#define JOURNAL_INCLUDE(FILE) \ #define JOURNAL_INCLUDE(FILE) \
{ JOURNAL_CHECK ::Journal::stream().include(FILE); } { JOURNAL_CHECK ::Journal::stream().include(FILE); }
/*!
Emits a comment into the header file. Useful for providing context
or error messages to someone reading the journal file.
*/
#define JOURNAL_COMMENT(CMNT) \ #define JOURNAL_COMMENT(CMNT) \
{ JOURNAL_CHECK { ::Journal::stream().comment_os() << CMNT << Base::ENDL; } } { JOURNAL_CHECK { ::Journal::stream().comment_os() << CMNT << Base::ENDL; } }
...@@ -554,12 +707,44 @@ template <typename T> auto argument_link(T& _vrbl, const char* const _name, ...@@ -554,12 +707,44 @@ template <typename T> auto argument_link(T& _vrbl, const char* const _name,
ARG, #ARG, ::Journal::LinkMode::AUTO) ARG, #ARG, ::Journal::LinkMode::AUTO)
#define JOURNAL_METHOD_RETURN_LINK(NAME, IMPL, ...) \ #define JOURNAL_METHOD_RETURN_LINK(NAME, IMPL, ...) \
::Journal::ReturnLinkT<decltype(IMPL)> JL(rtrn)(NAME); \ ::Journal::ReturnLink JL(rtrn)(NAME); \
JOURNAL(method, JL(rtrn), this, ##__VA_ARGS__); \ JOURNAL(method, JL(rtrn), this, ##__VA_ARGS__); \
return JL(rtrn) = IMPL return JL(rtrn) = IMPL;
/*!
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.
*/
#define JOURNAL_FUNCTION_RETURN_LINK(NAME, IMPL, ...) \ #define JOURNAL_FUNCTION_RETURN_LINK(NAME, IMPL, ...) \
::Journal::ReturnLinkT<decltype(IMPL)> JL(rtrn)(NAME); \ ::Journal::ReturnLink JL(rtrn)(NAME); \
JOURNAL(function, JL(rtrn), ##__VA_ARGS__); \ JOURNAL(function, JL(rtrn), ##__VA_ARGS__); \
return JL(rtrn) = IMPL return JL(rtrn) = IMPL
...@@ -576,6 +761,8 @@ template <typename T> auto argument_link(T& _vrbl, const char* const _name, ...@@ -576,6 +761,8 @@ template <typename T> auto argument_link(T& _vrbl, const char* const _name,
if (CND) { JOURNAL_ERROR(MSG); ACTN; } if (CND) { JOURNAL_ERROR(MSG); ACTN; }
#define JT(ARG) ::Journal::argument<decltype(ARG)>(ARG, #ARG) #define JT(ARG) ::Journal::argument<decltype(ARG)>(ARG, #ARG)
#define JH(PNTR, INDX) \
::Journal::HandleT<decltype(PNTR), decltype(INDX)>(PNTR, INDX)
#else #else
...@@ -589,7 +776,6 @@ template <typename T> auto argument_link(T& _vrbl, const char* const _name, ...@@ -589,7 +776,6 @@ template <typename T> auto argument_link(T& _vrbl, const char* const _name,
#define JOURNAL_DESTRUCTOR #define JOURNAL_DESTRUCTOR
#define JOURNAL_METHOD_OUTCOME(...) #define JOURNAL_METHOD_OUTCOME(...)
#define JOURNAL_METHOD_VOID(...) #define JOURNAL_METHOD_VOID(...)
#define JOURNAL_FUNCTION(VRBL, ...)
#define JOURNAL_FUNCTION_VOID(...) #define JOURNAL_FUNCTION_VOID(...)
#define JOURNAL_FUNCTION_OUTCOME(...) #define JOURNAL_FUNCTION_OUTCOME(...)
......
...@@ -26,10 +26,6 @@ Stream::Impl::Impl(const char* const _cmpn_name, const char* const _base_path) ...@@ -26,10 +26,6 @@ Stream::Impl::Impl(const char* const _cmpn_name, const char* const _base_path)
{ {
if (!make_directory(jrnl_path_)) // failed to make the directory? if (!make_directory(jrnl_path_)) // failed to make the directory?
throw INIT_FAILED; throw INIT_FAILED;
name_idx_ = 0;
name_book_.clear();
key_name_.clear();
} }
Stream::Impl::~Impl() {} Stream::Impl::~Impl() {}
...@@ -115,33 +111,42 @@ const char* Stream::Impl::book_unique_name(const char* const _name) ...@@ -115,33 +111,42 @@ const char* Stream::Impl::book_unique_name(const char* const _name)
void Stream::Impl::link_unique_name(const Key _key, const char* const _name) void Stream::Impl::link_unique_name(const Key _key, const char* const _name)
{ {
JOURNAL_ERROR_if(_key == INVALID_KEY, "INVALID_KEY for " << _name); // TODO: do we want to have the concept of an customizable invalid key for
key_name_[_key] = _name; // any key that is supported, i.e, 0 for pointers, SIZE_T_MAX for others, etc?
// If yes, we should expand such checks to the complete API
// JOURNAL_ERROR_if(_key == invld_key, "INVALID_KEY for " << _name);
key_name_map_[_key] = _name;
}
void Stream::Impl::unlink_unique_name(const Key _key)
{
key_name_map_.erase(_key);
} }
const char* Stream::Impl::search_unique_name(const Key _key) const const char* Stream::Impl::search_unique_name(const Key _key) const
{ {
const auto it = key_name_.find(_key); const auto it = key_name_map_.find(_key);
return it == key_name_.end() ? nullptr : it->second; return it == key_name_map_.end() ? nullptr : it->second;
} }
const char* Stream::Impl::retrieve_unique_name(const Key _key) const char* Stream::Impl::retrieve_unique_name(const Key _key) const
{ {
const auto* name = search_unique_name(_key); const auto* name = search_unique_name(_key);
JOURNAL_ERROR_if_do(name == nullptr, JOURNAL_ERROR_if_do(name == nullptr,
"Unique name not stored for key " "Unique name not stored for key <"
<< Base::format_hex(_key, Base::FormatHex::PREFIX_0x), << Base::format_hex(_key.first, Base::FormatHex::PREFIX_0x) << ", "
<< Base::format_hex(_key.second, Base::FormatHex::PREFIX_0x) << '>',
return "UNKNOWN_key"); return "UNKNOWN_key");
return name; return name;
} }
bool Stream::Impl::obtain_unique_name( bool Stream::Impl::obtain_unique_name(
const Key _key, const char* const _name_base, const char*& _name) const Key _key, const char* const _name_base, const char*& _name)
{ {
_name = search_unique_name(_key); _name = search_unique_name(_key);
if (_name != nullptr) if (_name != nullptr)
return false; return false;
_name = book_unique_name(_name_base); _name = stream().book_unique_name(_name_base);
link_unique_name(_key, _name); link_unique_name(_key, _name);
return true; return true;
} }
...@@ -152,12 +157,6 @@ void Stream::Impl::time() ...@@ -152,12 +157,6 @@ void Stream::Impl::time()
<< System::Environment::time(); // time() already contains ENDL << 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 }//namespace Journal
#endif//JOURNAL_ON #endif//JOURNAL_ON
...@@ -13,8 +13,8 @@ ...@@ -13,8 +13,8 @@
#include <Base/Journal/JournalStream.hh> #include <Base/Journal/JournalStream.hh>
#include <Base/Utils/OStringStream.hh> #include <Base/Utils/OStringStream.hh>
#include <map>
#include <set> #include <set>
#include <map>
#include <fstream> #include <fstream>
namespace Journal namespace Journal
...@@ -46,8 +46,9 @@ public: ...@@ -46,8 +46,9 @@ public:
String make_unique_name(const char* const _name); String make_unique_name(const char* const _name);
const char* book_unique_name(const char* const _name); const char* book_unique_name(const char* const _name);
void link_unique_name(const Key _key, const char* const _name); void link_unique_name(const Key _key, const char* const _name);
void unlink_unique_name(const Key _key);
const char* search_unique_name(const Key _key) const; const char* search_unique_name(const Key _key) const;
const char* retrieve_unique_name(const Key _key); const char* retrieve_unique_name(const Key _key) const;
bool obtain_unique_name( bool obtain_unique_name(
const Key _key, const char* const _name_base, const char*& _name); const Key _key, const char* const _name_base, const char*& _name);
...@@ -66,7 +67,6 @@ public: ...@@ -66,7 +67,6 @@ public:
virtual void emit_constructor( virtual void emit_constructor(
const char*