Tracer.h

// Tracer.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2004

#pragma once
#ifndef Ceda_cxUtils_Tracer_H
#define Ceda_cxUtils_Tracer_H

#include "cxUtils.h"
#include "xstring.h"
#include "StringStream.h"

namespace ceda
{

///////////////////////////////////////////////////////////////////////////////////////////////////
// TraceIndenter

cxUtils_API void IndentTrace(ssize_t offset);

// Number of space characters to indent the trace file
const ssize_t DEFAULT_TRACE_INDENTATION = 2;

struct cxUtils_API TraceIndenter
{
    TraceIndenter(ssize_t indentation = DEFAULT_TRACE_INDENTATION);
    ~TraceIndenter();

    ssize_t m_indentation;
};

///////////////////////////////////////////////////////////////////////////////////////////////////

cxUtils_API void ClearTheTraceFile();

/*
todo: support setting the format of the file.  E.g.

    UTF-8 with BOM
    UTF-8 without BOM
    UTF-16(BE) with BOM
    UTF-16(BE) without BOM
    UTF-16(LE) with BOM
    UTF-16(LE) without BOM
    
    returns true if the file was opened correctly (or stdout is used), false if it failed to
    open the file.  If it returns true it cannot be called again.
*/
cxUtils_API bool SetTraceFile(const xstring& path, bool appendToExistingFile, bool traceTime);

// returns true if SetTraceFile has already been called and succeeded
cxUtils_API bool TraceFileIsOpen();

cxUtils_API void DestroyTheTraceFile();

cxUtils_API void Trace(const xstring& s);


///////////////////////////////////////////////////////////////////////////////////////////////////
// Tracer

struct cxUtils_API TracerX : public StringStream
{
	~TracerX();
    TracerX& ref() { return *this; }
};

/*
todo: At warning level 4 this generates 

warning C4239: nonstandard extension used : 'type cast' : conversion from 'ceda::TracerX' to 
               'ceda::TracerX &'
A non-const reference may only be bound to an lvalue
*/

#define Tracer() ceda::TracerX().ref()


///////////////////////////////////////////////////////////////////////////////////////////////////

struct ITraceObserver
{
    virtual void OnTrace(const xstring& str) = 0;
};

cxUtils_API void SetTraceObserver(ITraceObserver* obs);
cxUtils_API ITraceObserver* GetTraceObserver();

struct cxUtils_API ScopedTraceObserver
{
    ceda::ITraceObserver* prev_;

    ScopedTraceObserver( ITraceObserver* o )
    {
        prev_ = ceda::GetTraceObserver();
        SetTraceObserver( o );
    }

    ~ScopedTraceObserver()
    {
        // restore the previous trace observer
        SetTraceObserver( prev_ );
    }
};

class cxUtils_API StreamTraceObserver : public ITraceObserver
{
    ITraceObserver* prev_;
    StringStream m_ss;

public:
    // supply a non-nullptr ITraceObserver to create a chain
    StreamTraceObserver( ITraceObserver* prev ) : prev_( prev ) {}

    virtual void OnTrace(const xstring& str)
    {
        m_ss << str;
        if( prev_ ) prev_->OnTrace( str );
    }

    StringStream& GetStream()
    {
        return m_ss;
    }

    xstring GetString()
    {
        return m_ss.GetString();
    }
};

} // namespace ceda

#endif // include guard