C++ 如何避免使用异常作为控制流的一种形式,通过深度嵌套的函数调用跳转?

C++ 如何避免使用异常作为控制流的一种形式,通过深度嵌套的函数调用跳转?,c++,nested,C++,Nested,我目前一直在使用自定义异常来实现跳过深度嵌套函数调用的目标,从而到达调用链中的特定函数。例如,考虑下面的代码: #include <iostream> struct label {}; void B(); void C(); void D(); void A() { return B(); } void B() { // I want to jump to the level of the B function in the call-chain. try {

我目前一直在使用自定义异常来实现跳过深度嵌套函数调用的目标,从而到达调用链中的特定函数。例如,考虑下面的代码:

#include <iostream>

struct label {};

void B();
void C();
void D();

void A() {
    return B();
}

void B() { // I want to jump to the level of the B function in the call-chain.
    try {
        return C();
    }
    catch(const label& e) {
        std::cout << "jumped to b function" << std::endl;
    }
}

void C() {
    return D();
}

void D() {
    throw label();
}

int main() {
    A();
    return 0;
}
#包括
结构标签{};
无效B();
无效C();
无效D();
作废{
返回B();
}
void B(){//我想跳到调用链中的B函数级别。
试一试{
返回C();
}
捕获(常数标签和e){
std::cout第1部分:-带异常的嵌套函数堆栈调用。

这可能不适合您的特定或确切需求,但是我愿意分享这个示例,因为我认为它可能提供一些见解,并且与您当前的情况相关

我有一组集成在一起的类,它们处理多个常见任务。以下一组类包括
块进程
块线程
文件处理程序
例外处理程序
记录器
和一个
实用程序
类。这里有几个文件,请记住t这个轻量级项目的目标是
Windows
,我使用的是带有预编译标题的Visual Studio 2017

我相信,人们可以轻松地去掉任何依赖于windows的
代码,并用其等效的系统、体系结构和环境以及功能来替换

我还使用了一个名为
demo
的名称空间,它封装了这个小项目中的所有类和函数;任何用户都应该用自己的名称空间名称替换这个名称空间

其主要目的是设计当
堆栈调用嵌套得很深时,我通常如何处理
异常

这些类集不仅允许使用不同类型消息的不同设置来控制控制台的日志信息、警告和错误,还允许将内容记录到文件中

在开发3D图形应用程序的过程中,这种类型的构造非常方便和通用,而3D图形应用程序的代码库可能变得非常密集

我不能完全相信这段代码,因为大部分代码都是由MASc的Marek a.Krzeminski启发和设计的,但我相信这段代码的概念和使用才是重要的


主要入口点:

main.cpp

ExceptionHandler.cpp


记录器:

Logger.h

Singleton.h

Singleton.cpp

#包括“stdafx.h”
#包括“Singleton.h”
#包括“Logger.h”
名称空间演示{
SingletonInfo结构{
常量std::字符串strSingleToName;
布尔已建成;
SingletonInfo(常量std::string和strSingletonNameIn):
strsingletoname(strsingletonamein),
isConstructed(假)
{}
}; 
//订单必须与Singleton::SingletonType枚举中定义的类型匹配
静态std::数组s_aSingletons={SingletonInfo(“Logger”)};
Singleton::Singleton(SingletonType eType):
eType(eType){
bool bSaveInLog=s_aSingletons.at(类型记录器)。已构建;
试一试{
如果(!s_aSingletons.at(eType).isConstructed){
//测试初始化顺序
for(int i=0;i
注意:-请不要对该答案投票,请参考第一个答案,因为这只是相关课程参考的延续

您可以找到第1部分。我不得不将其分为两个单独的答案,因为我比30000个字符的最大限制多了2000个字符。对于由此带来的不便,我深表歉意。但是,如果没有提供的类,则无法
应用
异常处理程序


文件处理程序:

FileHandler.h

FileHandler.cpp

TextFileReader.h

TextFileReader.cpp

TextFileWriter.cpp

blockprocess.cpp

BlockThread.h

BlockThread.cpp


实用程序:

Utility.h

Utility.cpp

stdafx.cpp

#包括“stdafx.h”
名称空间演示{
const unsigned INVALID_unsigned=静态_cast(-1);
const unsigned INVALID_unsigned_SHORT=static_cast(-1);
}//名称空间演示

为什么不直接返回错误并让
B
处理它们呢?比如说
C
尝试做一些事情,如果失败,它会返回一个
B
处理的错误。否则,
C
调用
D
,如果失败,它会再次将错误返回到
B
。使用异常没有任何问题。它们是核心,C++的基本特征。已经说过了;用<代码> STD::可选< /COD>和<代码> STD::VALIA:
#include "stdafx.h"
#include "BlockProcess.h"
#include "Logger.h"
#include "Utility.h"

//struct label {}; // Instead of throwing this struct in D() I'm throwing the ExceptionHandler

void B();
void C();
void D();

void A() {
    return B();
}

void B() {
    using namespace demo;

    try {
        return C();
    } catch ( ... ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " failed for some reason.";
        Logger::log( strStream, Logger::TYPE_INFO );
        Logger::log( strStream, Logger::TYPE_WARNING );
        Logger::log( strStream, Logger::TYPE_ERROR );
        Logger::log( strStream, Logger::TYPE_CONSOLE );
    }
}

void C() {
    return D();
}

void D() {
    using namespace demo;

    std::ostringstream strStream;
    strStream << __FUNCTION__ << " failed for some reason.";
    throw ExceptionHandler( strStream ); // By Default will log to file; otherwise pass false for second param.
}

int _tmain( int iNumArgs, _TCHAR* pArugmentText[] ) {
    using namespace demo;

    try {
        Logger log( "logger.txt" );

        A();

        // Prevent Multiple Start Ups Of This Application
        BlockProcess processBlock( "ExceptionManager.exe" );
        if ( processBlock.isBlocked() ) {
            std::ostringstream strStream;
            strStream << "ExceptionManager is already running in another window." << std::endl;
            throw ExceptionHandler( strStream, false );
        }

        Utility::pressAnyKeyToQuit();

    } catch ( ExceptionHandler& e ) {
        std::cout << "Exception Thrown: " << e.getMessage() << std::endl;
        Utility::pressAnyKeyToQuit();
        return RETURN_ERROR;

    } catch ( ... ) {
        std::cout << __FUNCTION__ << " Caught Unknown Exception" << std::endl;
        Utility::pressAnyKeyToQuit();
        return RETURN_ERROR;
    }

    return RETURN_OK;
}
#ifndef EXCEPTION_HANDLER_H
#define EXCEPTION_HANDLER_H

namespace demo {

class ExceptionHandler final {
private:
    std::string strMessage_;
public:
    explicit ExceptionHandler( const std::string& strMessage, bool bSaveInLog = true );
    explicit ExceptionHandler( const std::ostringstream& strStreamMessage, bool bSaveInLog = true );

    ~ExceptionHandler() = default;
    ExceptionHandler( const ExceptionHandler& c ) = default;

    const std::string& getMessage() const;

    ExceptionHandler& operator=( const ExceptionHandler& c ) = delete;
};

} // namespace demo

#endif // !EXCEPTION_HANDLER_H
#include "stdafx.h"
#include "ExceptionHandler.h"
#include "Logger.h"

namespace demo {

ExceptionHandler::ExceptionHandler( const std::string& strMessage, bool bSaveInLog ) :
strMessage_( strMessage ) {
    if ( bSaveInLog ) {
        Logger::log( strMessage_, Logger::TYPE_ERROR );
    }
}

ExceptionHandler::ExceptionHandler( const std::ostringstream& strStreamMessage, bool bSaveInLog ) :
strMessage_( strStreamMessage.str() ) {
    if ( bSaveInLog ) {
        Logger::log( strMessage_, Logger::TYPE_ERROR );
    }
}

const std::string& ExceptionHandler::getMessage() const {
    return strMessage_;
}

} // namespace demo
#ifndef LOGGER_H
#define LOGGER_H

#include "Singleton.h"

namespace demo { 
class Logger final : public Singleton {
public:
    enum LoggerType {
        TYPE_INFO = 0,
        TYPE_WARNING,
        TYPE_ERROR,
        TYPE_CONSOLE,
    }; // LoggerType

private:
    std::string strLogFilename_;
    unsigned   uMaxCharacterLength_;

    std::array<std::string, 4> aLogTypes_;
    const std::string          strUnknownLogType_;

    HANDLE hConsoleOutput_;
    WORD   consoleDefaultColor_;

public:
    explicit Logger( const std::string& strLogFilename );
    virtual ~Logger();

    static void log( const std::string& strText, LoggerType eLogType = TYPE_INFO );
    static void log( const std::ostringstream& strStreamText, LoggerType eLogType = TYPE_INFO );
    static void log( const char* szText, LoggerType eLogType = TYPE_INFO );

    Logger( const Logger& c ) = delete;
    Logger& operator=( const Logger& c ) = delete;
}; 

} // namespace demo 

#endif // !LOGGER_H
#include "stdafx.h"
#include "Logger.h"
#include "BlockThread.h"
#include "TextFileWriter.h"

namespace demo {

static Logger* s_pLogger = nullptr;
static CRITICAL_SECTION s_criticalSection;
static const WORD WHITE_ON_RED = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY | BACKGROUND_RED; // White Text On Red Background

Logger::Logger( const std::string& strLogFilename ) :
Singleton( TYPE_LOGGER ),
strLogFilename_( strLogFilename ),
uMaxCharacterLength_( 0 ),
strUnknownLogType_( "UNKNOWN" ) {
    // Oder must match types defined in Logger::Type enum
    aLogTypes_[0] = "Info";
    aLogTypes_[1] = "Warning";
    aLogTypes_[2] = "Error";
    aLogTypes_[3] = ""; // Console

    // Find widest log type string
    uMaxCharacterLength_ = strUnknownLogType_.size();
    for each ( const std::string& strLogType in aLogTypes_ ) {
        if ( uMaxCharacterLength_ < strLogType.size() ) {
             uMaxCharacterLength_ = strLogType.size();
        }
    }

    InitializeCriticalSection( &s_criticalSection );
    BlockThread blockThread( s_criticalSection ); // Enter critical section

    // Start log file
    TextFileWriter file( strLogFilename_, false, false );

    // Prepare console
    hConsoleOutput_ = GetStdHandle( STD_OUTPUT_HANDLE );

    CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
    GetConsoleScreenBufferInfo( hConsoleOutput_, &consoleInfo );
    consoleDefaultColor_ = consoleInfo.wAttributes;

    s_pLogger = this;

    logMemoryAllocation( true );

} // Logger()

Logger::~Logger() {
    logMemoryAllocation( false );
    s_pLogger = nullptr;
    DeleteCriticalSection( &s_criticalSection );
} // ~Logger


void Logger::log( const std::string& strText, LoggerType eLogType ) {
    log( strText.c_str(), eLogType );
} 

void Logger::log( const std::ostringstream& strStreamText, LoggerType eLogType ) {
    log( strStreamText.str().c_str(), eLogType );
} 

void Logger::log( const char* szText, LoggerType eLogType ) {
    if ( nullptr == s_pLogger ) {
        std::cout << "Logger has not been initialized, can not log " << szText << std::endl;
        return;
    } 

    BlockThread blockThread( s_criticalSection ); // Enter critical section

    std::ostringstream strStream;

    // Default White Text On Red Background
    WORD textColor = WHITE_ON_RED;

    // Choose log type text string, display "UNKNOWN" if eLogType is out of range
    strStream << std::setfill( ' ' ) << std::setw( s_pLogger->uMaxCharacterLength_ );

    try {
        if ( TYPE_CONSOLE != eLogType ) {
            strStream << s_pLogger->aLogTypes_.at( eLogType );
        }
        if ( TYPE_WARNING == eLogType ) {
            // Yellow
            textColor = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_RED | BACKGROUND_GREEN;
        } else if ( TYPE_INFO == eLogType ) {
            // Green
            textColor = FOREGROUND_GREEN;
        } else if ( TYPE_CONSOLE == eLogType ) {
            // Cyan
            textColor = FOREGROUND_GREEN | FOREGROUND_BLUE;
        }
    } catch ( ... ) {
        strStream << s_pLogger->strUnknownLogType_;
    }

    // Date & Time
    if ( TYPE_CONSOLE != eLogType ) {
        SYSTEMTIME time;
        GetLocalTime( &time );

        strStream << " [" << time.wYear << "."
            << std::setfill( '0' ) << std::setw( 2 ) << time.wMonth << "."
            << std::setfill( '0' ) << std::setw( 2 ) << time.wDay << " "
            << std::setfill( ' ' ) << std::setw( 2 ) << time.wHour << ":"
            << std::setfill( '0' ) << std::setw( 2 ) << time.wMinute << ":"
            << std::setfill( '0' ) << std::setw( 2 ) << time.wSecond << "."
            << std::setfill( '0' ) << std::setw( 3 ) << time.wMilliseconds << "] ";
    }
    strStream << szText << std::endl;

    // Log message
    SetConsoleTextAttribute( s_pLogger->hConsoleOutput_, textColor );
    std::cout << strStream.str();

    // Save same message to file
    try {
        TextFileWriter file( s_pLogger->strLogFilename_, true, false );
        file.write( strStream.str() );
    } catch ( ... ) {
        // Ignore, not saved in log file
        std::cout << __FUNCTION__ << " failed to write to file: " << strStream.str() << std::endl;
    }

    // Reset to default color
    SetConsoleTextAttribute( s_pLogger->hConsoleOutput_, s_pLogger->consoleDefaultColor_ );
}

} // namespace demo
#ifndef SINGLETON_H
#define SINGLETON_H

namespace demo {

class Singleton {
public:
    // Number of items in enum type must match the number of items and order of items stored in s_aSingletons
    enum SingletonType {
        TYPE_LOGGER = 0, // MUST BE FIRST!
    }; // enum SingleType

private:
    SingletonType eType_;

public:
    Singleton( const Singleton& c ) = delete;
    Singleton& operator=( const Singleton& c ) = delete;
    virtual ~Singleton();

protected:
    explicit Singleton( SingletonType eType );
    void logMemoryAllocation( bool isAllocated ) const;
};

} // namespace demo

#endif // !SINGLETON_H
#include "stdafx.h"
#include "Singleton.h"
#include "Logger.h"

namespace demo { 

struct SingletonInfo {
    const std::string strSingletonName;
    bool              isConstructed;

    SingletonInfo( const std::string& strSingletonNameIn ) :
        strSingletonName( strSingletonNameIn ),
        isConstructed( false ) 
    {}
}; 

// Order must match types defined in Singleton::SingletonType enum
static std::array<SingletonInfo, 1> s_aSingletons = { SingletonInfo( "Logger" ) };

Singleton::Singleton( SingletonType eType ) :
eType_( eType ) {
    bool bSaveInLog = s_aSingletons.at( TYPE_LOGGER ).isConstructed;

    try {
        if ( !s_aSingletons.at( eType ).isConstructed ) {
            // Test Initialize Order
            for ( int i = 0; i < eType; ++i ) {
                if ( !s_aSingletons.at( i ).isConstructed ) {
                    throw ExceptionHandler( s_aSingletons.at( i ).strSingletonName +
                                            " must be constructed before constructing " +
                                            s_aSingletons.at( eType ).strSingletonName,
                                            bSaveInLog );
                }
            }
            s_aSingletons.at( eType ).isConstructed = true;
        } else {
            throw ExceptionHandler( s_aSingletons.at( eType ).strSingletonName + 
                                    " can only be constructed once.", 
                                    bSaveInLog );
        }
    } catch ( std::exception& ) {
        // eType is out of range
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Invalid Singleton Type specified: " << eType;
        throw ExceptionHandler( strStream, bSaveInLog );
    }
}

Singleton::~Singleton() {
    s_aSingletons.at( eType_ ).isConstructed = false;
} 

void Singleton::logMemoryAllocation( bool isAllocated ) const {
    if ( isAllocated ) {
        Logger::log( "Created " + s_aSingletons.at( eType_ ).strSingletonName );
    } else {
        Logger::log( "Destroyed " + s_aSingletons.at( eType_ ).strSingletonName );
    }
} 

} // namespace demo
#ifndef FILE_HANDLER_H
#define FILE_HANDLER_H

namespace demo {

class FileHandler {
protected:
    std::fstream fileStream_;
    std::string  strFilePath_;
    std::string  strFilenameWithPath_;

private:
    bool bSaveExceptionInLog_;

public:
    virtual ~FileHandler();

    FileHandler( const FileHandler& c ) = delete;
    FileHandler& operator=( const FileHandler& c ) = delete;

protected:
    FileHandler( const std::string& strFilename, bool bSaveExceptionInLog );
    void throwError( const std::string& strMessage ) const;
    void throwError( const std::ostringstream& strStreamMessage ) const;

    bool getString( std::string& str, bool appendPath );    
};

} // namespace demo

#endif // !FILE_HANDLER_H
#include "stdafx.h"
#include "FileHandler.h"

namespace demo {

FileHandler::FileHandler( const std::string& strFilename, bool bSaveExceptionInLog ) :
bSaveExceptionInLog_( bSaveExceptionInLog ),
strFilenameWithPath_( strFilename ) {
    // Extract path info if it exists
    std::string::size_type lastIndex = strFilename.find_last_of( "/\\" );
    if ( lastIndex != std::string::npos ) {
        strFilePath_ = strFilename.substr( 0, lastIndex );
    }

    if ( strFilename.empty() ) {
        throw ExceptionHandler( __FUNCTION__ + std::string( " missing filename", bSaveExceptionInLog_ ) );
    }
}

FileHandler::~FileHandler() {
    if ( fileStream_.is_open() ) {
         fileStream_.close();
    }
}

void FileHandler::throwError( const std::string& strMessage ) const {
    throw ExceptionHandler( "File [" + strFilenameWithPath_ + "] " + strMessage, bSaveExceptionInLog_ );
}

void FileHandler::throwError( const std::ostringstream& strStreamMessage ) const {
    throwError( strStreamMessage.str() );
}

bool FileHandler::getString( std::string& str, bool appendPath ) {
    fileStream_.read( &str[0], str.size() );
    if ( fileStream_.fail() ) {
        return false;
    }

    // Trim Right
    str.erase( str.find_first_of( char( 0 ) ) );

    if ( appendPath && !strFilePath_.empty() ) {
        // Add path if one exists
        str = strFilePath_ + "/" + str;
    }

    return true;
}

} // namespace demo
#ifndef TEXT_FILE_READER_H
#define TEXT_FILE_READER_H

#include "FileHandler.h"

namespace demo {

class TextFileReader : public FileHandler {
public:
    explicit TextFileReader( const std::string& strFilename );
    virtual ~TextFileReader() = default;

    std::string readAll() const;
    bool        readLine( std::string& strLine );

    TextFileReader( const TextFileReader& c ) = delete;
    TextFileReader& operator=( const TextFileReader& c ) = delete;
};

} // namespace demo

#endif // !TEXT_FILE_READER_H
#include "stdafx.h"
#include "TextFileReader.h"

namespace demo {

TextFileReader::TextFileReader( const std::string& strFilename ) :
FileHandler( strFilename, true ) {
    fileStream_.open( strFilenameWithPath_.c_str(), std::ios_base::in );
    if ( !fileStream_.is_open() ) {
        throwError( __FUNCTION__ + std::string( " can not open file for reading" ) );
    }
}

std::string TextFileReader::readAll() const {
    std::ostringstream strStream;
    strStream << fileStream_.rdbuf();

    return strStream.str();
}

bool TextFileReader::readLine( std::string& strLine ) {
    if ( fileStream_.eof() ) {
        return false;
    }
    std::getline( fileStream_, strLine );
    return true;
}

} // namespace demo
#ifndef TEXT_FILE_WRITER_H
#define TEXT_FILE_WRITER_H

#include "FileHandler.h"

namespace demo {

class TextFileWriter : public FileHandler {
public:
    explicit TextFileWriter( const std::string& strFilename, bool bAppendToFile, bool bSaveExceptionInLog = true );
    virtual ~TextFileWriter() = default;

    void write( const std::string& str );

    TextFileWriter( const TextFileWriter& c ) = delete;
    TextFileWriter& operator=( const TextFileWriter& c ) = delete;
};

} // namespace demo

#endif // !TEXT_FILE_WRITER_H
#include "stdafx.h"
#include "TextFileWriter.h"

namespace demo {

TextFileWriter::TextFileWriter( const std::string& strFilename, bool bAppendToFile, bool bSaveExceptionInLog ) :
FileHandler( strFilename, bSaveExceptionInLog ) {
    fileStream_.open( strFilenameWithPath_.c_str(),
                      std::ios_base::out | (bAppendToFile ? std::ios_base::app : std::ios_base::trunc) );

    if ( !fileStream_.is_open() ) {
        throwError( __FUNCTION__ + std::string( " can not open file for writing" ) );
    }
}

void TextFileWriter::write( const std::string& str ) {
    fileStream_ << str;
}

} // namespace demo
#ifndef BLOCK_PROCESS_H
#define BLOCK_PROCESS_H

namespace demo {

class BlockProcess final {
private:
    HANDLE hMutex_;
public:
    explicit BlockProcess( const std::string& strName );
    ~BlockProcess();

    bool isBlocked() const;

    BlockProcess( const BlockProcess& c ) = delete;
    BlockProcess& operator=( const BlockProcess& c ) = delete;
};

} // namespace demo

#endif // !BLOCK_PROCESS_H
#include "stdafx.h"
#include "BlockProcess.h"

namespace demo {

BlockProcess::BlockProcess( const std::string& strName ) {
    hMutex_ = CreateMutex( nullptr, FALSE, strName.c_str() );
} 

BlockProcess::~BlockProcess() {
    CloseHandle( hMutex_ );
}

bool BlockProcess::isBlocked() const {
    return (hMutex_ == nullptr || GetLastError() == ERROR_ALREADY_EXISTS);
}

} // namespace demo
#ifndef BLOCK_THREAD_H
#define BLOCK_THREAD_H

namespace demo { 

class BlockThread final {
private:
    CRITICAL_SECTION* pCriticalSection_;

public:
    explicit BlockThread( CRITICAL_SECTION& criticalSection );
    ~BlockThread();

    BlockThread( const BlockThread& c ) = delete;
    BlockThread& operator=( const BlockThread& c ) = delete;
}; 

} // namespace demo

#endif // !BLOCK_THREAD_H
#include "stdafx.h"
#include "BlockThread.h"

namespace demo { 

BlockThread::BlockThread( CRITICAL_SECTION& criticalSection ) {
    pCriticalSection_ = &criticalSection;
    EnterCriticalSection( pCriticalSection_ );
} 

BlockThread::~BlockThread() {
    LeaveCriticalSection( pCriticalSection_ );
}

} // namespace demo
#ifndef UTILITY_H
#define UTILITY_H

namespace demo { 

class Utility {
public:
    static void pressAnyKeyToQuit();

    static std::string  toUpper( const std::string& str );
    static std::string  toLower( const std::string& str );
    static std::string  trim( const std::string& str, const std::string elementsToTrim = " \t\n\r" );

    static unsigned     convertToUnsigned( const std::string& str );
    static int          convertToInt( const std::string& str );
    static float        convertToFloat( const std::string& str );

    static std::vector<std::string> splitString( const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty = true );

    Utility( const Utility& c ) = delete;
    Utility& operator=( const Utility& c ) = delete;

private:
    Utility(); // Private - Not A Class Object

    template<typename T>
    static bool stringToValue( const std::string& str, T* pValue, unsigned uNumValues );

    template<typename T>
    static T getValue( const std::string& str, std::size_t& remainder );    
}; 

#include "Utility.inl"

} // namespace demo

#endif // !UTILITY_H
template<typename T>
static bool Utility::stringToValue( const std::string& str, T* pValue, unsigned uNumValues ) {
    int numCommas = std::count( str.begin(), str.end(), ',' );
    if ( numCommas != uNumValues - 1 ) {
        return false;
    }

    std::size_t remainder;
    pValue[0] = getValue<T>( str, remainder );

    if ( uNumValues == 1 ) {
        if ( str.size() != remainder ) {
            return false;
        }
    } else {
        std::size_t offset = remainder;
        if ( str.at( offset ) != ',' ) {
            return false;
        }

        unsigned uLastIdx = uNumValues - 1;
        for ( unsigned u = 1; u < uNumValues; ++u ) {
            pValue[u] = getValue<T>( str.substr( ++offset ), remainder );
            offset += remainder;
            if ( (u < uLastIdx && str.at( offset ) != ',') ||
                (u == uLastIdx && offset != str.size()) ) {
                return false;
            }
        }
    }
    return true;
}
#include "stdafx.h"
#include "Utility.h"

namespace demo {

void Utility::pressAnyKeyToQuit() {
    std::cout << "\nPress any key to quit." << std::endl;
    _getch();
} 

std::string Utility::toUpper( const std::string& str ) {
    std::string result = str;
    std::transform( str.begin(), str.end(), result.begin(), ::toupper );
    return result;
}

std::string Utility::toLower( const std::string& str ) {
    std::string result = str;
    std::transform( str.begin(), str.end(), result.begin(), ::tolower );
    return result;
} 

std::string Utility::trim( const std::string& str, const std::string elementsToTrim ) {
    std::basic_string<char>::size_type firstIndex = str.find_first_not_of( elementsToTrim );
    if ( firstIndex == std::string::npos ) {
        return std::string(); // Nothing Left
    }

    std::basic_string<char>::size_type lastIndex = str.find_last_not_of( elementsToTrim );
    return str.substr( firstIndex, lastIndex - firstIndex + 1 );
}

template<>
float Utility::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stof( str, &remainder );
}

template<>
int Utility::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stoi( str, &remainder );
}

template<>
unsigned Utility::getValue( const std::string& str, std::size_t& remainder ) {
    return std::stoul( str, &remainder );
}

unsigned Utility::convertToUnsigned( const std::string& str ) {
    unsigned u = 0;
    if ( !stringToValue( str, &u, 1 ) ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to unsigned";
        throw strStream.str();
    }
    return u;
}

int Utility::convertToInt( const std::string& str ) {
    int i = 0;
    if ( !stringToValue( str, &i, 1 ) ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to int";
        throw strStream.str();
    }
    return i;
}

float Utility::convertToFloat( const std::string& str ) {
    float f = 0;
    if ( !stringToValue( str, &f, 1 ) ) {
        std::ostringstream strStream;
        strStream << __FUNCTION__ << " Bad conversion of [" << str << "] to float";
        throw strStream.str();
    }
    return f;
}

std::vector<std::string> Utility::splitString( const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty ) {
    std::vector<std::string> vResult;
    if ( strDelimiter.empty() ) {
        vResult.push_back( strStringToSplit );
        return vResult;
    }

    std::string::const_iterator itSubStrStart = strStringToSplit.begin(), itSubStrEnd;
    while ( true ) {
        itSubStrEnd = search( itSubStrStart, strStringToSplit.end(), strDelimiter.begin(), strDelimiter.end() );
        std::string strTemp( itSubStrStart, itSubStrEnd );
        if ( keepEmpty || !strTemp.empty() ) {
            vResult.push_back( strTemp );
        }

        if ( itSubStrEnd == strStringToSplit.end() ) {
            break;
        }

        itSubStrStart = itSubStrEnd + strDelimiter.size();
    }

    return vResult;
}

} // namespace demo
#ifndef STDAFX_H
#define STDAFX_H

// Included files that typically will not change 
// during the development process of this application.   

// System - Architect Includes
#include <Windows.h>
#include <process.h>
//#include <mmsystem.h>

// Character & Basic IO
#include <conio.h> // for _getch()
#include <tchar.h>

//---------------------------------------------//
// Standard Library Includes

// Atomics, Regular Expressions, Localizations
#include <atomic>       // C++11
#include <clocale>
//#include <codecvt> // C++11 // Deprecated in C++17
#include <locale>
#include <regex>

// Numerics & Numeric Limits
#include <climits>
#include <cfloat>
#include <cstdint>      // C++11
#include <cinttypes>    // C++11
#include <limits>
#include <cmath>
#include <complex>
#include <valarray>
#include <random>       // C++11
#include <numeric>
#include <ratio>        // C++11
#include <cfenv>        // C++11

// Strings, Streams & IO 
#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <fstream>

// Thread Support
#include <thread>               // C++11
#include <mutex>                // C++11
#include <shared_mutex>         // C++14
#include <future>               // C++11
#include <condition_variable>   // C++11

// Containers
#include <array>         // C++11
#include <stack>
#include <list>
#include <forward_list>  // C++11
#include <map>
#include <unordered_map> // C++11
#include <queue>
#include <deque>
#include <set>
#include <unordered_set>  // C++11
#include <vector>

// Algorithms, Iterators
#include <algorithm>      // Note* C++ 17 also has <execution>
#include <iterator>

// Dynamic Memory
#include <new>
#include <memory>
#include <scoped_allocator> // C++11

// Utilities
#include <bitset>
#include <ctime>        // Compatability with C style time formarts
#include <chrono>       // C++ 11 - C++ Time Utilities
#include <functional>
#include <initializer_list>  // C++11
#include <memory>
#include <thread>
#include <typeinfo>
#include <typeindex>    // C++11
#include <type_traits>  // C++11
#include <tuple>        // C++11
#include <utility>

// C++ 17
#include <any>
#include <filesystem>
#include <optional>
#include <string_view>
#include <variant>    

// C++ 20
// #include <compare> 
// #include <charconv>
// #include <syncstream>

// 3rd Party Library Includes Here.

// User-Application Specific commonly used non changing headers.
#include "ExceptionHandler.h" 

namespace demo { 

enum ReturnCode {
    RETURN_OK = 0,
    RETURN_ERROR = 1,
}; // ReturnCode

extern const unsigned INVALID_UNSIGNED;
extern const unsigned INVALID_UNSIGNED_SHORT;

} // namespace demo

#endif // !STDAFX_H
#include "stdafx.h"

namespace demo {

const unsigned INVALID_UNSIGNED = static_cast<const unsigned>(-1);
const unsigned INVALID_UNSIGNED_SHORT = static_cast<const unsigned short>(-1);

} // namespace demo