Because of my device I can\'t use virtual functions. Suppose I have:
class Base
{
void doSomething() { }
};
class Derived : public Base
{
void doSom
Small program for understanding, we can use static_cast for down casting the base class pointer to the derived class and call the function.
#include<iostream>
using namespace std;
class Base
{
public:
void display()
{
cout<<"From Base class\n";
}
};
class Derived:public Base
{
public:
void display()
{
cout<<"From Derived class";
}
};
int main()
{
Base *ptr=new Derived;
Derived* d = static_cast<Derived*>(ptr);
ptr->display();
d->display();
return 0;
}
Output:
From Base class From Derived class
You could down cast the base class pointer to the derived class and call the function.
Base* obj = new Derived;
Derived* d = static_cast<Derived*>( obj );
d->doSomething();
Since doSomething()
is not declared virtual
, you should get the derived implementation.
Can you encapsulate the base class rather than deriving from it?
Then you can call doSomething() // gets derived
or base->doSomething() // calls base
I think it is possible with CRTP (if your 'Device' supports Templates).
#include <iostream>
template<class T> struct base{
void g(){
if(T *p = static_cast<T *>(this)){
p->f();
}
}
void f(){volatile int v = 0; std::cout << 1;}
virtual ~base(){}
};
struct derived1 : base<derived1>{
void f(){std::cout << 2;}
};
struct derived2 : base<derived2>{
void f(){std::cout << 3;}
};
int main(){
derived1 d1;
d1.g();
derived2 d2;
d2.g();
}
Since virtual methods are usually implemented by means of vtables, there's no magic happening that you can't replicate in code. You could, in fact, implement your own virtual dispatch mechanism. It takes some work, both on the part of the programmer who implements the base class and the programmer who implements the derived class, but it works.
Casting the pointer, like as suggested by ceretullis, is probably the first thing you should consider doing. But the solution I post here at least gives you the opportunity to write code that uses these classes as if your compiler supported virtual
. That is, with a simple function call.
This is a program that implements a Base
class with a function that returns a string
: "base", and a Derived
class that returns a string
: "der". The idea is to be able to support code like this:
Base* obj = new Der;
cout << obj->get_string();
...so that the get_string()
call will return "der" even though we are calling through a Base
pointer and using a compiler that doesn't support virtual
.
It works by implementing our own version of a vtable. Actually, it's not really a table. It's just a member-function pointer in the base class. In the base class' implementation of get_string()
, if the member-function pointer is non-null, the function is called. If it is null, the base class implementation is executed.
Simple, straightforward and pretty basic. This could probably be improved a lot. But it shows the basic technique.
#include <cstdlib>
#include <string>
#include <iostream>
using namespace std;
class Base
{
public:
typedef string (Base::*vptr_get_string)(void) const;
Base(vptr_get_string=0);
void set_derived_pointer(Base* derived);
string get_string() const;
protected:
Base* der_ptr_;
vptr_get_string get_string_vf_;
};
Base::Base(vptr_get_string get_string_vf)
: der_ptr_(0),
get_string_vf_(get_string_vf)
{
}
void Base::set_derived_pointer(Base* derived)
{
der_ptr_ = derived;
}
string Base::get_string() const
{
if( get_string_vf_ )
return (der_ptr_->*get_string_vf_)();
else
return "base";
}
class Der : public Base
{
public:
Der();
string get_string() const;
};
Der::Der()
: Base(static_cast<Base::vptr_get_string>(&Der::get_string))
{
set_derived_pointer(this);
}
string Der::get_string() const
{
return "der";
}
int main()
{
Base* obj = new Der;
cout << obj->get_string();
delete obj;
}
Sure you can do this; it's just not necessarily easy.
If there is a finite list of derived classes and you know what they are when you define the base class, you can do this using a non-polymorphic member function wrapper. Here is an example with two derived classes. It uses no standard library facilities and relies solely on standard C++ features.
class Base;
class Derived1;
class Derived2;
class MemFnWrapper
{
public:
enum DerivedType { BaseType, Derived1Type, Derived2Type };
typedef void(Base::*BaseFnType)();
typedef void(Derived1::*Derived1FnType)();
typedef void(Derived2::*Derived2FnType)();
MemFnWrapper(BaseFnType fn) : type_(BaseType) { fn_.baseFn_ = fn; }
MemFnWrapper(Derived1FnType fn) : type_(Derived1Type) {fn_.derived1Fn_ = fn;}
MemFnWrapper(Derived2FnType fn) : type_(Derived2Type) {fn_.derived2Fn_ = fn;}
void operator()(Base* ptr) const;
private:
union FnUnion
{
BaseFnType baseFn_;
Derived1FnType derived1Fn_;
Derived2FnType derived2Fn_;
};
DerivedType type_;
FnUnion fn_;
};
class Base
{
public:
Base() : doSomethingImpl(&Base::myDoSomething) { }
Base(MemFnWrapper::Derived1FnType f) : doSomethingImpl(f) { }
Base(MemFnWrapper::Derived2FnType f) : doSomethingImpl(f) { }
void doSomething() { doSomethingImpl(this); }
private:
void myDoSomething() { }
MemFnWrapper doSomethingImpl;
};
class Derived1 : public Base
{
public:
Derived1() : Base(&Derived1::myDoSomething) { }
private:
void myDoSomething() { }
};
class Derived2 : public Base
{
public:
Derived2() : Base(&Derived2::myDoSomething) { }
private:
void myDoSomething() { }
};
// Complete the MemFnWrapper function call operator; this has to be after the
// definitions of Derived1 and Derived2 so the cast is valid:
void MemFnWrapper::operator()(Base* ptr) const
{
switch (type_)
{
case BaseType: return (ptr->*(fn_.baseFn_))();
case Derived1Type: return (static_cast<Derived1*>(ptr)->*(fn_.derived1Fn_))();
case Derived2Type: return (static_cast<Derived2*>(ptr)->*(fn_.derived2Fn_))();
}
}
int main()
{
Base* obj0 = new Base;
Base* obj1 = new Derived1;
Base* obj2 = new Derived2;
obj0->doSomething(); // calls Base::myDoSomething()
obj1->doSomething(); // calls Derived1::myDoSomething()
obj2->doSomething(); // calls Derived2::myDoSomething()
}
(I originally suggested using std::function
, which does a lot of this work for you, but then I remembered it is a polymorphic function wrapper, so it necessarily uses virtual functions. :-P Oops. You can view the revision history to see what that one looked like)