Click here to Skip to main content
15,881,248 members
Articles / Programming Languages / Visual C++ 12.0
Tip/Trick

Simple Log Print Class with a Variadic Template Technique

Rate me:
Please Sign up or sign in to vote.
4.29/5 (5 votes)
26 Jan 2015CPOL5 min read 30.1K   163   4   16
This tip presents a simple class for printing of log information with some features of C++11.

Introduction

This tip presents a simple class for printing of log information. The developed class uses a variadic template of C++11 and std::lock_guard<std::mutex> for flexible printing of log information.

Background

In some my last projects, I needed about a simple class for printing some log information for checking the correctness of the program working. In some previous projects, I used ellipsis parameters - old technique of C:

C++
void foo(parm_list, ...);
void foo(...);

It is an old technique which allows to type any amount of arguments without the need of overriding of method signature.

However, since starting to use C++11 on Visual Studio 2013 and MinGW 4.82, I found that my old solution with ellipsis parameters is not working. I needed code for logging any amount information at the specific point of code, and it is nonsense to write hundred overridings of print out method. I had decided to use a variadic template of C++11 for getting the solution which close to the ellipsis parameters technique of C programming language.

Using the Code

The whole code of the class LogPringOut is presented in the next listing:

C++
#include <iostream>
#include <map>
#include <mutex>

	class LogPrintOut
	{
	public:

		enum Level
		{
			INFO_LEVEL=1,
			ERROR_LEVEL=2
		};
				
		static LogPrintOut& getInstance()
		{
			static LogPrintOut instance;

			return instance;
		}

		void setVerbose(bool state)
		{
			std::lock_guard<std::mutex> lock(mLogPrintOutMutex);

			mVerbose = state;
		}

		bool setPtrLogPrintOutStream(Level level, std::wostream* pwostream, bool selfReleased)
		{
			std::lock_guard<std::mutex> lock(mLogPrintOutMutex);

			bool result = false;

			if (pwostream == nullptr)
				return result;

			releaseStream(level);

			Stream stream;

			stream.pwostream = pwostream;

			stream.selfReleased = selfReleased;

			mLevelStreams[level] = stream;

			result = true;

			return result;
		}

		bool releaseLogPrintOutStream(Level level)
		{
			std::lock_guard<std::mutex> lock(mLogPrintOutMutex);

			return releaseStream(level);
		}

		template<typename T>
		inline bool printOutln(Level level, const T *data)
		{
			std::lock_guard<std::mutex> lock(mLogPrintOutMutex);

			bool result = false;

			if (mVerbose)
			{

				auto itr = mLevelStreams.find(level);

				if (itr == mLevelStreams.end())
					return result;

				if (itr->second.pwostream == nullptr)
					return result;

				switch (level)
				{
				case CaptureManager::LogPrintOut::INFO_LEVEL:
					(*(itr->second.pwostream)) << L"INFO_LEVEL: ";
					break;
				case CaptureManager::LogPrintOut::ERROR_LEVEL:
					(*(itr->second.pwostream)) << L"ERROR_LEVEL: ";
					break;
				default:
					break;
				}

				(*(itr->second.pwostream))<<(data) << std::endl;

				itr->second.pwostream->flush();

				result = true;
			}

			return result;
		}

		template<typename T>
		inline bool printOutln(Level level, const T data)
		{
			std::lock_guard<std::mutex> lock(mLogPrintOutMutex);

			bool result = false;

			if (mVerbose)
			{
				auto itr = mLevelStreams.find(level);

				if (itr == mLevelStreams.end())
					return result;

				if (itr->second.pwostream == nullptr)
					return result;

				switch (level)
				{
				case CaptureManager::LogPrintOut::INFO_LEVEL:
					(*(itr->second.pwostream)) << L"INFO_LEVEL: ";
					break;
				case CaptureManager::LogPrintOut::ERROR_LEVEL:
					(*(itr->second.pwostream)) << L"ERROR_LEVEL: ";
					break;
				default:
					break;
				}

				(*(itr->second.pwostream)) << (data) << std::endl;

				itr->second.pwostream->flush();

				result = true;
			}

			return result;
		}

		template <typename T, typename... Args>
		inline bool printOutln(Level level, const T data, const Args... rest)
		{
			std::lock_guard<std::mutex> lock(mLogPrintOutMutex);

			bool result = false;

			if (mVerbose)
			{

				auto itr = mLevelStreams.find(level);

				if (itr == mLevelStreams.end())
					return result;

				if (itr->second.pwostream == nullptr)
					return result;

				switch (level)
				{
				case CaptureManager::LogPrintOut::INFO_LEVEL:
					(*(itr->second.pwostream)) << L"INFO_LEVEL: ";
					break;
				case CaptureManager::LogPrintOut::ERROR_LEVEL:
					(*(itr->second.pwostream)) << L"ERROR_LEVEL: ";
					break;
				default:
					break;
				}

				write(itr->second.pwostream, data, rest...);

				itr->second.pwostream->operator<<(std::endl);

				itr->second.pwostream->flush();

				result = true;
			}

			return result;
		}
		
		template <typename T, typename... Args>
		inline bool printOutln(Level level, const T *data, const Args... rest)
		{
			std::lock_guard<std::mutex> lock(mLogPrintOutMutex);

			bool result = false;

			if (mVerbose)
			{

				auto itr = mLevelStreams.find(level);

				if (itr == mLevelStreams.end())
					return result;

				if (itr->second.pwostream == nullptr)
					return result;

				switch (level)
				{
				case CaptureManager::LogPrintOut::INFO_LEVEL:
					(*(itr->second.pwostream)) << L"INFO_LEVEL: ";
					break;
				case CaptureManager::LogPrintOut::ERROR_LEVEL:
					(*(itr->second.pwostream)) << L"ERROR_LEVEL: ";
					break;
				default:
					break;
				}

				write(itr->second.pwostream, data, rest...);

				itr->second.pwostream->operator<<(std::endl);

				itr->second.pwostream->flush();

				result = true;
			}

			return result;
		}

		template<typename T>
		inline bool printOut(Level level, const T *data)
		{
			std::lock_guard<std::mutex> lock(mLogPrintOutMutex);

			bool result = false;

			if (mVerbose)
			{

				auto itr = mLevelStreams.find(level);

				if (itr == mLevelStreams.end())
					return result;

				if (itr->second.pwostream == nullptr)
					return result;

				switch (level)
				{
				case CaptureManager::LogPrintOut::INFO_LEVEL:
					(*(itr->second.pwostream)) << L"INFO_LEVEL: ";
					break;
				case CaptureManager::LogPrintOut::ERROR_LEVEL:
					(*(itr->second.pwostream)) << L"ERROR_LEVEL: ";
					break;
				default:
					break;
				}

				(*(itr->second.pwostream)) << (data);

				itr->second.pwostream->flush();

				result = true;
			}

			return result;
		}

		template<typename T>
		inline bool printOut(Level level, const T data)
		{
			std::lock_guard<std::mutex> lock(mLogPrintOutMutex);

			bool result = false;

			if (mVerbose)
			{
				auto itr = mLevelStreams.find(level);

				if (itr == mLevelStreams.end())
					return result;

				if (itr->second.pwostream == nullptr)
					return result;

				switch (level)
				{
				case CaptureManager::LogPrintOut::INFO_LEVEL:
					(*(itr->second.pwostream)) << L"INFO_LEVEL: ";
					break;
				case CaptureManager::LogPrintOut::ERROR_LEVEL:
					(*(itr->second.pwostream)) << L"ERROR_LEVEL: ";
					break;
				default:
					break;
				}

				(*(itr->second.pwostream)) << (data);

				itr->second.pwostream->flush();

				result = true;
			}

			return result;
		}

		template <typename T, typename... Args>
		inline bool printOut(Level level, const T data, const Args... rest)
		{
			std::lock_guard<std::mutex> lock(mLogPrintOutMutex);

			bool result = false;

			if (mVerbose)
			{
				auto itr = mLevelStreams.find(level);

				if (itr == mLevelStreams.end())
					return result;

				if (itr->second.pwostream == nullptr)
					return result;

				switch (level)
				{
				case CaptureManager::LogPrintOut::INFO_LEVEL:
					(*(itr->second.pwostream)) << L"INFO_LEVEL: ";
					break;
				case CaptureManager::LogPrintOut::ERROR_LEVEL:
					(*(itr->second.pwostream)) << L"ERROR_LEVEL: ";
					break;
				default:
					break;
				}

				write(itr->second.pwostream, data, rest...);

				itr->second.pwostream->flush();

				result = true;
			}

			return result;
		}

		template <typename T, typename... Args>
		inline bool printOut(Level level, const T *data, const Args... rest)
		{
			std::lock_guard<std::mutex> lock(mLogPrintOutMutex);

			bool result = false;

			if (mVerbose)
			{
				auto itr = mLevelStreams.find(level);

				if (itr == mLevelStreams.end())
					return result;

				if (itr->second.pwostream == nullptr)
					return result;

				switch (level)
				{
				case CaptureManager::LogPrintOut::INFO_LEVEL:
					(*(itr->second.pwostream)) << L"INFO_LEVEL: ";
					break;
				case CaptureManager::LogPrintOut::ERROR_LEVEL:
					(*(itr->second.pwostream)) << L"ERROR_LEVEL: ";
					break;
				default:
					break;
				}

				write(itr->second.pwostream, data, rest...);

				itr->second.pwostream->flush();

				result = true;
			}

			return result;
		}
		
	private:

		struct Stream
		{
			std::wostream* pwostream = nullptr;

			bool selfReleased = false;
		};

		std::map<level, stream=""> mLevelStreams;

		bool mVerbose = true;

		std::mutex mLogPrintOutMutex;

		LogPrintOut()
		{
			setPtrLogPrintOutStream(LogPrintOut::INFO_LEVEL, &std::wcout, false);

			setPtrLogPrintOutStream(LogPrintOut::ERROR_LEVEL, &std::wcout, false);
		}

		~LogPrintOut()
		{
			std::lock_guard<std::mutex> lock(mLogPrintOutMutex);

			releaseStreams();
		}

		LogPrintOut(const LogPrintOut&);

		LogPrintOut& operator=(const LogPrintOut&);

		void releaseStreams()
		{
			auto itr = mLevelStreams.begin();

			for (; itr != mLevelStreams.end(); ++itr)
			{
				if (itr->second.selfReleased && itr->second.pwostream != nullptr)
				{
					itr->second.pwostream->flush();

					delete itr->second.pwostream;
				}
			}

			mLevelStreams.clear();
		}

		bool releaseStream(Level level)
		{

			bool result = false;

			auto itr = mLevelStreams.find(level);

			if (itr != mLevelStreams.end())
			{
				if (itr->second.selfReleased && itr->second.pwostream != nullptr)
				{
					itr->second.pwostream->flush();
					
					delete itr->second.pwostream;

					mLevelStreams.erase(itr);

					result = true;
				}
			}

			return result;
		}

		template<typename T>
		inline void write(std::wostream* pwostream, const T *data)
		{
			(*pwostream) << (data);
		}

		template<typename T>
		inline void write(std::wostream* pwostream, const T data)
		{
			(*pwostream) << (data);
		}

		template <typename T, typename... Args>
		inline void write(std::wostream* pwostream, const T *data, const Args... rest)
		{
			(*pwostream) << (data);

			write(pwostream, rest...);
		}

		template <typename T, typename... Args>
		inline void write(std::wostream* pwostream, const T data, const Args... rest)
		{
			(*pwostream) << (data);

			write(pwostream, rest...);
		}
	};

The class has the template methods and is defined in header file.

The class is written on singelton pattern and has main access method LogPrintOut& getInstance(). Method void setVerbose(bool state) allows to control of enable and disable printing out. Enumeration Level is used for defining of target out for printing - INFO_LEVEL, ERROR_LEVEL. The method bool setPtrLogPrintOutStream(Level level, std::wostream* pwostream, bool selfReleased) is used for setting to the specific level, pointer on output stream pwostream and flag for manual releasing of output stream pointer selfReleased.

The next code presents how to set and release the output stream pointer.

C++
wostringstream wost;

auto result = LogPrintOut::getInstance().setPtrLogPrintOutStream
(LogPrintOut::INFO_LEVEL, &wost, false);

result = LogPrintOut::getInstance().setPtrLogPrintOutStream
(LogPrintOut::INFO_LEVEL, new std::wofstream("app.log"), true);

result = LogPrintOut::getInstance().releaseLogPrintOutStream
(LogPrintOut::INFO_LEVEL);

The next main methods are an overridden form of the methods printOutln and printOut:

C++
template<typename T>
        inline bool printOutln(Level level, const T *data)

template<typename T>
        inline bool printOutln(Level level, const T data)

template <typename T, typename... Args>
        inline bool printOutln(Level level, const T *data, const Args... rest)

template <typename T, typename... Args>
        inline bool printOutln(Level level, const T data, const Args... rest)

template<typename T>
        inline bool printOut(Level level, const T *data)

template<typename T>
        inline bool printOut(Level level, const T data)

template <typename T, typename... Args>
        inline bool printOut(Level level, const T *data, const Args... rest)

template <typename T, typename... Args>
        inline bool printOut(Level level, const T data, const Args... rest)

The method printOutln is used for print out information with the break the line and the method printOut is used for print out information without the break the line. Each of the methods has an overridden signature for using template variables by value - const T data, and by pointer on - const T *data. The main important part is using of typename... Args signature - variadic template of C++11. It allows to present the different types of arguments as massive of templates. Compilator unrolls that massive of templates by the next way:

C++
template <typename T>
inline void write(std::wostream* pwostream, const T data)
{
       (*pwostream) << (data);
}

template <typename T, typename... Args>
inline void write(std::wostream* pwostream, const T data, const Args... rest)
{
       (*pwostream) << (data);

       write(pwostream, rest...);
}

Compilator calls code of
template <typename T, typename... Args> inline void write(std::wostream* pwostream, const T data, const Args... rest) method till massive of templates has finished - in that case compiler will set code of
template <typename T> inline void write(std::wostream* pwostream, const T data) method. It means that compilator digs the massive of template recursively and build fast recursive code.

How it can be used? Look at the next code:

C++
wostringstream wost;

bool result = LogPrintOut::getInstance().setPtrLogPrintOutStream(LogPrintOut::INFO_LEVEL, &wost, false);

LogPrintOut::getInstance().printOutln
(LogPrintOut::INFO_LEVEL, L"Line number: ", 9);

LogPrintOut::getInstance().printOutln
(LogPrintOut::INFO_LEVEL, 9, L"Line number: ");

LogPrintOut::getInstance().printOutln
(LogPrintOut::INFO_LEVEL, L"Line number: ", 9, L"Line number: ");

LogPrintOut::getInstance().printOutln
(LogPrintOut::INFO_LEVEL, L"Line number: ", L"Second argument");

LogPrintOut::getInstance().printOutln
(LogPrintOut::INFO_LEVEL, L"Line number: ", L"Second argument", 9);

So, you can see that we can change any order and type of arguments without changing the original signature of printOutln method - all such work is executed by compiler!!!. It is close enough to the ellipsis parameters technique and can be considered as replacement of it in some tasks.

You can say - it will work well with the primary buildin types like as int, float, double, etc. How about the developer defined types like classes. This case is presented in the next code listing:

C++
class TextClass
{
public:
    TextClass(const wchar_t *pStr)
    {
        text = pStr;
    }

    ~TextClass()
    {

    }

    wstring getText() const
    {
        return text;
    }

private:

    wstring text;
};


wostream& operator<<(wostream& s, const TextClass & textClass)
{
    return s << textClass.getText();
}

wostream& operator<<(wostream& s, const TextClass *ptrTextClass)
{
    return s << ptrTextClass->getText();
}

/////

.

.

.

.

///


wostringstream wost;

bool result = LogPrintOut::getInstance().setPtrLogPrintOutStream
(LogPrintOut::INFO_LEVEL, &wost, false);

LogPrintOut::getInstance().printOut(LogPrintOut::INFO_LEVEL, 
TextClass(L"Stack instance of TextClass"));

TextClass *ptrTextClass = new TextClass(L"in the heap instance of TextClass");

LogPrintOut::getInstance().printOut(LogPrintOut::INFO_LEVEL, ptrTextClass);

LogPrintOut::getInstance().printOut(LogPrintOut::INFO_LEVEL, 
ptrTextClass, TextClass(L" and Stack instance of TextClass"));

All we need is define the new signature for overridden method operator<< of std::wostream class for reference and pointer of the developer defined class.

Code of the LogPrintOut class can be downloaded at this link.

Points of Interest

In some projects, I had multithread patterns of code and I included into the class LogPrintOut primitive of multithread access synchronization - std::lock_guard<std::mutex> lock(mLogPrintOutMutex) for simple management of printing of log information in the different threads.

Update

After some time of using of the class LogPrintOut I have decided to inprove it by adding some new code:

C++
#ifndef LogPrintOut_H
#define LogPrintOut_H

#include <iostream>
#include <sstream>
#include <map>
#include <mutex>
#include <exception>

namespace LogPrint
{

    class LogPrintOut
	{
	public:

        enum Level
		{
			INFO_LEVEL=1,
			ERROR_LEVEL=2
		};
				
		static LogPrintOut& getInstance()
		{
            static LogPrintOut linstance;

            return linstance;
		}

        void setVerbose(bool aState)
		{
            std::lock_guard<std::mutex> llock(mLogPrintOutMutex);

            mVerbose = aState;
		}

        bool addPtrLogPrintOutStream(Level aLevel, std::wostream* aPtrWOStream, bool aSelfReleased)
		{
            std::lock_guard<std::mutex> llock(mLogPrintOutMutex);

            bool lResult = false;

            do
            {

                if (aPtrWOStream == nullptr)
                    break;

                StreamContainer lStreamContainer;

                lStreamContainer.mPtrWOStream = aPtrWOStream;

                lStreamContainer.mSelfReleased = aSelfReleased;

                mLevelStreams.insert(std::pair<level, streamcontainer="">(aLevel, lStreamContainer));

                lResult = true;

            }
            while (false);

            return lResult;
		}

        bool releaseLogPrintOutStream(Level aLevel)
		{
            std::lock_guard<std::mutex> llock(mLogPrintOutMutex);

            return releaseStream(aLevel);
		}

        bool releaseLogPrintOutStream(std::wostream* aPtrWOStream)
        {
            std::lock_guard<std::mutex> llock(mLogPrintOutMutex);

            return releaseStream(aPtrWOStream);
        }

		template<typename T>
        inline bool printOutln(Level aLevel, const T *aData)
		{
            std::lock_guard<std::mutex> llock(mLogPrintOutMutex);

            bool lResult = false;

            if(mVerbose)
            {
                lResult = writeToStream(aLevel, [](std::wostream &aWOStream, const T* aData){aWOStream << aData << std::endl;}, aData);
            }

            return lResult;
        }

        template<typename T>
        inline bool printOutln(Level aLevel, const T& aData)
		{
            std::lock_guard<std::mutex> llock(mLogPrintOutMutex);

            bool lResult = false;

            if (mVerbose)
            {
                lResult = writeToStream(aLevel, [](std::wostream &aWOStream, const T& aData){aWOStream << aData << std::endl;}, aData);
            }

            return lResult;
		}

		template <typename T, typename... Args>
        inline bool printOutln(Level aLevel, const T &aData, const Args... aRest)
		{
            std::lock_guard<std::mutex> llock(mLogPrintOutMutex);

            bool lResult = false;

            if (mVerbose)
            {
                lResult = writeToStream(aLevel, [aData, this](std::wostream &aWOStream, const Args... aRest){this->write(aWOStream, aData, aRest...); aWOStream << std::endl;}, aRest...);
            }

            return lResult;
		}
		
		template <typename T, typename... Args>
        inline bool printOutln(Level aLevel, const T *aData, const Args... aRest)
		{
            std::lock_guard<std::mutex> llock(mLogPrintOutMutex);

            bool lResult = false;

            if (mVerbose)
            {
                lResult = writeToStream(aLevel, [aData, this](std::wostream &aWOStream, const Args... aRest){this->write(aWOStream, aData, aRest...); aWOStream << std::endl;}, aRest...);
            }

            return lResult;
		}

		template<typename T>
        inline bool printOut(Level aLevel, const T* aData)
		{
            std::lock_guard<std::mutex> llock(mLogPrintOutMutex);

            bool lResult = false;

            if (mVerbose)
            {
                lResult = writeToStream(aLevel, [](std::wostream &aWOStream, const T* aData){aWOStream << aData;}, aData);
            }

            return lResult;
		}

		template<typename T>
        inline bool printOut(Level aLevel, const T& aData)
        {
            std::lock_guard<std::mutex> llock(mLogPrintOutMutex);

            bool lResult = false;

            if (mVerbose)
            {
                lResult = writeToStream(aLevel, [](std::wostream &aWOStream, const T& aData){aWOStream << aData;}, aData);
            }

            return lResult;
		}

		template <typename T, typename... Args>
        inline bool printOut(Level aLevel, const T aData, const Args... aRest)
        {
            std::lock_guard<std::mutex> llock(mLogPrintOutMutex);

            bool lResult = false;

            if (mVerbose)
            {
                lResult = writeToStream(aLevel, [aData, this](std::wostream &aWOStream, const Args... aRest){this->write(aWOStream, aData, aRest...);}, aRest...);
            }

            return lResult;
		}

		template <typename T, typename... Args>
        inline bool printOut(Level aLevel, const T *aData, const Args... aRest)
        {
            std::lock_guard<std::mutex> llock(mLogPrintOutMutex);

            bool lResult = false;

            if (mVerbose)
            {
                lResult = writeToStream(aLevel, [aData, this](std::wostream &aWOStream, const Args... aRest){this->write(aWOStream, aData, aRest...);}, aRest...);
            }

            return lResult;
		}
		
	private:

        struct StreamContainer
		{
            std::wostream* mPtrWOStream = nullptr;

            bool mSelfReleased = false;
		};

        std::multimap<level, StreamContainer> mLevelStreams;

		bool mVerbose = true;

		std::mutex mLogPrintOutMutex;




		LogPrintOut()
		{
            addPtrLogPrintOutStream(LogPrintOut::INFO_LEVEL, &std::wcerr, false);

            addPtrLogPrintOutStream(LogPrintOut::ERROR_LEVEL, &std::wcerr, false);
		}

		~LogPrintOut()
		{
            std::lock_guard<std::mutex> llock(mLogPrintOutMutex);

			releaseStreams();
		}


        LogPrintOut(const LogPrintOut&) = delete;

        LogPrintOut& operator=(const LogPrintOut&) = delete;



        template <typename WriteFunc, typename... Args>
        bool writeToStream(Level aLevel, const WriteFunc aWriteFunc, const Args... aRest)
        {
            bool lResult = false;

            do
            {
                if(!mVerbose)
                    break;

                auto lrangeItr = mLevelStreams.equal_range(aLevel);

                std::multimap<level, StreamContainer>::iterator litr;

                for (litr = lrangeItr.first; litr != lrangeItr.second; ++litr)
                {
                    if ((*litr).second.mPtrWOStream != nullptr)
                    {
                        (*litr).second.mPtrWOStream->flush();

                        switch (aLevel)
                        {
                        case LogPrint::LogPrintOut::INFO_LEVEL:
                            (*((*litr).second.mPtrWOStream)) << L"INFO_LEVEL: ";
                            break;
                        case LogPrint::LogPrintOut::ERROR_LEVEL:
                            (*((*litr).second.mPtrWOStream)) << L"ERROR_LEVEL: ";
                            break;
                        default:
                            break;
                        }

                        aWriteFunc((*((*litr).second.mPtrWOStream)), aRest...);

                        (*litr).second.mPtrWOStream->flush();

                    }
                }

                lResult = true;
            }
            while(false);

            return lResult;
        }

		void releaseStreams()
		{
			auto itr = mLevelStreams.begin();

			for (; itr != mLevelStreams.end(); ++itr)
			{
                if (itr->second.mSelfReleased && itr->second.mPtrWOStream != nullptr)
				{
                    itr->second.mPtrWOStream->flush();

                    delete itr->second.mPtrWOStream;
				}
			}

			mLevelStreams.clear();
		}

        bool releaseStream(Level aLevel)
		{

            bool lResult = false;

            auto lrangeItr = mLevelStreams.equal_range(aLevel);

            std::multimap<level, StreamContainer>::iterator litr;

            for (litr = lrangeItr.first; litr != lrangeItr.second; ++litr)
            {
                if ((*litr).second.mSelfReleased && (*litr).second.mPtrWOStream != nullptr)
                {
                    (*litr).second.mPtrWOStream->flush();

                    delete (*litr).second.mPtrWOStream;
                }
            }

            mLevelStreams.erase(aLevel);

            lResult = true;

            return lResult;

		}

        bool releaseStream(std::wostream* aPtrWOStream)
        {

            bool lResult = false;


            auto itr = mLevelStreams.begin();

            for (; itr != mLevelStreams.end(); ++itr)
            {
                if (itr->second.mPtrWOStream != nullptr && itr->second.mPtrWOStream == aPtrWOStream)
                {
                    lResult = true;

                    mLevelStreams.erase(itr);

                    break;
                }
            }

            return lResult;

        }

		template <typename T>
        inline void write(std::wostream &pwostream, const T *data)
		{
            pwostream << (data);
		}

		template <typename T>
        inline void write(std::wostream &pwostream, const T data)
		{
            pwostream << (data);
		}

		template <typename T, typename... Args>
        inline void write(std::wostream &pwostream, const T *data, const Args... rest)
        {
            pwostream << (data);

			write(pwostream, rest...);
		}

		template <typename T, typename... Args>
        inline void write(std::wostream &pwostream, const T data, const Args... rest)
		{
            pwostream << (data);

			write(pwostream, rest...);
		}

	};






    class ExceptionWithLogPrintOut : public std::exception
    {
    public:
      template <typename... Args>
      ExceptionWithLogPrintOut(const Args... aRest) _GLIBCXX_USE_NOEXCEPT
      {
          gPtrStrStram = new std::wostringstream();

          LogPrint::LogPrintOut::getInstance().addPtrLogPrintOutStream(LogPrint::LogPrintOut::ERROR_LEVEL, gPtrStrStram, false);

          LogPrintOut::getInstance().printOutln(LogPrintOut::ERROR_LEVEL, aRest...);

          LogPrint::LogPrintOut::getInstance().releaseLogPrintOutStream(gPtrStrStram);
      }

      virtual ~ExceptionWithLogPrintOut() _GLIBCXX_USE_NOEXCEPT
      {
        delete gPtrStrStram;
      }

      virtual const char* what() const _GLIBCXX_USE_NOEXCEPT
      {
          std::wstring lstring = gPtrStrStram->str();

          return std::string(lstring.begin(), lstring.end() ).c_str();
      }

    private:
      std::wostringstream* gPtrStrStram;
    };
}

#endif

</level,>

The new code has many changes:

  1. I add a new method addPtrLogPrintOutStream - method for adding of the output stram with interface std::wostream. It allows to attach some output streams to the one output level LogPrint::LogPrintOut::Level. The LogPrintOut class can print out the message into the different destinations (for example wcerr or file). The method can be used in such way:
    C++
    LogPrint::LogPrintOut::getInstance().addPtrLogPrintOutStream(LogPrint::LogPrintOut::INFO_LEVEL, new std::wofstream("app.log"), true);
  2. I add a new method releaseLogPrintOutStream(std::wostream* aPtrWOStream) - method for removing of the attached output stream.
  3. I add C++11 lambda expressions for remove redundancy of template code. The using of variadic tamplate needs to make overrided template methods and as a result, coping of code for managment of output streams eight times. The fact is that such work can be done by compiler. I added new method bool writeToStream(Level aLevel, const WriteFunc aWriteFunc, const Args... aRest). The new method has a variadic argument const Args... aRest and argument of functor class template const WriteFunc aWriteFunc. The code of the method has the next listing:
    C++
    template <typename WriteFunc, typename... Args>
    bool writeToStream(Level aLevel, const WriteFunc aWriteFunc, const Args... aRest)
    {
        bool lResult = false;
    
        do
        {
            if(!mVerbose)
                break;
    
            auto lrangeItr = mLevelStreams.equal_range(aLevel);
    
            std::multimap<level, StreamContainer>::iterator litr;
    
            for (litr = lrangeItr.first; litr != lrangeItr.second; ++litr)
            {
                if ((*litr).second.mPtrWOStream != nullptr)
                {
                    (*litr).second.mPtrWOStream->flush();
    
                    switch (aLevel)
                    {
                    case LogPrint::LogPrintOut::INFO_LEVEL:
                        (*((*litr).second.mPtrWOStream)) << L"INFO_LEVEL: ";
                        break;
                    case LogPrint::LogPrintOut::ERROR_LEVEL:
                        (*((*litr).second.mPtrWOStream)) << L"ERROR_LEVEL: ";
                        break;
                    default:
                        break;
                    }
    
                    aWriteFunc((*((*litr).second.mPtrWOStream)), aRest...);
    
                    (*litr).second.mPtrWOStream->flush();
    
                }
            }
    
            lResult = true;
        }
        while(false);
    
        return lResult;
    }
    This method is used in such ways:
    C++
    template <typename T>
    inline bool printOutln(Level aLevel, const T *aData)
    {
        std::lock_guard<std::mutex> llock(mLogPrintOutMutex);
    
        bool lResult = false;
    
        if(mVerbose)
        {
            lResult = writeToStream(aLevel, [](std::wostream &aWOStream, const T* aData){aWOStream << aData << std::endl;}, aData);
        }
    
        return lResult;
    }
    
    
    
    template <typename T, typename... Args>
    inline bool printOutln(Level aLevel, const T *aData, const Args... aRest)
    {
        std::lock_guard<std::mutex> llock(mLogPrintOutMutex);
    
        bool lResult = false;
    
        if (mVerbose)
        {
            lResult = writeToStream(aLevel, [aData, this](std::wostream &aWOStream, const Args... aRest){this->write(aWOStream, aData, aRest...); aWOStream << std::endl;}, aRest...);
        }
    
        return lResult;
    }
    It allows to remove many strings of code.

I was surprised by the fact that it is possible to mix variadic template with the lambda expression in such way:

C++
[aData, this](std::wostream &aWOStream, const Args... aRest){this->write(aWOStream, aData, aRest...); aWOStream << std::endl;}

It allows to create a beautiful solution.

In addition I wrote a new exception class which allows to print out information about exception into the output stream and has a variadic template constructor:

C++
    class ExceptionWithLogPrintOut : public std::exception
    {
    public:
      template <typename... Args>
      ExceptionWithLogPrintOut(const Args... aRest) _GLIBCXX_USE_NOEXCEPT
      {
          gPtrStrStram = new std::wostringstream();

          LogPrint::LogPrintOut::getInstance().addPtrLogPrintOutStream(LogPrint::LogPrintOut::ERROR_LEVEL, gPtrStrStram, false);

          LogPrintOut::getInstance().printOutln(LogPrintOut::ERROR_LEVEL, aRest...);

          LogPrint::LogPrintOut::getInstance().releaseLogPrintOutStream(gPtrStrStram);
      }

      virtual ~ExceptionWithLogPrintOut() _GLIBCXX_USE_NOEXCEPT
      {
        delete gPtrStrStram;
      }

      virtual const char* what() const _GLIBCXX_USE_NOEXCEPT
      {
          std::wstring lstring = gPtrStrStram->str();

          return std::string(lstring.begin(), lstring.end() ).c_str();
      }

    private:
      std::wostringstream* gPtrStrStram;
    };
}

#endif

This implementation of std exception class allows to use ONE ExceptionWithLogPrintOut class of EXCEPTION with variable size arguments

C++
throw new LogPrint::ExceptionWithLogPrintOut(L"Test", L"Value: ", 12123);

or

C++
throw new LogPrint::ExceptionWithLogPrintOut(L"Test Value: ", 12123, ", Addition value: ", 4321);

All work of the creating of constructor with the specific arguments signture is executed by compiler. From my experience I can say that it is very useful solution.

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionCopy paste? Pin
c-smile26-Jan-15 12:52
c-smile26-Jan-15 12:52 
AnswerRe: Copy paste? Pin
Evgeny Pereguda27-Jan-15 3:03
Evgeny Pereguda27-Jan-15 3:03 
Hi,
I see your idea, but you make the common mistake: in code
C++
template<typename T>
        inline bool printOut(Level level, const T *data)
 
template<typename T>
        inline bool printOut(Level level, const T data)


the first template work with pointer on heap, and the second work with the instance in stack - they are different types. For example, there is well known macross:


C++
typedef char True; // sizeof(True) == 1

typedef struct { char a[2]; } False; // sizeof(False) > 1
//...
template <typename T> True isPtr( T * );
False isPtr( ... );

#define is_ptr( e ) (sizeof(isPtr(e))==sizeof(True))

There is_ptr allows to define is the argument of pointer or not. The conception SFINAE - Substitution Failure Is Not An Error is the fundament of the template in C++.

In the case of the your code:

C++
template<typename T>
        inline bool printOut(Level level, T data)


the compiler will generate the next specialization of template code:

C++
char g;

template<typename T>
        inline bool printOut(Level level, g); => template<char>
        inline bool printOut(Level level, g);



char *text = "test text";

template<typename T>
        inline bool printOut(Level level, text); => template<int>
        inline bool printOut(Level level, text);


In the first case compiler define template argument T as type of stack instance g - it is like find the size of stack instance sizeof(g) = 1. However, for pointer on heap it is not right - what is it pointer - it is a cell in memory which store adress on another section of memory. As a result all pointers have the same type and size - for example sizeof(text) = 4(x86) or 8(for x86-64). For the correct defining of pointer, the compiler must know can it applies unnaming operator *text or not. The fact is that the stack instance usually does not have operator *(). However, if class define unname operator *() - for example iterator, then more specialized version template<typename T> inline bool printOut(Level level, const T *data) is used.
So, I just want to say that pointer on class in heap DOES NOT have type of class:
C++
char *text = "test test";

typeof(text) != typeof(char);


With best regards,
Evgeny Pereguda
GeneralRe: Copy paste? Pin
c-smile27-Jan-15 8:05
c-smile27-Jan-15 8:05 
GeneralRe: Copy paste? Pin
Evgeny Pereguda27-Jan-15 20:51
Evgeny Pereguda27-Jan-15 20:51 
GeneralRe: Copy paste? Pin
c-smile28-Jan-15 11:28
c-smile28-Jan-15 11:28 
GeneralRe: Copy paste? Pin
Evgeny Pereguda28-Jan-15 19:49
Evgeny Pereguda28-Jan-15 19:49 
GeneralRe: Copy paste? Pin
c-smile29-Jan-15 13:42
c-smile29-Jan-15 13:42 
QuestionYou are very close! Pin
David O'Neil1-Dec-14 1:24
professionalDavid O'Neil1-Dec-14 1:24 
AnswerRe: You are very close! Pin
Evgeny Pereguda4-Dec-14 2:51
Evgeny Pereguda4-Dec-14 2:51 
GeneralRe: You are very close! Pin
David O'Neil4-Dec-14 9:22
professionalDavid O'Neil4-Dec-14 9:22 
GeneralRe: You are very close! Pin
Evgeny Pereguda4-Dec-14 13:39
Evgeny Pereguda4-Dec-14 13:39 
QuestionHere is a method you may find easier to use and extend Pin
David O'Neil1-Nov-14 5:27
professionalDavid O'Neil1-Nov-14 5:27 
AnswerRe: Here is a method you may find easier to use and extend Pin
Evgeny Pereguda3-Nov-14 11:45
Evgeny Pereguda3-Nov-14 11:45 
GeneralRe: Here is a method you may find easier to use and extend Pin
David O'Neil3-Nov-14 17:39
professionalDavid O'Neil3-Nov-14 17:39 
AnswerRe: Here is a method you may find easier to use and extend Pin
Evgeny Pereguda3-Nov-14 18:25
Evgeny Pereguda3-Nov-14 18:25 
GeneralRe: Here is a method you may find easier to use and extend Pin
David O'Neil4-Nov-14 3:30
professionalDavid O'Neil4-Nov-14 3:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.