23

Because of my device I can't use virtual functions. Suppose I have:

class Base
{
    void doSomething() { }
};

class Derived : public Base
{
    void doSomething() { }
};

// in any place
{
    Base *obj = new Derived;
    obj->doSomething();
}

the obj->doSomething() will call just the Base::doSomething()

Is there a way with Base *obj, to call the doSomething of the Derived?

I know I can just put a virtual before doSomething() of Base it solve the problem, but I'm limited by my device, the compiler doesn't support it.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
okami
  • 2,093
  • 7
  • 28
  • 40
  • 4
    May I ask what compiler you are using? Do you have any option of maybe changing your compiler to one that does support virtual? – default Sep 29 '10 at 02:20
  • 1
    tag as "embedded" if appropriate. – Jason S Sep 29 '10 at 02:38
  • 3
    A slightly greater explanation of what *my device* would be appropriate. Virtual functions do not need hardware support, only language support. What compiler are you using? Are you sure that there is support for classes and inheritance and not polymorphism? You might be barking at the wrong tree here. – David Rodríguez - dribeas Sep 29 '10 at 07:46

11 Answers11

17

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.

JFMR
  • 23,265
  • 4
  • 52
  • 76
oz10
  • 153,307
  • 27
  • 93
  • 128
  • 5
    Shouldn't you use `dynamic_cast` to cast a base to a derived? (I'm a C++ noob) – dreamlax Sep 29 '10 at 02:15
  • 13
    @dreamlax, If virtual functions don't work, dynamic_cast isn't likely to either. – Mark Ransom Sep 29 '10 at 02:17
  • 5
    Right; in order to use `dynamic_cast`, the class must be polymorphic (which means it or one of its base classes must have at least one virtual function). – James McNellis Sep 29 '10 at 02:20
  • 4
    @dreamlax: Firstly, you *can* use `dynamic_cast` to cast to derived, but that doesn't mean that you *should* do it all the time. Secondly, `dynamic_cast` only works with polymorphic classes, and in this case we specifically have a non-polymorphic class. `dynamic_cast` cannot be used in this case. – AnT stands with Russia Sep 29 '10 at 02:21
  • 8
    This presumes the programmer still knows the exact derived type at the point of casting, which is a totally trivial case and IMHO doesn't answer the questioner's intent (though he invited this mis-reading by carelessly grouping the steps). Heck, to use this as is you'd need to have a guaranteed sequencing of the `Base* obj` assignment and casting, in which case you'd simply use `Derived* obj` to begin with. Real solutions: combine this with type-descriminatory data and a switch between castings; function pointers; if the behaviours can be resolved at compile time use templates over OO. – Tony Delroy Sep 29 '10 at 03:02
  • @Andrey_T, @James, @Mark: Good to know, and this seems to indicate it is time for me to revise some of my C++ books... – dreamlax Sep 29 '10 at 03:31
  • @MarkRansom good one. removed "do dishes" and "buy hanukkah gift for girlfriend" from brain in order to make room, and placed the "`dynamic_cast<>()` needs polymorphic trait" tidbit instead. thanks! – wilhelmtell Nov 26 '10 at 23:32
9

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)

James McNellis
  • 348,265
  • 75
  • 913
  • 977
9

My first answer shows that it is indeed possible to get at least a limited form of polymorphic-like behavior without actually relying on the language's support for polymorphism.

However, that example has an enormous amount of boilerplate. It certainly wouldn't scale well: for every class that you add you have to modify six different places in the code, and for every member function that you want to support, you need to duplicate most of that code. Yuck.

Well, good news: with the help of the preprocessor (and the Boost.Preprocessor library, of course), we can easily extract most of that boilderplate and make this solution manageable.

To get the boilerplate out of the way, you'll need these macros. You can put them in a header file and forget about them if you want; they are fairly generic. [Please don't run away after reading this; if you aren't familiar with the Boost.Preprocessor library, it probably looks terrifying :-) After this first code block, we'll see how we can use this to make our application code a lot cleaner. If you want, you can just ignore the details of this code.]

The code is presented in the order it is because if you copy and past each of the code blocks from this post, in order, into a C++ source file, it will (I mean should!) compile and run.

I've called this the "Pseudo-Polymorphic Library;" any names beginning with "PseudoPM," with any capitalization, should be considered reserved by it. Macros beginning with PSEUDOPM are publicly callable macros; macros beginning with PSEUDOPMX are for internal use.

#include <boost/preprocessor.hpp>

// [INTERNAL] PSEUDOPM_INIT_VTABLE Support
#define PSEUDOPMX_INIT_VTABLE_ENTRY(r, c, i, fn)                              \
  BOOST_PP_COMMA_IF(BOOST_PP_NOT_EQUAL(0, i))                                 \
  & c :: BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(4, 0, fn), Impl)

// [INTERNAL] PSEUDOPM_DECLARE_VTABLE Support
#define PSEUDOPMX_DECLARE_VTABLE_STRUCT_MEMBER(r, c, i, fn)                   \
  BOOST_PP_TUPLE_ELEM(4, 1, fn)                                               \
  (c :: * BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(4, 0, fn), Ptr))                   \
  BOOST_PP_TUPLE_ELEM(4, 3, fn);

#define PSEUDOPMX_DECLARE_VTABLE_STRUCT(r, memfns, c)                         \
  struct BOOST_PP_CAT(PseudoPMIntVTable, c)                                   \
  {                                                                           \
    friend class c;                                                           \
    BOOST_PP_SEQ_FOR_EACH_I(PSEUDOPMX_DECLARE_VTABLE_STRUCT_MEMBER, c, memfns)\
  };

#define PSEUDOPMX_DECLARE_VTABLE_ENUM_MEMBER(r, x, i, c)                      \
  BOOST_PP_COMMA_IF(BOOST_PP_NOT_EQUAL(0, i)) BOOST_PP_CAT(PseudoPMType, c)

#define PSEUDOPMX_DECLARE_VTABLE_UNION_MEMBER(r, x, c)                        \
  BOOST_PP_CAT(PseudoPMIntVTable, c) BOOST_PP_CAT(BOOST_PP_CAT(table_, c), _);

#define PSEUDOPMX_DECLARE_VTABLE_RESET_FN(r, x, c)                            \
  void Reset(BOOST_PP_CAT(PseudoPMIntVTable, c) table)                        \
  {                                                                           \
    type_ = BOOST_PP_CAT(PseudoPMType, c);                                    \
    table_.BOOST_PP_CAT(BOOST_PP_CAT(table_, c), _) = table;                  \
  }

#define PSEUDOPMX_DECLARE_VTABLE_PUBLIC_FN(r, x, fn)                          \
  BOOST_PP_TUPLE_ELEM(4, 1, fn)                                               \
  BOOST_PP_TUPLE_ELEM(4, 0, fn)                                               \
  BOOST_PP_TUPLE_ELEM(4, 3, fn);

// [INTERNAL] PSEUDOPM_DEFINE_VTABLE Support
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST0
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST1 a0
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST2 a0, a1
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST3 a0, a1, a2
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST4 a0, a1, a2, a3
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST5 a0, a1, a2, a3, a4
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST6 a0, a1, a2, a3, a4, a5
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST7 a0, a1, a2, a3, a4, a5, a6
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST8 a0, a1, a2, a3, a4, a5, a6, a7
#define PSEUDOPMX_DEFINE_VTABLE_ARGLIST9 a0, a1, a2, a3, a4, a5, a6, a7, a8

#define PSEUDOPMX_DEFINE_VTABLE_FNP(r, x, i, t)                               \
  BOOST_PP_COMMA_IF(BOOST_PP_NOT_EQUAL(0, i))                                 \
  t BOOST_PP_CAT(a, i)

#define PSEUDOPMX_DEFINE_VTABLE_FN_CASE(r, fn, i, c)                          \
  case BOOST_PP_CAT(PseudoPMType, c) : return                                 \
  (                                                                           \
    static_cast<c*>(this)->*pseudopm_vtable_.table_.                          \
    BOOST_PP_CAT(BOOST_PP_CAT(table_, c), _).                                 \
    BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(4, 0, fn), Ptr)                          \
  )(                                                                          \
    BOOST_PP_CAT(                                                             \
      PSEUDOPMX_DEFINE_VTABLE_ARGLIST,                                        \
      BOOST_PP_TUPLE_ELEM(4, 2, fn)                                           \
    )                                                                         \
  );

#define PSEUDOPMX_DEFINE_VTABLE_FN(r, classes, fn)                            \
  BOOST_PP_TUPLE_ELEM(4, 1, fn)                                               \
  BOOST_PP_SEQ_HEAD(classes) :: BOOST_PP_TUPLE_ELEM(4, 0, fn)                 \
  (                                                                           \
    BOOST_PP_SEQ_FOR_EACH_I(                                                  \
      PSEUDOPMX_DEFINE_VTABLE_FNP, x,                                         \
      BOOST_PP_TUPLE_TO_SEQ(                                                  \
        BOOST_PP_TUPLE_ELEM(4, 2, fn),                                        \
        BOOST_PP_TUPLE_ELEM(4, 3, fn)                                         \
      )                                                                       \
    )                                                                         \
  )                                                                           \
  {                                                                           \
    switch (pseudopm_vtable_.type_)                                           \
    {                                                                         \
      BOOST_PP_SEQ_FOR_EACH_I(PSEUDOPMX_DEFINE_VTABLE_FN_CASE, fn, classes)   \
    }                                                                         \
  }

// Each class in the classes sequence should call this macro at the very 
// beginning of its constructor.  'c' is the name of the class for which
// to initialize the vtable, and 'memfns' is the member function sequence.
#define PSEUDOPM_INIT_VTABLE(c, memfns)                                       \
  BOOST_PP_CAT(PseudoPMIntVTable, c) pseudopm_table =                         \
  {                                                                           \
    BOOST_PP_SEQ_FOR_EACH_I(PSEUDOPMX_INIT_VTABLE_ENTRY, c, memfns)           \
  };                                                                          \
  pseudopm_vtable_.Reset(pseudopm_table); 

// The base class should call this macro in its definition (at class scope).
// This defines the virtual table structs, enumerations, internal functions, 
// and declares the public member functions.  'classes' is the sequence of
// classes and 'memfns' is the member function sequence.
#define PSEUDOPM_DECLARE_VTABLE(classes, memfns)                              \
  protected:                                                                  \
  BOOST_PP_SEQ_FOR_EACH(PSEUDOPMX_DECLARE_VTABLE_STRUCT, memfns, classes)     \
                                                                              \
  enum PseudoPMTypeEnum                                                       \
  {                                                                           \
    BOOST_PP_SEQ_FOR_EACH_I(PSEUDOPMX_DECLARE_VTABLE_ENUM_MEMBER, x, classes) \
  };                                                                          \
                                                                              \
  union PseudoPMVTableUnion                                                   \
  {                                                                           \
    BOOST_PP_SEQ_FOR_EACH(PSEUDOPMX_DECLARE_VTABLE_UNION_MEMBER, x, classes)  \
  };                                                                          \
                                                                              \
  class PseudoPMVTable                                                        \
  {                                                                           \
  public:                                                                     \
    BOOST_PP_SEQ_FOR_EACH(PSEUDOPMX_DECLARE_VTABLE_RESET_FN, x, classes)      \
  private:                                                                    \
    friend class BOOST_PP_SEQ_HEAD(classes);                                  \
    PseudoPMTypeEnum type_;                                                   \
    PseudoPMVTableUnion table_;                                               \
  };                                                                          \
                                                                              \
  PseudoPMVTable pseudopm_vtable_;                                            \
                                                                              \
  public:                                                                     \
  BOOST_PP_SEQ_FOR_EACH(PSEUDOPMX_DECLARE_VTABLE_PUBLIC_FN, x, memfns)

// This macro must be called in some source file after all of the classes in
// the classes sequence have been defined (so, for example, you can create a 
// .cpp file, include all the class headers, and then call this macro.  It 
// actually defines the public member functions for the base class.  Each of 
// the public member functions calls the correct member function in the 
// derived class.  'classes' is the sequence of classes and 'memfns' is the 
// member function sequence.
#define PSEUDOPM_DEFINE_VTABLE(classes, memfns)                               \
  BOOST_PP_SEQ_FOR_EACH(PSEUDOPMX_DEFINE_VTABLE_FN, classes, memfns)

(We should make the vtable static, but I'll leave that as an excercise for the reader. :-D)

Now that that is out of the way, we can actually look at what you need to do in your application to use this.

First, we need to define the list of classes that are going to be in our class hierarchy:

// The sequence of classes in the class hierarchy.  The base class must be the
// first class in the sequence.  Derived classes can be in any order.
#define CLASSES (Base)(Derived)

Second, we need to define the list of "virtual" member functions. Note that with this (admittedly limited) implementation, the base class and every derived class must implement every one of the "virtual" member functions. If a class doesn't define one of these, the compiler will get angry.

// The sequence of "virtual" member functions.  Each entry in the sequence is a
// four-element tuple:
// (1) The name of the function.  A function will be declared in the Base class
//     with this name; it will do the dispatch.  All of the classes in the class
//     sequence must implement a private implementation function with the same 
//     name, but with "Impl" appended to it (so, if you declare a function here 
//     named "Foo" then each class must define a "FooImpl" function.
// (2) The return type of the function.
// (3) The number of arguments the function takes (arity).
// (4) The arguments tuple.  Its arity must match the number specified in (3).
#define VIRTUAL_FUNCTIONS               \
  ((FuncNoArg,  void, 0, ()))           \
  ((FuncOneArg, int,  1, (int)))        \
  ((FuncTwoArg, int,  2, (int, int)))

Note that you can name these two macros whatever you want; you'll just have to update the references in the following snippets.

Next, we can define our classes. In the base class, we need to call PSEUDOPM_DECLARE_VTABLE to declare the virtual member functions and define all the boilerplate for us. In all of our class constructors, we need to call PSEUDOPM_INIT_VTABLE; this macro generates the code required to initialize the vtable correctly.

In each class we must also define all of the member functions we listed above in the VIRTUAL_FUNCTIONS sequence. Note that we need to name the implementations with an Impl suffix; this is because the implementations are always called through the dispatcher functions that are generated by the PSEUDOPM_DECLARE_VTABLE macro.

class Base 
{ 
public: 
    Base()
    {
      PSEUDOPM_INIT_VTABLE(Base, VIRTUAL_FUNCTIONS)
    }

    PSEUDOPM_DECLARE_VTABLE(CLASSES, VIRTUAL_FUNCTIONS)
private:
    void FuncNoArgImpl() { }
    int FuncOneArgImpl(int x) { return x; }
    int FuncTwoArgImpl(int x, int y) { return x + y; }
}; 

class Derived : public Base 
{
public: 
    Derived() 
    { 
        PSEUDOPM_INIT_VTABLE(Derived, VIRTUAL_FUNCTIONS)
    } 
private: 
    void FuncNoArgImpl() { }
    int FuncOneArgImpl(int x) { return 2 * x; }
    int FuncTwoArgImpl(int x, int y) { return 2 * (x + y); }
};

Finally, in some source file, you'll need to include all the headers where all the classes are defined and call the PSEUDOPM_DEFINE_VTABLE macro; this macro actually defines the dispatcher functions. This macro cannot be used if all of the classes have not yet been defined (it has to static_cast the base class this pointer, and this will fail if the compiler doesn't know that the derived class is actually derived from the base class).

PSEUDOPM_DEFINE_VTABLE(CLASSES, VIRTUAL_FUNCTIONS)

Here is some test code that demonstrates the functionality:

#include <cassert>

int main() 
{ 
    Base* obj0 = new Base; 
    Base* obj1 = new Derived; 
    obj0->FuncNoArg(); // calls Base::FuncNoArg
    obj1->FuncNoArg(); // calls Derived::FuncNoArg

    assert(obj0->FuncTwoArg(2, 10) == 12); // Calls Base::FuncTwoArg
    assert(obj1->FuncTwoArg(2, 10) == 24); // Calls Derived::FuncTwoArg
} 

[Disclaimer: This code is only partially tested. It may contain bugs. (In fact, it probably does; I wrote most of it at 1 am this morning :-P)]

Community
  • 1
  • 1
James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • lol, OP never explained why they (think they) can't use virtuals, but you accepted it on faith & showed them how to implement a manual spaghetti of virtual functions in place of the native one. That's... a weird kind of dedication. :P Props for effort, but anyone thinking they need to do this needs to rethink - or admit they're in the wrong place & just use C, maybe with a pre-existing object framework. They'll eventually realise there was no valid reason to avoid virtuals, but it's too late...they can only look at their codebase & dream what might have been, had they asked the right question. – underscore_d Aug 14 '16 at 14:40
6

You can downcast the object to the Derived type and call it, like so:

static_cast<Derived*>(obj)->doSomething();

though that does not afford any guarantees that what 'obj' points to really is of type Derived.

I'm more concerned that you don't even have access to virtual functions. How do destructors work if none of your functions can be virtual, and you are subclassing?

wkl
  • 77,184
  • 16
  • 165
  • 176
  • 5
    With the destructor, you just have to be very careful not to `delete` a derived class object through a base class pointer. – James McNellis Sep 29 '10 at 02:20
  • 2
    James McNellis is right about the deletion of a derived class, thanks for reminding me. I'm wondering if this is an actual limitation of the compiler he's using, or if the original poster is following some guideline or is being restricted from using virtual functions due to the overhead of dynamic dispatch, for perhaps performance reasons? – wkl Sep 29 '10 at 02:24
4

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;
}
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • "there's no magic happening that you can't replicate in code. You could, in fact, implement your own virtual dispatch mechanism." The former statement amplifies the question everyone should've been asking about the latter: Why did the OP _think_ they wanted to avoid virtual functions? It's precisely the lack of magic - and, btw, no requirement for RTTI, in case the OP was worried about that - that makes manually reinventing it all absurd. Applause for effort, but if someone wants polymorphism but has made their mind up not to use virtual functions, them I'm not sure C++ is a language for them. – underscore_d Aug 14 '16 at 14:38
  • @underscore_d: OP stated at the beginning that they are unable to use virtual 'because of their device.' It is very possible that the system they were coding for doesn't have native support for virtual (the keyword, not the functionality). I had supposed that they did not 'make up their mind' that they didn't want to use virtual, but didn't have access to the keyword. This is not at all absurd. – John Dibling Aug 21 '16 at 20:15
2

You can make your own vtable, I suppose. I'd just be a struct containing your "virtual" function pointers as part of Base, and have code to set up the vtable.

This is kind of a gross solution -- it's the C++ compiler's job to handle this feature.

But here goes:

#include <iostream>

class Base
{
protected:
    struct vt {
        void (*vDoSomething)(void);
    } vt;
private:
    void doSomethingImpl(void) { std::cout << "Base doSomething" << std::endl; }
public:
    void doSomething(void) { (vt.vDoSomething)();}
    Base() : vt() { vt.vDoSomething = (void(*)(void)) &Base::doSomethingImpl;}
};

class Derived : public Base
{
public:
    void doSomething(void) { std::cout << "Derived doSomething" << std::endl; }
    Derived() : Base() { vt.vDoSomething = (void(*)(void)) &Derived::doSomething;}
};
ldav1s
  • 15,885
  • 2
  • 53
  • 56
  • 2
    This yields undefined behavior; you cannot call a function through a function pointer that has a type that is incompatible with the type of the function. – James McNellis Sep 29 '10 at 03:11
  • 1
    `This is kind of a gross solution -- it's the C++ compiler's job to handle this feature.` Right on the mark. The fact that the example, perhaps unintentionally, invokes UB and therefore adds additional grossness makes me tempted to upvote it in a meta, 4th-wall-breaking sorta way. – underscore_d Aug 14 '16 at 14:33
1

Can you encapsulate the base class rather than deriving from it?

Then you can call doSomething() // gets derived
or base->doSomething() // calls base

Martin Beckett
  • 94,801
  • 28
  • 188
  • 263
  • Then you couldn't keep a `Derived` via `Base *`; which was the whole point/problem. Right? – Jon-Eric Sep 29 '10 at 02:53
  • @Jon-Eric: Actually, he whole point of doing so is _polymorphism_, which requires virtual functions... – sbi Sep 29 '10 at 08:22
  • 1
    The way most people are taught OO means that everyone thinks inheritance is the only solution. Often encapsulation is better – Martin Beckett Sep 29 '10 at 15:19
  • 1
    @MartinBeckett That's a great point for many other situations, but it can't provide polymorphic behaviour via a base pointer/reference "in any place" as the OP requested. – underscore_d Aug 21 '16 at 21:13
0

You can use template for compile-time polymorphism.

template<class SomethingDoer> class MyClass
{
    public:
        void doSomething() {myDoer.doSomething();}
    private:
        SomethingDoer myDoer;
};

class BaseSomethingDoer
{
    public:
        void doSomething() { // base implementation }
};

class DerivedSomethingDoer
{
    public:
        void doSomething() { // derived implementation }
};

typedef MyClass<BaseSomethingDoer> Base;
typedef MyClass<DerivedSomethingDoer> Derived;

Now, we can't point to a Derived with a Base pointer, but we can have templated functions that take in a MyClass, and that will work with both Base and Derived objects.

JohnMcG
  • 8,709
  • 6
  • 42
  • 49
  • 1
    "we can't point to a `Derived` with a `Base` pointer", and yet that ability is exactly what the OP asked for. Static polymorphism is a fantastic tool but is not relevant here. See also [this comment](http://stackoverflow.com/questions/3818277/can-i-get-polymorphic-behavior-without-using-virtual-functions#comment65241419_3827715) – underscore_d Aug 14 '16 at 14:42
-1

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();
}
Chubsdad
  • 24,777
  • 4
  • 73
  • 129
  • 1
    OP was clearly looking for a (never justified and probably misguided) way to implement dynamic polymorphism, in the sense that objects of different derived types could be accessed through polymorphic pointers/references/containers with behaviour determined at runtime. The CRTP does not provide that; it only allows selection of different behaviours at compile time, hence the better name _static polymorphism_. – underscore_d Aug 14 '16 at 14:32
-1

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

-2

There is simply no simple way to do this without virtual methods.

Alexander Rafferty
  • 6,134
  • 4
  • 33
  • 55
  • @John: I see you haven't contributed - or visibly endorsed by way of comment - any answer. In one way, Alexander's quite right in that the questioner may well be aware of the potential workarounds (manually tracking the derived type and switching between castings, manually using function pointers) but asking if there's any clever way he doesn't know about to work with the existing design. IMO there's no harm in listing the things he may or may not be aware of, but it's as likely as not that Alexander's just cutting to the chase. – Tony Delroy Sep 29 '10 at 03:16
  • @Tony: You're right, I've put my money where my mouth is now. – John Dibling Sep 29 '10 at 12:21