Potential Design Pattern bug in Chibi Abstract peripheral interfaces Topic is solved

Report here problems in any of ChibiOS components. This forum is NOT for support.
User avatar
Giovanni
Site Admin
Posts: 14893
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 997 times

Re: Potential Design Pattern bug in Chibi Abstract peripheral interfaces

Post by Giovanni »

A possible solution would be to make the first pointer in an object (after the VMT pointer) a pointer to the object instance data. This would require a double indirection for data access.

For example:

Code: Select all

struct LIS302DLDriver {
  /** @brief BaseSensor Virtual Methods Table. */
  const struct BaseSensorVMT *vmt_sensor;
  _base_sensor_data
  /** @brief BaseAccelerometer Virtual Methods Table. */
  const struct LIS302DLAccelerometerVMT *vmt_accelerometer;
  _base_accelerometer_data
  _lis302dl_data
};


The first field of _base_sensor_data and "_base_accelerometer_data" and "_base_accelerometer_data" would point back to the object instance.

Accessing data in the super-classes must be done using the interface pointer (to the VMT field), accessing data in "_lis302dl_data" should be done using a double indirection.

This back-pointer could be added to the root class type.

I don't like particularly, it is complex and requires more fields in those classes that could be used in multiple inheritance.

Edit:
Alternatively, the first element of the VMT could be, by convention, an offset to be added to the the interface pointer to get back to the object base. This would be not slower and save RAM in objects. In the previous example the offset for BaseSensor would be 0, for BaseAccelerometer it would be -sizeof(BaseSensor).

Giovanni
User avatar
Giovanni
Site Admin
Posts: 14893
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 997 times

Re: Potential Design Pattern bug in Chibi Abstract peripheral interfaces

Post by Giovanni »

This is how it could be done. I decided also to avoid the pointers to void for instances.

Code: Select all

/* Base class.*/
#define _base_object_methods                                                \
  size_t instance_offset;

#define _base_object_data

struct BaseObjectVMT {
  _base_object_methods
};

typedef struct {
  const struct BaseObjectVMT *vmt;
  _base_object_data
} BaseObject;

/* Derived class aaa, this is simple inheritance.*/
typedef struct aaa_struct aaa;

#define _aaa_methods                                                        \
  _base_object_methods                                                      \
  void (*put)(aaa *instance, uint8_t b);                                    \
  msg_t (*get)(aaa *instance);

#define _aaa_data                                                           \
  _base_object_data

struct aaaVMT {
  _aaa_methods
};

struct aaa_struct {
  const struct aaaVMT *vmt;
  _aaa_data
};

/* Methods implementation examples.*/
void _aaa_put_impl(aaa *instance, uint8_t b) {
  (void)instance;
  (void)b;
}

msg_t _aaa_get_impl(aaa *instance) {
  (void)instance;
  return MSG_OK;
}

/* Derived class bbb, this is simple inheritance.*/
typedef struct bbb_struct bbb;

#define _bbb_methods                                                        \
  _base_object_methods                                                      \
  void (*x)(bbb *instance);                                                 \
  void (*y)(bbb *instance);

#define _bbb_data                                                           \
  _base_object_data

struct bbbVMT {
  _bbb_methods
};

struct bbb_struct {
  const struct bbbVMT *vmt;
  _bbb_data
};

/* Methods implementation examples.*/
void _bbb_x_impl(bbb *instance) {
  (void)instance;
}

void _bbb_y_impl(bbb *instance) {
  (void)instance;
}

/* Derived class mmm, this is multiple inheritance from aaa and bbb.*/
typedef struct mmm_struct mmm;

#define _mmm_methods                                                        \
  _base_object_methods                                                      \
  int (*getcount)(bbb *instance);

#define _mmm_data                                                           \
  _base_object_data                                                         \
  int count;

struct mmmVMT {
  _mmm_methods
};

struct mmm_struct {
  aaa aaa_interface; /* instance_offset = 0 */
  bbb bbb_interface; /* instance_offset = sizeof (aaa) */
  const struct mmmVMT *vmt;
  _mmm_data
};

void _mmm_put_impl(aaa *instance, uint8_t b) {
  /* The trick for accessing instance data is here, probably a macro is required for this.*/
  mmm *mmmp = (mmm *)((size_t)instance - instance->vmt->instance_offset);

  (void)b;
  (void)mmmp;
}

int _mmm_getcount_impl(mmm *instance) {
  return instance->count;
}


Note that methods would have to be called not on the last object but on pointers of interfaces, for example:

Code: Select all

aaaPut(&mmmvar.aaa_interface, 'A');
i = mmmGetCount(&mmmvar);


Passing a wrong pointer would give a warning because now instances would no more be void pointers.

Giovanni
Post Reply