dev-resources.site
for different kinds of informations.
How I did Go abstract classes one time
Published at
12/26/2024
Categories
Author
Jacob Hummer
Categories
1 categories in total
open
The trick is an X__this
pointer to the top of your inheritance chain stored at the bottom of the inheritance hierarchy.
package main
import (
"fmt"
"os"
"path/filepath"
)
type path string
type PlatformDirs interface {
UserCacheDir() string
UserCachePath() path
isPlatformDirs()
}
type PlatformDirsImpl struct {
X__this PlatformDirs // topmost impl to dyn dispatch to
appname string
appauthor string
}
func NewPlatformDirs(appname string, appauthor string) *PlatformDirsImpl {
p := &PlatformDirsImpl{
appname: appname,
appauthor: appauthor,
}
p.X__this = p
return p
}
func (p *PlatformDirsImpl) UserCacheDir() string {
panic("abstract")
}
func (p *PlatformDirsImpl) UserCachePath() path {
return path(p.X__this.(PlatformDirs).UserCacheDir())
}
func (p *PlatformDirsImpl) isPlatformDirs() {}
type Windows interface {
PlatformDirs
isWindows()
}
type WindowsImpl struct {
PlatformDirsImpl
}
func NewWindows(appname string, appauthor string) *WindowsImpl {
w := &WindowsImpl{*NewPlatformDirs(appname, appauthor)}
w.X__this = w
return w
}
func (p *WindowsImpl) UserCacheDir() string {
d, err := os.UserHomeDir()
if err != nil {
panic(err)
}
return filepath.Join(d, "the-cache-folder", p.appauthor, p.appname)
}
func (p *WindowsImpl) isWindows() {}
func main() {
w := NewWindows("MyApp", "MyCompany")
fmt.Println(w.UserCachePath())
// Output: /root/the-cache-folder/MyCompany/MyApp
}
https://go.dev/play/p/a3ijyB9h77-
X__this
can be any implementation of the root interface (which every child class will be) and then the parent class can call all child methods (which can be overridden ) on that `Xthis` pointer. I think that's cool. There are a few caveats though:
- You have to store an interface pointer which has a size and performance cost
- This requires parallel interface and struct hierarchies that intertwine
- This requires the user to make sure they don't do unexpected things with the returned instance's pointer
- You have to manually set the
X__this
pointer to the topmost implementation when you construct or compose it -
X__this
must be public (hence theX__
prefix) if you want to extend the class outside the current package - The
ThingImpl
struct must be public so it is embedded publicly in child classes
But you finally get dynamic dispatch on child-overridden methods!
Articles
12 articles in total
How I did Go abstract classes one time
currently reading
JSDoc types are not TypeScript types
read article
assert.h even in release mode
read article
uv is awesome
read article
GoatCounter is awesome
read article
TIL C11 Annex K exists but you shouldn't use it
read article
TIL emalloc() auto-exits on out-of-memory errors
read article
It's easy to dev blog
read article
Use cosmocc to cross‐compile a CMake project
read article
Use zig cc to cross-compile a CMake project
read article
Put dev dependencies in tools.go
read article
Fix "cannot take address of {value}" in Go
read article
Featured ones: