I'm currently working on a DLL that needs to convert back and forth between the friendly name for a value and the value itself. As this code is used in many places throughout the codebase, I want to try and keep it simple and in a single function or object so I only have to declare them once.
From my reading it looks like CMap is the tool for the job, but I can't seem to discover any combination of template arguments that compiles without errors.
My values are CString and int. I tried the following definition:
CMap<int, int, CString, CString> encodermap;
which compiles, but when I try to add a value:
encodermap["Encoder 1"] = 0;
I get the following compiler errors:
error C2057: expected constant expression
error C2466: cannot allocate an array of constant size 0
error C2501: 'encodermap' : missing storage-class or type specifiers
error C2040: 'encodermap' : 'int []' differs in levels of indirection from 'CMap<KEY,ARG_KEY,VALUE,ARG_VALUE>'
I've tried changing the CMap to this:
CMap<CString, CString, int, int> encodermap;
but I get the same four errors.
I'm sure I must be missing something but I'm at a loss as to what.
Because of the SDK being used for this work I'm require VS2003
The problem
I think you've inverted the key type and the valye type.
Your original declaration defines int as being the key to be searched for with operator[]. So encodermap[0] = "Encoder 1"; would work.
But when your compiler sees encodermap["Encoder 1"] = 0;, he tries to find an operator[] which takes char* (or something to which char * can be converted to) and returns an int. The last error message tells you that he couldn't find such an operator for your map.
With MSVC 2015, the error message is more concise: C2679.
The solution
You should define your CMap with a CString key and an int value. The trick to know, is that for a CString KEY, the the ARG_KEY should be LPCWSTR . So the right definition would be:
CMap<CString, LPCWSTR, int, int> encodermap;
This permits to use CString as key in the map's operator[].
Now if you use MFC on windows, you probably use UNICODE and wide chars (therefore the LPCWSTR instead of LPCSTR). When calling the operator you then have either to use a CString or a wide literal:
encodermap[L"Encoder 1"] = 0;
encodermap[CString("Encoder 2")] = 1;
Try this:
CMap<CString, LPCTSTR, int, int> encodermap;
This CodeProject article CMap How-to may be of some help.
Many people get confused about CMap's declaration CMap < KEY, ARG_KEY,
VALUE, ARG_VALUE >, why not just CMap < KEY, VALUE >?
In fact, the ultimate data container in CMap is CPair, and the
internal of CPair is {KEY, VALUE}. Therefore, CMap will really store a
KEY, and not ARG_KEY. However, if you check with the MFC source code,
almost all the internal parameters passing within CMap itself is
called with ARG_KEY and ARG_VALUE, therefore, using KEY & as ARG_KEY
seems always a correct thing, except when:
You are using primitive date types like int, char, where pass-by-value makes no difference (may be even faster) with
pass-by-reference.
If you use CString as KEY, you should use LPCTSTR as ARG_KEY and not CString &, we will talk more about this later.
Edit: Cristophe, another option for the assignment is encodermap[_T("Encoder 1")] = 0;, which will work for single-byte, multi-byte or Unicode with the LPCTSTR typedef. You will also need to #include <tchar.h>.
Related
I would guess that the last two lines in this code should compile.
#include "rapidjson/document.h"
int main(){
using namespace rapidjson ;
using namespace std ;
Document doc ;
Value obj(kObjectType) ;
obj.AddMember("key", "value", doc.GetAllocator()) ; //this compiles fine
obj.AddMember("key", string("value").c_str(), doc.GetAllocator()) ; //this does not compile!
}
My guess would be wrong, though. One line compiles and the other does not.
The AddMember method has several variants as documented here, but beyond that... why is the return of .c_str() not equivalent to a string literal?
My understanding was that where ever a string literal was accepted, you could pass string::c_str() and it should work.
PS: I'm compiling with VC++ 2010.
EDIT:
The lack of #include <string> is not the problem. It's already included by document.h
This is the error:
error C2664: 'rapidjson::GenericValue<Encoding> &rapidjson::GenericValue<Encoding>::AddMember(rapidjson::GenericValue<Encoding> &,rapidjson::GenericValue<Encoding> &,Allocator &)'
: cannot convert parameter 1 from 'const char [4]' to 'rapidjson::GenericValue<Encoding> &'
with
[
Encoding=rapidjson::UTF8<>,
Allocator=rapidjson::MemoryPoolAllocator<>
]
and
[
Encoding=rapidjson::UTF8<>
]
EDIT2:
Please ignore the fact that .c_str() is called on a temporal value. This example is just meant to show the compile error. The actual code uses a string variable.
EDIT3:
Alternate version of the code:
string str("value") ;
obj.AddMember("key", "value", doc.GetAllocator()) ; //compiles
obj.AddMember("key", str, doc.GetAllocator()) ; // does not compile
obj.AddMember("key", str.c_str(), doc.GetAllocator()) ; // does not compile
The std::string::c_str() method returns a char const*. The type of a string literal is char const[N] where N is the number of characters in the string (including the null terminator). Correspondingly, the result of c_str() can not be used in all places where a string literal can be used!
I'd be surprised if the interface you are trying to call requires a char array, though. That is, in your use it should work. It is more likely that you need to include <string>.
even if this code compiled:
obj.AddMember("key2", string("value").c_str(), doc.GetAllocator());
You cannot guarantee that it is safe.
The const char* returned by std::string::c_str() will be valid until the end of this statement.
If the AddMember method stores a copy of the string itself, all well and good. If it stores a pointer then you're doomed. You need knowledge of the inner workings of AddMember before you can reason about the correctness of your code.
I suspect the authors have already thought of this and have constructed overloads that demand that you either send in a std::string object (or equivalent) or a string literal reference (template<std::size_t N> void AddMember(const char (&str)[N]))
Even if this is not what they had in mind, they might be looking to protect you from yourself, in case you inadvertently send in an invalid pointer.
While seemingly an inconvenience, this compile time error indicates a possibly-faulty program. It's a tribute to the library's authors. Because compile time errors are a gazillion times more useful than runtime errors.
Looking at the documentation you linked to, it seems like you are trying to call the overload of AddMember taking two StringRefTypes (and an Allocator). StringRefType is a typedef for GenericStringRef<Ch>, which has two overloaded constructors taking a single argument:
template<SizeType N>
GenericStringRef(const CharType(&str)[N]) RAPIDJSON_NOEXCEPT;
explicit GenericStringRef(const CharType *str);
When you pass a string literal, the type is const char[N], where N is the length of the string + 1 (for the null terminator). This can be implicitly converted to a GenericStringRef<Ch> using the first constructor overload. However, std::string::c_str() returns a const char*, which cannot be converted implicitly to a GenericStringRef<Ch>, because the second constructor overload is declared explicit.
The error message you get from the compiler is caused by it choosing another overload of AddMember which is a closer match.
Re
” why is the return of .c_str() not equivalent to a string literal
A string literal is a zero-terminated string in an array with size known at compile time.
c_str() produces a pointer to (the first item in) a zero-terminated string in an array with size known only at run-time.
Usually a string literal expression will be used in a context where the expression decays to pointer to first item, but in some special cases it does not decays. These cases include
binding to a reference to array,
using the sizeof operator, and
forming a larger literal by compile time concatenation of string literals (simply writing them in order).
I think that's an exhaustive list.
The error message you cite,
” cannot convert parameter 1 from 'const char [4]' to 'rapidjson::GenericValue &
… does not match your presented code
#include "rapidjson/document.h"
int main(){
using namespace rapidjson ;
using namespace std ;
Document doc ;
Value obj(kObjectType) ;
obj.AddMember("key1", "value", doc.GetAllocator()) ; //this compiles fine
obj.AddMember("key2", string("value").c_str(), doc.GetAllocator()) ; //this does not compile!
}
Nowhere in this code is there a three character long string literal.
Hence the claims that “this compiles” and “this does not compile”, are not very trustworthy.
You
should have quoted the actual error message and actual code (at least one of them is not what you had when you compiled), and
should have quoted the documentation of the function you're calling.
Also, note that the actual argument that compiler reacts to in the quoted diagnostic, is a literal or an array declared as such, not a c_str() call.
Question: How to use string/char* variable as the path parameter to GetPrivateProfileInt method.
I m trying to use GetPrivateProfileInt given for windows. The following code runs perfeclty without any issue:
int x = GetPrivateProfileInt(L"x",L"y",1,L"This\\is\\the\\path");
But in my case, the path is being passed to the function. Something like this:
void fun(std::string path)
{
//error const char* is incampatible with LPCWSTR.
int x = GetPrivateProfileInt(L"x",L"y",1,path.c_str());
}
In some attempts given below, x is recieving the default value. i.e. the path is not being passed to the GetPrivateProfileInt method correctly.
Following are several other attempts made by me:
Attempt1:
// No error, default value is being read.
int x = GetPrivateProfileInt(L"x",L"y",1,(LPCTSTR)path.c_str());
Attempt2:
// No error, default value is being read.
int x = GetPrivateProfileInt(L"x",L"y",1,(wchar_t*)path.c_str());
Attempt3:
//_T() macro giving error.
// 'Ls' : undeclared identifier.identifier "Ls" is undefined.
LPCTSTR path_s = _T(path.c_str());
int x = GetPrivateProfileInt(L"x",L"y",1,path_s);
I went through the answers here but not able find out the solution.
There are two versions of the function, one takes UCS-2 characters (GetPrivateProfileIntW) and one takes char characters (GetPrivateProfileIntA). There are no versions which allow you to mix the parameters. Your options are to either change the appname and keyname parameters to single-byte to match your data
GetPrivateProfileIntA("x", "y", 1, path.c_str());
or to convert the last parameter to UCS-2 using MultibyteToWideChar, then call GetPrivateProfileIntW.
Pointer-casting is NOT a conversion of the character encoding, and will not work. The compiler type system is there to help you, and making it shut up with a cast is nearly always the wrong thing to do (exception: the return value of GetProcAddress does need a cast).
I'm receiving following error:
Debug Assertion Failed!
Expression: string iterators incompatible
When trying to run such a code:
std::string string_Dir(){return ".\\Dir\\";}
std::wstring wstring_Dir=std::wstring(
string_Dir().begin()
,string_Dir().end()
);
SetDllDirectory(wstring_Dir.c_str());
Does someone know why
BTW: I followed this.
You are calling string_Dir() twice and then using iterators from different std::string objects to initialize your std::wstring. That is why you are getting an incompatibility error. You must use iterators from the same std::string object, so call string_Dir() once and assign the return value to a variable:
std::string dir = string_Dir();
std::wstring wstring_Dir(dir.begin(), dir.end());
SetDllDirectory(wstring_Dir.c_str());
// or better: SetDllDirectoryW(wstring_Dir.c_str());
That being said, you are not converting from ANSI to UTF-16, so this code will only work correctly if string_Dir() returns a std::string that contains only 7bit ASCII characters. It will fail if the std::string contains any non-ASCII 8bit characters.
There is a simpler solution - you can call SetDllDirectoryA() instead. You don't need the std::wstring, and the OS can do the ANSI-to-UTF16 conversion for you:
SetDllDirectoryA(string_Dir().c_str());
According to the documentation, the value in the function call is supposed to be LPCTSTR instead of LPCTWSTR.
char Substitution::encodeChar(char a, std::map<char, char>&){
return &[a];
}
This is my implementation attempt (based on a pre defined Class header which I may not change for the assignment). In Visual Studio I get the error (see title) over the semicolon?
Trying
&.find(a)
instead gives me "expected an expression" over the period.
I think I spotted somewhere saying something about const char vs. char for this problem, but I can't wrap my head around it. I've used map char char earlier this way, but somehow using it in this context doesn't work.
It looks like you're trying to treat & as a variable name. It's not. Variable names consist only of letters, digits, and underscores.
Actually, in the function parameters, std::map<char, char>& means that the type of the parameter is a "reference to std::map<char, char>". Note that I said "reference to". That's what the & means. It's part of the type and makes the parameter a reference parameter.
So you need to give your parameter a name and then use that name:
char Substitution::encodeChar(char a, std::map<char, char>& my_map){
return my_map[a];
}
We can read the parameter std::map<char, char>& my_map as saying the my_map is a "reference to std::map<char, char>". Then, my_map[a] accessing the key a in that map.
a C++ noob here. I am trying to tweak some code, with the following key lines (meaning they are not the only ones, but they are the only ones that should matter for this question). By the way, I am using Visual Studio 2010 C++ compiler on Windows.
CMap<ATL::CAtlString,LPCTSTR,UINT,UINT> mapForDuplicates; // "dict" definition
ATL::CAtlString strDescription = ... // let's just say it gets set to a value.
UINT nFound = 0; // What is this for???
BOOL bFound = mapForDuplicates.Lookup(strDescription, nFound);
mapForDuplicates[strDescription] = 1;
Now ... I really do not want to use the UINT here, as bool is all I really need. However, I could not figure out what all of the arguments for the CMap constructor really are. When using C#, all I have to specify is the type of the key and the type of the value. The fact that ATL::CAtlString does not match LPCSTR really confuses me. What exactly are KEY, ARG_KEY, VALUE, and ARG_VALUE? Why do I need all four and can all four be different? Thanks.
...
template<class KEY, class ARG_KEY, class VALUE, class ARG_VALUE>
class CMap : public CObject
...
Note: I could use std::map here instead(although I have not used it either); the only non-negotiable is ATL::CAtlString - I have to use this type. Let me know if you have questions.
IIRC the four args to the template are there so you can throw one type in and get another (const) type back. Here it throws in CAtlStrings, but it'll get back LPCTSTR. Often you just specify the same to types twice (e.g. int, int, float, float for a map of ints -> floats).
Grr, that extra L really irks me nowadays, it was great for 16-bit Windows but nowadays... PCSTR is all that's needed. 'L' is the useless appendix of Windows programming.
nFound is something coming out of the map, the map maps to UINT so nFound is a UINT.
Start with the docs for the class =- there is a ref to a sample here too
http://msdn.microsoft.com/en-us/library/s897094z(VS.71).aspx
By the way, the following pseudo-sample did the trick for me.
std::set<CAtlString> setOfDescriptions;
for each(...)
{
CAtlString strDescription = GetDescription();
if (setOfDescriptions.find(strDescription) != setOfDescriptions.end())
{
// Remove a duplicate.
}
setOfDescriptions.insert(strDescription); // Mark as seen.
}