Josh C
Josh C

Reputation: 1111

Using a Singleton to assign a Static Member

I didn't want to over-complicate the title but I've designed something which is a fair bit more complicated than I have indicated. The code nearly resembles the following, at least in basic principle.

template<output_stream>
class logger
{
  output_stream streamer
}

using xlog = logger<x>

class singleton_manager
{
  static int register_log(string x)
  {
     logs.pushback(x related thing)
     return logs.size() - 1
  }
  static std::ostringstream(int index)
  static std::vector<xlog> logs
}

class unrelated
{
  static int logindex
  void error()
}

int logindex = singleton_manager::register_log("simplest_example.log")

So the problem is that when I call the line above, from my unrelated.cpp, register_log() doesn't keep the new log.


It runs the method..

creates the new log..

returns the index of the new log..

assigns the value returned to the static member..

poof the new log doesn't actually exist..


The only thing I can fathom is that because the assignment is for a static member and uses a static method of a singleton it only pretends to create the new log. Or perhaps it is because of the template? Any input on what my bug is will be much appreciated.

edit: you asked for it.

file: s3d_mesh.h

#ifndef S3DMESH_H_
#define S3DMESH_H_
#include <vector>
#include <fstream>
#include <debug_toolset.h>

using std::vector;
using std::string;
using std::ifstream;

struct s3d_vertex
{
    float array[8];
    void Load(ifstream& file)
    {
        file >> array[0];
        file >> array[1];
        file >> array[2];
        file >> array[3];
        file >> array[4];
        file >> array[5];
        file >> array[6];
        file >> array[7];
    }
};

struct s3d_index
{
    float array[3];
    void Load(ifstream& file)
    {
        file >> array[0];
        file >> array[1];
        file >> array[2];
    }
};

class s3d_mesh
{
    private:
        static int errorlog_index;
        int total_indices;
        int total_vertices;
        string texture_file;

    protected:
        vector<s3d_index> Indices;
        vector<s3d_vertex> Vertices;

    public:
        s3d_mesh(){}
        s3d_mesh(string file_path);
        void Load(string file_path);
};

#endif

file: s3d_mesh.cpp

#include "s3d_mesh.h"
#include <sstream>
#include <debug_toolset.h>

using std::stringstream;

int s3d_mesh::errorlog_index = dbg::FileLog_Mgr::RegisterNewLog(L"s3d_errors.log");

s3d_mesh::s3d_mesh(string file_path)
{
    Load(file_path);
}

void s3d_mesh::Load(string file_path)
{
    ifstream File_Handle;
    File_Handle.open(file_path, std::ios::in);
    try
    {
        File_Handle >> total_vertices;
        for ( int i = 0; i < total_vertices; ++i )
        {
            s3d_vertex temp;
            temp.Load(File_Handle);
            Vertices.push_back(temp);
        }
        File_Handle >> total_indices;
        for ( int i = 0; i < total_indices; ++i )
        {
            s3d_index temp;
            temp.Load(File_Handle);
            Indices.push_back(temp);
        }

        File_Handle >> texture_file;
        File_Handle >> texture_file;
        File_Handle >> texture_file;
        File_Handle >> texture_file;

        if ( File_Handle.fail() )
            throw File_Handle.rdstate();

        File_Handle.close();
    }
    catch ( ... )
    {
        dbg::FileLog_Mgr::Start();
        LOGFILEX(errorlog_index, dbg::logERROR) << "\tS3D File - Critical Failure Loading";
        dbg::FileLog_Mgr::Stop();
    }
}

file: debug_toolset.h

/*The MIT License (MIT)

Copyright (c) <2014> <Josh S, Cooper>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.*/

#ifndef _DBG_LOG_
#define _DBG_LOG_
#include "lib_linkers\dbglinker.h"

#include <assert.h>
#include <cstdio>

#include <vector>

#include <fstream>
#include <sstream>

#include <thread>
#include <functional>

#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
    #include <windows.h>
    //#include <stdio.h>
/*
Function for grabbing the timestamps
Windows Version
*/
    inline std::string NowTime()
    {
        const int MAX_LEN = 200;
        char buffer[MAX_LEN];
        if ( GetTimeFormatA(LOCALE_USER_DEFAULT, 0, 0,
            "HH':'mm':'ss", buffer, MAX_LEN) == 0 )
            return "Error in NowTime()";

        char result[100] = { 0 };
        //std::string result = "";
        static DWORD first = GetTickCount();
        /*result += buffer;
        result += ".";
        result += (long)first;*/
        std::sprintf(result, "%s.%03ld", buffer, ((long)(GetTickCount() - first) % 1000));
        return result;
    }

#else
    #include <sys/time.h>

    inline std::string NowTime()
    {
        char buffer[11];
        time_t t;
        time(&t);
        tm r = {0};
        strftime(buffer, sizeof(buffer), "%X", localtime_r(&t, &r));
        struct timeval tv;
        gettimeofday(&tv, 0);
        char result[100] = {0};
        std::sprintf(result, "%s.%03ld", buffer, (long)tv.tv_usec / 1000); 
        return result;
    }

#endif //WIN32

namespace dbg
{
    //Enums representing the different log levels
    // Implemented as bits in a Byte as to facilitate turning specific levels on and off with a #define macro
    enum LogLevel 
    { 
        logFATAL = 1 << 0,          logERROR = 1 << 1, 
        logWARNING = 1 << 2,        logINFO = 1 << 3, 
        logDEBUG1 = 1 << 4,     logDEBUG2 = 1 << 5, 
        logDEBUG3 = 1 << 6,     logDEBUG4 = 1 << 7 
    };

    //Forward declaration for friend statement
    class FileLog_Mgr;

    /*
    Logger template class

    Implemented with a built-in Object of the OutputPolicy class
    Logger expects very little of this Policy class itself as its methods' only use of the
    Output Object are where it is being overwritten with a new OutputPolicy object

    REQUIRED are an operator= overload + copy constructor
    */
    template <typename OutputPolicy>
    class Logger
    {
        friend FileLog_Mgr;
        public:
            virtual ~Logger()
            {
            }
            Logger(){}
            Logger(const Logger& other)
            {
                Output = other.Output;
            }
            Logger& operator=(const Logger& other)
            {
                Output = other.Output;
                return *this;
            }

            inline LogLevel& ReportingLevel()
            {
                static LogLevel reportingLevel = logDEBUG4;
                return reportingLevel;
            }
            inline std::string ToString(LogLevel level)
            {
                switch ( level )
                {
                    case logFATAL:
                        return "\t~FATAL~\t\t";
                        break;

                    case logERROR:
                        return "\tERROR: \t\t";
                        break;

                    case logWARNING:
                        return "WARNING: \t";
                        break;

                    case logINFO:
                        return "INFO:\t  ";
                        break;

                    case logDEBUG1:
                        return "DEBUG1:\t\t";
                        break;

                    case logDEBUG2:
                        return "DEBUG2:\t\t  ";
                        break;

                    case logDEBUG3:
                        return "DEBUG3:\t\t    ";
                        break;

                    case logDEBUG4:
                        return "DEBUG4:\t\t      ";
                        break;
                }
            }
            inline std::ostringstream& Get(LogLevel level = logINFO)
            {
                    buffer << std::endl << " -  " << NowTime() << " - \t";
                    buffer << ToString(level);
                    return buffer;
            }

        protected:
            std::ostringstream buffer;
            OutputPolicy Output;  //templated output
    };

    /*
    */
    class FileStream_Policy
    {
        public:
            virtual ~FileStream_Policy()
            {
                if ( file_stream.is_open() )
                {
                    file_stream.close();
                }
            }
            FileStream_Policy(){};
            FileStream_Policy(const FileStream_Policy& other)
            {
                file_name = other.file_name;
            }
            FileStream_Policy& operator=(const FileStream_Policy& other)
            {
                file_name = other.file_name;
                return *this;
            }

            inline std::ofstream& Stream()
            {
                if ( !file_stream.is_open() )
                    file_stream.open(file_name, std::ofstream::out);// | std::ofstream::app
                return file_stream;
            }
            inline std::wstring& FileName()
            {
                return file_name;
            }

        protected:
            std::ofstream file_stream;
            std::wstring file_name;
    };

    //Type Definition for a File Log using Logger<FileStream_Policy>
    using FileLog = Logger<FileStream_Policy>;

    class FileLog_Mgr
    {
        public:
            static std::ostringstream& Get(int index, LogLevel level)
            {
                try
                {
                    return Logs[index].Get(level);
                }
                catch ( int exception )
                {
                    assert("Indexed Log does not exist");
                    exit(-404);
                }
            }
            static int RegisterNewLog(std::wstring file_name)
            {
                if ( !ThreadRunning )
                {
                    for ( int i = 0; i < Logs.size(); ++i )
                    {
                        if ( Logs[i].Output.FileName() == file_name )
                            return -2;
                    }
                    FileLog newLog;
                    newLog.Output.FileName() = file_name;
                    Logs.push_back(newLog);
                    return Logs.size() - 1;
                }
                else
                {
                    return -1;
                }
            }
            static bool CheckIndex(int index)
            {
                if ( index >= 0 && index < Logs.size() )
                    return true;
                return false;
            }
            static bool macroCheck(int index, LogLevel level)
            {
                if ( index >= 0 && index < Logs.size() )
                {
                    if ( level > Logs[index].ReportingLevel() || !Logs[index].Output.Stream() )
                        return false;
                    return true;
                }
                return false;
            }
            static bool Start()
            {
                if ( WtL_Thread.joinable() )
                    return false;

                ThreadRunning = true;
                WtL_Thread = std::thread(&FileLog_Mgr::WriteToLogs);
                std::this_thread::sleep_for(std::chrono::milliseconds(StartDelay));
                return true;
            }
            static bool Stop()
            {
                if ( WtL_Thread.joinable() )
                {
                    ThreadRunning = false;
                    WtL_Thread.join();
                    return true;
                }
                return false;
            }

        protected:
            static std::vector<FileLog> Logs;

        private:
            virtual ~FileLog_Mgr()
            {
                Stop();
            }
            static bool ThreadRunning;
            static int WriteInterval;
            static int StartDelay;
            static std::thread WtL_Thread;

            static void WriteToLogs()
            {
                while ( ThreadRunning )
                {
                    for ( int i = 0; i < Logs.size(); ++i )
                    {
                        Logs[i].Output.Stream() << Logs[i].buffer.str();
                        Logs[i].buffer.str("");
                        Logs[i].Output.Stream().flush();
                    }
                    std::this_thread::sleep_for(std::chrono::milliseconds(WriteInterval));
                }

                //There might be buffered data
                for ( int i = 0; i < Logs.size(); ++i )
                {
                    Logs[i].Output.Stream() << Logs[i].buffer.str();
                    Logs[i].buffer.str("");
                    Logs[i].Output.Stream().flush();
                }
            }
    };
}

#ifndef LOG_MAX_LEVEL
#define LOG_MAX_LEVEL (dbg::logFATAL + dbg::logERROR + dbg::logWARNING + dbg::logINFO + dbg::logDEBUG1 + dbg::logDEBUG2 + dbg::logDEBUG3 + dbg::logDEBUG4 )
#endif

#define LOGFILEX(index, level) \
    if ( level & ~LOG_MAX_LEVEL || !dbg::FileLog_Mgr::macroCheck(index, level) ); \
    else dbg::FileLog_Mgr::Get(index, level)

#define LOGFILE1(level) \
    if ( level & ~LOG_MAX_LEVEL || !dbg::FileLog_Mgr::macroCheck(0, level) ); \
    else dbg::FileLog_Mgr::Get(0, level)

#define LOGFILE2(level) \
    if ( level & ~LOG_MAX_LEVEL || !dbg::FileLog_Mgr::macroCheck(1, level) ); \
    else dbg::FileLog_Mgr::Get(1, level)

#define LOGFILE3(level) \
    if ( level & ~LOG_MAX_LEVEL || !dbg::FileLog_Mgr::macroCheck(2, level) ); \
    else dbg::FileLog_Mgr::Get(2, level)

#define LOGFILE4(level) \
    if ( level & ~LOG_MAX_LEVEL || !dbg::FileLog_Mgr::macroCheck(3, level) ); \
    else dbg::FileLog_Mgr::Get(3, level)

#define LOGFILE5(level) \
    if ( level & ~LOG_MAX_LEVEL || !dbg::FileLog_Mgr::macroCheck(4, level) ); \
    else dbg::FileLog_Mgr::Get(4, level)

#endif

Upvotes: 0

Views: 132

Answers (1)

Oguk
Oguk

Reputation: 1137

As discussed in the comments, I cannot really run your code, because it is not a minimal working example. But after what you've tried, it seems to be a matter of static initialization order.

Replace

static std::vector<xlog> logs;

by a function

static std::vector<xlog>& getLogs() {
    static std::vector<xlog> logs;
    return logs;
}

and use getLogs() where ever you use logs in register_log. So, for example:

static int register_log(string x)
{
    getLogs().push_back(x related thing);
    return getLogs().size() - 1;
}

This will ensure the vector is constructed before anything is pushed to it.

Upvotes: 1

Related Questions