Polymorphic Function Pointers

B

Ben Pope

Hi all,

This is not something I've played around with much, but I'm designing
some factories and I want a function like this:

template<class T>
T* Creator() {
return new T;
}

So that I could store a pointer to an instance in a map along with a
string Id.

Now, the things I want to return would be polymorphic, and so I would
make use of covariant return types. The problem I ran into was
something like the following:



struct Base { virtual ~Base(){} };
struct Derived : Base {};

typedef Base* (*BaseFuncPtr)();
typedef Derived* (*DerivedFuncPtr)();

Base* CreateBase() { return new Base; }
Derived* CreateDerived() { return new Derived; }

int main()
{
// Covariant return types
Base* b(CreateDerived());

// Fine
BaseFuncPtr bfp(&CreateBase);

// Cannot convert?
BaseFuncPtr dfp(&CreateDerived);
}


Although the function pointers are to different types, the type being
returned is polymorphic and covariant. Can somebody just explain why
this doesn't work? Am I expecting too much?

Cheers for any insights,

Ben Pope
 
T

Tomás

I compiled your code with G++ and here's what I got:

g++ creator.cpp -ansi -pedantic -o creator.exe

creator.cpp: In function `int main()':
creator.cpp:19: warning: invalid conversion from `Derived*(*)()' to `Base*
(*)()


It compiled without errors but gave a warning.


Should it give a warning, should it give an error... seems like a quote from
the Standard is in order.


-Tomás
 
V

Victor Bazarov

Ben said:
This is not something I've played around with much, but I'm designing
some factories and I want a function like this:

template<class T>
T* Creator() {
return new T;
}

So that I could store a pointer to an instance in a map along with a
string Id.

Now, the things I want to return would be polymorphic, and so I would
make use of covariant return types. The problem I ran into was
something like the following:



struct Base { virtual ~Base(){} };
struct Derived : Base {};

typedef Base* (*BaseFuncPtr)();
typedef Derived* (*DerivedFuncPtr)();

Base* CreateBase() { return new Base; }
Derived* CreateDerived() { return new Derived; }

int main()
{
// Covariant return types
Base* b(CreateDerived());

// Fine
BaseFuncPtr bfp(&CreateBase);

// Cannot convert?
BaseFuncPtr dfp(&CreateDerived);
}


Although the function pointers are to different types, the type being
returned is polymorphic and covariant. Can somebody just explain why
this doesn't work?

WHAT doesn't work? Your attempt to declare a pointer to a function that
takes no args and returns A and initialise it with an address of another
function that takes no args and returns B? They are of different types.
Unrelated.

B func(); // 'func' is a function that takes no args and returns B
A (*pf)() = func; // 'pf': ptr to a func, no args, returns A
// *cannot* be initialised with _anything_ except
// another func that takes no args, returns A

Covariance applies _only_ to virtual functions, and not pointers to
functions.
> Am I expecting too much?

Absolutely.

V
 
V

Victor Bazarov

Tomás said:
I compiled your code with G++ and here's what I got:

g++ creator.cpp -ansi -pedantic -o creator.exe

creator.cpp: In function `int main()':
creator.cpp:19: warning: invalid conversion from `Derived*(*)()' to `Base*
(*)()


It compiled without errors but gave a warning.


Should it give a warning, should it give an error... seems like a quote from
the Standard is in order.

From 4.3: "An lvalue of function type T can be converted to an rvalue
of type “pointer to T.” The result is a pointer to the function."

Since return value type is part of the function type, if functions have
different return value types, their respective types T1 and T2 are
different, hence a function of type T1 cannot be converted to a pointer
to [function type] T2.

V
 
B

Ben Pope

Victor said:
Covariance applies _only_ to virtual functions, and not pointers to
functions.

Yes, I have just realised that this is the difference between
overloading and overriding.
Absolutely.

Of course.

Thanks Victor.

Ben Pope
 
A

Alf P. Steinbach

* Victor Bazarov:
Ben said:
struct Base { virtual ~Base(){} };
struct Derived : Base {};

typedef Base* (*BaseFuncPtr)();
typedef Derived* (*DerivedFuncPtr)();

Base* CreateBase() { return new Base; }
Derived* CreateDerived() { return new Derived; }

int main()
{
// Covariant return types
Base* b(CreateDerived());

// Fine
BaseFuncPtr bfp(&CreateBase);

// Cannot convert?
BaseFuncPtr dfp(&CreateDerived);
}
[snip]
Am I expecting too much?

Absolutely.

Unfortunately, yes. One would expect standard C++ to support this, and
there is no technical reason I'm aware why it couldn't. But it doesn't.

The support for covariance in C++ is half-baked, and there is no support
for contravariance -- we lack data flow direction designators such as
C# and Ada 'out'.

Although there is the general problem of argument forwarding, for a
given function of known signature something like the following can be a
practical solution:

struct Base { virtual ~Base(){} };
struct Derived : Base {};

typedef Base* (*BaseFuncPtr)();
typedef Derived* (*DerivedFuncPtr)();

Base* CreateBase() { return new Base; }
Derived* CreateDerived() { return new Derived; }

template< typename R, R (*foo)() >
inline Base* asBaseFunction() { return foo(); }

int main()
{
// Covariant return types
Base* b(CreateDerived());

// Fine
BaseFuncPtr bfp(&CreateBase);

// Cannot convert?
BaseFuncPtr dfp( &asBaseFunction<Derived*, &CreateDerived> );
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top