wxLog implementation and unicode Topic is solved

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
User avatar
doublemax
Moderator
Moderator
Posts: 19115
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Post by doublemax »

but I noted that the first function came by a format conversion function for char* and the second the function for wchar_t.
This confirms that something in your library build is not as it should be. But i don't really now what's responsible, i'd guess some setting in setup.h. Do you have the chance to test with a totally clean 2.9.1?

Or you can ignore that problem for now and just don't use wxT().

I think that both the crashes and garbage output you got were caused by mixing wide char strings and 8bit char strings. You must make sure that doesn't happen.

I have to admit that i'm a little confused myself now, when reading the docs for wxString::c_str():

Code: Select all

c_str () const
Returns a lightweight intermediate class which is in turn implicitly convertible to both const char* and to const wchar_t*. 
Use the source, Luke!
tigerbeard
Earned some good credits
Earned some good credits
Posts: 123
Joined: Sat Oct 07, 2006 1:56 pm

Post by tigerbeard »

doublemax wrote:This confirms that something in your library build is not as it should be. But i don't really now what's responsible, i'd guess some setting in setup.h. Do you have the chance to test with a totally clean 2.9.1?
Well I did make a new clean compilation of the lib, though I did not touch any options as they all were fine for me. I am using the MSVC6 though. It already gave me some other side effects with 2.9.1 due to specific VC6 legacy code in the sources. But maybe you're right suspecting is not knowing and a recompilation does not take that long.
I think that both the crashes and garbage output you got were caused by mixing wide char strings and 8bit char strings. You must make sure that doesn't happen.
I think you got a point. I just found a solution for the garbage problem by working with wx_str().

Code: Select all

wxLogDebugTs("Test is %s", strTest.c_str() ); // produces rubbish
wxLogDebugTs("Test is %s", strTest.wc_str() ); // correct output!! 
I did try type casts before, but they did not work. This shows your statement is giving the right hint, but still I do not fully get it. When the format string without wxT() is narrow char, then wx_str() is providing wide char as arguments - so I am stunned that this works. Using mb_str() for a narrow char argument does produce garbage again. Whats my mistake that I would have expected the opposite to work?

I have to admit that i'm a little confused myself now, when reading the docs for wxString::c_str():

Code: Select all

c_str () const
Returns a lightweight intermediate class which is in turn implicitly convertible to both const char* and to const wchar_t*. 
I guess that fits nicely to your first statement in the topic, where the compiler resolves this by itself. It should be easy for a clear prototype. Confusing for vararg case though.
System config: Xubuntu22.04, wxWidgets 3.0.5,gcc, C::B
Auria
Site Admin
Site Admin
Posts: 6695
Joined: Thu Sep 28, 2006 12:23 am
Contact:

Post by Auria »

tigerbeard wrote: I did try type casts before, but they did not work.
This is to be expected, since casting a char* into wchar_t* or vice-versa does not do a character conversion (even less an encoding conversion) but simply re-interprets the bytes at that pointer, usually leading to garbage.
"Keyboard not detected. Press F1 to continue"
-- Windows
User avatar
doublemax
Moderator
Moderator
Posts: 19115
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Post by doublemax »

I did a little bit of research... ;)

While wx allows to use 8bit chars in user code now, internally it still uses the native platform dependent string representation, e.g. UTF8 on Linux, UCS-2 on MSW.

So when you trace into the wxLog functions, the one that is actually used in your case is the one taking wxChar * as argument (so it was wrong what i said before about changing to char *). wx uses a wxArgNormalizer class for that, which is not publicly documented, the comments in /wx/include/wx/strvararg.h decribe the underlying mechanics.

For your particular case, when messing with the vararg list, on MSW, the correct combination is to use wide chars for the format string and the string argument, like:

Code: Select all

wxLogDebugTs( L"Test is %s", strTest.wc_str() );
But this really feels like a hack and is platform dependent. I would recommend to forget about chaining into the vararg list and use the "proper" wx way: Create your own log target by deriving from wxLog.
Use the source, Luke!
tigerbeard
Earned some good credits
Earned some good credits
Posts: 123
Joined: Sat Oct 07, 2006 1:56 pm

Post by tigerbeard »

doublemax wrote: So when you trace into the wxLog functions, the one that is actually used in your case is the one taking wxChar * as argument (so it was wrong what i said before about changing to char *).
Even so it was a good advise for the MyLog class function, because it allows me to call it without the L prefix. I did use wxChar* in my initial version with quite unsatisfactory results. Thanks for the hint with the wxArgNormaizer class which I did not see. Maybe I am doing things not right, but I find it quite a nightmare to debug though code that makes such heavy use of macros. Maybe the code looks quite clear, but the dynamics of it...

I would recommend to forget about chaining into the vararg list and use the "proper" wx way: Create your own log target by deriving from wxLog.
Makes sense. In that particular case it would bring just too much change in the application, however.

So here is what I have now and what is working for me:

Header

Code: Select all

class DLL_IMP_EXP CMyLog						
{
public:
	CMyLog();
	virtual ~CMyLog();
	void		wxLogDebugTs(const char* pszFormat, ...); // variable args as printf()
	
private:
	void		SendLogDebug(wxString strLogText);		// sends a ID_LOG_MSG message
	wxWindow*	m_pMsgTarget;							
};
Implementation

Code: Select all

                                                              
void CMyLog::wxLogDebugTs(const char* szFormat, ...)                    
{                                                                 
   wxString  strOutput;
   va_list   argptr;                                                 

    va_start(argptr, szFormat);                                     
    strOutput = wxString::FormatV(szFormat, argptr);
    SendLogDebug(strOutput);				           
    va_end(argptr);                                                 
}
Only drawback beside the plattform dependency is the need to call it with wc_str() like below. That means I must change all string arguments when I migrate code.

Code: Select all

wxLogDebugTs( "Test is %s", strTest.wc_str() );

Although its working now and my problem is solved, I am still left with the (more academic) question that I could not fully mimic the build-in wxLogDebug behaviour which would be either plattform independent and get rid of the wx_str() postfix.
So I'd still be interested in case anyone could post a modified version of my above function which exactly accepts the wxLogDebug's arguments.

Thanks
Tiger
System config: Xubuntu22.04, wxWidgets 3.0.5,gcc, C::B
User avatar
doublemax
Moderator
Moderator
Posts: 19115
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Post by doublemax »

Although its working now and my problem is solved, I am still left with the (more academic) question that I could not fully mimic the build-in wxLogDebug behaviour which would be either plattform independent and get rid of the wx_str() postfix.
So I'd still be interested in case anyone could post a modified version of my above function which exactly accepts the wxLogDebug's arguments.
This should be possible by tearing the wxLog macros apart and doing the same in your code. But frankly, i was too lazy to do that right now ;)
Use the source, Luke!
Post Reply