diff options
Diffstat (limited to 'indra/llcommon')
| -rwxr-xr-x | indra/llcommon/llapp.cpp | 245 | ||||
| -rwxr-xr-x | indra/llcommon/llapp.h | 50 | ||||
| -rwxr-xr-x | indra/llcommon/llerror.cpp | 11 | ||||
| -rwxr-xr-x | indra/llcommon/llerrorthread.cpp | 71 | ||||
| -rwxr-xr-x | indra/llcommon/llstring.h | 5 | ||||
| -rwxr-xr-x | indra/llcommon/stringize.h | 51 | ||||
| -rwxr-xr-x | indra/llcommon/tests/stringize_test.cpp | 20 | 
7 files changed, 222 insertions, 231 deletions
| diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 67a98d5fb8..2c5da5d2a7 100755 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -46,8 +46,8 @@  #include "llstl.h" // for DeletePointer()  #include "llstring.h"  #include "lleventtimer.h" -  #include "google_breakpad/exception_handler.h" +#include "stringize.h"  //  // Signal handling @@ -55,6 +55,8 @@  // Windows uses structured exceptions, so it's handled a bit differently.  //  #if LL_WINDOWS +#include "windows.h" +  LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop);  BOOL ConsoleCtrlHandler(DWORD fdwCtrlType);  bool windows_post_minidump_callback(const wchar_t* dump_path, @@ -71,7 +73,9 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *);  #if LL_LINUX  #include "google_breakpad/minidump_descriptor.h" -bool unix_minidump_callback(const google_breakpad::MinidumpDescriptor& minidump_desc, void* context, bool succeeded); +static bool unix_minidump_callback(const google_breakpad::MinidumpDescriptor& minidump_desc,  +                                   void* context,  +                                   bool succeeded);  #else  // Called by breakpad exception handler after the minidump has been generated.  bool unix_post_minidump_callback(const char *dump_dir, @@ -109,11 +113,6 @@ BOOL LLApp::sLogInSignal = FALSE;  LLApp::EAppStatus LLApp::sStatus = LLApp::APP_STATUS_STOPPED; // Keeps track of application status  LLAppErrorHandler LLApp::sErrorHandler = NULL;  BOOL LLApp::sErrorThreadRunning = FALSE; -#if !LL_WINDOWS -LLApp::child_map LLApp::sChildMap; -LLAtomicU32* LLApp::sSigChildCount = NULL; -LLAppChildCallback LLApp::sDefaultChildCallback = NULL; -#endif  LLApp::LLApp() : mThreadErrorp(NULL) @@ -128,11 +127,6 @@ void LLApp::commonCtor()  	LLCommon::initClass(); -#if !LL_WINDOWS -	// This must be initialized before the error handler. -	sSigChildCount = new LLAtomicU32(0); -#endif -  	// initialize the options structure. We need to make this an array  	// because the structured data will not auto-allocate if we  	// reference an invalid location with the [] operator. @@ -149,12 +143,13 @@ void LLApp::commonCtor()  	// Set the application to this instance.  	sApplication = this; -	 +  	mExceptionHandler = 0;  	// initialize the buffer to write the minidump filename to  	// (this is used to avoid allocating memory in the crash handler) -	memset(minidump_path, 0, MAX_MINDUMP_PATH_LENGTH); +	memset(mMinidumpPath, 0, MAX_MINDUMP_PATH_LENGTH); +	mCrashReportPipeStr = L"\\\\.\\pipe\\LLCrashReporterPipe";  }  LLApp::LLApp(LLErrorThread *error_thread) : @@ -166,10 +161,6 @@ LLApp::LLApp(LLErrorThread *error_thread) :  LLApp::~LLApp()  { -#if !LL_WINDOWS -	delete sSigChildCount; -	sSigChildCount = NULL; -#endif  	// reclaim live file memory  	std::for_each(mLiveFiles.begin(), mLiveFiles.end(), DeletePointer()); @@ -244,6 +235,20 @@ bool LLApp::parseCommandOptions(int argc, char** argv)  		}  		++ii;  		value.assign(argv[ii]); + +#if LL_WINDOWS +		//Windows changed command line parsing.  Deal with it. +		S32 slen = value.length() - 1; +		S32 start = 0; +		S32 end = slen; +		if (argv[ii][start]=='"')start++; +		if (argv[ii][end]=='"')end--; +		if (start!=0 || end!=slen)  +		{ +			value = value.substr (start,end); +		} +#endif +  		commands[name] = value;  	}  	setOptionData(PRIORITY_COMMAND_LINE, commands); @@ -288,6 +293,32 @@ void LLApp::stepFrame()  	mRunner.run();  } +#if LL_WINDOWS +//The following code is needed for 32-bit apps on 64-bit windows to keep it from eating +//crashes.   It is a lovely undocumented 'feature' in SP1 of Windows 7. An excellent +//in-depth article on the issue may be found here:  http://randomascii.wordpress.com/2012/07/05/when-even-crashing-doesn-work/ +void EnableCrashingOnCrashes() +{ +	typedef BOOL (WINAPI *tGetPolicy)(LPDWORD lpFlags); +	typedef BOOL (WINAPI *tSetPolicy)(DWORD dwFlags); +	const DWORD EXCEPTION_SWALLOWING = 0x1; + +	HMODULE kernel32 = LoadLibraryA("kernel32.dll"); +	tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32, +		"GetProcessUserModeExceptionPolicy"); +	tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32, +		"SetProcessUserModeExceptionPolicy"); +	if (pGetPolicy && pSetPolicy) +	{ +		DWORD dwFlags; +		if (pGetPolicy(&dwFlags)) +		{ +			// Turn off the filter +			pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING); +		} +	} +} +#endif  void LLApp::setupErrorHandling()  { @@ -295,7 +326,10 @@ void LLApp::setupErrorHandling()  	// occasionally checks to see if the app is in an error state, and sees if it needs to be run.  #if LL_WINDOWS +  #if LL_SEND_CRASH_REPORTS +	EnableCrashingOnCrashes(); +  	// This sets a callback to handle w32 signals to the console window.  	// The viewer shouldn't be affected, sicne its a windowed app.  	SetConsoleCtrlHandler( (PHANDLER_ROUTINE) ConsoleCtrlHandler, TRUE); @@ -304,8 +338,48 @@ void LLApp::setupErrorHandling()  	if(mExceptionHandler == 0)  	{  		llwarns << "adding breakpad exception handler" << llendl; -		mExceptionHandler = new google_breakpad::ExceptionHandler( -			L"C:\\Temp\\", 0, windows_post_minidump_callback, 0, google_breakpad::ExceptionHandler::HANDLER_ALL); + +		std::wstring wpipe_name; +		wpipe_name =  mCrashReportPipeStr + wstringize(getPid()); + +		::Sleep(3000);  //HACK hopefully a static wait won't blow up in my face before google fixes their implementation. +		const std::wstring wdump_path(wstringize(mDumpPath)); + +		//HACK this for loop is ueless.  Breakpad dumbly returns success when the OOP handler isn't initialized. +		for (int retries=0;retries<5;++retries) +		{ +			mExceptionHandler = new google_breakpad::ExceptionHandler( +														wdump_path,		 +														NULL,		//No filter +														windows_post_minidump_callback, +														0, +														google_breakpad::ExceptionHandler::HANDLER_ALL, +														MiniDumpNormal, //Generate a 'normal' minidump. +														wpipe_name.c_str(), +														NULL);  //No custom client info. +			if (mExceptionHandler) +			{ +				break; +			} +			else +			{ +				::Sleep(100);  //Wait a tick and try again. +			} +		} +		if (!mExceptionHandler) +		{ +				llwarns << "Failed to initialize OOP exception handler.  Defaulting to In Process handling" << llendl; +				mExceptionHandler = new google_breakpad::ExceptionHandler( +																  wstringize(mDumpPath),		 +																  0,		//dump filename	 +																  windows_post_minidump_callback,  +																  0,  +																  google_breakpad::ExceptionHandler::HANDLER_ALL); +		} +		if (mExceptionHandler) +		{ +			mExceptionHandler->set_handle_debug_exceptions(true); +		}  	}  #endif  #else @@ -355,14 +429,17 @@ void LLApp::setupErrorHandling()  	if(installHandler && (mExceptionHandler == 0))  	{ -		std::string dumpPath = "/tmp/"; -		mExceptionHandler = new google_breakpad::ExceptionHandler(dumpPath, 0, &unix_post_minidump_callback, 0, true, 0); +		mExceptionHandler = new google_breakpad::ExceptionHandler(mDumpPath, 0, &unix_post_minidump_callback, 0, true, 0);  	}  #elif LL_LINUX  	if(installHandler && (mExceptionHandler == 0))  	{ -		google_breakpad::MinidumpDescriptor desc("/tmp"); -	        new google_breakpad::ExceptionHandler(desc, 0, &unix_minidump_callback, 0, true, 0); +		if (mDumpPath.empty()) +		{ +			mDumpPath = "/tmp"; +		} +		google_breakpad::MinidumpDescriptor desc(mDumpPath); +	    mExceptionHandler = new google_breakpad::ExceptionHandler(desc, NULL, unix_minidump_callback, NULL, true, -1);  	}  #endif @@ -418,19 +495,35 @@ void LLApp::setError()  void LLApp::setMiniDumpDir(const std::string &path)  { +	if (path.empty()) +	{ +		mDumpPath = "/tmp"; +	} +	else +	{ +		mDumpPath = path; +	} +  	if(mExceptionHandler == 0) return;  #ifdef LL_WINDOWS  	wchar_t buffer[MAX_MINDUMP_PATH_LENGTH]; -	mbstowcs(buffer, path.c_str(), MAX_MINDUMP_PATH_LENGTH); +	mbstowcs(buffer, mDumpPath.c_str(), MAX_MINDUMP_PATH_LENGTH);  	mExceptionHandler->set_dump_path(std::wstring(buffer));  #elif LL_LINUX -        google_breakpad::MinidumpDescriptor desc(path); +        //google_breakpad::MinidumpDescriptor desc("/tmp");	//path works in debug fails in production inside breakpad lib so linux gets a little less stack reporting until it is patched. +        google_breakpad::MinidumpDescriptor desc(mDumpPath);	//path works in debug fails in production inside breakpad lib so linux gets a little less stack reporting until it is patched.  	mExceptionHandler->set_minidump_descriptor(desc);  #else -	mExceptionHandler->set_dump_path(path); +	mExceptionHandler->set_dump_path(mDumpPath);  #endif  } +void LLApp::setDebugFileNames(const std::string &path) +{ +  	mStaticDebugFileName = path + "static_debug_info.log"; +  	mDynamicDebugFileName = path + "dynamic_debug_info.log"; +} +  void LLApp::writeMiniDump()  {  	if(mExceptionHandler == 0) return; @@ -507,34 +600,11 @@ bool LLApp::isCrashloggerDisabled()  	return (sDisableCrashlogger == TRUE);   } -#if !LL_WINDOWS -// static -U32 LLApp::getSigChildCount() -{ -	if (sSigChildCount) -	{ -		return U32(*sSigChildCount); -	} -	return 0; -} - -// static -void LLApp::incSigChildCount() -{ -	if (sSigChildCount) -	{ -		(*sSigChildCount)++; -	} -} - -#endif - -  // static  int LLApp::getPid()  {  #if LL_WINDOWS -	return 0; +    return GetCurrentProcessId();  #else  	return getpid();  #endif @@ -610,43 +680,6 @@ BOOL ConsoleCtrlHandler(DWORD fdwCtrlType)  }   #else //!LL_WINDOWS -void LLApp::setChildCallback(pid_t pid, LLAppChildCallback callback) -{ -	LLChildInfo child_info; -	child_info.mCallback = callback; -	LLApp::sChildMap[pid] = child_info; -} - -void LLApp::setDefaultChildCallback(LLAppChildCallback callback) -{ -	LLApp::sDefaultChildCallback = callback; -} - -pid_t LLApp::fork() -{ -	fflush(NULL); // flush all buffers before the child inherits them -	pid_t pid = ::fork(); -	if( pid < 0 ) -	{ -		int system_error = errno; -		llwarns << "Unable to fork! Operating system error code: " -				<< system_error << llendl; -	} -	else if (pid == 0) -	{ -		// Sleep a bit to allow the parent to set up child callbacks. -		ms_sleep(10); - -		// We need to disable signal handling, because we don't have a -		// signal handling thread anymore. -		setupErrorHandling(); -	} -	else -	{ -		llinfos << "Forked child process " << pid << llendl; -	} -	return pid; -}  void setup_signals()  { @@ -747,19 +780,6 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)  			llinfos << "Signal handler - Got SIGCHLD from " << info->si_pid << llendl;  		} -		// Check result code for all child procs for which we've -		// registered callbacks THIS WILL NOT WORK IF SIGCHLD IS SENT -		// w/o killing the child (Go, launcher!) -		// TODO: Now that we're using SIGACTION, we can actually -		// implement the launcher behavior to determine who sent the -		// SIGCHLD even if it doesn't result in child termination -		if (LLApp::sChildMap.count(info->si_pid)) -		{ -			LLApp::sChildMap[info->si_pid].mGotSigChild = TRUE; -		} -		 -		LLApp::incSigChildCount(); -  		return;  	case SIGABRT:  		// Abort just results in termination of the app, no funky error handling. @@ -880,21 +900,26 @@ bool unix_minidump_callback(const google_breakpad::MinidumpDescriptor& minidump_  	// heap allocations in a crash handler.  	// path format: <dump_dir>/<minidump_id>.dmp -	int dirPathLength = strlen(minidump_desc.path()); +	 +	//HACK:  *path points to the buffer in getMiniDumpFilename which has already allocated space +	//to avoid doing allocation during crash. +	char * path = LLApp::instance()->getMiniDumpFilename(); +	int dir_path_len = strlen(path);  	// The path must not be truncated. -	llassert((dirPathLength + 5) <= LLApp::MAX_MINDUMP_PATH_LENGTH); +	S32 remaining =  LLApp::MAX_MINDUMP_PATH_LENGTH - dir_path_len; + +	llassert( (remaining - strlen(minidump_desc.path())) > 5); -	char * path = LLApp::instance()->getMiniDumpFilename(); -	S32 remaining = LLApp::MAX_MINDUMP_PATH_LENGTH; -	strncpy(path, minidump_desc.path(), remaining); -	remaining -= dirPathLength; -	path += dirPathLength; -	if (remaining > 0 && dirPathLength > 0 && path[-1] != '/') +	path += dir_path_len; + +	if (dir_path_len > 0 && path[-1] != '/')  	{  		*path++ = '/';  		--remaining;  	} + +	strncpy(path, minidump_desc.path(), remaining);  	llinfos << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << llendl;  	LLApp::runErrorHandler(); @@ -942,7 +967,7 @@ bool unix_post_minidump_callback(const char *dump_dir,  		strncpy(path, ".dmp", remaining);  	} -	llinfos << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << llendl; +	llinfos << "generated minidump: " << path << llendl;  	LLApp::runErrorHandler();  #ifndef LL_RELEASE_FOR_DOWNLOAD diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index afa06df23e..828965b1fa 100755 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -31,7 +31,6 @@  #include "llrun.h"  #include "llsd.h"  #include "lloptioninterface.h" -  // Forward declarations  template <typename Type> class LLAtomic32;  typedef LLAtomic32<U32> LLAtomicU32; @@ -42,7 +41,6 @@ class LLLiveFile;  #endif  typedef void (*LLAppErrorHandler)(); -typedef void (*LLAppChildCallback)(int pid, bool exited, int status);  #if !LL_WINDOWS  extern S32 LL_SMACKDOWN_SIGNAL; @@ -51,13 +49,6 @@ extern S32 LL_HEARTBEAT_SIGNAL;  // Clear all of the signal handlers (which we want to do for the child process when we fork  void clear_signals(); -class LLChildInfo -{ -public: -	LLChildInfo() : mGotSigChild(FALSE), mCallback(NULL) {} -	BOOL mGotSigChild; -	LLAppChildCallback mCallback; -};  #endif  namespace google_breakpad { @@ -206,10 +197,6 @@ public:  	static bool isQuitting();  	static bool isError();  	static bool isExiting(); // Either quitting or error (app is exiting, cleanly or not) -#if !LL_WINDOWS -	static U32  getSigChildCount(); -	static void incSigChildCount(); -#endif  	static int getPid();  	/** @name Error handling methods */ @@ -238,32 +225,16 @@ public:  	// change the directory where Breakpad minidump files are written to  	void setMiniDumpDir(const std::string &path); +    void setDebugFileNames(const std::string &path);  	// Return the Google Breakpad minidump filename after a crash. -	char *getMiniDumpFilename() { return minidump_path; } +	char *getMiniDumpFilename() { return mMinidumpPath; } +    std::string* getStaticDebugFile() { return &mStaticDebugFileName; } +    std::string* getDynamicDebugFile() { return &mDynamicDebugFileName; }  	// Write out a Google Breakpad minidump file.  	void writeMiniDump(); -#if !LL_WINDOWS -	// -	// Child process handling (Unix only for now) -	// -	// Set a callback to be run on exit of a child process -	// WARNING!  This callback is run from the signal handler due to -	// Linux threading requiring waitpid() to be called from the thread that spawned the process. -	// At some point I will make this more behaved, but I'm not going to fix this right now - djs -	void setChildCallback(pid_t pid, LLAppChildCallback callback); - -    // The child callback to run if no specific handler is set -	void setDefaultChildCallback(LLAppChildCallback callback);  -	 -    // Fork and do the proper signal handling/error handling mojo -	// *NOTE: You need to make sure your signal handling callback is -	// correct after you fork, because not all threads are duplicated -	// when you fork! -	pid_t fork();  -#endif  	/**  	  * @brief Get a reference to the application runner @@ -286,13 +257,9 @@ protected:  	static EAppStatus sStatus; // Reflects current application status  	static BOOL sErrorThreadRunning; // Set while the error thread is running  	static BOOL sDisableCrashlogger; // Let the OS handle crashes for us. +	std::wstring mCrashReportPipeStr;  //Name of pipe to use for crash reporting. -#if !LL_WINDOWS -	static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received. -	typedef std::map<pid_t, LLChildInfo> child_map; // Map key is a PID -	static child_map sChildMap; -	static LLAppChildCallback sDefaultChildCallback; -#endif +    std::string mDumpPath;  //output path for google breakpad.  Dependency workaround.  	/**  	  * @brief This method is called once a frame to do once a frame tasks. @@ -303,7 +270,10 @@ private:  	void startErrorThread();  	// Contains the filename of the minidump file after a crash. -	char minidump_path[MAX_MINDUMP_PATH_LENGTH]; +	char mMinidumpPath[MAX_MINDUMP_PATH_LENGTH]; +     +    std::string mStaticDebugFileName; +    std::string mDynamicDebugFileName;  	// *NOTE: On Windows, we need a routine to reset the structured  	// exception handler when some evil driver has taken it over for diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index d2af004cde..cc42bef0c9 100755 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -1197,14 +1197,15 @@ namespace LLError  #endif  	void crashAndLoop(const std::string& message)  	{ -		// Now, we go kaboom! -		int* make_me_crash = NULL; - -		*make_me_crash = 0; -  		while(true)  		{  			// Loop forever, in case the crash didn't work? +             +            // Now, we go kaboom! +            int* make_me_crash = NULL; +             +            *make_me_crash = 0; +  		}  		// this is an attempt to let Coverity and other semantic scanners know that this function won't be returning ever. diff --git a/indra/llcommon/llerrorthread.cpp b/indra/llcommon/llerrorthread.cpp index 950fcd6e83..ebfbe19ff7 100755 --- a/indra/llcommon/llerrorthread.cpp +++ b/indra/llcommon/llerrorthread.cpp @@ -109,79 +109,8 @@ void LLErrorThread::run()  	llinfos << "thread_error - Waiting for an error" << llendl;  	S32 counter = 0; -#if !LL_WINDOWS -	U32 last_sig_child_count = 0; -#endif  	while (! (LLApp::isError() || LLApp::isStopped()))  	{ -#if !LL_WINDOWS -		// Check whether or not the main thread had a sig child we haven't handled. -		U32 current_sig_child_count = LLApp::getSigChildCount(); -		if (last_sig_child_count != current_sig_child_count) -		{ -			int status = 0; -			pid_t child_pid = 0; -			last_sig_child_count = current_sig_child_count; -			if (LLApp::sLogInSignal) -			{ -				llinfos << "thread_error handling SIGCHLD #" << current_sig_child_count << llendl; -			} -			for (LLApp::child_map::iterator iter = LLApp::sChildMap.begin(); iter != LLApp::sChildMap.end();) -			{ -				child_pid = iter->first; -				LLChildInfo &child_info = iter->second; -				// check the status of *all* children, in case we missed a signal -				if (0 != waitpid(child_pid, &status, WNOHANG)) -				{ -					bool exited = false; -					int exit_status = -1; -					get_child_status(status, exit_status, exited, LLApp::sLogInSignal); - -					if (child_info.mCallback) -					{ -						if (LLApp::sLogInSignal) -						{ -							llinfos << "Signal handler - Running child callback" << llendl; -						} -						child_info.mCallback(child_pid, exited, status); -					} -					LLApp::sChildMap.erase(iter++); -				} -				else -				{ -					// Child didn't terminate, yet we got a sigchild somewhere... -					if (child_info.mGotSigChild && child_info.mCallback) -					{ -						child_info.mCallback(child_pid, false, 0); -					} -					child_info.mGotSigChild = FALSE; -					iter++; -				} -			} - -			// check the status of *all* children, in case we missed a signal -			// Same as above, but use the default child callback -			while(0 < (child_pid = waitpid( -1, &status, WNOHANG ))) -			{ -				if (0 != waitpid(child_pid, &status, WNOHANG)) -				{ -					bool exited = false; -					int exit_status = -1; -					get_child_status(status, exit_status, exited, LLApp::sLogInSignal); -					if (LLApp::sDefaultChildCallback) -					{ -						if (LLApp::sLogInSignal) -						{ -							llinfos << "Signal handler - Running default child callback" << llendl; -						} -						LLApp::sDefaultChildCallback(child_pid, true, status); -					} -				} -			} -		} - - -#endif  		ms_sleep(10);  		counter++;  	} diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index f9702868c8..16a19e7021 100755 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -518,10 +518,13 @@ LL_COMMON_API S32 wchar_to_utf8chars(llwchar inchar, char* outchars);  LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str, S32 len);  LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str); -  LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str, S32 len);  LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str); +#if LL_WINDOWS +inline std::string wstring_to_utf8str(const llutf16string &utf16str) { return utf16str_to_utf8str(utf16str);} +#endif +  // Length of this UTF32 string in bytes when transformed to UTF8  LL_COMMON_API S32 wstring_utf8_length(const LLWString& wstr);  diff --git a/indra/llcommon/stringize.h b/indra/llcommon/stringize.h index 72f2e58ce1..acae74b584 100755 --- a/indra/llcommon/stringize.h +++ b/indra/llcommon/stringize.h @@ -31,21 +31,64 @@  #include <sstream>  #include <boost/lambda/lambda.hpp> +#include <llstring.h>  /** - * stringize(item) encapsulates an idiom we use constantly, using + * gstringize(item) encapsulates an idiom we use constantly, using   * operator<<(std::ostringstream&, TYPE) followed by std::ostringstream::str() + * or their wstring equivalents   * to render a string expressing some item.   */ -template <typename T> -std::string stringize(const T& item) +template <typename CHARTYPE, typename T> +std::basic_string<CHARTYPE> gstringize(const T& item)  { -    std::ostringstream out; +    std::basic_ostringstream<CHARTYPE> out;      out << item;      return out.str();  }  /** + *partial specialization of stringize for handling wstring + *TODO: we should have similar specializations for wchar_t[] but not until it is needed. + */ +inline std::string stringize(const std::wstring& item) +{ +    llwarns << "WARNING:  Possible narrowing" << llendl; +     +    std::string s; +     +    s = wstring_to_utf8str(item); +    return gstringize<char>(s); +} + +/** + * Specialization of gstringize for std::string return types + */ +template <typename T> +std::string stringize(const T& item) +{ +    return gstringize<char>(item); +} + +/** + * Specialization for generating wstring from string. + * Both a convenience function and saves a miniscule amount of overhead. + */ +inline std::wstring wstringize(const std::string& item) +{ +    return gstringize<wchar_t>(item.c_str()); +} + +/** + * Specialization of gstringize for std::wstring return types + */ +template <typename T> +std::wstring wstringize(const T& item) +{ +    return gstringize<wchar_t>(item); +} + +/**   * stringize_f(functor)   */  template <typename Functor> diff --git a/indra/llcommon/tests/stringize_test.cpp b/indra/llcommon/tests/stringize_test.cpp index 3d34f23998..3e4ca548e5 100755 --- a/indra/llcommon/tests/stringize_test.cpp +++ b/indra/llcommon/tests/stringize_test.cpp @@ -67,6 +67,8 @@ namespace tut              llsd["i"]   = i;              llsd["d"]   = d;              llsd["abc"] = abc; +            def = L"def ghi"; +          }          char        c; @@ -76,6 +78,7 @@ namespace tut          float       f;          double      d;          std::string abc; +		std::wstring def;          LLSD        llsd;      };      typedef test_group<stringize_data> stringize_group; @@ -92,6 +95,7 @@ namespace tut          ensure_equals(stringize(f),    "3.14159");          ensure_equals(stringize(d),    "3.14159");          ensure_equals(stringize(abc),  "abc def"); +        ensure_equals(stringize(def),  "def ghi"); //Will generate llwarns due to narrowing.          ensure_equals(stringize(llsd), "{'abc':'abc def','d':r3.14159,'i':i34}");      } @@ -101,4 +105,20 @@ namespace tut          ensure_equals(STRINGIZE("c is " << c), "c is c");          ensure_equals(STRINGIZE(std::setprecision(4) << d), "3.142");      } + +    template<> template<> +    void stringize_object::test<3>() +    { +        //Tests rely on validity of wstring_to_utf8str() +        ensure_equals(wstring_to_utf8str(wstringize(c)),    wstring_to_utf8str(L"c")); +        ensure_equals(wstring_to_utf8str(wstringize(s)),    wstring_to_utf8str(L"17")); +        ensure_equals(wstring_to_utf8str(wstringize(i)),    wstring_to_utf8str(L"34")); +        ensure_equals(wstring_to_utf8str(wstringize(l)),    wstring_to_utf8str(L"68")); +        ensure_equals(wstring_to_utf8str(wstringize(f)),    wstring_to_utf8str(L"3.14159")); +        ensure_equals(wstring_to_utf8str(wstringize(d)),    wstring_to_utf8str(L"3.14159")); +        ensure_equals(wstring_to_utf8str(wstringize(abc)),  wstring_to_utf8str(L"abc def")); +        ensure_equals(wstring_to_utf8str(wstringize(abc)),  wstring_to_utf8str(wstringize(abc.c_str()))); +        ensure_equals(wstring_to_utf8str(wstringize(def)),  wstring_to_utf8str(L"def ghi")); + //       ensure_equals(wstring_to_utf8str(wstringize(llsd)), wstring_to_utf8str(L"{'abc':'abc def','d':r3.14159,'i':i34}")); +    }  } // namespace tut | 
