-1

TL;DR

Is it possible, for a typedef function of type foo to point to another type bar and so on? In my experience yes, but how is this?

Preface

Inspired by this question, I tried to play around with typedef function pointers. Something really interesting came out. I have heard that it's not possible to cast function type with typedef.

This is definitely true in cases such as:

typedef existing type sometypedef(/*parameters*/);
   sometypedef testfunc{ 
                  ^
             /*Error*/
   };

This not only because it is impossible, but also completely useless because of template, but not always...

When the magic comes in

Pointers can be very handy and lead to great confusion, but really helpful when used right. Consider the following example:

We have a function foo, that we want to refer as a pointer. No problem's as long as no complicated parameters.

But now want to refer both foo, and other variable's inside function bar's parameters. Now we're stuck, because

void* foo(/*parameters*/);
void bar(foo* f)

or anything like that is not allowed. We simply cannot refer to foo. But don't give up yet! Because here typedef comes useful. We can make something like

typedef void (*newfunctype)(/*pointer parameters*/);
newfunctype* foo();
newfunctype bar(newfunctype* nf);

and now it works!!

...But this was not the actual question

Because the real question is how? How, for example the following piece of code works?:

typedef void (*sometype)();
typedef sometype(*anothertype)(sometype);
typedef anothertype(*yetanothertype)(sometype);
yetanothertype d(anothertype);
/*etc, other initializations*/

We can even make functions with that stuff, like

anothertype anotherfunc(anothertype* an, yetanothertype* sn);

and they... work like charm?

How long can these pointers to new typedef types be expanded? Forever? Is this really standard c++ (or c?) syntax, or does it lead to undefined behavior?

Community
  • 1
  • 1
Tatu
  • 109
  • 1
  • 4
  • 1
    `yetanothertype* f = new yetanothertype();` I don't think it's a good idea. – Guillaume Racicot Dec 25 '16 at 14:59
  • @GuillaumeRacicot Why so? Isn't initializing pointers always a good idea? – Tatu Dec 25 '16 at 15:24
  • You are dynamically allocating a function pointer. The free store is read-write memory, but not executable. Any attempts to use it will cause a segmentation fault. – Guillaume Racicot Dec 25 '16 at 15:26
  • @GuillaumeRacicot EDIT: removed it, because it was not really part of the main question – Tatu Dec 25 '16 at 15:31
  • @GuillaumeRacicot The fact that the pointer itself is in non-executable memory doesn't mean that it cannot point to a perfectly fine function, which would be callable through that pointer with no problems (think `std::vector`). Of course, OP's code was value-initializing the pointer, and calling through *that* value would, of course, not lead to a happy result. OP: From the way you worded the question it's not clear to me if you know that the declaration of `d` also declares a function (there's no initialization there). Just wanted to make sure you're aware of that. – bogdan Dec 25 '16 at 16:04
  • @bogdan ahh yes, declaring a pointer to pointer to function has no problem indeed, you are right. – Guillaume Racicot Dec 25 '16 at 16:16

1 Answers1

1

Why shouldn't it work? What's wrong with it working? Nothing really. But anyways...

typedefs only are aliases to other type. Let's start with an example :

using MyType = int; // same thing as typedef int MyType

MyType a;
MyType b;

To the eyes of the compiler, the code above becomes this:

int a;
int b;

So now you ask how your examples works. Well, let's expand the typedefs into the real types:

typedef int* MyType1;
typedef MyType1* MyType2;
typedef MyType2* MyType3;

MyType3 a;

We'll start by using a cleaner syntax :

using MyType1 = int*;
using MyType2 = MyType1*;
using MyType3 = MyType2*;

MyType3 a;

Now that the name is out of the type, it's easy to see how the compiler expands types. Let's do it manually :

// using MyType1 = int*;
using MyType2 = int**;
using MyType3 = MyType2*;

MyType3 a;

Then :

// using MyType1 = int*;
// using MyType2 = int**;
using MyType3 = int***;

MyType3 a;

Then :

// using MyType1 = int*;
// using MyType2 = int**;
// using MyType3 = int***;

int*** a;

But it's really that. Typedefs are alias to types, they are not variables nor any runtime entity. It's just the compiler writing out complex types for you by giving it a pretty name.

So no, you can't have undefined behavior by making too much typedefs. You can have undefined behavior is you misuse the types however, but that's another thing.

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141