Shop OBEX P1 Docs P2 Docs Learn Events
FlexBASIC class interfaces — Parallax Forums

FlexBASIC class interfaces

There was some discussion over on the FlexProp thread about trying to have the parent object in Spin2 influence child objects, and similar object oriented issues. Solving this for Spin2 is somewhat tricky, and also would need buy-in from the other compiler writers. But FlexBASIC does not have any other implementations (yet) and so we can do some innovation there. So I'd like to use this thread to brainstorm ways we can improve object oriented programming in FlexBASIC.

I'm thinking of use cases like code that wants to interface with @rogloh 's memory drivers, or @jrullan 's GUI library which wants to interface with a low-level drawing object. So for example we want to be able to use the GUI library with different drawing code -- VGA, LCD, HDMI, and so forth. What I'm thinking is that we could define an interface for the low level driver, something like:

interface DrawDriver
  sub drawPoint(x as integer, y as integer, color as uinteger)
  sub drawLine(x0 as integer, y0 as integer, x1 as integer, y1 as integer)
  sub drawBox(x0 as integer, y0 as integer, w as integer, h as integer)
  sub drawCircle(x0 as integer, y0 as integer, r as integer)
end interface

and then our GUI could be declared as something like:

dim drv as DrawDriver
function init(d as DrawDriver) as boolean
  drv = d
  return true
end function

sub drawMenu(m as Menu)
  drv.drawBox(m.topx, m.topy, m.width, m.height)
  ' and more stuff here
end sub

The actual drivers would be declared as regular classes (or Spin2 or C objects) which implement the subroutines and functions specified in the interface. Any class which implements all of these functions could be passed where a DrawDriver is expected. Under the hood the compiler would cast the class to a DrawDriver, which would involve filling out a table of method pointers for you automatically. I think most of this could be done at compile time. There would be a run time overhead, namely functions would have to go through the method pointers rather than jump directly, but I don't think there's any alternative and that is a pretty small overhead.

Thoughts? Alternative suggestions?

Comments

  • pik33pik33 Posts: 2,357
    edited 2023-06-06 08:07

    I do not fully understand what it changes, except adding

    a run time overhead
    

    I also have several different Spin graphic driver: 8bpp HDMI, 24bpp HDMI, 8bpp vga, universal displaylisted HDMI. They have (near) the same set of high level functions, so

    dim v as class using hg009.spin2
    'dim v as class using ht009.spin2
    'dim v as class using hng050.spin2
    

    The problem can occur if I use a video driver that has another high level API so it has for example drawpoint(color,x,t)instead of putpixel (x,y,color)

    For this kind of problem, defining a driver interface in one place can be useful.

    dim v as class using hg009.spin2
    
    interface DrawDriver
      sub drawPoint(x as integer, y as integer, color as uinteger) as v.putpixel (x,y,color)
      sub drawLine(x0 as integer, y0 as integer, x1 as integer, y1 as integer) as v.draw(x0,y0,x1,y1,v.writecolor)         ' note draw has 5 paramerers in the driver
      sub drawBox(x0 as integer, y0 as integer, w as integer, h as integer) as v.box (x0,y0,x0+w,y1+h,v.writecolor)    ' my "box" has incompatible parameters 
      sub drawCircle(x0 as integer, y0 as integer, r as integer)  as v.box (x0,y0,r,v.writecolor)   
    end interface
    

    This can really make things easier when swap the driver as all needed functions are implemented in one place to redefine.


    Edit: ... but this can be also done using a preprocessor as in C, if available...

  • The main use case for this is to try to make drivers that the user doesn't have to edit to use. For example, my ANSI VGA driver starts off:

    '' first, select the desired resolution by uncommenting the appriate timing below
    OBJ
      vt: "timing640x480"
    '  vt: "timing800x600"
    '  vt: "timing1024x768"
    
    '' next, select the font to use
    '   fnt: "unscii-8"   ' 8x8 font
       fnt: "unscii-16"  ' 8x16 font
    '   fnt: "p1font"     ' 16x32 font
    
    CON
    '' next, select the cell size (bytes needed per character)
    '' NOTE:
    ''   not all cell sizes work with all resolutions, because the cell size
    ''   determines how complicated the rendering is; full color (CELL_SIZE=8)
    ''   is the most forgiving
    
      CELL_SIZE = 8  ' full color (24 bpp foreground and background, all effects)
    ' CELL_SIZE = 4  ' 8bpp fg/bg, all effects
    ' CELL_SIZE = 2  ' 4bpp fg/3bpp bg, blink effect
    ' CELL_SIZE = 1  ' monochrome only, blink effect
    

    Which means that the user can't just say "I want an ANSI VGA screen with 800x600" in their program, they have to manually edit (or copy) the ansi.spin2 file. Similar restrictions apply to BASIC, as you pointed out yourself. It would be nice if we could write plug in libraries that the user can use without having to edit, and in particular could use multiple times in the same program without having to make copies.

    The most recent Spin2 changes help the above situation a little bit, as the CELL_SIZE constant can now be overridden in sub-classes. But this kind of override only works for compile time constants. It'd be nice to allow some form of override for objects and/or object types, as well.

  • @ersmith said:
    Which means that the user can't just say "I want an ANSI VGA screen with 800x600" in their program, they have to manually edit (or copy) the ansi.spin2 file. Similar restrictions apply to BASIC, as you pointed out yourself. It would be nice if we could write plug in libraries that the user can use without having to edit, and in particular could use multiple times in the same program without having to make copies.

    +100 Exactly.

    This would be nice, especially for Propeller beginners, to easily make use of software without fully having to understand very much of it at the start or need to edit it. That can always come later over time if they are interested in learning or customizing the software more to their needs, and is still of course recommended to do if you want a deeper understanding.

    If the application code can configure the behavior of the child object/driver through some parameters and the parameters allow the driver to customize its own use of other internal objects/APIs I feel that this will be helpful both for simplifying things to the user and also for the driver writer's own software maintenance instead of providing lots of duplicated code as workarounds to these limitations, or requiring the user to first know a lot about altering these drivers for their particular use.

  • pik33pik33 Posts: 2,357

    How can it help in this ANSI VGA case? This thing has several selectable objects and constant to edit: how will it look after adding the new syntax ?

  • @pik33 said:
    How can it help in this ANSI VGA case? This thing has several selectable objects and constant to edit: how will it look after adding the new syntax ?

    Well, I'd have to re-write it in BASIC, obviously :). But the idea is that it would end up looking something like:

    dim disp as class using("low_level_vga", BASEPIN=48, RESOLUTION=1280) ' override constants in low_level_vga
    dim font as class using("unscii_font")
    dim screen as class using("ansi")
    
    disp.start()
    screen.init(disp, font)
    

    or maybe like:

    dim disp as class using("low_level_vga", BASEPIN=48, RESOLUTION=1280)
    dim fnt as class using("unscii_font")
    dim screen as class using("ansi", display_driver=disp, font=fnt)
    disp.start()
    screen.init()
    

    without any editing of ansi.bas being required.

  • brilliant. @cgracey read this please.

    Mike

Sign In or Register to comment.