diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 212ae4fcfc..2274b75e75 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -4,6 +4,19 @@ KiCad ChangeLog 2010 Please add newer entries at the top, list the date and your name with email address. +2010-Dec-28 UPDATE Dick Hollenbeck +================================================================================ +++richio: + * Deleted kicad_exceptions, because it required the big #include and + that was slowing down compiling. Moved that stuff back into richio.h where + it came from. + * Enhanced IO_ERROR to format an errorText. + * Added THROW_IO_ERROR() and THROW_PARSE_ERROR() macros to capture the + the call site of the thrower. If you have problems compiling, it is probably + due to the definition of __LOC__ in richio.h. Some compilers may not support + __func__ in C++ yet. Find a macro that identifies your compiler, and we can + work out something in the #define of __LOC__. + 2010-Dec-28 UPDATE Dick Hollenbeck ================================================================================ ++new: diff --git a/common/dsnlexer.cpp b/common/dsnlexer.cpp index 8c91bed4d3..b1ded2154e 100644 --- a/common/dsnlexer.cpp +++ b/common/dsnlexer.cpp @@ -251,12 +251,14 @@ bool DSNLEXER::IsSymbol( int aTok ) void DSNLEXER::ThrowIOError( wxString aText, int charOffset ) throw( IO_ERROR ) { + // @todo convert this to THROW_PARSE_ERROR() + // append to aText, do not overwrite aText << wxT(" ") << _("in") << wxT(" \"") << CurSource() << wxT("\" ") << _("on line") << wxT(" ") << reader->LineNumber() << wxT(" ") << _("at offset") << wxT(" ") << charOffset; - throw IO_ERROR( aText ); + THROW_IO_ERROR( aText ); } diff --git a/common/richio.cpp b/common/richio.cpp index 0a526b5460..4fb458ec29 100644 --- a/common/richio.cpp +++ b/common/richio.cpp @@ -113,7 +113,7 @@ unsigned FILE_LINE_READER::ReadLine() throw( IO_ERROR ) length += strlen( line + length ); if( length == maxLineLength ) - throw IO_ERROR( _("Line length exceeded") ); + THROW_IO_ERROR( _("Line length exceeded") ); // a normal line breaks here, once through while loop if( length+1 < capacity || line[length-1] == '\n' ) @@ -164,7 +164,7 @@ unsigned STRING_LINE_READER::ReadLine() throw( IO_ERROR ) if( length ) { if( length >= maxLineLength ) - throw IO_ERROR( _("Line length exceeded") ); + THROW_IO_ERROR( _("Line length exceeded") ); if( length+1 > capacity ) // +1 for terminating nul expandCapacity( length+1 ); @@ -305,7 +305,7 @@ std::string OUTPUTFORMATTER::Quoted( const std::string& aWrapee ) throw( IO_ERRO // a decision was made to make all S-expression strings be on a single // line. You can embed \n (human readable) in the text but not // '\n' which is 0x0a. - throw IO_ERROR( _( "S-expression string has newline" ) ); + THROW_IO_ERROR( _( "S-expression string has newline" ) ); } } @@ -363,7 +363,7 @@ void STREAM_OUTPUTFORMATTER::write( const char* aOutBuf, int aCount ) throw( IO_ if( !os.IsOk() ) { - throw IO_ERROR( _( "OUTPUTSTREAM_OUTPUTFORMATTER write error" ) ); + THROW_IO_ERROR( _( "OUTPUTSTREAM_OUTPUTFORMATTER write error" ) ); } } } diff --git a/eeschema/sch_component.h b/eeschema/sch_component.h index 89c41dd9b5..49773edcf4 100644 --- a/eeschema/sch_component.h +++ b/eeschema/sch_component.h @@ -17,25 +17,6 @@ class LIB_PIN; class LIB_COMPONENT; -/** - * Holder of an error message and may be thrown from functions. - */ -struct Error -{ - wxString errorText; - - Error( const wxChar* aMsg ) : - errorText( aMsg ) - { - } - - - Error( const wxString& aMsg ) : - errorText( aMsg ) - { - } -}; - /// A container for several SCH_FIELD items typedef std::vector SCH_FIELDS; diff --git a/include/kicad_exceptions.h b/include/kicad_exceptions.h deleted file mode 100644 index 659f507690..0000000000 --- a/include/kicad_exceptions.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This program source code file is part of KICAD, a free EDA CAD application. - * - * Copyright (C) 2010 SoftPLC Corporation, - * Copyright (C) 2010 Kicad Developers, see change_log.txt for contributors. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#ifndef KICAD_EXCEPTIONS_H_ -#define KICAD_EXCEPTIONS_H_ - -/** - * @ingroup exception_types - * @{ - */ - - -#include -#include - -/** - * Struct IO_ERROR - * is a class used to hold an error message and may be used to throw exceptions - * containing meaningful error messages. - * @author Dick Hollenbeck - */ -struct IO_ERROR -{ - wxString errorText; - - /** - * Constructor ( const wxChar* ) - * handles the case where _() is passed as aMsg. - */ - IO_ERROR( const wxChar* aMsg ) : - errorText( aMsg ) - { - } - - IO_ERROR( const wxString& aMsg ) : - errorText( aMsg ) - { - } - - IO_ERROR( const std::string& aMsg ) : - errorText( wxConvertMB2WX( aMsg.c_str() ) ) - { - } -}; - - -/** - * Class PARSE_ERROR - * contains a filename or source description, a line number, a character offset, - * and an error message. - * @author Dick Hollenbeck - */ -struct PARSE_ERROR : public IO_ERROR -{ - wxString source; ///< filename typically, or other source - int lineNumber; - int byteIndex; ///< char offset, starting from 1, into the problem line. - - PARSE_ERROR( const wxString& aMsg, const wxString& aSource, - int aLineNumber, int aByteIndex ) : - IO_ERROR( aMsg ), - source( aSource ), - lineNumber( aLineNumber ) - { - } -}; - -/** @} exception_types */ - - -#endif // KICAD_EXCEPTIONS_H_ diff --git a/include/richio.h b/include/richio.h index fac82ed820..ce40141a20 100644 --- a/include/richio.h +++ b/include/richio.h @@ -38,7 +38,134 @@ // but the errorText needs to be wide char so wxString rules. #include #include -#include + + +/** + * @ingroup exception_types + * @{ + */ + + +#define IO_FORMAT _( "IO_ERROR: '%s'\n from %s : %s" ) +#define PARSE_FORMAT _( "PARSE_ERROR: '%s' in input/source '%s', line %d, offset %d\n from %s : %s" ) + +// references: +// http://stackoverflow.com/questions/2670816/how-can-i-use-the-compile-time-constant-line-in-a-string +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + +// use one of the following __LOC__ defs, depending on whether your +// compiler supports __func__ or not, and how it handles __LINE__ +#define __LOC__ ((std::string(__func__) + " : ") + TOSTRING(__LINE__)).c_str() +//#define __LOC__ TOSTRING(__LINE__) + +/// macro which captures the "call site" values of __FILE_ & __LOC__ +#define THROW_IO_ERROR( msg ) throw IO_ERROR( __FILE__, __LOC__, msg ) + +/** + * Struct IO_ERROR + * is a class used to hold an error message and may be used to throw exceptions + * containing meaningful error messages. + * @author Dick Hollenbeck + */ +struct IO_ERROR // : std::exception +{ + wxString errorText; + + /** + * Constructor + * + * @param aThrowersFile is the __FILE__ preprocessor macro but generated + * at the source file of thrower. + * + * @param aThrowersLoc can be either a function name, such as __func__ + * or a stringified __LINE__ preprocessor macro but generated + * at the source function of the thrower, or concatonation. Use macro + * THROW_IO_ERROR() to wrap a call to this constructor at the call site. + * + * @param aMsg is error text that will be streamed through wxString.Printf() + * using the format string IO_FORMAT above. + */ + IO_ERROR( const char* aThrowersFile, + const char* aThrowersLoc, + const wxString& aMsg ) + { + init( aThrowersFile, aThrowersLoc, aMsg ); + } + + IO_ERROR( const char* aThrowersFile, + const char* aThrowersLoc, + const std::string& aMsg ) + { + init( aThrowersFile, aThrowersLoc, wxString::FromUTF8( aMsg.c_str() ) ); + } + + /** + * handles the case where _() is passed as aMsg. + */ + IO_ERROR( const char* aThrowersFile, + const char* aThrowersLoc, + const wxChar* aMsg ) + { + init( aThrowersFile, aThrowersLoc, wxString( aMsg ) ); + } + + void init( const char* aThrowersFile, const char* aThrowersLoc, const wxString& aMsg ) + { + errorText.Printf( IO_FORMAT, aMsg.GetData(), + wxString::FromUTF8( aThrowersFile ).GetData(), + wxString::FromUTF8( aThrowersLoc ).GetData() ); + } + + IO_ERROR() {} + + ~IO_ERROR() throw ( /*none*/ ){} +}; + + +#define THROW_PARSE_ERROR( msg, input, line, offset ) throw PARSE_ERROR( __FILE__, __LOC__, msg, input, line, offset ) + +/** + * Class PARSE_ERROR + * contains a filename or source description, a line number, a character offset, + * and an error message. + * @author Dick Hollenbeck + */ +struct PARSE_ERROR : public IO_ERROR +{ + // wxString errorText is still public from IO_ERROR + + int lineNumber; ///< at which line number, 1 based index. + int byteIndex; ///< at which character position within the line, 1 based index + + PARSE_ERROR( const char* aThrowersFile, const char* aThrowersLoc, + const wxString& aMsg, const wxString& aSource, + int aLineNumber, int aByteIndex ) : + IO_ERROR() + { + init( aThrowersFile, aThrowersLoc, aMsg, aSource, aLineNumber, aByteIndex ); + } + + void init( const char* aThrowersFile, const char* aThrowersLoc, + const wxString& aMsg, const wxString& aSource, + int aLineNumber, int aByteIndex ) + { + // save line and offset in binary for Sweet text editor, which will catch exceptions + lineNumber = aLineNumber; + byteIndex = aByteIndex; + + // #define PARSE_FORMAT _( "PARSE_ERROR: %s in source %s, line %d, offset %d\nfrom cpp:%s func:%s" ) + + errorText.Printf( PARSE_FORMAT, aMsg.GetData(), aSource.GetData(), + aLineNumber, aByteIndex, + wxString::FromUTF8( aThrowersFile ).GetData(), + wxString::FromUTF8( aThrowersLoc ).GetData() ); + } + + ~PARSE_ERROR() throw ( /*none*/ ){} +}; + +/** @} exception_types */ #define LINE_READER_LINE_DEFAULT_MAX 100000 diff --git a/new/sch_dir_lib_source.cpp b/new/sch_dir_lib_source.cpp index 55a5f81435..0e7f0a8f32 100644 --- a/new/sch_dir_lib_source.cpp +++ b/new/sch_dir_lib_source.cpp @@ -41,16 +41,10 @@ */ -#include -using namespace SCH; - -#include - #include #include #include #include - #include #include #include @@ -61,11 +55,8 @@ using namespace SCH; #include using namespace std; -/// This file extension is an implementation detail specific to this LIB_SOURCE -/// implementation, and to a corresponding LIB_SINK. -/// Core EESCHEMA should never have to see this. -#define SWEET_EXT ".part" -#define SWEET_EXTZ (sizeof(SWEET_EXT)-1) +#include +using namespace SCH; /* __func__ is C99 prescribed, but just in case: @@ -318,7 +309,7 @@ void DIR_LIB_SOURCE::readString( STRING* aResult, const STRING& aFileName ) thro { STRING msg = strerror( errno ); msg += "; cannot open(O_RDONLY) file " + aFileName; - throw( IO_ERROR( msg ) ); + THROW_IO_ERROR( msg ); } struct stat fs; @@ -330,9 +321,14 @@ void DIR_LIB_SOURCE::readString( STRING* aResult, const STRING& aFileName ) thro { STRING msg = aFileName; msg += " seems too big. ( > 1 mbyte )"; - throw IO_ERROR( msg ); + THROW_IO_ERROR( msg ); } +#if 0 + // I read somewhere on the Internet that std::string chars are not guaranteed + // (over time) to be contiguous in future implementations of C++, so this + // strategy is here for that eventuality. We buffer through readBuffer here. + // reuse same readBuffer, which is not thread safe, but the API // is not advertising thread safe (yet, if ever). if( (int) fs.st_size > (int) readBuffer.size() ) @@ -343,13 +339,27 @@ void DIR_LIB_SOURCE::readString( STRING* aResult, const STRING& aFileName ) thro { STRING msg = strerror( errno ); msg += "; cannot read file " + aFileName; - throw( IO_ERROR( msg ) ); + THROW_IO_ERROR( msg ); } - // std::string chars are not guaranteed to be contiguous in - // future implementations of C++, so this is why we did not read into - // aResult directly. aResult->assign( &readBuffer[0], count ); +#else + + // read into the string directly + aResult->resize( fs.st_size ); + + int count = read( fw, &(*aResult)[0], fs.st_size ); + if( count != (int) fs.st_size ) + { + STRING msg = strerror( errno ); + msg += "; cannot read file " + aFileName; + THROW_IO_ERROR( msg ); + } + + // test trailing nul is there, which should have been put there with resize() above + // printf( "'%s'\n", aResult->c_str() ); // checked OK. +#endif + } @@ -371,7 +381,7 @@ DIR_LIB_SOURCE::DIR_LIB_SOURCE( const STRING& aDirectoryPath, if( sourceURI.size() == 0 ) { - throw( IO_ERROR( STRING("aDirectoryPath cannot be empty") ) ); + THROW_IO_ERROR( STRING("aDirectoryPath cannot be empty") ); } // remove any trailing separator, so we can add it back later without ambiguity @@ -439,7 +449,7 @@ void DIR_LIB_SOURCE::ReadPart( STRING* aResult, const STRING& aPartName, const S if( !useVersioning && (aRev.size() || rev) ) { STRING msg = "this type 'dir' LIB_SOURCE not using 'useVersioning' option, cannot ask for a revision"; - throw IO_ERROR( msg ); + THROW_IO_ERROR( msg ); } if( aRev.size() ) @@ -462,7 +472,7 @@ void DIR_LIB_SOURCE::ReadPart( STRING* aResult, const STRING& aPartName, const S if( it == partnames.end() ) // part not found { partName += " not found."; - throw IO_ERROR( partName ); + THROW_IO_ERROR( partName ); } readString( aResult, makeFileName( partName ) ); @@ -482,7 +492,7 @@ void DIR_LIB_SOURCE::ReadPart( STRING* aResult, const STRING& aPartName, const S { partName.insert( partName.begin(), '\'' ); partName += "' is not present without a revision."; - throw IO_ERROR( partName ); + THROW_IO_ERROR( partName ); } readString( aResult, makeFileName( *it ) ); @@ -547,7 +557,7 @@ void DIR_LIB_SOURCE::cacheOneDir( const STRING& aCategory ) throw( IO_ERROR ) { STRING msg = strerror( errno ); msg += "; scanning directory " + curDir; - throw( IO_ERROR( msg ) ); + THROW_IO_ERROR( msg ); } struct stat fs; @@ -573,7 +583,7 @@ void DIR_LIB_SOURCE::cacheOneDir( const STRING& aCategory ) throw( IO_ERROR ) { STRING msg = partName; msg += " has already been encountered"; - throw IO_ERROR( msg ); + THROW_IO_ERROR( msg ); } } @@ -656,7 +666,7 @@ void DIR_LIB_SOURCE::Test( int argc, char** argv ) printf( "std::exception\n" ); } - catch( IO_ERROR ioe ) + catch( IO_ERROR& ioe ) { printf( "exception: %s\n", (const char*) ioe.errorText.ToUTF8() ) ); } diff --git a/new/sch_dir_lib_source.h b/new/sch_dir_lib_source.h index 5e64ddec05..d7a7b9667c 100644 --- a/new/sch_dir_lib_source.h +++ b/new/sch_dir_lib_source.h @@ -26,11 +26,18 @@ #define DIR_LIB_SOURCE_H_ -#include - #include #include +#include + + +/// This file extension is an implementation detail specific to this LIB_SOURCE +/// and to a corresponding LIB_SINK. +/// Core EESCHEMA should never have to see this. +#define SWEET_EXT ".part" +#define SWEET_EXTZ (sizeof(SWEET_EXT)-1) + /** * struct BY_REV diff --git a/new/sch_lib.h b/new/sch_lib.h index 3f1bc7ca66..c05bfcf863 100644 --- a/new/sch_lib.h +++ b/new/sch_lib.h @@ -26,7 +26,7 @@ #define SCH_LIB_H_ #include -#include +#include namespace SCH { diff --git a/new/sch_lib_table.cpp b/new/sch_lib_table.cpp index 8a2b552c1e..a5100c5d55 100644 --- a/new/sch_lib_table.cpp +++ b/new/sch_lib_table.cpp @@ -132,7 +132,7 @@ void LIB_TABLE::Parse( SCH_LIB_TABLE_LEXER* in ) throw( IO_ERROR ) msg += row->logicalName; msg += '\''; msg += " is a duplicate logical lib name"; - throw IO_ERROR( msg ); + THROW_IO_ERROR( msg ); } } } @@ -207,7 +207,7 @@ LIB* LIB_TABLE::lookupLib( const LPID& aLPID, LIB* aFallBackLib ) STRING msg = "lib table contains no logical lib '"; msg += aLPID.GetLogicalLib(); msg += '\''; - throw IO_ERROR( msg ); + THROW_IO_ERROR( msg ); } if( !row->lib ) @@ -226,7 +226,7 @@ LIB* LIB_TABLE::lookupLib( const LPID& aLPID, LIB* aFallBackLib ) } STRING msg = "lookupLib() requires logicalLibName or a fallback lib"; - throw IO_ERROR( msg ); + THROW_IO_ERROR( msg ); } @@ -276,7 +276,7 @@ void LIB_TABLE::loadLib( ROW* aRow ) throw( IO_ERROR ) STRING msg = "cannot load unknown libType: '"; msg += libType; msg += '\''; - throw IO_ERROR( msg ); + THROW_IO_ERROR( msg ); } } @@ -342,9 +342,12 @@ bool LIB_TABLE::InsertRow( std::auto_ptr& aRow, bool doReplace ) void LIB_TABLE::Test() { - // the null string is not really a legal DSN token since any duplicated - // double quote ("") is assumed to be a single double quote ("). - // To pass an empty string, we can pass " " to (options " ") + // A pair of double quotes alone, is not really a legal DSN token since + // any duplicated double quote ("") is assumed to be a single double quote (") + // in any DSN lexer. + // To pass an empty string, we can pass " " to (options " "), or if you passed + // """" this would show up as "" with quotes present in the parser. The parser + // probably doesn't want a pair of double quotes, strlen() = 2. SCH_LIB_TABLE_LEXER slr( "(lib_table \n" " (lib (logical www) (type http) (full_uri http://kicad.org/libs) (options \" \"))\n" @@ -355,25 +358,12 @@ void LIB_TABLE::Test() wxT( "inline text" ) // source ); - try - { - // read the "( lib_table" pair of tokens - slr.NextTok(); - slr.NextTok(); + // read the "( lib_table" pair of tokens + slr.NextTok(); + slr.NextTok(); - // parse the rest of input to slr - Parse( &slr ); - } - - catch( std::exception& ex ) - { - printf( "std::exception\n" ); - } - - catch( IO_ERROR& ioe ) - { - printf( "exception: %s\n", (const char*) ioe.errorText.ToUTF8() ); - } + // parse the rest of input to slr + Parse( &slr ); STRING_FORMATTER sf; @@ -404,17 +394,10 @@ void LIB_TABLE::Test() printf( "logicalName: %s\n", it->c_str() ); } - try - { - // find a part - LPID lpid( "meparts:tigers/ears/rev10" ); + // find a part + LPID lpid( "meparts:tigers/ears/rev10" ); - LookupPart( lpid ); - } - catch( IO_ERROR& ioe ) - { - printf( "exception: %s\n", (const char*) ioe.errorText.ToUTF8() ); - } + LookupPart( lpid ); } @@ -422,7 +405,18 @@ int main( int argc, char** argv ) { LIB_TABLE lib_table; - lib_table.Test(); + try + { + // test exceptions: + // THROW_PARSE_ERROR( wxT("test problem"), wxT("input"), 23, 46 ); + //THROW_IO_ERROR( wxT("io test") ); + + lib_table.Test(); + } + catch( IO_ERROR& ioe ) + { + printf( "%s\n", (const char*) ioe.errorText.ToUTF8() ); + } return 0; } diff --git a/new/sch_lpid.cpp b/new/sch_lpid.cpp index 3f80c702d0..d80fb61a66 100644 --- a/new/sch_lpid.cpp +++ b/new/sch_lpid.cpp @@ -184,7 +184,7 @@ LPID::LPID( const STRING& aLPID ) throw( PARSE_ERROR ) if( offset != -1 ) { - throw PARSE_ERROR( + THROW_PARSE_ERROR( _( "Illegal character found in LPID string" ), wxConvertMB2WX( aLPID.c_str() ), 0, diff --git a/new/sch_lpid.h b/new/sch_lpid.h index 9b385edf24..44d08a17a4 100644 --- a/new/sch_lpid.h +++ b/new/sch_lpid.h @@ -26,7 +26,7 @@ #define SCH_LPID_H_ #include -#include +#include namespace SCH { diff --git a/pcbnew/specctra.cpp b/pcbnew/specctra.cpp index cae5c4854a..5726de8f2a 100644 --- a/pcbnew/specctra.cpp +++ b/pcbnew/specctra.cpp @@ -542,7 +542,7 @@ void SPECCTRA_DB::ThrowIOError( const wxChar* fmt, ... ) throw( IO_ERROR ) errText.PrintfV( fmt, args ); va_end( args ); - throw IO_ERROR( errText ); + THROW_IO_ERROR( errText ); }