I'm trying to migrate an old project from Borland BCC 5 to Qt 5.6.3 with Microsoft MSVC 2015 64bit compiler.
The following code is a model of original work. It works as expected in Visual Studio 2015 and Borland BCC 5, However, when I try to run it in Qt 5.6.3 with Qt Creator 3.6.1. It throws an error as "*hcID" is not accessible.
#include <stdlib.h>
typedef void (*ProcFoo) (void *pvData, unsigned long ulBarTag);
static char* gpcID = NULL;
typedef struct FooType
{
ProcFoo pfFoo; ///< pointer to Foo callback function
unsigned long ulBarTag; ///< base id
} FooType;
FooType* pFoo = NULL;
void RegisterAsFoo(unsigned long ulBarTag, ProcFoo pfFoo)
{
pFoo = (FooType*)malloc(sizeof(FooType));
pFoo->ulBarTag = ulBarTag;
pFoo->pfFoo = pfFoo;
}
void GetBarTag(unsigned long *pulBarTag, unsigned long ulBarTag)
{
*pulBarTag = ulBarTag;
}
int main()
{
char s[] = "Some Value";
gpcID = s;
char ** hcID = NULL;
RegisterAsFoo((unsigned long)&gpcID, (ProcFoo)GetBarTag);
pFoo->pfFoo(&hcID, pFoo->ulBarTag);
// after the execution, the hcID is still NULL in Qt 5.6.3, however,
// it is valid in Visual Studio 2015 and Borland BCC 5.
char* result = (*hcID);
return 1;
}
Thanks if you have any comments or ideas!
The problem is in the
(unsigned long)
cast which works for 32 bit.
Use
(unsigned long long)
instead. You need to do the replacement all over the place.
Otherwise, get rid of the int to pointer conversion or use a 32 bit compiler instead
Related
I am learning SAL Annotations, I tested this example in Visual Studio 2017.
I thought the compiler will report warning or error when I pass a NULL pointer to InCallee, however, it still can build correctly.so my question is whether SAL is just like code comments and won't validate legality of the data, or it can check the data, just because I made something wrong?
void InCallee(_In_ int *pInt) //_In_ is allowed to be NULL
{
int i = *pInt;
}
void GoodInCaller()
{
int *pInt = new int;
*pInt = 5;
InCallee(pInt);
delete pInt;
}
void BadInCaller()
{
int *pInt = NULL;
InCallee(pInt); // pInt should not be NULL
}
I am using visual studio 2013 with the visual studio 2010 compiler.
I am re-using some c code in my c++/CLI project and I am getting compile errors.
All of the files are in the same project and all of the body files are not using pre-compiled headers
I am trying not to edit the re-used code, if possible...
Here is a sample of the code, note libFunc.h and .c are the re-used code and I am writing wrapper.cpp:
// libFunc.h
typedef struct
{
double max;
double var;
} myStruct;
void libFunc(char * filename, myStruct *(*arg)[]);
void readVals(char * filename, myStruct *(*arg)[]);
void assignVals(myStruct *arg, char vals[][1024]);
// libFunc.c
void libFunc(char * filename, myStruct *(*arg)[])
{
readVals(filename, arg);
}
void readVals(char * filename, myStruct *(*arg)[])
{
char vals[15][1024];
char *line = (char *) malloc(1024);
s = fopen(filename, "r");
while(fgets(line, 1024, s) != NULL)
{
// parse tokens into vals
assignVals((*arg)[index], vals);
}
}
void assignVals(myStruct *arg, char vals[][1024])
{
arg->max = atof(values[0]);
arg->var = atof(values[1]);
}
// wrapper.cpp
#include "libFunc.h"
int wrapper()
{
myStruct *myVar[1];
char filename[512];
strcpy(filename, "input.txt");
libFunc(filename, &myVar);
}
I get the error:
cannot convert myStruct *(*)[1] to myStruct *(*)[].
I tried:
myStruct ** myVar = new myStruct*[1];
libFunc(&myVar);
This resulted in the error:
cannot convert myStruct *** to myStruct *(*)[].
How do I pass in something that it will accept?
The code does not have to be compiled as c++
Any solution to this issue must work in Visual Studio 2013 with Visual Studio 2010 compiler, and by "work" I mean that "Build Solution" has to build everything including these c files.
Some people say that C is a subset of C++. This is incorrect. What is correct is that there exists a subset that is common to both languages. You are running into one of those aspects of C that is not supported in C++.
Since this is something that is not supported in C++, any answer is of dubious quality. That said, here's an answer of dubious quality: Simply cast your &myVar to a pointer to an array of pointers to myStruct:
libFunc((myStruct*(*)[])&myVar);
This compiles clean. Whether it "works", that's a different question. There's a comment in the question that makes me think this will not work:
void libFunc(myStruct *(*arg)[]){ // does stuff, sets arg
If that function truly does set arg (as opposed to sets contents of arg), that is something that is not supported at all in C++.
Aside: Why does that function take a pointer to an array of pointers to myStruct? You haven't shown the body of that code. I don't know if setting *arg is valid in C, but it certainly is not valid in C++.
Update:
Thanks to #user2079303 for the Coliru resource and test code.
I've now got code that demonstrates this issue when compiled with VS2013 - it works fine when compiled on Coliru, so it's definitely as VS2013 compiler problem. As #user2079303 correctly suggested the previous code did NOT have the error.
The issue appears to be having a wrapper function around the array access before the copy to result:
SWIGINTERN ConGroup ConGroupArray_getitem(ConGroup *self, int index){
return self[index];
}
SWIGEXPORT void * SWIGSTDCALL CSharp_ConGroupArray_getitem(void * jarg1, int jarg2) {
void * jresult;
ConGroup *arg1 = (ConGroup *)0;
int arg2;
ConGroup result;
arg1 = (ConGroup *)jarg1;
arg2 = (int)jarg2;
result = ConGroupArray_getitem(arg1, arg2);
// result = arg1[arg2];
jresult = new ConGroup((const ConGroup &)result);
return jresult;
}
Here's a link to Coliru that has the modified source: Coliru Example
If run on Coliru it works fine, BUT when build with VS2013 in a C++ console app with CLR turned on it fails.
I'm struggling to explain some very strange behaviour that I've been seeing with copy constructors in VS2013, and I'm wondering if any of you have seen anything similar and can shed any light on it.
I've been wrapping a 3rd party C++ DLL using SWIG and was having problems with getting valid data out of a specific structure.
The 3rd party DLL exposes all of its data via POD structures defined in the API header file and most where working fine - giving me valid data, however, a few appeared broken and were producing invalid data.
After much debugging eventually I discovered that replacing the compiler generated copy constructor with my own that simply uses memcpy solves the problem.
The question is really about what can possible be going wrong?
How can a compiler generated copy constructor be going wrong without generating any errors?
As far as I can tell there's nothing particularly different about the struct that doesn't work when compared to one that does... so I'm happy to confess to being highly confused.
Here's the structure in question, and the structures it contains, with my added copy constructor:
struct ConGroupSec
{
int show,trade;
int execution;
double comm_base;
int comm_type;
int comm_lots;
double comm_agent;
int comm_agent_type;
int spread_diff;
int lot_min,lot_max;
int lot_step;
int ie_deviation;
int confirmation;
int trade_rights;
int ie_quick_mode;
int autocloseout_mode;
double comm_tax;
int comm_agent_lots;
int freemargin_mode;
int reserved[3];
};
struct ConGroupMargin
{
char symbol[12];
double swap_long,swap_short;
double margin_divider;
int reserved[7];
};
struct ConGroup
{
char group[16];
int enable;
int timeout;
int adv_security;
char company[128];
char signature[128];
char support_page[128];
char smtp_server[64];
char smtp_login[32];
char smtp_password[32];
char support_email[64];
char templates[32];
int copies;
int reports;
int default_leverage;
double default_deposit;
int maxsecurities;
ConGroupSec secgroups[32];
ConGroupMargin secmargins[128];
int secmargins_total;
char currency[12];
double credit;
int margin_call;
int margin_mode;
int margin_stopout;
double interestrate;
int use_swap;
int news;
int rights;
int check_ie_prices;
int maxpositions;
int close_reopen;
int hedge_prohibited;
int close_fifo;
int hedge_largeleg;
int unused_rights[2];
char securities_hash[16];
int margin_type;
int archive_period;
int archive_max_balance;
int stopout_skip_hedged;
int archive_pending_period;
UINT news_languages[8];
UINT news_languages_total;
int reserved[17];
ConGroup() {}
ConGroup(const ConGroup& src)
{
memcpy(this, &src, sizeof(ConGroup));
}
};
When the constructors are commented out, the struct is not copied correctly, when commented back in things work fine.
Can anyone out there explain this? I can't.
As requested here's the code that actually uses the copy constructor:
SWIGEXPORT void * SWIGSTDCALL CSharp_ConGroupArray_getitem(void * jarg1, int jarg2) {
void * jresult ;
ConGroup *arg1 = (ConGroup *) 0 ;
int arg2 ;
ConGroup result;
arg1 = (ConGroup *)jarg1;
arg2 = (int)jarg2;
result = arg1[arg2];
jresult = new ConGroup((const ConGroup &)result);
return jresult;
}
This is code generated by SWIG, and is part of a managed C++ wrapper class for the native 3rd party DLL.
In the VS2013 debugger, I can see that arg1 is a valid array of ConGroup structures that contain valid data and doing manual pointer arithmetic in the memory window allowed me to check that for a number of the array contents.
As it happens I've worked around the issue by changing the code above to the following, which removes the copying entirely:
SWIGEXPORT void * SWIGSTDCALL CSharp_ConGroupArray_getitem(void * jarg1, int jarg2) {
void * jresult ;
ConGroup *arg1 = (ConGroup *) 0 ;
int arg2 ;
ConGroup* result;
arg1 = (ConGroup *)jarg1;
arg2 = (int)jarg2;
result = &arg1[arg2];
jresult = (void*)result;
return jresult;
}
That code works perfectly, so valid data does exist at the correct locations, hence I believe the issue must be a copy constructor problem...
Time to close this question.
It seems the issue here is entirely related to having the CLR enabled on a C++ project. Enabling the CLR appears the alter the way the copy default constructors work within the C++ code when using Visual Studio 2013 and 2015.
Given that could be better phrased as a new question I'm closing this one.
I just tried to move a C++ project from Windows XP to Windows 7 (make it run in win7).
the compile is OK however the application crashes as soon as reach the following code (The code is a piece of example code, but it has same issue).
void Test(const BSTR& b)
{
_variant_t t;
t.vt = VT_BSTR;
t.bstrVal = b;
}
int main()
{
const BSTR b = L"Test";
Test(b);
}
The code is working fine in windows XP but in Windows 7 a heap corruption occurs.
If I change the code from
_variant_t t;
t.vt = VT_BSTR;
t.bstrVal = b;
to
_variant_t t(b);
then it works fine on windows 7.
My Question, why the first piece of code is not working on Windows 7?
Is there an direct way to manage C++/CLI Strings in a wrapper to char* parameter for native C++ method?!
example:
void Test::TestFunc(System::String ^%str)
{
// right conversion to char* and consider the reference
}
native C++ function:
void TestB::Func(char *str)
{
...
}
my tricky way:
void Test::TestFunc(System::String ^%szRecvBuffer)
{
pin_ptr<const wchar_t> szRecvBufferP = PtrToStringChars(szRecvBuffer);
// Convert to a char*
size_t origsize = wcslen(szRecvBufferP) + 1;
const size_t newsize = 100;
size_t convertedChars = 0;
char nstring[newsize];
wcstombs_s(&convertedChars, nstring, origsize, szRecvBufferP, _TRUNCATE);
strcat_s(nstring, " (char *)");
char *szRecvBufferCh = nstring;
m_object->TestCommand(szRecvBufferCh); // parameter char*
System::String ^tmp = gcnew System::String(szRecvBufferCh);
szRecvBuffer = tmp;
}
greets leon22
You're doing a lot manually that the runtime will take care of for you.
void Test::TestFunc(String^% szRecvBuffer)
{
using System::Runtime::InteropServices::Marshal;
IntPtr memHandle = Marshal::StringToHGlobalAnsi(szRecvBuffer);
try
{
char* const str = static_cast<char*>(memHandle.ToPointer());
m_object->TestCommand(str);
szRecvBuffer = gcnew String(str);
}
finally
{
Marshal::FreeHGlobal(memHandle);
}
}
If you're using VC++ 2010, you can use std::unique_ptr<> with a custom deleter to avoid using try..finally, ultimately cutting the number of lines of code in half.
If the char* is an output parameter, then in C++/CLI you must pass System::StringBuilder^
If you search for "System::String" with Google, the second link is How to convert from System::String* to Char* in Visual C++ . This is obviously a FAQ!
(There appear to be about 5 different ways depending on the specifics of the conversion required, so I won't summarise them here: go read the article.)
You can't avoid having conversions in both directions, because System::String uses wchar_t, and a Unicode->Multibyte conversion is necessary to get char from that.
But marshal_as should make the conversion much easier.