How to do "derived" type extensions?

K

Kira Yamato

Suppose class B is a subtype of class A, i.e.,
class B : public A
{
...
};
Then, in C++, a pointer to type B can be treated as a pointer to type
A. So, I can do the following:
B *b;
A *a = b;
This morphism of types is what makes C++ so powerful.

But now how do I do the following kind of morphism? Suppose I have functions
void foo(const B &);
and
void bar(const A &);
I like to be able to declare a function pointer
void (*fp)(const B&);
and make "polymorphic" assignments like
p = foo; // this is ok in C++.
p = bar; // semantically this make sense, but C++ syntax won't
allow this!

I know the last line of code is not valid C++ because the function
signatures are required to be the same. However, on a semantic level,
I should be allowed to substitute calls to foo with calls to bar. This
is what I mean:
B b;
foo(b); // I can substitute the function foo in this line to
bar(b); // the function bar in this line.
So, at least on a semantic level I should be able to declare a
"polymorphic" function pointer that can points to both foo and bar
types.
void (*fp)(const B&) = bar;
fp(b); // here, b is passed as type A to fp since bar accepts
type A argument.

But I can't, because C++ doesn't work this way.

Now, I know the mantra "When in Rome, do as Romans." So, if this is
not a feature of C++, then I should rework a solution that is in C++.
But I'm just curious if anyone has a "good hack" to simulate this type
of polymorphism in C++?
 
D

dizzy

Kira said:
Suppose class B is a subtype of class A, i.e.,
class B : public A
{
...
};
Then, in C++, a pointer to type B can be treated as a pointer to type
A. So, I can do the following:
B *b;
A *a = b;
This morphism of types is what makes C++ so powerful.

But now how do I do the following kind of morphism? Suppose I have
functions
void foo(const B &);
and
void bar(const A &);
I like to be able to declare a function pointer
void (*fp)(const B&);
and make "polymorphic" assignments like
p = foo; // this is ok in C++.
p = bar; // semantically this make sense, but C++ syntax won't
allow this!
Yes.


I know the last line of code is not valid C++ because the function
signatures are required to be the same. However, on a semantic level,
I should be allowed to substitute calls to foo with calls to bar. This
is what I mean:
B b;
foo(b); // I can substitute the function foo in this line to
bar(b); // the function bar in this line.
So, at least on a semantic level I should be able to declare a
"polymorphic" function pointer that can points to both foo and bar
types.
void (*fp)(const B&) = bar;
fp(b); // here, b is passed as type A to fp since bar accepts
type A argument.

But I can't, because C++ doesn't work this way.

Right. You are using pointer to functions which are low level and have
limited semantics vs what you want. You should instead use something like
boost::function<> where you specify the signature of the function as per
valid call not to match identically with the called function (that is, as
long as the called function returned argument is convertible to your
specified returned type for boost::function and as long as the input
arguments are convertible from your specified ones to the ones the function
actually takes then it compiles).

This is basically some template machinery (you store the original function
pointer as a type independent of the type you are suposed to call but when
calling the proper conversions happen).
Now, I know the mantra "When in Rome, do as Romans." So, if this is
not a feature of C++, then I should rework a solution that is in C++.
But I'm just curious if anyone has a "good hack" to simulate this type
of polymorphism in C++?

No need for a hack, but a nice C++ compile time typesafe solution, something
like boost::function. Here example of what you want:

#include <iostream>
#include <typeinfo>
#include <boost/function.hpp>

struct A {};
struct B: A {};

bool func(A const& arg)
{
std::cout << "called: " << typeid(arg).name() << std::endl;
}

int main()
{
boost::function<int (B const&)> f = func;
f(B());
}
 
L

LR

Kira said:
Suppose class B is a subtype of class A, i.e.,
class B : public A
{
...
};
But now how do I do the following kind of morphism? Suppose I have
functions
void foo(const B &);
and
void bar(const A &);
I like to be able to declare a function pointer
void (*fp)(const B&);
and make "polymorphic" assignments like
p = foo; // this is ok in C++.
p = bar; // semantically this make sense, but C++ syntax won't
allow this!


Would it be acceptable to create a function,
void bar(const B &);
and call bar(const A &) from that function?

LR
 
S

Salt_Peter

Suppose class B is a subtype of class A, i.e.,
class B : public A
{
...
};
Then, in C++, a pointer to type B can be treated as a pointer to type
A. So, I can do the following:
B *b;
A *a = b;
This morphism of types is what makes C++ so powerful.

What perhaps needs to be noted is that an instance of type B is_an
instance of type A.
Its not correct to say that a pointer to a B 'can be treated' as a
ponter to an A.

Now, ask yourself what makes polymorphism work?
But now how do I do the following kind of morphism? Suppose I have functions
void foo(const B &);
and
void bar(const A &);
I like to be able to declare a function pointer
void (*fp)(const B&);
and make "polymorphic" assignments like
p = foo; // this is ok in C++.
p = bar; // semantically this make sense, but C++ syntax won't
allow this!

Of course it doesn't. If it did we'ld all play russian roulette.
I know the last line of code is not valid C++ because the function
signatures are required to be the same.
However, on a semantic level,
I should be allowed to substitute calls to foo with calls to bar. This
is what I mean:
B b;
foo(b); // I can substitute the function foo in this line to
bar(b); // the function bar in this line.

Yes, absolutely, its not 'should' - you definitely can and do.
So, at least on a semantic level I should be able to declare a
"polymorphic" function pointer that can points to both foo and bar
types.
void (*fp)(const B&) = bar;
fp(b); // here, b is passed as type A to fp since bar accepts
type A argument.

But I can't, because C++ doesn't work this way.

Thankfully so. You are expecting a pointer to a function with a given
signature to accept a function signature of another type. How do you
expect a call for one signature to induce a call to anything viable?
Are you seriously suggesting that a program should silently construct
an instance of B in order to accept a reference to a base class? Thats
ludicrous and insane.

Basicly, the solution is to declare
void (*fp)(const A&);
the parameter of function at the pointer can be any_kind_of_A.

#include <iostream>
#include <typeinfo>

class A
{
public:
virtual ~A() { } // <---- polymorphism
};

class B : public A { };
class C : public B { };

void bar(const A& a)
{
std::cout << typeid(a).name() << std::endl;
}

void foo(const B& b)
{
std::cout << typeid(b).name() << std::endl;
}

int main()
{
void (*fp)(const A&);

fp = &bar;
A a;
fp(a);
B b;
fp(b); // <- b is passed as a B, see output below
C c;
fp(c);

void (*fp_b)(const B&);
fp_b = &foo;
fp_b(b);
// fp_b(a); // error:
// invalid initialization of reference
// of type 'const B&' from expression
// of type A
}

/*
A
B // <- whats this !
C
B
*/

Read the error again. Do you understand the implications if the
program was allowed to generate an instance of B silently?
Now, I know the mantra "When in Rome, do as Romans." So, if this is
not a feature of C++, then I should rework a solution that is in C++.
But I'm just curious if anyone has a "good hack" to simulate this type
of polymorphism in C++?

Sorry, hacking is not programming.
 
K

Kira Yamato

What perhaps needs to be noted is that an instance of type B is_an
instance of type A.
Its not correct to say that a pointer to a B 'can be treated' as a
ponter to an A.

I never even define the term "can be treated". So, how can you say it
is incorrect?
[...]
But now how do I do the following kind of morphism? Suppose I have functions
void foo(const B &);
and
void bar(const A &);
I like to be able to declare a function pointer
void (*fp)(const B&);
and make "polymorphic" assignments like
p = foo; // this is ok in C++.
p = bar; // semantically this make sense, but C++ syntax won't
allow this!

Of course it doesn't. If it did we'ld all play russian roulette.
Huh?
I know the last line of code is not valid C++ because the function
signatures are required to be the same.
However, on a semantic level,
I should be allowed to substitute calls to foo with calls to bar. This
is what I mean:
B b;
foo(b); // I can substitute the function foo in this line to
bar(b); // the function bar in this line.

Yes, absolutely, its not 'should' - you definitely can and do.
So, at least on a semantic level I should be able to declare a
"polymorphic" function pointer that can points to both foo and bar
types.
void (*fp)(const B&) = bar;
fp(b); // here, b is passed as type A to fp since bar accepts
type A argument.

But I can't, because C++ doesn't work this way.

Thankfully so. You are expecting a pointer to a function with a given
signature to accept a function signature of another type. How do you
expect a call for one signature to induce a call to anything viable?

But it is not just any arbitrary type. B is a subtype of A. This is
important. It makes the calls compatible in the following sense (in my
comment after the following paragraph).
Are you seriously suggesting that a program should silently construct
an instance of B in order to accept a reference to a base class?

No. There is no need to construct an instance of B. Suppose we forced
void (*fp)(const B &) = (void (*)(const B &)) bar;
where I remind you that
void bar(const A &);
Then the call
fp(B());
is equivalent to
bar(B());
which is perfectly ok because B is a subtype of A.
Thats
ludicrous and insane.

Maybe it's just because you didn't understand what I was trying to do.
I'm saying this because your program below is not what I was trying to
do.
Basicly, the solution is to declare
void (*fp)(const A&);
the parameter of function at the pointer can be any_kind_of_A.

#include <iostream>
#include <typeinfo>

class A
{
public:
virtual ~A() { } // <---- polymorphism
};

class B : public A { };
class C : public B { };

void bar(const A& a)
{
std::cout << typeid(a).name() << std::endl;
}

void foo(const B& b)
{
std::cout << typeid(b).name() << std::endl;
}

int main()
{
void (*fp)(const A&);

fp = &bar;
A a;
fp(a);
B b;
fp(b); // <- b is passed as a B, see output below
C c;
fp(c);

void (*fp_b)(const B&);
fp_b = &foo;
fp_b(b);
// fp_b(a); // error:
// invalid initialization of reference
// of type 'const B&' from expression
// of type A
}

/*
A
B // <- whats this !
C
B
*/

Your program is not what I'm trying to accomplish. Your polymorphism
is just on the argument of the function. In fact, why did you even
declare pointers to functions fp and fp_b at all? You could've just
used bar and foo directly.

What I was trying to do is to declare a single pointer to functions
that do not have exact signature but are compatible in the sense I
described above, which I will describe to you again:

If I declare a pointer which points to a function that expects type B,
then it is semantically accepted to use in place of this pointer
another function that actually expects only type A. This is because
when you invoke through this function pointer, the argument of type B
can be passed as an argument of type A. Here we assume B is a subtype
of A.
[...]
Sorry, hacking is not programming.

Just curious... What you define hacking as?
 
K

Kira Yamato

Right. You are using pointer to functions which are low level and have
limited semantics vs what you want. You should instead use something like
boost::function<> where you specify the signature of the function as per
valid call not to match identically with the called function (that is, as
long as the called function returned argument is convertible to your
specified returned type for boost::function and as long as the input
arguments are convertible from your specified ones to the ones the function
actually takes then it compiles).

This is basically some template machinery (you store the original function
pointer as a type independent of the type you are suposed to call but when
calling the proper conversions happen).


No need for a hack, but a nice C++ compile time typesafe solution, something
like boost::function. Here example of what you want:

#include <iostream>
#include <typeinfo>
#include <boost/function.hpp>

struct A {};
struct B: A {};

bool func(A const& arg)
{
std::cout << "called: " << typeid(arg).name() << std::endl;
}

int main()
{
boost::function<int (B const&)> f = func;
f(B());
}

This is neat! I didn't look deeper into it yet; but assuming this does
indeed do compile-time type-checking, then this is some cool stuff.

Can't wait to dig into boost to see how it's done.

Thanks.
 
S

Salt_Peter

You mean
void foo(const B &);

he's referring to the signature.
Why not?

void foo(const B &b)
{
void bar(const A &);
bar(b);

}

because you'ld call foo(const A& a) instead. or better:

#include <iostream>
#include <typeinfo>

class A { public: virtual ~A() {} };
class B : public A { };

template< typename T >
void func(const T& t )
{
std::cout << typeid(t).name() << std::endl;
}

int main()
{
A a;
func(a);
B b;
func(b);

void (*fp)(const A&);
fp = &func<A>;
fp(a);
fp(b);
}
 
S

Salt_Peter

What perhaps needs to be noted is that an instance of type B is_an
instance of type A.
Its not correct to say that a pointer to a B 'can be treated' as a
ponter to an A.

I never even define the term "can be treated". So, how can you say it
is incorrect?
[...]
But now how do I do the following kind of morphism? Suppose I have functions
void foo(const B &);
and
void bar(const A &);
I like to be able to declare a function pointer
void (*fp)(const B&);
and make "polymorphic" assignments like
p = foo; // this is ok in C++.
p = bar; // semantically this make sense, but C++ syntax won't
allow this!
Of course it doesn't. If it did we'ld all play russian roulette.
Huh?
Yes, absolutely, its not 'should' - you definitely can and do.
Thankfully so. You are expecting a pointer to a function with a given
signature to accept a function signature of another type. How do you
expect a call for one signature to induce a call to anything viable?

But it is not just any arbitrary type. B is a subtype of A. This is
important. It makes the calls compatible in the following sense (in my
comment after the following paragraph).
Are you seriously suggesting that a program should silently construct
an instance of B in order to accept a reference to a base class?

No. There is no need to construct an instance of B. Suppose we forced
void (*fp)(const B &) = (void (*)(const B &)) bar;
where I remind you that
void bar(const A &);
Then the call
fp(B());
is equivalent to
bar(B());
which is perfectly ok because B is a subtype of A.
Thats
ludicrous and insane.

Maybe it's just because you didn't understand what I was trying to do.
I'm saying this because your program below is not what I was trying to
do.




Basicly, the solution is to declare
void (*fp)(const A&);
the parameter of function at the pointer can be any_kind_of_A.
#include <iostream>
#include <typeinfo>
class A
{
public:
virtual ~A() { } // <---- polymorphism
};
class B : public A { };
class C : public B { };
void bar(const A& a)
{
std::cout << typeid(a).name() << std::endl;
}
void foo(const B& b)
{
std::cout << typeid(b).name() << std::endl;
}
int main()
{
void (*fp)(const A&);
fp = &bar;
A a;
fp(a);
B b;
fp(b); // <- b is passed as a B, see output below
C c;
fp(c);
void (*fp_b)(const B&);
fp_b = &foo;
fp_b(b);
// fp_b(a); // error:
// invalid initialization of reference
// of type 'const B&' from expression
// of type A
}
/*
A
B // <- whats this !
C
B
*/

Your program is not what I'm trying to accomplish. Your polymorphism
is just on the argument of the function. In fact, why did you even
declare pointers to functions fp and fp_b at all? You could've just
used bar and foo directly.

What I was trying to do is to declare a single pointer to functions
that do not have exact signature but are compatible in the sense I
described above, which I will describe to you again:

If I declare a pointer which points to a function that expects type B,
then it is semantically accepted to use in place of this pointer
another function that actually expects only type A. This is because
when you invoke through this function pointer, the argument of type B
can be passed as an argument of type A. Here we assume B is a subtype
of A.
[...]
Sorry, hacking is not programming.

Just curious... What you define hacking as?

I'm sorry if i upset you, was not my intention. honest.
 
S

Salt_Peter

What perhaps needs to be noted is that an instance of type B is_an
instance of type A.
Its not correct to say that a pointer to a B 'can be treated' as a
ponter to an A.

I never even define the term "can be treated". So, how can you say it
is incorrect?
[...]
But now how do I do the following kind of morphism? Suppose I have functions
void foo(const B &);
and
void bar(const A &);
I like to be able to declare a function pointer
void (*fp)(const B&);
and make "polymorphic" assignments like
p = foo; // this is ok in C++.
p = bar; // semantically this make sense, but C++ syntax won't
allow this!
Of course it doesn't. If it did we'ld all play russian roulette.
Huh?
Yes, absolutely, its not 'should' - you definitely can and do.
Thankfully so. You are expecting a pointer to a function with a given
signature to accept a function signature of another type. How do you
expect a call for one signature to induce a call to anything viable?

But it is not just any arbitrary type. B is a subtype of A. This is
important. It makes the calls compatible in the following sense (in my
comment after the following paragraph).
Are you seriously suggesting that a program should silently construct
an instance of B in order to accept a reference to a base class?

No. There is no need to construct an instance of B. Suppose we forced
void (*fp)(const B &) = (void (*)(const B &)) bar;
where I remind you that
void bar(const A &);
Then the call
fp(B());
is equivalent to
bar(B());
which is perfectly ok because B is a subtype of A.
Thats
ludicrous and insane.

Maybe it's just because you didn't understand what I was trying to do.
I'm saying this because your program below is not what I was trying to
do.




Basicly, the solution is to declare
void (*fp)(const A&);
the parameter of function at the pointer can be any_kind_of_A.
#include <iostream>
#include <typeinfo>
class A
{
public:
virtual ~A() { } // <---- polymorphism
};
class B : public A { };
class C : public B { };
void bar(const A& a)
{
std::cout << typeid(a).name() << std::endl;
}
void foo(const B& b)
{
std::cout << typeid(b).name() << std::endl;
}
int main()
{
void (*fp)(const A&);
fp = &bar;
A a;
fp(a);
B b;
fp(b); // <- b is passed as a B, see output below
C c;
fp(c);
void (*fp_b)(const B&);
fp_b = &foo;
fp_b(b);
// fp_b(a); // error:
// invalid initialization of reference
// of type 'const B&' from expression
// of type A
}
/*
A
B // <- whats this !
C
B
*/

Your program is not what I'm trying to accomplish. Your polymorphism
is just on the argument of the function. In fact, why did you even
declare pointers to functions fp and fp_b at all? You could've just
used bar and foo directly.

What I was trying to do is to declare a single pointer to functions
that do not have exact signature but are compatible in the sense I
described above, which I will describe to you again:

If I declare a pointer which points to a function that expects type B,
then it is semantically accepted to use in place of this pointer
another function that actually expects only type A. This is because
when you invoke through this function pointer, the argument of type B
can be passed as an argument of type A. Here we assume B is a subtype
of A.
[...]
Sorry, hacking is not programming.

Just curious... What you define hacking as?


Not meant to upset you. Honest.
take a look at the following, perhaps you'll get my drift:

#include <iostream>
#include <typeinfo>

class A
{
public:
virtual ~A() {}
virtual void foo() const { std::cout << "A::foo()\n"; }
virtual void bar() const { std::cout << "A::bar()\n"; }
};

class B : public A
{
public:
void foo() const { std::cout << "B::foo()\n"; }
void bar() const { std::cout << "B::bar()\n"; }
};

template< typename T >
void func(const T& t )
{
std::cout << typeid(t).name() << std::endl;
t.foo();
}

int main()
{
A a;
func(a);
B b;
func(b);

void (*fp)(const A&);
fp = &func<A>;
fp(a);
fp(b);
}

/*
A
A::foo()
B
B::foo()
A
A::foo()
B
B::foo()
*/
 
L

LR

Kira said:
You mean
void foo(const B &);

No, that's not what I meant. I don't think that I made myself clear, so
I'll try again.

Untested, and not complete...

This is what I think you started with.
class A {
};
class B : public A {
};
void foo(const B &) {
}
void bar(const A &) {
}

now I suggest adding this function
void bar(const B &b) {
::bar( reinterpret_cast<const A&>(b) );
}

so you can
void (*fp)(const B&);
and
B b;
fp = bar; // will assign the address of void bar(const B &) to fp
fp(b);
fp = foo;
fp(b);

Or else I didn't understand your question.

LR
 
P

Pete Becker

class A {
};
class B : public A {
};
void foo(const B &) {
}
void bar(const A &) {
}

now I suggest adding this function
void bar(const B &b) {
::bar( reinterpret_cast<const A&>(b) );
}

Use static_cast instead of reinterpret_cast here.
 
L

LR

Pete said:
Use static_cast instead of reinterpret_cast here.

I meant to type dynamic_cast. For the two compilers I've tried this
kind of thing with I like the error messages better when I make other
mistakes.

But I agree that reininterpret_cast doesn't belong there. Thanks.

LR
 
P

Pete Becker

I meant to type dynamic_cast. For the two compilers I've tried this
kind of thing with I like the error messages better when I make other
mistakes.

Nevertheless, the correct cast is static_cast. This kind of conversion
is exactly what it's intended for.
 
L

LR

Pete said:
Nevertheless, the correct cast is static_cast. This kind of conversion
is exactly what it's intended for.

Why in this case is static_cast better than dynamic_cast?

LR
 
K

Kira Yamato

he's referring to the signature.


because you'ld call foo(const A& a) instead. or better:

#include <iostream>
#include <typeinfo>

class A { public: virtual ~A() {} };
class B : public A { };

template< typename T >
void func(const T& t )
{
std::cout << typeid(t).name() << std::endl;
}

int main()
{
A a;
func(a);
B b;
func(b);

void (*fp)(const A&);
fp = &func<A>;
fp(a);
fp(b);
}

But the following will not compile:
fp = &func<B>;

So, if I specialize for func<B>, your code won't work as is.

However, there is probably a solution using templates and casts, as a
few other posters have hinted.
 
K

Kira Yamato

No, that's not what I meant. I don't think that I made myself clear,
so I'll try again.

Untested, and not complete...

This is what I think you started with.
class A {
};
class B : public A {
};
void foo(const B &) {
}
void bar(const A &) {
}

now I suggest adding this function
void bar(const B &b) {
::bar( reinterpret_cast<const A&>(b) );
}

so you can
void (*fp)(const B&);
and
B b;
fp = bar; // will assign the address of void bar(const B &) to fp
fp(b);
fp = foo;
fp(b);

Yes. This is a nice work-around in the sense that it is direct and
seemingly not too much artifact.

Your solution is similar to the adaptor pattern (or am I misusing the
terminology here?).

Thanks for the suggestion.
 
L

LR

Kira said:
Yes. This is a nice work-around in the sense that it is direct and
seemingly not too much artifact.

Your solution is similar to the adaptor pattern (or am I misusing the
terminology here?).

I suppose in some ways, maybe not in others. However, consider this
well known, although I don't know the source, quote: "any [programming]
problem can be solved by introducing an extra level of indirection."

Thanks for the suggestion.


You're welcome.

Please don't overlook Pete Becker's correction and advice to use
static_cast instead of reinterpret_cast elsethread.

BTW, I've been thinking about this, and wondering about what you want to
do because I suspect this might be a little bit of a maintenance problem
if you're using this extensively in your code and your class structure
changes.

LR
 
K

Kira Yamato

Kira said:
Yes. This is a nice work-around in the sense that it is direct and
seemingly not too much artifact.

Your solution is similar to the adaptor pattern (or am I misusing the
terminology here?).

I suppose in some ways, maybe not in others. However, consider this
well known, although I don't know the source, quote: "any [programming]
problem can be solved by introducing an extra level of indirection."

Thanks for the suggestion.


You're welcome.

Please don't overlook Pete Becker's correction and advice to use
static_cast instead of reinterpret_cast elsethread.

Yes. I've read that.
BTW, I've been thinking about this, and wondering about what you want
to do because I suspect this might be a little bit of a maintenance
problem if you're using this extensively in your code and your class
structure changes.

It started as a purely academic question. Since I notice the power of
C++ comes from the abstraction that allows writing codes which operates
on compatible types, I wonder if this concept could extend to "derived"
types too.

By "derived" types, I mean types that depend on object types, e.g.,
functions with parameters of polymorphic types, or templates that
depend on object types. How would you declare pointers to those things?

We can even go further with this idea, like types that depend on these
derived types. For example, a function whose parameter is a template
of object types.
 

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
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top