(svn r10391) -Add: debug support - structured dump of variables/classes/structures (with anti-recursion repository) into string
This commit is contained in:
166
src/misc/dbg_helpers.h
Normal file
166
src/misc/dbg_helpers.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/* $Id:$ */
|
||||
|
||||
/** @file dbg_helpers.h */
|
||||
|
||||
#ifndef DBG_HELPERS
|
||||
#define DBG_HELPERS
|
||||
|
||||
#include <new>
|
||||
#include <map>
|
||||
#include <stack>
|
||||
|
||||
#include "blob.hpp"
|
||||
#include "str.hpp"
|
||||
|
||||
/** Helper template class that provides C array length and item type */
|
||||
template <typename T> struct ArrayT;
|
||||
|
||||
/** Helper template class that provides C array length and item type */
|
||||
template <typename T, size_t N> struct ArrayT<T[N]> {
|
||||
static const size_t length = N;
|
||||
typedef T item_t;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Helper template function that returns item of array at given index
|
||||
* or t_unk when index is out of bounds.
|
||||
*/
|
||||
template <typename E, typename T>
|
||||
inline typename ArrayT<T>::item_t ItemAtT(E idx, T &t, typename ArrayT<T>::item_t t_unk)
|
||||
{
|
||||
if ((size_t)idx >= ArrayT<T>::length) {
|
||||
return t_unk;
|
||||
}
|
||||
return t[idx];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper template function that returns item of array at given index
|
||||
* or t_inv when index == idx_inv
|
||||
* or t_unk when index is out of bounds.
|
||||
*/
|
||||
template <typename E, typename T>
|
||||
inline typename ArrayT<T>::item_t ItemAtT(E idx, T &t, typename ArrayT<T>::item_t t_unk, E idx_inv, typename ArrayT<T>::item_t t_inv)
|
||||
{
|
||||
if ((size_t)idx < ArrayT<T>::length) {
|
||||
return t[idx];
|
||||
}
|
||||
if (idx == idx_inv) {
|
||||
return t_inv;
|
||||
}
|
||||
return t_unk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper template function that returns compound bitfield name that is
|
||||
* concatenation of names of each set bit in the given value
|
||||
* or t_inv when index == idx_inv
|
||||
* or t_unk when index is out of bounds.
|
||||
*/
|
||||
template <typename E, typename T>
|
||||
inline CStrA ComposeNameT(E value, T &t, const char* t_unk, E val_inv, const char* name_inv)
|
||||
{
|
||||
CStrA out;
|
||||
if (value == val_inv) {
|
||||
out = name_inv;
|
||||
} else if (value == 0) {
|
||||
out = "<none>";
|
||||
} else {
|
||||
for (int i = 0; i < ArrayT<T>::length; i++) {
|
||||
if ((value & (1 << i)) == 0) continue;
|
||||
out.AddFormat("%s%s", (out.Size() > 0 ? "+" : ""), t[i]);
|
||||
value &= ~(E)(1 << i);
|
||||
}
|
||||
if (value != 0) out.AddFormat("%s%s", (out.Size() > 0 ? "+" : ""), t_unk);
|
||||
}
|
||||
return out.Transfer();
|
||||
}
|
||||
|
||||
CStrA ValueStr(Trackdir td);
|
||||
CStrA ValueStr(TrackdirBits td_bits);
|
||||
CStrA ValueStr(DiagDirection dd);
|
||||
CStrA ValueStr(SignalType t);
|
||||
|
||||
/** Class that represents the dump-into-string target. */
|
||||
struct DumpTarget {
|
||||
|
||||
/** Used as a key into map of known object instances. */
|
||||
struct KnownStructKey {
|
||||
size_t m_type_id;
|
||||
const void *m_ptr;
|
||||
|
||||
KnownStructKey(size_t type_id, const void *ptr)
|
||||
: m_type_id(type_id)
|
||||
, m_ptr(ptr)
|
||||
{}
|
||||
|
||||
KnownStructKey(const KnownStructKey &src)
|
||||
{
|
||||
m_type_id = src.m_type_id;
|
||||
m_ptr = src.m_ptr;
|
||||
}
|
||||
|
||||
bool operator < (const KnownStructKey &other) const
|
||||
{
|
||||
if ((size_t)m_ptr < (size_t)other.m_ptr) return true;
|
||||
if ((size_t)m_ptr > (size_t)other.m_ptr) return false;
|
||||
if (m_type_id < other.m_type_id) return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<KnownStructKey, CStrA> KNOWN_NAMES;
|
||||
|
||||
CStrA m_out; ///< the output string
|
||||
int m_indent; ///< current indent/nesting level
|
||||
std::stack<CStrA> m_cur_struct; ///< here we will track the current structure name
|
||||
KNOWN_NAMES m_known_names; ///< map of known object instances and their structured names
|
||||
|
||||
DumpTarget()
|
||||
: m_indent(0)
|
||||
{}
|
||||
|
||||
static size_t& LastTypeId();
|
||||
CStrA GetCurrentStructName();
|
||||
bool FindKnownName(size_t type_id, const void* ptr, CStrA &name);
|
||||
|
||||
void WriteIndent();
|
||||
|
||||
void WriteLine(const char *format, ...);
|
||||
void WriteValue(const char *name, const char *value_str);
|
||||
void WriteTile(const char *name, TileIndex t);
|
||||
|
||||
/** Dump given enum value (as a number and as named value) */
|
||||
template <typename E> void WriteEnumT(const char *name, E e)
|
||||
{
|
||||
WriteValue(name, ValueStr(e).Data());
|
||||
}
|
||||
|
||||
void BeginStruct(size_t type_id, const char *name, const void *ptr);
|
||||
void EndStruct();
|
||||
|
||||
/** Dump nested object (or only its name if this instance is already known). */
|
||||
template <typename S> void WriteStructT(const char *name, const S *s)
|
||||
{
|
||||
static size_t type_id = ++LastTypeId();
|
||||
|
||||
if (s == NULL) {
|
||||
/* No need to dump NULL struct. */
|
||||
WriteLine("%s = <null>", name);
|
||||
return;
|
||||
}
|
||||
CStrA known_as;
|
||||
if (FindKnownName(type_id, s, known_as)) {
|
||||
/* We already know this one, no need to dump it. */
|
||||
WriteLine("%s = known_as.%s", name, known_as.Data());
|
||||
} else {
|
||||
/* Still unknown, dump it */
|
||||
BeginStruct(type_id, name, s);
|
||||
s->Dump(*this);
|
||||
EndStruct();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* DBG_HELPERS */
|
Reference in New Issue
Block a user