An example of virtual inheritance in C using undefined behavior?

In a series of articles , Dan Sacks introduces a possible implementation of virtual functions in C. Relying on static type checking, this is a different approach from A.-T. Schreiner with pointers void *

and dynamic type checking.

Below is an example without using vptr

and vtable

versioning Saks (for simplicity, function pointers are members of struct Base

and struct Derived

).

#include <stdlib.h>
#include <stdio.h>

typedef struct Base Base;

// Base "class"
struct Base {
    int (*get_param)(Base const *self);
};

inline int Base_get_param(Base const *self)
{
    return  self->get_param(self);
}

typedef struct Derived Derived;

// Derived "class"
struct Derived {
    int (*get_param)(Derived const *self);
    int param;
};

Derived * Derived_new(int param)
{
    Derived *self = malloc(sizeof(Derived));
    if (!self) abort();
    self->get_param = Derived_get_param;
    self->param = param;
    return self;
}

void Derived_delete(Derived *self)
{
    free(self);
}

inline int Derived_get_param(Derived const *self)
{
    return self->param;
}


int main()
{
    Derived *d = Derived_new(5);
    printf("%d\n", Derived_get_param(d));
    printf("%d\n", Base_get_param((Base *) d));  // <== undefined behavior?
    Derived_delete(d);
    return 0;
}

      

The gist is a function call (and cast) Base_get_param((Base *) d)

. Does this mean that the function pointer int (*get_param)(Derived const *self)

gets "implicitly castable" on int (*get_param)(Base const *self)

? Am I using undefined behavior here (as per C99 and C11 standards) due to incompatible types?

I am getting correct output with both GCC 4.8 and clang 3.4. Is there a situation where the above implementation might be broken?

Below is a detailed answer about firing function pointers and compatible types, but I'm not sure about this case.

+3


source to share


1 answer


This program does indeed cause undefined behavior , you have a violation of the strict alias rules here:

printf("%d\n", Base_get_param((Base *) d));
                               ^^^^^^^^^

      

Strict alias rules make the behavior undefined for accessing an object using a pointer of a different type, although there is an exception for char * that we are allowed to use for an alias without causing undefined behavior.

Basically the compiler can optimize on the assumption that pointers of different types do not point to the same memory. When you invoke undefined behavior, the result of your program becomes unpredictable.



Practically in other cases, for example in this question, I could not get the compiler to do the wrong thing, but in other more complex cases, things can go wrong. See gcc, strict anti-aliasing and horrors for cases where this caused problems. The article Type Punning, Strict Aliasing, and Optimization contains the following code:

#include <stdio.h>

void check (int *h, long *k)
{
  *h = 5;
  *k = 6;
  printf("%d\n", *h);
}

int main (void)
{
  long k;
  check((int *)&k, &k);
  return 0;
}

      

which violates strict alias and produce different outputs using -O1

Vs -O2

.

Strong anti-aliasing for gcc

can be disabled with -fno-strict-aliasing

, and perhaps the author makes such an assumption, although I could not find it in the article. This disables some optimizations, so it is not a free flag.

+1


source







All Articles