Rewrite class UTF8 to contain rather than extend std::string storage.

This forces the compiler class specific features rather than borrowing
from the base class's std::string.  In some cases prior to this,
wxString( std::string ) was being called rather than UTF8::operator
wxString() leading to garbled wxStrings.

Added function UTF8::wx_str() which is of great convenience also.

Implicit conversions still work as before, and hopefully more reliably.
This commit is contained in:
Dick Hollenbeck 2017-07-25 14:14:31 -05:00 committed by Wayne Stambaugh
parent 0aedaffed9
commit 19e6bde09a
18 changed files with 170 additions and 97 deletions

View File

@ -286,7 +286,7 @@ MODULE* FP_LIB_TABLE::FootprintLoad( const wxString& aNickname, const wxString&
LIB_ID& fpid = (LIB_ID&) ret->GetFPID();
// Catch any misbehaving plugin, which should be setting internal footprint name properly:
wxASSERT( aFootprintName == (wxString) fpid.GetLibItemName() );
wxASSERT( aFootprintName == fpid.GetLibItemName().wx_str() );
// and clearing nickname
wxASSERT( !fpid.GetLibNickname().size() );

View File

@ -83,7 +83,7 @@ int RevCmp( const char* s1, const char* s2 )
//----<Policy and field test functions>-------------------------------------
static inline int okLogical( const std::string& aField )
static inline int okLogical( const UTF8& aField )
{
// std::string::npos is largest positive number, casting to int makes it -1.
// Returning that means success.
@ -91,7 +91,7 @@ static inline int okLogical( const std::string& aField )
}
static int okRevision( const std::string& aField )
static int okRevision( const UTF8& aField )
{
char rev[32]; // C string for speed
@ -175,7 +175,7 @@ int LIB_ID::Parse( const UTF8& aId )
}
LIB_ID::LIB_ID( const std::string& aId )
LIB_ID::LIB_ID( const UTF8& aId )
{
int offset = Parse( aId );

View File

@ -40,11 +40,17 @@
UTF8::UTF8( const wxString& o ) :
std::string( (const char*) o.utf8_str() )
m_s( (const char*) o.utf8_str() )
{
}
wxString UTF8::wx_str() const
{
return wxString( c_str(), wxConvUTF8 );
}
UTF8::operator wxString () const
{
return wxString( c_str(), wxConvUTF8 );
@ -53,7 +59,7 @@ UTF8::operator wxString () const
UTF8& UTF8::operator=( const wxString& o )
{
std::string::operator=( (const char*) o.utf8_str() );
m_s = (const char*) o.utf8_str();
return *this;
}
@ -193,7 +199,7 @@ bool IsUTF8( const char* aString )
UTF8::UTF8( const wchar_t* txt ) :
// size initial string safely large enough, then shrink to known size later.
std::string( wcslen( txt ) * 4, 0 )
m_s( wcslen( txt ) * 4, 0 )
{
/*
@ -206,9 +212,9 @@ UTF8::UTF8( const wchar_t* txt ) :
*/
int sz = wxConvUTF8.WC2MB( (char*) data(), txt, size() );
int sz = wxConvUTF8.WC2MB( (char*) m_s.data(), txt, m_s.size() );
resize( sz );
m_s.resize( sz );
}

View File

@ -811,7 +811,7 @@ int CVPCB_MAINFRAME::ReadSchematicNetlist( const std::string& aNetlist )
for( unsigned ii = 0; ii < m_netlist.GetCount(); ii++ )
{
if( m_netlist.GetComponent( ii )->GetFPID().GetLibItemName() == std::string( "$noname" ) )
m_netlist.GetComponent( ii )->SetFPID( LIB_ID( wxEmptyString ) );
m_netlist.GetComponent( ii )->SetFPID( LIB_ID() );
}
// Sort components by reference:

View File

@ -672,7 +672,7 @@ void PART_LIBS::LoadAllLibraries( PROJECT* aProject, bool aShowProgress )
{
// Use a different exception type so catch()er can route to proper use
// of the HTML_MESSAGE_BOX.
THROW_PARSE_ERROR( wxEmptyString, UTF8( __func__ ), UTF8( libs_not_found ), 0, 0 );
THROW_PARSE_ERROR( wxEmptyString, __func__, TO_UTF8(libs_not_found), 0, 0 );
}
#if defined(DEBUG) && 1

View File

@ -205,14 +205,14 @@ void DIALOG_SYMBOL_REMAP::remapSymbolsToLibTable( REPORTER& aReporter )
if( !remapSymbolToLibTable( symbol ) )
{
msg.Printf( _( "No symbol '%s' founded in symbol library table." ),
FROM_UTF8( symbol->GetLibId().GetLibItemName() ) );
symbol->GetLibId().GetLibItemName().wx_str() );
aReporter.Report( msg, REPORTER::RPT_WARNING );
}
else
{
msg.Printf( _( "Symbol '%s' mapped to symbol library '%s'." ),
FROM_UTF8( symbol->GetLibId().GetLibItemName() ),
FROM_UTF8( symbol->GetLibId().GetLibNickname() ) );
symbol->GetLibId().GetLibItemName().wx_str(),
symbol->GetLibId().GetLibNickname().wx_str() );
aReporter.Report( msg, REPORTER::RPT_ACTION );
}
}

View File

@ -106,7 +106,7 @@ bool SCH_EDIT_FRAME::CreateArchiveLibrary( const wxString& aFileName )
catch( ... /* IO_ERROR ioe */ )
{
msg.Printf( _( "Failed to add symbol %s to library file '%s'" ),
wxString( component->GetLibId().GetLibItemName() ), aFileName );
component->GetLibId().GetLibItemName().wx_str(), aFileName );
DisplayError( this, msg );
return false;
}

View File

@ -264,7 +264,7 @@ public:
for( SCH_COMPONENT* each_component : *( aRescuer.GetComponents() ) )
{
wxString part_name( each_component->GetLibId().GetLibItemName() );
wxString part_name = each_component->GetLibId().GetLibItemName();
if( last_part_name != part_name )
{
@ -373,7 +373,7 @@ public:
for( SCH_COMPONENT* each_component : *( aRescuer.GetComponents() ) )
{
wxString part_name( each_component->GetLibId().GetLibItemName() );
wxString part_name = each_component->GetLibId().GetLibItemName();
if( old_part_name != part_name )
{

View File

@ -271,7 +271,7 @@ LIB_ALIAS* SYMBOL_LIB_TABLE::LoadSymbol( const wxString& aNickname, const wxStri
LIB_ID& id = (LIB_ID&) ret->GetPart()->GetLibId();
// Catch any misbehaving plugin, which should be setting internal alias name properly:
wxASSERT( aAliasName == (wxString) id.GetLibItemName() );
wxASSERT( aAliasName == id.GetLibItemName().wx_str() );
// and clearing nickname
wxASSERT( !id.GetLibNickname().size() );

View File

@ -70,7 +70,7 @@ public:
*
* @param aId is a string to be parsed into the LIB_ID object.
*/
LIB_ID( const std::string& aId );
LIB_ID( const UTF8& aId );
LIB_ID( const wxString& aId );

View File

@ -50,13 +50,13 @@ bool IsUTF8( const char* aString );
/**
* Class UTF8
* is an 8 bit std::string that is assuredly encoded in UTF8, and supplies special
* conversion support to and from wxString, and has iteration over unicode characters.
* is an 8 bit string that is assuredly encoded in UTF8, and supplies special
* conversion support to and from wxString, to and from std::string, and has
* non-mutating iteration over unicode characters.
*
* <p>I've been careful to supply only conversion facilities and not try
* and duplicate wxString() with many member functions. In the end it is
* to be a std::string. There are multiple ways to create text into a std::string
* without the need of too many member functions:
* and duplicate wxString() with many member functions. There are multiple ways
* to create text into a std::string without the need of too many member functions:
*
* <ul>
* <li>richio.h's StrPrintf()</li>
@ -70,7 +70,7 @@ bool IsUTF8( const char* aString );
*
* @author Dick Hollenbeck
*/
class UTF8 : public std::string
class UTF8
{
public:
@ -79,7 +79,7 @@ public:
/// This is a constructor for which you could end up with
/// non-UTF8 encoding, but that would be your fault.
UTF8( const char* txt ) :
std::string( txt )
m_s( txt )
{
MAYBE_VERIFY_UTF8( c_str() );
}
@ -89,13 +89,12 @@ public:
UTF8( const wchar_t* txt );
UTF8( const std::string& o ) :
std::string( o )
m_s( o )
{
MAYBE_VERIFY_UTF8( c_str() );
}
UTF8() :
std::string()
UTF8()
{
}
@ -103,61 +102,97 @@ public:
{
}
// expose some std::string functions publicly, since base class must be private.
const char* c_str() const { return m_s.c_str(); }
bool empty() const { return m_s.empty(); }
std::string::size_type find( char c ) const { return m_s.find( c ); }
std::string::size_type find( char c, size_t& s ) const { return m_s.find( c, s ); }
void clear() { m_s.clear(); }
std::string::size_type length() const { return m_s.length(); }
std::string::size_type size() const { return m_s.size(); }
int compare( const std::string& s ) const { return m_s.compare( s ); }
bool operator==( const UTF8& rhs ) const { return m_s == rhs.m_s; }
bool operator==( const std::string& rhs ) const { return m_s == rhs; }
bool operator==( const char* s ) const { return m_s == s; }
std::string::size_type find_first_of( const std::string& str, std::string::size_type pos = 0 ) const
{
return m_s.find_first_of( str, pos );
}
UTF8& operator+=( const UTF8& str )
{
m_s += str.m_s;
MAYBE_VERIFY_UTF8( c_str() );
return (UTF8&) *this;
}
UTF8& operator+=( char ch )
{
m_s.operator+=( ch );
MAYBE_VERIFY_UTF8( c_str() );
return (UTF8&) *this;
}
UTF8& operator+=( const char* s )
{
m_s.operator+=( s );
MAYBE_VERIFY_UTF8( c_str() );
return (UTF8&) *this;
}
static const std::string::size_type npos = std::string::npos;
UTF8& operator=( const wxString& o );
UTF8& operator=( const std::string& o )
{
std::string::operator=( o );
m_s = o;
MAYBE_VERIFY_UTF8( c_str() );
return *this;
}
UTF8& operator=( const char* s )
{
std::string::operator=( s );
m_s = s;
MAYBE_VERIFY_UTF8( c_str() );
return *this;
}
UTF8& operator=( char c )
{
std::string::operator=( c );
m_s = c;
MAYBE_VERIFY_UTF8( c_str() );
return *this;
}
UTF8 substr( size_t pos = 0, size_t len = npos ) const
// a substring of a UTF8 is not necessarily a UTF8 if a multibyte character
// was split, so return std::string not UTF8
std::string substr( size_t pos = 0, size_t len = npos ) const
{
return std::string::substr( pos, len );
return m_s.substr( pos, len );
}
operator const std::string& () const { return m_s; }
//operator std::string& () { return m_s; }
//operator std::string () const { return m_s; }
wxString wx_str() const;
operator wxString () const;
/// This one is not in std::string, and one wonders why... might be a solid
/// enough reason to remove it still.
operator char* () const
{
return (char*) c_str();
}
/**
* Function uni_forward
* advances over a single UTF8 encoded multibyte character, capturing the
* unicode character as it goes, and returning the number of bytes consumed.
*
* @param aSequence is the UTF8 byte sequence, must be aligned on start of character.
* @param aResult is where to put the unicode character, and may be NULL if no interest.
* @return int - the count of bytes consumed.
*/
static int uni_forward( const unsigned char* aSequence, unsigned* aResult = NULL );
// "Read only" iterating over bytes is done with these, use the uni_iter to iterate
// over UTF8 (multi-byte) characters
std::string::const_iterator begin() const { return m_s.begin(); }
std::string::const_iterator end() const { return m_s.end(); }
#ifndef SWIG
/**
* class uni_iter
* is a non-muting iterator that walks through unicode code points in the UTF8 encoded
* is a non-mutating iterator that walks through unicode code points in the UTF8 encoded
* string. The normal ++(), ++(int), ->(), and *() operators are all supported
* for read only access and some return an unsigned holding the unicode character
* appropriate for the respective operator.
@ -168,11 +203,10 @@ public:
const unsigned char* it;
// private constructor.
// private constructor
uni_iter( const char* start ) :
it( (const unsigned char*) start )
{
// for the human: assert( sizeof(unsigned) >= 4 );
}
@ -204,7 +238,6 @@ public:
return ret;
}
/*
/// return unicode at current position
unsigned operator->() const
{
@ -214,7 +247,6 @@ public:
uni_forward( it, &result );
return result;
}
*/
/// return unicode at current position
unsigned operator*() const
@ -226,6 +258,8 @@ public:
return result;
}
uni_iter operator-( int aVal ) const { return uni_iter( (char*) it - aVal ); }
bool operator==( const uni_iter& other ) const { return it == other.it; }
bool operator!=( const uni_iter& other ) const { return it != other.it; }
@ -243,7 +277,7 @@ public:
*/
uni_iter ubegin() const
{
return uni_iter( data() );
return uni_iter( m_s.data() );
}
/**
@ -252,9 +286,24 @@ public:
*/
uni_iter uend() const
{
return uni_iter( data() + size() );
return uni_iter( m_s.data() + m_s.size() );
}
/**
* Function uni_forward
* advances over a single UTF8 encoded multibyte character, capturing the
* unicode character as it goes, and returning the number of bytes consumed.
*
* @param aSequence is the UTF8 byte sequence, must be aligned on start of character.
* @param aResult is where to put the unicode character, and may be NULL if no interest.
* @return int - the count of bytes consumed.
*/
static int uni_forward( const unsigned char* aSequence, unsigned* aResult = NULL );
#endif // SWIG
protected:
std::string m_s;
};
#endif // UTF8_H_

View File

@ -1165,7 +1165,7 @@ MODULE* EAGLE_PLUGIN::makeModule( wxXmlNode* aPackage, const string& aPkgName )
{
std::unique_ptr<MODULE> m( new MODULE( m_board ) );
m->SetFPID( LIB_ID( aPkgName ) );
m->SetFPID( LIB_ID( UTF8(aPkgName) ) );
// Get the first package item and iterate
wxXmlNode* packageItem = aPackage->GetChildren();

View File

@ -116,7 +116,7 @@ private:
static int m_fileOpt;
static int m_fileFormat;
void initDialog();
void initDialog();
void OnOutputDirectoryBrowseClicked( wxCommandEvent& event ) override;
void OnOKButton( wxCommandEvent& event ) override;
@ -524,7 +524,7 @@ int PCB_EDIT_FRAME::DoGenFootprintsPositionFile( const wxString& aFullFileName,
line << csv_sep;
line << list[ii].m_Value;
line << csv_sep;
line << wxString( list[ii].m_Module->GetFPID().GetLibItemName() );
line << list[ii].m_Module->GetFPID().GetLibItemName().wx_str();
line << csv_sep;
line << wxString::Format( "%f%c%f%c%f",

View File

@ -98,9 +98,9 @@ using namespace std;
static const char* PRETTY_DIR = "allow_pretty_writing_to_this_dir";
typedef boost::ptr_map<string, wxZipEntry> MODULE_MAP;
typedef MODULE_MAP::iterator MODULE_ITER;
typedef MODULE_MAP::const_iterator MODULE_CITER;
typedef boost::ptr_map< wxString, wxZipEntry > MODULE_MAP;
typedef MODULE_MAP::iterator MODULE_ITER;
typedef MODULE_MAP::const_iterator MODULE_CITER;
/**
@ -160,7 +160,7 @@ void GITHUB_PLUGIN::FootprintEnumerate( wxArrayString& aFootprintNames,
for( MODULE_ITER it = m_gh_cache->begin(); it!=m_gh_cache->end(); ++it )
{
unique.insert( FROM_UTF8( it->first.c_str() ) );
unique.insert( it->first );
}
for( MYSET::const_iterator it = unique.begin(); it != unique.end(); ++it )
@ -205,9 +205,7 @@ MODULE* GITHUB_PLUGIN::FootprintLoad( const wxString& aLibraryPath,
}
}
UTF8 fp_name = aFootprintName;
MODULE_CITER it = m_gh_cache->find( fp_name );
MODULE_CITER it = m_gh_cache->find( aFootprintName );
if( it != m_gh_cache->end() ) // fp_name is present
{
@ -234,7 +232,7 @@ MODULE* GITHUB_PLUGIN::FootprintLoad( const wxString& aLibraryPath,
// any name found in the pretty file; any name in the pretty file
// must be ignored here. Also, the library nickname is unknown in
// this context so clear it just in case.
ret->SetFPID( fp_name );
ret->SetFPID( aFootprintName );
return ret;
}
@ -447,6 +445,7 @@ void GITHUB_PLUGIN::cacheLib( const wxString& aLibraryPath, const PROPERTIES* aP
wxZipInputStream zis( mis, wxConvUTF8 );
wxZipEntry* entry;
wxString fp_name;
while( ( entry = zis.GetNextEntry() ) != NULL )
{
@ -454,9 +453,7 @@ void GITHUB_PLUGIN::cacheLib( const wxString& aLibraryPath, const PROPERTIES* aP
if( fn.GetExt() == kicad_mod )
{
UTF8 fp_name = fn.GetName(); // omit extension & path
wxASSERT( IsUTF8( fp_name.c_str() ) );
fp_name = fn.GetName(); // omit extension & path
m_gh_cache->insert( fp_name, entry );
}

View File

@ -133,9 +133,9 @@ bool FP_CACHE_ITEM::IsModified() const
}
typedef boost::ptr_map< std::string, FP_CACHE_ITEM > MODULE_MAP;
typedef MODULE_MAP::iterator MODULE_ITER;
typedef MODULE_MAP::const_iterator MODULE_CITER;
typedef boost::ptr_map< wxString, FP_CACHE_ITEM > MODULE_MAP;
typedef MODULE_MAP::iterator MODULE_ITER;
typedef MODULE_MAP::const_iterator MODULE_CITER;
class FP_CACHE
@ -304,12 +304,13 @@ void FP_CACHE::Load()
m_owner->m_parser->SetLineReader( &reader );
UTF8 name = fullPath.GetName();
MODULE* footprint = (MODULE*) m_owner->m_parser->Parse();
MODULE* footprint = (MODULE*) m_owner->m_parser->Parse();
// The footprint name is the file name without the extension.
footprint->SetFPID( LIB_ID( fullPath.GetName() ) );
m_modules.insert( name, new FP_CACHE_ITEM( footprint, fullPath ) );
wxString fpName = fullPath.GetName();
footprint->SetFPID( LIB_ID( fpName ) );
m_modules.insert( fpName, new FP_CACHE_ITEM( footprint, fullPath ) );
}
catch( const IO_ERROR& ioe )
{
@ -333,9 +334,7 @@ void FP_CACHE::Load()
void FP_CACHE::Remove( const wxString& aFootprintName )
{
std::string footprintName = TO_UTF8( aFootprintName );
MODULE_CITER it = m_modules.find( footprintName );
MODULE_CITER it = m_modules.find( aFootprintName );
if( it == m_modules.end() )
{
@ -349,7 +348,7 @@ void FP_CACHE::Remove( const wxString& aFootprintName )
// Remove the module from the cache and delete the module file from the library.
wxString fullPath = it->second->GetFileName().GetFullPath();
m_modules.erase( footprintName );
m_modules.erase( aFootprintName );
wxRemoveFile( fullPath );
}
@ -400,7 +399,7 @@ bool FP_CACHE::IsModified( const wxString& aLibPath, const wxString& aFootprintN
}
else
{
MODULE_CITER it = m_modules.find( TO_UTF8( aFootprintName ) );
MODULE_CITER it = m_modules.find( aFootprintName );
if( it == m_modules.end() || it->second->IsModified() )
return true;
@ -1848,7 +1847,7 @@ void PCB_IO::FootprintEnumerate( wxArrayString& aFootprintNames,
for( MODULE_CITER it = mods.begin(); it != mods.end(); ++it )
{
aFootprintNames.Add( FROM_UTF8( it->first.c_str() ) );
aFootprintNames.Add( it->first );
}
if( !errorMsg.IsEmpty() )
@ -1867,7 +1866,7 @@ MODULE* PCB_IO::FootprintLoad( const wxString& aLibraryPath, const wxString& aFo
const MODULE_MAP& mods = m_cache->GetModules();
MODULE_CITER it = mods.find( TO_UTF8( aFootprintName ) );
MODULE_CITER it = mods.find( aFootprintName );
if( it == mods.end() )
{
@ -1902,7 +1901,7 @@ void PCB_IO::FootprintSave( const wxString& aLibraryPath, const MODULE* aFootpri
THROW_IO_ERROR( msg );
}
std::string footprintName = aFootprint->GetFPID().GetLibItemName();
wxString footprintName = aFootprint->GetFPID().GetLibItemName();
MODULE_MAP& mods = m_cache->GetModules();

View File

@ -442,7 +442,7 @@ void LEGACY_PLUGIN::loadAllSections( bool doAppend )
ReplaceIllegalFileNameChars( &fpName );
if( !fpName.empty() )
fpid = LIB_ID( fpName );
fpid = LIB_ID( UTF8( fpName ) );
module->SetFPID( fpid );
@ -3315,7 +3315,7 @@ void LP_CACHE::LoadModules( LINE_READER* aReader )
ReplaceIllegalFileNameChars( &footprintName );
// set the footprint name first thing, so exceptions can use name.
module->SetFPID( LIB_ID( footprintName ) );
module->SetFPID( LIB_ID( UTF8( footprintName ) ) );
m_owner->loadMODULE( module.get() );
@ -3370,7 +3370,7 @@ void LP_CACHE::LoadModules( LINE_READER* aReader )
{
nameOK = true;
m->SetFPID( LIB_ID( newName ) );
m->SetFPID( LIB_ID( UTF8( newName ) ) );
std::pair<MODULE_ITER, bool> r = m_modules.insert( newName, m );
wxASSERT_MSG( r.second, wxT( "error doing cache insert using guaranteed unique name" ) );

View File

@ -345,7 +345,7 @@ MODULE* MWAVE::CreateMicrowaveInductor( INDUCTOR_PATTERN& inductorPattern,
MODULE* module = aPcbFrame->CreateNewModule( msg );
// here the module is already in the BOARD, CreateNewModule() does that.
module->SetFPID( LIB_ID( std::string( "mw_inductor" ) ) );
module->SetFPID( LIB_ID( wxString( "mw_inductor" ) ) );
module->SetAttributes( MOD_VIRTUAL | MOD_CMS );
module->ClearFlags();
module->SetPosition( inductorPattern.m_End );

View File

@ -1,15 +1,37 @@
#include <string>
#include <utf8.h>
#include <wx/string.h>
void callee( const wxString& aString )
{
UTF8 arg = aString;
printf( "%s: '%s'\n", __func__, arg.c_str() );
}
int main( int argc, char** argv )
{
UTF8 bozo = "ü";
UTF8 bozo = "bozo";
callee( bozo );
wxString s = bozo;
wxString b = bozo;
if( s.IsEmpty() )
{
printf( "string is empty\n" );
}
if( s != bozo.wx_str() )
{
printf( "string miscompare\n" );
}
return 0;
}