How does this method being called with CLASS macro work? - c++

I recently received a request from an acquaintance to assist with building a C++ solution after their developer unfortunately passed away. I'm relatively new to C++ and don't quite understand what the following lines are doing.
This code is from a customized version of the dcraw.cpp library by Dave Coffin.
The MACRO is defined as
#define CLASS
The method being called looks like this
void CLASS merror (void *ptr, char *where)
{
if ( ptr ) return;
//fprintf (stderr,_("%s: Out of memory in %s\n"), ifname, where);
sprintf (PSstring(),"%s: Out of memory in %s\n", ifname, where);
PSputDiag ();
longjmp (failure, 1);
}
And the method call looks like this
merror (fimg, "wavelet_denoise()");
When trying to compile the code I receive about 1800 errors, 258 of which are similar to the following.
C2664 'void merror(void *,char *)': cannot convert argument 2 from 'const char [15]' to 'char *' Photoshoot5 C:\Users\source\projectname\MVDcraw.cpp 991
I've been searching for quite some time to figure out what is going on, but since I don't even know what the method call is doing I've been unable to find anything that is relevant.
Any help would be greatly appreciated.

The macro
#define CLASS
has nothing to do with your error. After the macro is expanded the function is:
void merror (void *ptr, char *where)
{
if ( ptr ) return;
//fprintf (stderr,_("%s: Out of memory in %s\n"), ifname, where);
sprintf (PSstring(),"%s: Out of memory in %s\n", ifname, where);
PSputDiag ();
longjmp (failure, 1);
}
That is, the macro is replaced by nothing.
The code appears to be written before C++11. From cppreference...
until C++11:
String literals are convertible and assignable to non-const char* or wchar_t* in order to be compatible with C, where string literals are of types char[N] and wchar_t[N]. Such implicit conversion is deprecated.
since C++11:
String literals are not convertible or assignable to non-const CharT*. An explicit cast (e.g. const_cast) must be used if such conversion is wanted.
Converting string literals to char* never was ok, but only since C++11 it is an error. If you are using C++17 you can use std::string::data():
std::string where{"wavelet_denoise()"};
merror (fimg, where.data());
Actually better would be to change the method, as suggested in a comment by Richard Critten, to take a const char* where.

Related

How to convert char* to LPCWSTR?

I know this has already been discussed in several questions on SO, but none of those solutions have worked for me.
I start with a char* because this is for a DLL that will be called from VBA, and char* is necessary for VBA to pass a string to the DLL.
I need to return a LPCWSTR because that's the input parameter for the API function I'm trying to call, and I can't enable casting by switching from Unicode to multi-byte character set in the Properties window, because the API has this code:
#if !defined(UNICODE) && !defined(NOUNICODE)
#error UNICODE is not defined. UNICODE must be defined for correct API arguments.
#endif
I tried this:
LPCWSTR convertCharArrayToLPCWSTR(char* charArray)
{
const char* cs=charArray;
wchar_t filename[4096] = {0};
MultiByteToWideChar(0, 0, cs[1], strlen(cs[1]), filename, strlen(cs[1]));
}
which gave these errors:
error C2664: 'strlen' : cannot convert parameter 1 from 'const char' to 'const char *'
error C2664: 'MultiByteToWideChar' : cannot convert parameter 3 from 'const char' to 'LPCCH'
I tried this (same function header), loosely adapted from this post:
size_t retVal;
const char * cs = charArray;
size_t length=strlen(cs);
wchar_t * buf = new wchar_t[length](); // value-initialize to 0 (see below)
size_t wn = mbsrtowcs_s(&retVal,buf,20, &cs, length + 1, NULL);
return buf;
This compiled ok, but when I passed it an example string of "xyz.xlsx", mbsrtowcs_s() set buf to an empty string: L""
So, how do I make this conversion?
Following Hans Passant's advice regarding pointers to local variables, I worked out this approach, which seems to work well:
wchar_t *convertCharArrayToLPCWSTR(const char* charArray)
{
wchar_t* wString=new wchar_t[4096];
MultiByteToWideChar(CP_ACP, 0, charArray, -1, wString, 4096);
return wString;
}
I'm aware that the use of new requires memory management, which I perform in the function that calls this one.
Since cs is a const char*, cs[1] is a const char. C++ won't convert it to a pointer for you, because in most cases that doesn't make sense.
You could instead say &cs[1] or cs+1 if the intent is to skip the first char. (That's what you're doing when you pass a pointer to the 1th element; in C++, indexes start at 0.) If the intent is to pass the whole string, then just pass cs.

Annoying C++ gcc warning message

I've written the following program to match regular expressions in C++
#include <regex.h>
#include <iostream>
using namespace std;
/*
* Match string against the extended regular expression in
* pattern, treating errors as no match.
*
* return true for match, false for no match
*/
bool match(const char *string, char *pattern)
{
int status; regex_t re;
if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB) != 0)
return false;
/* report error */
status = regexec(&re, string, (size_t) 0, NULL, 0);
regfree(&re);
if (status != 0) {
return false; /* report error */
}
return true;
}
int main()
{
string str = "def fadi 100";
bool matchExp = match(str.c_str(), "^[Dd][Ee][Ff][' '\t]+[A-z]+([,])?[''\t]+[0-9]+$");
cout << (matchExp == true ? "Match": "No match") << endl;
}
The program works fine just as expected, but when I compile the code using gcc with the -Wall -Werror arguments (Linux environment), I get a very annoying warning message saying the following:
main.cpp: In function ‘int main()’:
main.cpp:33:90: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
Is there a way to force the compiler to believe that str.c_str() is the same as char * str? if so, how?
No, there isn't. That conversion was deprecated in C++03 and is illegal in C++11; don't do it.
Deprecation of that conversion comes from the fact that string literals are read-only, hence const; accessing them using a pointer to non-const char could possibly lead to modifying const objects, hence invoking undefined behavior. The warning isn't annoying; it is meant to save you from possibly crashing your application - or worse.
Also, you are wrong in reading the warning message; it isn't about c_str(), it is about passing string literal as char *.
The only way to really fix your code is to change second parameter of your match to be const char *, not char *, and copy the passed string to a new, buffer, internal to that function (why not in main()? Because with internal buffer, you have less boilerplate on the caller's side).
I'd also like to suggest totally different solution, since the question is tagged "C++": Boost.Regex.
Is there a way to force the compiler to believe that str.c_str() is the same as char * str?
That's actually not the issue here - you are already passing str.c_str() as a const char*.
The issue is that the second parameter is (also) a string literal, but has type char*. Try changing the second parameter to const char*.
If that still raises errors (due to the regex.h functions not specifying the correct const-ness), you're going to have to do something like this in main() or match():
char pattern[] = "^[Dd][Ee]...etc";
bool matchExp = match(str.c_str(), pattern);
See here for the reason why.
The problem is that a string literal should be only assigned to a pointer of a const char, so you need to change match to take a char const* pattern (which should be possible when you pass a string literal)
Make 2 parameter of function match const char *, warning is because of it

Cannot pass objects of type `const DictionaryKey' through `...'

I have this issue with one of my functions in this C++ program I'm porting over from HP-UX to LINUX. Basically, what seems to be the problem is that for the function
const char * Dictionary::lookup(const DictionaryKey &, ...)
the gcc compiler appears to be complaining that I cannot pass objects of type const Dictionary through ...
The source code for the program is pretty old and looks to utilize a lot of C-style constructs. This particular function accepts a variable list of arguments as allowed through the stdarg.h header. In addition, the warning is only reported in gcc (version 2.95.2) for HP-UX and not in LINUX's gcc (version 4.3.2).
$ gcc -lstdc++ foo1.cc foo2.cc
foo1.cc: In method `const char * Dictionary::lookup(const DictionaryKey &, ...)':
foo1.cc:178: warning: cannot pass objects of type `const DictionaryKey' through `...'
The source code for the function is available below:
const char *Dictionary::lookup (const DictionaryKey& key, ...)
{
static char res[MAX_LINE];
char *param[9];
va_list ap;
WordDictionary::iterator entry = dictionary.find (key);
if (entry != dictionary.end())
{
int idx = 1;
va_start (ap, key);
while ((param[idx] = va_arg (ap, char *)) != NULL) idx++;
va_end (ap);
char *ts = (*entry).second.textdata;
char *ts2 = res;
while (*ts)
{
if (*ts == '$')
{
ts++;
idx = *ts - '0';
strcpy (ts2, param[idx]);
ts2 += strlen(param[idx]);
ts++;
} else
{
*ts2++ = *ts++;
}
}
*ts2++ = 0;
return res;
}
else
{
return key.keydata; // Not found in dictionary
}
}
I was wondering if there was a way to repair this issue. I don't understand what is causing this warning to appear. From what I found on other websites, it appears that this warning may cause the compiled program to behave unexpectedly. Some people are saying that this should really be an error rather than a warning. I haven't had much luck determining the cause of this problem.
The warning is correct. You can only pass POD types as variable arguments -- attempting to pass a non-POD type gives undefined behavior [In case anybody cares (§5.2.2/7): "If the argument has a non-POD class type (clause 9), the behavior is undefined."]
Since you're treating all the other items being passed as char *, it's probably better to pass a vector<char *> or (perhaps even better) a vector<string>. If you can update to 4.7, you can switch to the latter fairly painlessly, so something like:
somedict.lookup(mykey, "a", "b", "c", NULL);
would turn into:
somedict.lookup(mykey, {"a", "b", "c"});

Deprecated conversion from string constant to char * error [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
C++ deprecated conversion from string constant to ‘char*’
I am having following code, though i didn't copy full code because it is huge.
Following code is in template class, and i am getting warning as below. Because of warning in template i am not able to instantiate it and getting "instantiated from here" error.
warning: deprecated conversion from string constant to 'char*''
void ErrorMessageInRaphsodyCode(char* pcCompleteMessage, char* pcMessage, char* pcFileName, unsigned int RowNo)
{
//...
}
char cCompleteMessage[200];
memset(cCompleteMessage, 0x00, sizeof(cCompleteMessage));
char*cMessage = "add reorgenize failed";
ErrorMessageInRaphsodyCode(cCompleteMessage, cMessage, "omcollec.h", __LINE__);
My question is what is best way to get rid of above warning ?
If a function takes a char const *, it guarantees that it only reads whatever data the pointer points to. However, if it takes a non-const pointer, like char *, it might write to it.
As it is not legal to write to a string literal, the compiler will issue a warning.
The best solution is to change the function to accept char const * rather than char *.
char cMessage[] = "add reorganize failed";
This should get rid of the warning.
Best way to get rid of it is to fix the function that is taking the parameter.
If your code is correct and the function does indeed take string constants, it should say so in its prototype:
void ErrorMessageInRaphsodyCode(char* pcCompleteMessage, char* pcMessage, const char* pcFileName, unsigned int RowNo)
If you can't do that (you don't have the code), you can create an inline wrapper:
inline void ErrorMessageInRaphsodyCodeX(char* p1, char* p2, const char* p3, unsigned int p4)
{ ErrorMessageInRaphsodyCode(p1,p2,(char*)p3,p4); }
and use the wrapper instead.
If your code is incorrect and the function does actually require writeable memory (which I highly doubt), you will need to make the string writeable by either creating a local array as Jan suggested, or mallocating enough memory.
(1) Make the variable a const char*
(..., const char* pcFileName, ...)
(2) If above is not possible and you want to retain the state of char* and const char* then make the function a template:
template<typename CHAR_TYPE> // <--- accepts 'char*' or 'const char*'
void ErrorMessageInRaphsodyCode(char* pcCompleteMessage, CHAR_TYPE* pcMessage, char* pcFileName, unsigned int RowNo)
{
//...
}
function c_str() of std::string class.

Are there gotchas using varargs with reference parameters

I have this piece of code (summarized)...
AnsiString working(AnsiString format,...)
{
va_list argptr;
AnsiString buff;
va_start(argptr, format);
buff.vprintf(format.c_str(), argptr);
va_end(argptr);
return buff;
}
And, on the basis that pass by reference is preferred where possible, I changed it thusly.
AnsiString broken(const AnsiString &format,...)
{
... the rest, totally identical ...
}
My calling code is like this:-
AnsiString s1, s2;
s1 = working("Hello %s", "World");
s2 = broken("Hello %s", "World");
But, s1 contains "Hello World", while s2 has "Hello (null)". I think this is due to the way va_start works, but I'm not exactly sure what's going on.
If you look at what va_start expands out to, you'll see what's happening:
va_start(argptr, format);
becomes (roughly)
argptr = (va_list) (&format+1);
If format is a value-type, it gets placed on the stack right before all the variadic arguments. If format is a reference type, only the address gets placed on the stack. When you take the address of the reference variable, you get the address or the original variable (in this case of a temporary AnsiString created before calling Broken), not the address of the argument.
If you don't want to pass around full classes, your options are to either pass by pointer, or put in a dummy argument:
AnsiString working_ptr(const AnsiString *format,...)
{
ASSERT(format != NULL);
va_list argptr;
AnsiString buff;
va_start(argptr, format);
buff.vprintf(format->c_str(), argptr);
va_end(argptr);
return buff;
}
...
AnsiString format = "Hello %s";
s1 = working_ptr(&format, "World");
or
AnsiString working_dummy(const AnsiString &format, int dummy, ...)
{
va_list argptr;
AnsiString buff;
va_start(argptr, dummy);
buff.vprintf(format.c_str(), argptr);
va_end(argptr);
return buff;
}
...
s1 = working_dummy("Hello %s", 0, "World");
Here's what the C++ standard (18.7 - Other runtime support) says about va_start() (emphasis mine) :
The restrictions that ISO C places on
the second parameter to the
va_start() macro in header
<stdarg.h> are different in this
International Standard. The parameter
parmN is the identifier of the
rightmost parameter in the variable
parameter list of the function
definition (the one just before the
...).
If the parameter parmN is declared with a function, array, or reference
type, or with a type that is not
compatible with the type that results
when passing an argument for which
there is no parameter, the behavior
is undefined.
As others have mentioned, using varargs in C++ is dangerous if you use it with non-straight-C items (and possibly even in other ways).
That said - I still use printf() all the time...
A good analysis why you don't want this is found in N0695
According to C++ Coding Standards (Sutter, Alexandrescu):
varargs should never be used with C++:
They are not type safe and have UNDEFINED behavior for objects of class type, which is likely causing your problem.
Here's my easy workaround (compiled with Visual C++ 2010):
void not_broken(const string& format,...)
{
va_list argptr;
_asm {
lea eax, [format];
add eax, 4;
mov [argptr], eax;
}
vprintf(format.c_str(), argptr);
}
Side note:
The behavior for class types as varargs arguments may be undefined, but it's consistent in my experience. The compiler pushes sizeof(class) of the class's memory onto the stack. Ie, in pseudo-code:
alloca(sizeof(class));
memcpy(stack, &instance, sizeof(class);
For a really interesting example of this being utilized in a very creative way, notice that you can pass a CString instance in place of a LPCTSTR to a varargs function directly, and it works, and there's no casting involved. I leave it as an exercise to the reader to figure out how they made that work.