Jump to content

  • Log In with Google      Sign In   
  • Create Account

FREE SOFTWARE GIVEAWAY

We have 4 x Pro Licences (valued at $59 each) for 2d modular animation software Spriter to give away in this Thursday's GDNet Direct email newsletter.


Read more in this forum topic or make sure you're signed up (from the right-hand sidebar on the homepage) and read Thursday's newsletter to get in the running!


C++ Multi-Byte - prepare string of extensions for OpenFileDialog


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
12 replies to this topic

#1 mynameisnafe   Members   -  Reputation: 252

Like
0Likes
Like

Posted 01 July 2014 - 01:04 PM

HI all,

 

I'm having some trouble figuring out how to do this one.

Assimp gives me a string, containing all the file extensions it can handle.

I found some code to tokenize strings on SO and wrapped it in a function.

The format of the string that is used to initialise the accepted extensions list for the OPENFILENAME structure is as follows:
 

L"All\0*.*\0Text\*.txt\0"

And so I'm trying to recreate that programatically. Which is a nightmare, as I'm trying to stick '\0' in the middle of a string.

// Get a vector of extensions supported by Assimp ( assimp exts string, tokenise by ';' )
std::vector<std::string> exts = Scatterbrain::Split(extsAccepted.c_str(), ';');
	
//create a string with a bunch of '\0' s in it, if possible
std::string extsList="";
for(size_t i = 0; i < exts.size(); i++)
	extsList.append(exts[i] + '\0');
	
// contains the first extension.
CString pwszExts(extsList.c_str());

// Initialize OPENFILENAME
ZeroMemory(&ofn, sizeof(ofn));

ofn.lpstrFilter = (LPCWSTR)pwszExts; // This one
...

// Display the Open dialog box. 
if( GetOpenFileName(&ofn) == TRUE ) 
{
...

Okay - I see one bug already: the dialog is going to say 'extension_a extension_b', instead of 'ext_name_a ext_a', but I'm happy to add blank spaces for names.

how is the generation of a string 

L"Hello\0World\0How\0Are\0You\0'

 done?

Thanks in advance

 



Sponsor:

#2 Paradigm Shifter   Crossbones+   -  Reputation: 5439

Like
0Likes
Like

Posted 01 July 2014 - 01:11 PM

Using a vector<char> will work for that. Or wchar_t in Unicode.
"Most people think, great God will come from the sky, take away everything, and make everybody feel high" - Bob Marley

#3 fastcall22   Crossbones+   -  Reputation: 4474

Like
1Likes
Like

Posted 01 July 2014 - 01:24 PM

how is the generation of a string

L"Hello\0World\0How\0Are\0You\0'

done?

Well, in your case, it would just be replacing ';' with '\0'.

So, maybe something like this:
std::wstring ext;
ext.reserve(str.length()+1);

std::transform(str.begin(),str.end(),back_inserter(ext),[](char t) -> wchar_t {
	return t == ';' ? 0 : t;
});
ext.push_back(0); // (This is necessary to indicate where the array of strings end)

wchar_t* buf = ext.c_str(); // An extra null terminator should be appended here.

// str = "Hello;World;How;Are;You"
// buf = L"Hello\0World\0How\0Are\0You\0\0"
EDIT:
Code sample here: http://ideone.com/wqrckP

Edited by fastcall22, 01 July 2014 - 03:00 PM.

c3RhdGljIGNoYXIgeW91cl9tb21bMVVMTCA8PCA2NF07CnNwcmludGYoeW91cl9tb20sICJpcyBmYXQiKTs=

#4 mynameisnafe   Members   -  Reputation: 252

Like
0Likes
Like

Posted 01 July 2014 - 02:44 PM

What is this lambda magic?
 

[](char t)->wchar_t

Is that it? It's not the replacing that I'm struggling with, it's the char ->wchar_t.. and there is the char->wchar_t

I'm going to try basically an empty lambda, meanwhile, whats with the extra push_back(0)'s? are those important (i.e. the real null terminator of the returned exts wstring?

 



#5 fastcall22   Crossbones+   -  Reputation: 4474

Like
0Likes
Like

Posted 01 July 2014 - 02:58 PM

According to the documentation for OPENFILENAME, lpstrFilter must be terminated by two NULL characters, as I had suspected. However, according to std::string::c_str, there should already be a null terminator appended to its contents. Therefore, the second push_back(0) is not needed. I will modify my previous post.

As for the lambda, [](char t)->wchar_t{}, it reads as: A lambda that takes one parameter, captures no variables in its parent scope(s), and returns a wchar_t. The equivalent C++03 code would look something like:
struct ReplaceAndWiden {
	char to_find;
	wchar_t to_replace;
	
	ReplaceAndWiden(char f, wchar_t r) : to_find(f), to_replace(r) {
	}

	wchar_t operator() (char t) const {
		return t == to_find ? to_replace : (wchar_t)t;
	}
};

std::transform( str.begin(), str.end(), back_inserter(ext), ReplaceAndWiden(';',0) );

c3RhdGljIGNoYXIgeW91cl9tb21bMVVMTCA8PCA2NF07CnNwcmludGYoeW91cl9tb20sICJpcyBmYXQiKTs=

#6 mynameisnafe   Members   -  Reputation: 252

Like
0Likes
Like

Posted 01 July 2014 - 03:01 PM

I bailed on my split fn, all that CString jazz.. now I have this beautiful code:


std::string App::DoOpenFileDialog(std::string extsAccepted)
{
	std::string fName;
	OPENFILENAME ofn;       // common dialog box structure
	char szFile[260];       // buffer for file name

	std::wstring wextsList;
	std::transform(extsAccepted.begin(), extsAccepted.end(), back_inserter(wextsList), [](char t)->wchar_t {
		return t==';' ? '\0' : t;
	});

	// Initialize OPENFILENAME
	ZeroMemory(&ofn, sizeof(ofn));
	ofn.lStructSize = sizeof(ofn);
	ofn.hwndOwner = m_hWnd;
	ofn.lpstrFile = (LPWSTR)szFile;
	ofn.lpstrFile[0] = '\0';
	ofn.nMaxFile = sizeof(szFile);
	ofn.lpstrFilter = (LPCWSTR)wextsList.c_str();
	ofn.nFilterIndex = 1;
	ofn.lpstrFileTitle = NULL;
	ofn.nMaxFileTitle = 0;
	ofn.lpstrInitialDir = NULL;
	ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;

	// Display the Open dialog box. 

	if( GetOpenFileName(&ofn) == TRUE ) 
	{	

..Problem is, it's missing every other file format - this is something I saw before while straying through the realms of doing it a char at a time in a for loop, and also while using a CString..

PS: D'oh! I've got Unicode set in my project settings (VS 2012), so that should make things easier?
PPS: I just saw your reply - I've used lambdas before, just appreciating the sly-as-hell widening, just implicit. Must use lambdas much more.Loving that one, using it, thank you smile.png

Crap - I just added the extra null terminator.

 


Edited by mynameisnafe, 01 July 2014 - 03:21 PM.


#7 Bregma   Crossbones+   -  Reputation: 5481

Like
2Likes
Like

Posted 01 July 2014 - 03:15 PM

std::transform(str.begin(),str.end(),back_inserter(ext),[](char t) -> wchar_t { return t == ';' ? 0 : t; });

I don't think that's actually going to do the right thing if there's anything other than US ASCII in the original string. A simple integral promotion from a multibyte UTF-8 string into Microsoft's proprietary UCS-2 wide-character Unicode variant is a fail.

Of course, if you're restricting your somain to US ASCII, you're fine.


Stephen M. Webb
Professional Free Software Developer

#8 fastcall22   Crossbones+   -  Reputation: 4474

Like
0Likes
Like

Posted 01 July 2014 - 03:25 PM

Bregma is correct, Assimp's aiString is UTF-8. Therefore, a temporary buffer and mbstowcs are needed for proper UTF-8 handling.

Edited by fastcall22, 01 July 2014 - 03:26 PM.

c3RhdGljIGNoYXIgeW91cl9tb21bMVVMTCA8PCA2NF07CnNwcmludGYoeW91cl9tb20sICJpcyBmYXQiKTs=

#9 mynameisnafe   Members   -  Reputation: 252

Like
0Likes
Like

Posted 01 July 2014 - 03:45 PM

Okay..

 

Everything seems to be working with the lambda. The reason I have half the file formats visible in the dialog is to do with the OPENFILENAME and the GetOpenFileName() function

 

http://msdn.microsoft.com/en-gb/library/windows/desktop/ms646829(v=vs.85).aspx#open_file

 

I think I need a name for each format.. "Model" will do.

Hello people above smile.png std::string seems to work fine - I think (it appears) Assimp provides an overload - pretty sure I've seen it documented.


std::string GLFactory::GetAssimpExtsList()
{
std::string exts;
Assimp::Importer importer;
importer.GetExtensionList(exts);
return exts;
}

 

 

I don't think that's actually going to do the right thing if there's anything other than US ASCII in the original string.

 

Indeed. I'm happy to assume Assimp don't bother with language packs. PS That all sounds like a lot of effort - have you seen that lambda?! ohmy.png


Edited by mynameisnafe, 01 July 2014 - 03:46 PM.


#10 wack   Members   -  Reputation: 1348

Like
0Likes
Like

Posted 01 July 2014 - 03:48 PM

Bregma is correct, Assimp's aiString is UTF-8. Therefore, a temporary buffer and mbstowcs are needed for proper UTF-8 handling.

 

That will probably not be a correct conversion either, as mbstowcs expects the multibyte string to be in the system native multibyte encoding, which on Windows probably never is UTF-8.

 

You probably want something like this for a correct conversion:

#include <codecvt>
...
std::wstring_convert<std::codecvt_utf8<wchar_t>> wc;
std::wstring converted_str = wc.from_bytes(utf8str);


#11 Chris_F   Members   -  Reputation: 2465

Like
0Likes
Like

Posted 01 July 2014 - 03:49 PM

 

std::transform(str.begin(),str.end(),back_inserter(ext),[](char t) -> wchar_t { return t == ';' ? 0 : t; });

I don't think that's actually going to do the right thing if there's anything other than US ASCII in the original string. A simple integral promotion from a multibyte UTF-8 string into Microsoft's proprietary UCS-2 wide-character Unicode variant is a fail.

Of course, if you're restricting your somain to US ASCII, you're fine.

 

 

Microsoft hasn't used UCS-2 since Windows 2000. All modern versions of Windows use UTF-16 which is a multibyte encoding the same as UTF-8. Converting a UTF-8 string to UTF-16 is not going to make it easier to process.

 

http://www.utf8everywhere.org/


Edited by Chris_F, 01 July 2014 - 03:53 PM.


#12 L. Spiro   Crossbones+   -  Reputation: 14374

Like
0Likes
Like

Posted 01 July 2014 - 04:27 PM

If you are going to hard-code an L"" string, you are required to use the W version of OPENFILENAME (OPENFILENAMEW) and GetOpenFileName (GetOpenFileNameW).

Never mix-and-match "", _T( "" ), and L"" strings.

The solution is to convert from UTF-8 to wide-character strings via MultiByteToWideChar() (passing CP_UTF8) and use OPENFILENAMEW and GetOpenFileNameW() to open the file.


L. Spiro


Edited by L. Spiro, 01 July 2014 - 04:33 PM.

It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

#13 mynameisnafe   Members   -  Reputation: 252

Like
0Likes
Like

Posted 03 July 2014 - 11:39 AM

L. Spiro replies to my thread? Honoured!

I shall give it a try in 5 mins and let you guys know what happened..




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS