11

First of all type-safe means that anything that a compiler can catch straight away if done incorrectly.

Now, I heard function pointers are not type safe however whenever I tried to use them incorrectly the compiler did report errors for me. So, how is it type unsafe ?

E.g This is a function prototype that takes in a function pointer

void SortElements(void* MyArray, unsigned int iNumofElems,size_t size, int(*compare_funct)(void* First,void* SecondElem))

I have defined few functions to pass to it as:

int MySortAsc(void* First, void* Second);
void MyFunct2();
void MyFunct3(void* First);

The code only compiles for:

SortElements(MyArray, 10, sizeof(DataType), &MySortAsc); //Compiles
SortElements(MyArray, 10, sizeof(DataType), &MyFunct2);  //Fails

Any idea how can I mis-use function pointers here ?

Is it because of this:

void (*functionPointer)();
...
int integer = 0xFFFFFFFF;     
functionPointer = (void(*)())integer;      
functionPointer();

Answer: What I got to see is that function pointers in C++ are type safe. Ofcourse, they can be used in an unsafe manner by casting it incorectly but that does not make them a reason to be called as type unsafe. .NET delegates are strongly typed as well and to me it looks like both are type safe.

Frank Q.
  • 6,001
  • 11
  • 47
  • 62
  • 3
    Where did you see it described as unsafe? Seeing the source will help understand the intention of the writer. – selalerer Apr 19 '12 at 22:40
  • 1
    "Typesafe" means that the correctness of a piece of code follows from its static typing alone, and does not depend on particular *values* of variables. For example, `printf` is not typesafe because its correctness depends on the *value* of the format string as well as its argument types. – Kerrek SB Apr 19 '12 at 22:41
  • Why is it told then that .NET delegates are type safe function pointers compared to normal function pionters ? – Frank Q. Apr 19 '12 at 22:43
  • 1
    @KerrekSB: That is not the case here. Printf is unsafe because of the ellipsis that is a catch all that does not maintain the types (i.e. the caller and the callee must agree on how to interpret the types. A function pointer is as safe as a function from that point of view, even if in this particular case it is unsafe for other reason: the use of `void*` breaks type safety. – David Rodríguez - dribeas Apr 20 '12 at 00:27
  • @DavidRodríguez-dribeas I can always do casting in C++ and break the type safety as I showed in the above example where if I invoke functionPointer() program will crash. As far I know in .NET delegates you cannot do that. Does that make function pointers unsafe in this sense ? – Frank Q. Apr 20 '12 at 06:33
  • @FrankQ. You can always break type safety *forcibly*, the language is designed to allow you to shoot yourself in the foot. But the point is that you have to take the safety out in one case (cast), while in the other the safeties are already off from the beginning. – David Rodríguez - dribeas Apr 20 '12 at 12:18

4 Answers4

10

So, how is it type unsafe ?

void SortElements(void* MyArray,              // what type is pointed here?
                  unsigned int N,             // Are there really N elements?
                  size_t size,                // Is the size correct?
                  int(*cmp)(void*,void*));    // Is this the correct function?

The code that you present is type-unsafe, not because of the function pointer but rather because of the use of void* in both the SortElements signature and the signature of the function pointer.

The reason why this is unsafe is because the caller has the whole responsibility of passing the right arguments, and the compiler cannot ensure that the pointer MyArray points to a contiguous memory region that holds iNumofElems each of which has the size offered in the interface. If the programmer makes a mistake, the compiler will not be able to help there, if a maintainer modifies the type stored in the array (size changes) or the number of elements, the compiler will not be able to detect it and tell you that you need to update the call to SortElements. Finally, because the function pointer that is passed also uses void*, the signature of a comparator that compares apples and pears is exactly the same, and the compiler cannot help if you pass the incorrect function pointer.

struct Apple {
   int weight;
};
struct Pear {
   double weight;
};
int compare_pears( void * pear1, void * pear2 ) {
   return static_cast<Pear*>(pear1)->weight - static_cast<Pear*>(pear2)->weight;
}
int main() {
   Apple apples[10];
   SortElements( apples, 20, sizeof(Pear), compare_pears );
}

While the compiler is able to verify that the signature of the function pointer matches the signature that the function needs, the function pointer itself is unsafe, and allows you to pass a comparator for basically anything.

Compare that with this other alternative:

template <typename T, std::size_t N>
void SortElements( T (&array)[N], int (*cmp)( T const &, T const & ) );

Here the compiler will infer the type of the elements T and the size of the array N from the call. There is no need to pass the size of T, as the compiler knows it. The comparator function passed to this version of SortElements is strongly typed: it takes two constant references to the type of the element stored in the array and returns an int. If we tried this in the previous program:

int compare_pears( Pear const & lhs, Pear const & rhs );
int compare_apples( Apple const & l, Apple const & r );
Apple array[10];
//SortElements( array, compare_pears );   // Error!!!!
SortElements( array, compare_apples );    // Good!

You cannot mistake the size of the array or the size of the elements, if someone changes the type Apple, the compiler will pick it up, if the size of the array changes, the compiler will pick it up. You cannot mistake the comparator that is passed to the function as the compiler will also pick it up. Now the program is type safe, even if it uses function pointers (that might have an impact in performance as they inhibit inlining, which is why std::sort is usually faster than qsort)

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • This is great explanation. So, basically function pointers by nature are type safe and it depends on how they are being used that can make them do unsafe things, as in the case of my example ? – Frank Q. Apr 20 '12 at 06:35
  • 5
    No, I would not say that. Function pointers are type safe. Any code, not limited to function pointers can be type unsafe when you start casting. It's the casting that makes things unsafe. Function pointers have absolutely no special properties in relation to type safety. – David Heffernan Apr 20 '12 at 07:25
  • 3
    @FrankQ. The type safety issues in your code come from the design of the interface to the functions, that removes type information, not from the fact that you are passing function pointers. Removal of the proper types by means of the implicit cast to `void*` implies that the burden of managing what type each bit in memory is falls in the hands of the programmer (casts) without any help from the compiler to verify correctness. Also note, not mentioned before, that the function can only manage the data as bits, and that means that you will cause undefined behavior for anything other than PODs. – David Rodríguez - dribeas Apr 20 '12 at 12:22
  • (i.e. the `SortElements` can move bits, but not *objects*, it will not be able to apply user defined copies/moves) – David Rodríguez - dribeas Apr 20 '12 at 12:23
6

Function pointers are type safe. However, many environments force upon the programmer the need to recast them. An incorrect casting could cause significant problems.

wallyk
  • 56,922
  • 16
  • 83
  • 148
5

Function pointers are in fact type checked and are type safe.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
1

Function pointers are strongly discouraged in nesC (a dialect of C used in TinyOs), for the reason that they hinder optimisation. Here static code analysis (or rather the lack of its applicability) is a bigger concern than type-safety, but I'm not sure whether these issues could be confused.

Another issue might be the use of function pointers as event handlers. When using a general event scheduler, you may want to abstract from the proper type, which would mean that you could have the idea to store function pointers as void* just for the sake of modularity. This would be a prominent example of type-unsafe usage of function pointers instead of type-safe dynamic binding usage.

bitmask
  • 32,434
  • 14
  • 99
  • 159