OOP in C & Driver-NG

How to Write Object Oriented Code in C

πŸ’• 𝙸𝙳𝙡 Driver Team πŸ’•

Overview πŸ“–

  • OOP Primitives in C
  • Design Pattern - Iterator
  • Think in Driver-NG

OOP Primitives in C 🧱

Encapsulation1 🎁
  • "interface/public_base.h"
    typedef struct public_base_t public_base_t;
    struct public_base_t {
    // public members
    int member_id;
    // public methods
    err_t (*func)(public_base_t* obj, void* param);
    // always have this dtor
    err_t (*del)(public_base_t* obj);
    };
    
  • obj <=> self(Python), this(Java)
  • Where should private members / methods go?
Encapsulation2 🎁
  • "public_api.h"
    // declare opaque pointer for object handle
    typedef struct public_base_t* public_base_handle_t;
    // wrapper of public methods declared in `public_base_t`
    err_t public_base_func(public_base_handle_t obj, void* param);
    // destructor function of public base object
    err_t del_public_base(public_base_handle_t obj);
    
  • "public_api.c"
    #include "interface/public_base.h"
    #include "public_api.h"
    err_t public_base_func(public_base_handle_t obj, void* param)
    {
      return obj->func(obj, param);
    }
    
  • How to create the public_base_handle_t object?
Inheritance1 πŸ”
  • "my_private_obj.c"
    #include "interface/public_base.h"
    typedef struct my_type_t my_type_t;
    struct my_type_t {
      // Inherit one base type
      public_base_t base1;
      // Inherit another base type
      another_public_base_t base2;
      // private members
      char name[MAX_NAME_LEN];
      // private methods
      int (*my_func)(my_type_t* obj, void* param);
    };
    
  • There's my_type2_t that also inherits the public_base_t
  • Thus have separate constructors for these private types is a good practice
Inheritance2 πŸ”
  • Create public_base_handle_t object of type my_type_t
Polymorphism πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦

Design Pattern - Iterator 🎨

Think in Driver-NG 🧌

What prevents a driver from growing further 🧐
  • Functional shrinking 🀏
  • Poor scalability πŸ‘΄
  • No FSM, No lifecycle 😡
  • No modeling, No design 🈚
  • Uncertain workaround ➰
  • Lack of scenario πŸ₯…
  • Bad taste in code πŸͺ°
  • Uncertainty of Cache safety πŸ€”
What is driver-ng ❓️
  • driver/xxx.h for public API
  • esp_private/xxx.h for private API
API naming conventions 1 ☯
  • Resource allocation
    esp_err_t <periph>_new_<obj>(const <periph>_<obj>_config_t *config,
                       <periph>_<obj>_handle_t *ret_handle);
    esp_err_t <periph>_del_<obj>(<periph>_<obj>_handle_t handle);
    
    • If the resource is lazy allocated, driver should take of any potential concurrency issue
  • IO Controls
    esp_err_t <periph>_<obj>_<verb>(<periph>_<obj>_handle_t handle,
                                    optional_configurations);
    
  • ISR callbacks
    typedef bool (*<periph>_<event>_cb_t) (<periph>_<obj>_handle_t handle,
                  <periph>_<event>_data_t *edata,
                  void *user_ctx);
    
API naming conventions 2 ☯
  • Register ISR callbacks
    esp_err_t gptimer_register_event_callbacks(<periph>_<obj>_handle_t handle,
                                              <periph>_event_callbacks_t *cbs,
                                              void *user_data);
    
Necessary test
  • Cross section reference check
  • PM enable
  • Release mode
Minimize breaking change 🦹
  • Any breaking change should have a transition phase
  • Reserve the legacy driver API
  • Adding both compile time and runtime deprecating warnings
  • Prevent coexist of the legacy driver and new driver, with the help from linker
Asking yourself πŸ™‹
  • What if this API is invoked in different threads (or ISR handler)? Have you protected all shared resources (including the register fields)?
  • Which kind of lock is the best for this scenario?
  • Can this API be invoked in ISR handler?
  • Is the memory resource allocated from SRAM or PSRAM? Should it be DMA capable?
  • During driver initialization, are these configurations necessary?
  • What if the peripheral supports more interrupt and DMA features?
  • Have you hide all details/concepts that the users don't have to know?
  • Should the ISR handler work wen cache disabled?