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
}
Related
Background
I am cleaning up a legacy codebase by applying a coding guideline for the new statement.
There is code like auto x = new(ClassName); that I rewrite to auto x = new ClassName();. It's quite obvious that this is not a placement new and I don't need to think about it.
However, there's also code like auto x = new(ClassName)(argument); which looks much like a placement new. For now I blindly rewrote such code to auto x = new ClassName(argument); as well.
Question
Might there be a case where a real placement new like auto x = new(placement-params)(Type); is rewritten as auto x = new placement-params(Type); and it still compiles but changes the meaning of the program?
placement-params is not a type, it is a value.
Consider this code with a placement new:
int* buf = new int;
int* a = new(buf)(int);
If we remove parenthesis around buf, the compiler can easily detect buf is not a type.
int* a = new buf(int); // compile error
Even if we create a type named buf, by the name lookup rules, the name in the inner scope is found first:
class buf { // found second
buf(int) {}
};
int main() {
int *buf = new int; // found first
int* a = new buf(int); // compile error
return 0;
}
According to this answer, when type and value are in a same scope, the value is found first. For example:
class buf { // found second
buf(int) {}
};
int *buf = new int; // found first
int main() {
int *a = new buf(int); // compile error
return 0;
}
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
Even though the following piece of code compiles and runs fine i want to know if it is a valid c++ code?
int main()
{
int *i= new int;
cout<<*i;
int &ref=*i;
cout<<ref;
delete &ref; //Especially is this statement valid?
return 0;
}
If it is valid then this must also be valid :
int& getInt() {
int* i = new int;
return *i; // OK?
}
int main(){
int& myInt = getInt(); // these two lines are same as shown in the example above ?
delete &myInt; //is this OK too?
}
It's correct code and it will work on all platforms and compilers.
However, it's probably not best practice as the reference is usually used when the called party retains the ownership of the object.
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 am trying to add SALto my code... i worked according msdn and found bug in msdn examples, don't know how to deal with it.
Here litle changed example "Output of pointer to caller (Example: The Outptr Annotation)" from Understanding SAL
Outptr is used to annotate a parameter that's intended to return a
pointer. The parameter itself should not be NULL, and the called
function returns a non-NULL pointer in it and that pointer points to
initialized data.
My code:
#include "stdafx.h"
#include "assert.h"
void GoodOutPtrCallee(_Outptr_ int **pInt)
{
int *pInt2 = new int;
if (*pInt != NULL)
{
*pInt2 = 1;
}
else
{
*pInt2 = 2;
}
*pInt = pInt2;
}
int _tmain(int argc, _TCHAR* argv[])
{
int* nullValue = NULL;
GoodOutPtrCallee(&nullValue);
assert(*nullValue == 2);
int someValue = 22;
int* someValuePtr = &someValue;
GoodOutPtrCallee(&someValuePtr);
assert(*someValuePtr == 1);
return 0;
}
If i compile it in VS2013 with code alalysys enabled i got C6001: using uninitialized memory
for
if (*pInt != NULL)
row.
What is worng here in my annotation and how can i fix it?
Since you're reading from the value passed through the pointer parameter pInt you can't use _Outptr_ , as this describes a parameter that's only used as an output, not also as an input. Use _Inout_ instead.
You might want to reconsider using SAL. It's very poorly documented, and as a result I can't say with any certainty that _Inout_ is actually the best annotation to use here. All I know for sure is that it's best match I could find based on Microsoft's vague descriptions, and it gets rid of the warning. Of course so would not using an annotation.
EDIT: I was confused by similar variable names, pInt and pInt2.
You're probably should mark pInt as input and output, not just as output, because you're reading it's value to check whether it is NULL