Isn't pointer type checking disabled in DLL/C-Connect, and is that OK? - smalltalk

After this somehow related question Why can't I pass an UninterpretedBytes to a void* thru DLL/C-Connect? where we saw that I could not pass a Smalltalk array of bits to a void * parameter, I further analyzed the method responsible for checking the compatibility of formal pointer description with effective object passed as argument, and I think that I discovered another questionable piece:
CPointerType>>coerceForArgument: anObject
...snip...
(anObject isKindOf: self defaultDatumClass)
ifTrue: [
(referentType = anObject type referentType
or: [(referentType isVoid
and: [anObject type referentType isConstant not])
or: [anObject type isArray not
or: [anObject type baseArrayType = referentType]]])
ifTrue: [^anObject asPointer]].
...snip...
It means the following:
It first checks if the argument is CDatum (a proxy to some C-formatted rawdata and associated CType).
If so, it checks whether the type is the same as the formal definition in external method prototype (self).
If not, it could be that the argument is void *, in which case any kind of pointer is accepted (it has been checked that it is a pointer in the code that I snipped), except if it is pointer on a const thing.
There is a first discrepancy: it should check if the formal definition is const void * and accept any pointer on const in this case... But that does not matter much, we rarely have actual argument declared const.
If not, it checks if either not an array (for example, int foo[2]), or an array whose type matches (same base type and dimension).
So, if the formal definition is for example struct {int a; char *b} *foo, and that I pass a double * bar, the type does not match, there is no const qualifier mismatch, and the parameter is not an array, conclusion: we can safely pass it without any further checking!
That's a kind of pointer aliasing. We do not have an optimizing compiler making any speculation about the absence of such aliasing in Smalltalk, so that won't be the source of undefined behaviour. It could be that we deliberately want to force this sort of dirty reinterpret_cast for obscure reasons (since we can explicitly cast a CDatum, I would prefer the explicit way).
BUT, it might be that we completely messed up and passed the wrong object, with wrong type, wrong dimension, and that the address foo->b in my example above will contain either some re-interpreted garbage if pointer is 32bits aligned, or be completely undefined on 64 bits machine (because beyond the sizeof double).
A C compiler would warn me for sure about the aliasing, and prevent production of artifact with -Wall -Werror.
What troubles me here is that I do not even get a warning...
Does it sound correct?

Short answer: it's not OK to correct this behavior, because some low level user interface stuff depends on it (event loop). We can't even introduce a Warning or anything.
Longer story: I tried to rewrite the whole method with double dispatching (ask anObject if compatible with formal CPointerType rather than testing every possible Object class with repeated isKindOf: ).
But when ommitting the disgracious pointer aliasing tolerance, it invariably screw my Macosx 8.3 image with tons of blank windows opening, and blocked uninterruptable UI...
After instrumenting, it appears that the event loop relies on it, and pass aString asNSString (which is transformed into utf16, but stored into a ByteArray and thus declared unsigned char *), to an Objective C method expecting an unsigned short *.
It's a case where the pointer aliasing is benign, as long as we pass the good bytes.
If I try and fix asNSString with a proper cast to unsigned short *, then the UI blocks (I don't know why, but it would require debugging at VM level).
Conclusion: it's true that some distinction such as (unsigned char *) vs (char *) can be germane and should better not be completely prohibited (whether char is signed or not is platform dependent, and not all libraries have cleanly defined APIs). Same goes with platform dependent wide character, we have conversion methods producing the good bytes, but not the good types. We could eventually make an exception for char * like we did for void * (before void * was introduced, char * was the way to do it anyway)... Right now, I have no good solution for this because of the event loop.

Related

No matching function for call to pthread_create Objective-C ARC conversion

converting my project to ARC but says it can't due to the following error 'No matching function for call to pthread_create'. Here is the code it falls in, happens specifically on the line starting with pthread create. How can I fix this? It also says Candidate function not viable: no known conversion from 'NSString *' to 'void * _Nullable' for 4th argument in the sidebar underneath the error.
I've cut off the rest of the function but can provide more detail if necessary.
void World::loadWorld(std::string name)
{
if(doneLoading==0)
{
doneLoading=1;
Resources::getResources->stopMenuTune();
if(LOW_MEM_DEVICE)
{
menu->deactivate();
Resources::getResources->unloadMenuTextures();
terrain->allocateMemory();
terrain->loadTerrain(name,TRUE);
doneLoading=2;
hud->fade_out=1;
}
else
{
terrain->allocateMemory();
pthread_t foo;
pthread_create(&foo,NULL,loadWorldThread, nsstring(name));
}
}
As your error message indicates the 4th argument to pthread_create is of type void *. Under ARC you cannot simply pass an Obj-C object reference as a void * as ARC would is not able to track the reference once it is stored in a C(++) pointer variable, and therefore cannot manage the object's memory.
For situations where an Obj-C reference must be passed into the C(++) world a bridge cast can be used to inform ARC how the memory should be managed. However in your case there a better way, just pass the C++ pointer, name, without creating an NSString. If loadWorldThread expects a std::string that is the correct thing to do anyway. If it expects an NSString * then either:
modify it to take a std::string and do any required conversion to NSString * within it; or
write a small intermediate function which takes a std::string, produces an NSString * from it, and then calls loadWorldThread. Pass this new function to pthread_create.
Doing either of the above avoids the use of a bridge cast in the pthread_create call to move the Obj-C reference into the C(++) world and out of ARC control; and another bridge cast in loadWorldThread (or intermediate function as above) to move it back into the Obj-C world and into ARC control.
Addendum
Expanding on the last paragraph, as the method there seems better suited to your situation. First, it is assumed that your code:
nsstring(name)
takes a value of type std::string and returns a value of type NSString, if it does not then look up how to do this conversion.
After the above expression you have a reference to an NSString under ARC control. You cannot simply pass such a reference as a void *, you must take it out of ARC's control first and take responsibility for its memory management (but not for long as you will see). You can bridge cast your NSString * to a CFStringRef:
CFStringRef cfName = (__bridge_retain CFStringRef)nsstring(name);
You can now pass cfName, which is a reference to a heap-allocated CFString, as a void *.
Now in loadWorldThread; which should be declared to take a void *, something like void loadWorldThread(void *arg) { ... }; you need to bridge cast your CFStringRef back to NSString * and hands responsibility for its memory management back to ARC:
NSString *nsName = (__bridge_transfer NSString *)arg;
The above is a standard pattern to pass an ARC controlled reference though an anonymous reference (void *).
(Note: the above uses CFStringRef to make it clear that you are passing around a reference to a manually managed CFString, you can cast directly to void * and back again, indeed you will notice that when casting back arg was not first cast to a CFStringRef to demonstrate this.)
HTH

Why does the C compiler not warn about malloc size errors?

I made a member struct that I assigned in the ViewDidLoad of my iOS app. I used malloc to allocate space for this struct that was then used throughout my class. Like this:
self.myData = malloc(sizeof(MyData));
Except what I really did was this:
self.myData = malloc(sizeof(MyOtherStruct));
I accidentally set sizeof() in the malloc call to be a different struct (that isn't the same size). I didn't notice this mistake for a very long time because the app only rarely crashed. An update to the OS caused the crash to happen more frequently.
My question is, why can compiler's not warn about this sort of thing? Is it something compiler's don't know about or is it a design choice to allow users's to malloc whatever size they please?
"How can I find this error faster?"
There are a bunch of ways to find the error faster.
Solution #1
The static analyzer cathes this error. Press command-shift-B in Xcode. For example, take the following code:
#include <stdlib.h>
struct x { double x; };
struct y { char y; };
int main(int argc, char **argv) {
struct x *p = malloc(sizeof(struct y));
p->x = 1.0;
return 0;
}
Running the analyzer produces this error for me:
Result of 'malloc' is converted to a pointer of type 'struct x' which is incompatible with sizeof operand type 'struct y'
Solution #2
It is recommended to write the code this way instead:
self.myData = malloc(sizeof(*self.myData));
Just do it this way in the future. This is not only less error-prone, but it is easier to remember.
Solution #3
Use a language like Swift or C++ where the language's type system help you avoid this kind of error. C is less forgiving in many ways. It was invented in the early 1970s, you just kind of have to accept that if you want to use it, and these kinds of errors are a major part of the reason why C++ and Swift even exist in the first place.
Solution #4
Use a run-time memory bounds checker, like the address sanitizer. This will detect the error when memory is accessed, not when it is allocated, but it will still give you stack traces for both access and allocation (and free, if the memory has been freed). Anyone writing C these days should familiarize themselves with the address sanitizer and its friends, tsan, ubsan, etc.
Valgrind also achieves the same effect but the address sanitizer has a better user experience for common use cases.
Question as asked
The compiler only really gives you errors and warnings for type errors. This isn't a type error, it's a runtime error. There are a few "likely" runtime errors that the compiler can detect, but they are very few in number. Things like forgetting to use the return value of malloc()... e.g.,
void f(void) {
malloc(1); // warning
}
The compiler isn't much better than that.
Again, this is the impetus for newer languages like C++ and Swift, which have type systems which allow you to generate errors when you allocate things incorrectly, and this is also the impetus for static analysis (which is a tough problem).
That happens because is not ARC responsability to deal with malloc() and even free()
The ARC just handle with objects allocated like [Object alloc]
In your case, when you do self.myData = malloc(sizeof(MyOtherStruct));, that can be interpreted for example with something like this:
self.myData = malloc(N*sizeof(MyData));
//what can represents self.myData[0]..self.myData[N-1]
For the last, remember when you use sizeof(), it will tell you about the size of the type, that you are passing as a paramter, calculated in compile-time.
You can check this link for more information about object allocation
And also check Apple Documentation about Memory Alloc

ObjC ternary operator and const strings

If you have a static const string, then its usage may cause inconsistent interpretation by the compiler. For example in this case:
const NSString* kUntitled = #"Untitled";
NSString* title = kUntitled;
the compiler will complain about assigning a const pointer to a non-const one ("discards qualifiers") and probably rightly so. This can be solved by either not using const at all, or by invoking kUntitled.copy (I somehow don't like the idea of typecasting (NSString*)kUntitled)
However, if you have instead:
NSString* title = aTitle ?: kUntitled;
then the compiler doesn't complain.
My first question is, can the warning be ignored in the first example? Are there any potential dangers in assigning a const NSString to a non-const one?
Second is, why does the compiler ignore the case with the ternary operator?
Welcome to the Weird and Wonderful World of C Declarations - the stuff of quiz questions ;-)
const NSString* kUntitled = #"Untitled";
You probably haven't written what you intended here. This defines kUntitled to be a mutable pointer to a "constant" string - usually referred to as a "pointer to a constant"... However it's "constant" for a reason as despite the common "pointer to a constant" it is actually a "read-only pointer" meaning you can read but not write via the pointer, what is pointed at might well be mutable but it is not mutable via this point if so...
Confused? What the above all means is that you can later write:
kUntitled = #"oops you probably thought you couldn't assign";
As the pointer itself is mutable, it can be changed to point at other things.
What you probably intended was:
NSString * const kUntitled = #"Untitled";
which declares a constant pointer to a string - it is the pointer itself which cannot be changed so:
kUntitled = #"this will produce a compile error, can't change a constant";
If you use this version of the declaration then you won't get an error on your assignments:
NSString* title = kUntitled;
NSString* title = aTitle ?: kUntitled;
However that still leaves the question of why you didn't get an error from the second with your original declaration...
The RHS of the assignment, aTitle ?: kUntitled is actually valid, the weird world of C again. This expression is just shorthand for aTitle ? aTitle : kUntitled and the rules for this operator in C state that the second and third arguments can be of the same base pointer type, NSString * in your case, but differ in qualifiers, const in your case, and the resultant type is the base pointer type with all the qualifiers of the two operands.
In other words the result of this expression is treated as const NSString *. Which means you should get the same warning as for the first assignment.
It appears that the compiler is treating the operator as though the resultant type is the base pointer type with none or only the common qualifiers of the two operands - i.e. the opposite of the definition.
So for the second problem you may have found a compiler bug, you should report it to Apple (bug reporter.apple.com) and see what they say.
HTH
The warning is a side effect of the fact that const NSString * kUntitled is incorrect. This is a declaration of a pointer-to-readonly-NSString. Note the placement of the "read only" there -- it's referring to the string, not the pointer. But ObjC objects are never read only, never const. You may say that literal NSStrings are, of course, but that's implementation dependent, and even modifiable in some runtime environments.
Thus you can never correctly assign this object anywhere else (unless that variable also was a pointer to a const object).
The declaration that you should be using is NSString * const kUntitled -- this is "readonly-pointer-to-NSString", i.e., the pointer cannot be changed to point at another object.

Why cStringUsingEncoding: returns const char * instead of char *?

cStringUsingEncoding: returns a "const char *" despite it is returning a dynamically allocated C string(from it's documentation). So, what is the purpose of const here? We could simply modify the returned C string by casting to char *.
cStringUsingEncoding:
The returned C string is guaranteed to be valid only until either the
receiver is freed, or until the current autorelease pool is emptied,
whichever occurs first.
I think library is following the common practice of pointer-to-const; it's not expected to be modified or released.
From Objective-C runtime;
const char * object_getClassName(id obj) -- Nothing specified about the returned string.
char * method_copyArgumentType(Method method, unsigned int index) -- You must free the string with free(). (May be it's advising because of it's returning a copy.)
The common pattern is that you should not modify buffers that you don't own. const documents and (somewhat) enforces this.
As for cStringUsingEncoding:, The documentation is saying that the returned buffer is only valid as long as the NSString from which you received it, or for the duration of the current autorelease pool. This implies that you do not own the returned buffer, because you're not expected to release it.
Your last two examples from the runtime follow the same convention:
const char * object_getClassName(id obj)
Doesn't inform you that you should release the buffer, and the name doesn't contain any indication that you own the buffer. Therefore you don't free() it, and you don't modify it.
char * method_copyArgumentType(Method method, unsigned int index)
The docs explicitly tell you that you should free the buffer, and the function name contains the tell-tale copy which also implies that you own the buffer. Therefore you can modify it all you want, and must free() it.
Thing is, the result is const because
modifying it will not change the string itself, and the cString is really just meant to be a different representation of the string
it will probably return the same cString "over and over again", as long as the string doesn't change.
Other than that, declaring a result to be const even if the implementation doesn't enforce or require that is something an interface designer can do, maybe because he wants it to be treated that way. And it leaves the path open to optimize things for cases where the "const" is useful.

Why does an object variable have to be a pointer?

If I declare an NSMutableString
NSMutableString *str_value;
Why do I have to declare this as a pointer (using *)? If I don't, I get a compilation error.
Could someone explain this clearly?
Recall that Objective C is a superset of C. When you declare a variable without * in C, it is an indication that the memory for that variable is allocated either in the automatic storage if it is a local variable, as part of its outer structure if it is a member of a structure, or in the static memory if it is a static or a global. Using the typename or a structure tag without * in a parameter list of a function indicates passing by value.
The designers of the Objective C language could have taken the Java-like route, making every class instance is a pointer without the pointer syntax, but then the readers of programs in Objective C would need to know if a name represents a typedef based on a struct or an id type to answer even the most basic questions about objects of that type, such as if it is implicitly passed by pointer or by value, if it is allocated as part of the owning structure or as a heap object pointed to by a pointer inside the structure, and so on.
To avoid this kind of confusion, designers of Objective C decided to preserve the explicit pointer syntax for id objects.
A pointer means you are pointing / referencing to that class. Yes it will cause a compilation error, the reason for a pointer is mainly for memory. One data type (int or BOOL or float etc...) is only a few bytes, therefore it is not necessary to have a pointer. But NSMutableString and other Objective-C classes have a lot of properties and methods, with a lot of code. Therefore, since in your apps will have a lot of objects, which will use a lot of memory and thus slow down your app/decrease performance. Of course you should release the object once you make a pointer.