Pure Virtual Functions Problem

Hello,

I'm trying to write a base class and then several derived classes to work with some usb devices from 'Phidget' (http://www.phidgets.com/)

I want the base class to provide functions that are common to all the different type of Phidget modules e.g. temperature sensors, motor controllers. These are functions like 'getDeviceSerialNumber' etc. I've made the base class abstract by including a couple of pure virtual functions, one in particular is called createHandle(ptr). The intention of this is so that each derived class must implement its own createHandle function as the Phidget API has seperate handle creation functions for each different module e.g. createAccelerometerHandle(),createStepperMotorHandle() etc.

So far this is the (relevant parts) of the base class :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Interface_PhidgetModule {
public:
    .. Irrelevant code removed ..

    /*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*
     * ABSTRACT public member functions
     *^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*/

    virtual int createHandle()=0; //<==== this is causing problems
    virtual int onDetachHandler() = 0;
    
private:
    virtual int open(CPhidgetHandle hnd, int serialNumber) = 0;

protected:
   
};


Now the problem comes when I try to derive a class, more specifically when I try to override the createHandle() method :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class PhidgetAccelerometer : public Interface_PhidgetModule{
public:
    

    /*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*
     * Virtual functions declared
     * in the base class
     *^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*^*/
    virtual int createHandle(CPhidgetHandle accel); //<==== Different to base 
    virtual int onDetachHandler();

private:
    //static map<int,CPhidgetAccelerometerHandle> accelerometers;
std::map<CPhidgetAccelerometerHandle,CPhidgetAccelerometerHandle*> accelerometers;
virtual int open(CPhidgetHandle hnd, int serialNumber);


};


As you can see, I've prototyped the createHandle function in the DERIVED class with a parameter, but as I've just found out, the compiler doesn't like this ! If I understand correctly now, if I want to pass a parameter in the derived class' createHandle() function, I have to declare it in the base class pure virtual function prototype. The problem is this though :

1) All derived classes MUST have the createHandle() function AND
2) Each derived createHandle() function must accept a different type of handle

How can I make a pure virtual function in the base class accept different types of input parameters ? I've considered using templates and such but I just can't figure it out.
Last edited on
How can I make a pure virtual function in the base class accept different types of input parameters ? I've considered using templates and such but I just can't figure it out.


You can't. What you can do is pass an abstracted pointer/reference to a virtual base class representing the handle, and then downcast it appropriately afterwards:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/*
 * assume CPhidgetHandle is derived from CHandleBase and CHandleBase has at least 1 virtual
 *  function (a virtual dtor is typical)
*/

class Interface_PhidgetModule
{
  virtual int createHandle(const CHandleBase& h) = 0;
};

//--------------

class PhidgetAccelerometer : public Interface_PhidgetModule
{
  // the handle specific to this widget:
  int createHandle(const CPhidgetHandle& h)
  {
    // do whatever with 'h' here
  }

  // the handle generic to this widget
  virtual int createHandle(const CHandleBase& h)
  {
    // downcast from generic CHandleBase to specific CPhidgetHandle
    return createHandle( dynamic_cast<const CPhidgetHandle&>(h) );
  }
};


Note:
- To avoid amiguous call errors, you may want to name the functions something different. Maybe make the generic one createHandle and the specific one createPhidgetHandle or something like that.

- dynamic_cast (when casting a reference, as I'm doing in the above example) will throw an std::bad_cast exception if attempting to do a bad downcast. You may want to nest this in try/catch blocks if such an instance may occur in your code. Or you can change it to use pointers instead (dynamic_cast merely returns a null pointer if a pointer cast is bad, rather than throwing an exception).

- static_cast can be used instead of dynamic_cast only if you're 100% sure the cast will always be correct. Bad casts will silently appear to work even though they're bad, leading to very hard-to-find (and often fatal) runtime bugs.
Disch,

Thanks for that. One thing that has puzzled me about casting.. how would I know if it's possible to cast from one pointer to another ?

For example taking the Phidget modules, it has two types of handle, what appears to be a Generic CPhidgetHandle and device-specific handles e.g. CPhidgetAccelerometerHandle.

I've tried casting CPhidgetHandle to CPhidgetAccelerometer handle but it's greeted with a compiler error.

So how do I know what I can cast as what ?
You can only cast between class pointer types if the classes have a parent/child relationship. Casting CPhidgetHandle to CPhidgetAccelerometer would only make sense in this instance if CPhidgetHandle is a parent of CPhidgetAccelerometer (or vice versa)

Furthermore, if you're using dynamic_cast, the classes must be polymorphic. A class is polymorphic if it (or any of its parents) have at least 1 virtual function.

1
2
3
4
5
6
7
8
9
10
11
12
13
// abstract base class for Phidget handles
class CPhidgetHandle
{
public:
  // a virtual dtor just to make the class polymorphic
  virtual ~CPhidgetHandle() { }
};

// specialized class CPhidgetAccelerometer
class CPhidgetAccelerometer : public CPhidgetHandle
{
  // stuff here
};


Because CPhidgetHandle is polymorphic (and by extension, CPhidgetAccelerometer, because it's a child), you may now dynamic_cast between CPhidgetHandle<->CPhidgetAccelerometer.

Classes don't need to be polymophic if you're static_casting, however beware of unsafe static_casting (see my previous post).

Lastly, if you're desperate, you can always fall back to reinterpret_cast to basically force the compiler to accept it even if it's wrong. However I mention this only for completeness and I certainly don't recommend it in this situation!
Thanks Disch,

I've rooted round in some Phidget header files and found that the relevant handles I'm trying to use are declared thus :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
typedef struct _CPhidget *CPhidgetHandle;

struct _CPhidget {
	CPhidgetRemoteHandle networkInfo; //NULL if local, !NULL if remote
	int(CCONV *fptrError)(CPhidgetHandle , void *, int, const char *);
	void *fptrErrorptr;
	int(CCONV *fptrConnect)(CPhidgetHandle , void *);
	void *fptrConnectptr; 
	int(CCONV *fptrDisconnect)(CPhidgetHandle , void *);
	void *fptrDisconnectptr;
	CThread_mutex_t lock; /* protects status */
	int status;
	CThread_mutex_t openCloseLock; /* protects status */
	int keyCount; //counts key during network open
	int initKeys; //number of initial keys during network open
	CThread_mutex_t writelock; /* protects write - exclusive */
	CThread readThread;
	CThread writeThread;
	HANDLE deviceHandle;
#ifdef _WINDOWS
	OVERLAPPED asyncRead;
	BOOL readPending;
	EVENT closeReadEvent;
	OVERLAPPED asyncWrite;
	unsigned char inbuf[MAX_IN_PACKET_SIZE+1];
#endif
	int specificDevice;
	CPhidget_DeviceClass deviceID;
	CPhidget_DeviceID deviceIDSpec;
	int Phid_Device_Def_index;
	int deviceVersion;
	unsigned short ProductID;
	unsigned short VendorID;
	int serialNumber;
	const char *deviceType;
	unsigned short outputReportByteLength;
	unsigned short inputReportByteLength;
	char label[11];
	int(CCONV *fptrInit)(CPhidgetHandle);
	int(CCONV *fptrClear)(CPhidgetHandle);
	int(CCONV *fptrEvents)(CPhidgetHandle);
	int(CCONV *fptrData)(CPhidgetHandle, unsigned char *buffer, int length);
	int(CCONV *fptrGetPacket)(CPhidgetHandle, unsigned char *buffer,
	  unsigned int *length);
	unsigned char lastReadPacket[MAX_IN_PACKET_SIZE];
	unsigned char awdc_enabled;
	void *dnsServiceRef;
	void *CPhidgetFHandle;
	CThread_mutex_t outputLock; /* device-specific code responsible */
	EVENT writeAvailableEvent; /* device-specific code sets w/o OLL held */
	EVENT writtenEvent; /* device-specific code clears w/OLL held */
	int writeStopFlag; /* set when closing */
	int(CCONV *fptrAttach)(CPhidgetHandle , void *);
	void *fptrAttachptr; 
	int(CCONV *fptrDetach)(CPhidgetHandle , void *);
	void *fptrDetachptr;
	CPhidgetAttr attr;
};

typedef struct _CPhidgetAccelerometer *CPhidgetAccelerometerHandle;


I've not been able to find where the _CPhidgetAccelerometer is defined but I'm guessing it's not derived from _CPhidget.

Anway thanks for your help. It's made my understanding of C++ a little more clear !
Topic archived. No new replies allowed.