Sturla Molden skrev:

> Second, you should be flogged for calling new outside a class 
> contructor. That will give you memory or resource leaks due to C++ 
> exceptions skipping the call to delete. 
This is by the way one of the biggest differences between C and C++. In 
C you can use and place malloc and free wherever you want, as no 
exceptions can mess up your pairing of malloc and free.

When programmers "graduate" from C to C++, they think new and delete can 
be used as drop-in replacements for malloc and delete. Even most C++ 
textbooks will tell you this, only to demonstrate the author in 
incompetent.

In C++, the drop in replacement for a pair of malloc and free is like this:

   { // int *a = (int*)malloc(n);
      std::vector<int> a(n);
      // code between malloc and free goes here
   } // free(a);

The curly brackets determine the life of the std::vector<int> object, 
and has similar function to the placement of malloc and free in C.

This is not entirely true though: In C, setjmp and longjmp can mess up 
pairing of free and malloc like C++ exceptions. Programs that use setjmp 
and longjmp (like Apace) must therefore use design techniques like 
"memory pools" to safeguard against this.

This also affects use of other C resources in C++. They must only be 
allocated and deallocated in constructors and destructors. If you use 
cstdio instead of C++ iostream, it is only safe to put std::fopen in a 
constructor and std::fclose in a destructor.

If you have C code like this:

   FILE *myfile = fopen(name);
   fclose(myfile);

You must write a wrapper,

   #include <cstdio>
   class file {
       std::FILE* fid;
       file(const char *name) {fid = std::fopen(name);};
       ~file() {if (fid != NULL) std::fclose(fid);};
   };

And then you have this translation from C to C++:

   { // FILE *myfile = fopen(name);
       file myfile();
   } // fclose(myfile);


I know this is the Cython list, but it applies when we use Cython to 
generate C++ or interface with C++ as well.

C resource allocation in constructors and destructors also applies to 
Cython. We must use __cinit__ and __dealloc__ just like C++ constructors 
and destructors. Again, this is due to exceptions producing resource 
leaks if we don't do this. So for example when we use Cython to 
interface with Windows API, calls to CloseHandle() always belongs in a 
__dealloc__ method. Any other placement means it can be skipped by an 
exception, and we have a resource leak. fopen and malloc are only safe 
in __cinit__, and free and fclose are obly safe in __dealloc__.

C is very different from Cython and C++ because C has no exceptions 
(ignoring setjmp and longjmp -- don't even consider using those with 
Cython).

How many users of C++ or Cython knows or even understands this? I guess 
very few, and that is why C++ generates so many software bugs. In 
Cython, we should be aware that we have the same problem, due to direct 
access to C. So Cython can be a bug generator like C++.

Java and Python do not have this issue, as there are no direct access to 
C. (Well actually, Python using ctypes has this issue too, but that is a 
special case.) Therefore I do not expect average Python or Java 
developers to know this as well.

I think a warning in the docs is in place.

Better still, known cases of C resource allocation (stdio.fopen or 
stdlib.malloc) should be disallowed outside __cinit__. Ditto for known 
cases of C resource deallocation and __dealloc__. Code that don't match 
allocation and deallocation should also we disallowed. There should be 
some sort of tag to use in the declaration to tell the Cython compiler 
this. This is just a suggestion:

  cres void *malloc(int), void free(void*)  # cres for "C resource"
  cres void *fopen(FILE*), fclose(FILE*)
  cres HANDLE CreateEvent(LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCTSTR), 
BOOL CloseHandle(HANDLE)


Sturla
_______________________________________________
Cython-dev mailing list
[email protected]
http://codespeak.net/mailman/listinfo/cython-dev

Reply via email to