Reputation: 3941
I am implementing a special purpose handle class.
ihandle
is an interface that all handles must implement, in my real code it would have the operator overloadings for ->
*
. But for this example, I wanted to keep it simple and it just has the get
function.
template <typename T>
class ihandle {
public:
virtual T* get();
};
One possible implementation would be ptr
, which is just a raw pointer.
template <typename T>
class ptr : public ihandle<T>
{
T* t;
public:
ptr(T* t = nullptr) : t(t) {}
T* get(){return t;}
};
And then there is handle
which which is intended for doing null safety checks.
template <typename T>
class handle
{
public:
ihandle<T>* h;
T* get(){return h->get();}
handle(ihandle<T>* h = nullptr) : h(h) {}
template <typename D>
handle(handle<D>& hd)
: h((ihandle<T>*)hd.h)
{
static_assert(is_base_of<T, D>::value, "error");
}
};
There is this constructor for being able to cast from a handle of an inheriting class to a handle of a base class.
template <typename D>
handle(handle<D>& hd)
: h((ihandle<T>*)hd.h)
{
static_assert(is_base_of<T, D>::value, "error");
}
For example, if B
inherits from A
, I would like to be able to call this function with an instance of handle<B>
.
void foo(handle<A> ha) {
// do something
}
But this gives a segfault for the following sample test.
struct A {
virtual void talk() {printf("A\n");}
};
struct B : public A {
void talk() {printf("B\n");}
};
int main()
{
handle<B> hb(new ptr<B>(new B));
//hb.get()->talk(); // if uncomment, no segfault
handle<A> ha = hb;
ha.get()->talk(); // segfault here
return 0;
}
I suspect the problem might be in the handle(handle<D>& hd)
constructor but I don't understand what's happening.
You can test it by clicking this link: https://onlinegdb.com/BkAYuQZ3z
Upvotes: 3
Views: 72
Reputation: 2069
Q: what compiler/linker are you using ? when I compile your stuff using VS-2017, I get a linker error. It sais it expects a B->get() when I include the declaration of hb like in your main test,
handle<B> hb(new ptr<B>(new B));
..but when I provide some default implementation for the virtual method ihandle.get(), like this..
template <typename T>
class ihandle {
public:
virtual T* get() { return NULL; }
};
return NULL.. my linker error is gone.. and there is no crash. You test reports B or B B.
Hope I can help. Thanks for the code ! I'm learning C++ atm, above example is a good study..
Upvotes: 1
Reputation: 37468
Even if condition is_base_of<T, D>::value
is true it does not make (ihandle<T>*) hd.h
cast valid because ptr<B>
and ihandle<A>
types are not related. I guess this is an example of situation where c-style cast should be definitely avoided. To perform conversion safely you can utilize dynamic_cast
with a check:
: h(dynamic_cast<ihandle<T>*>(hd.h))
{
if(hd.h && !h)
{
throw ::std::runtime_error{"pointers are not related"};
}
}
Upvotes: 5