A coding standard suggestion: Components as first base


#1

While analyzing the Juce headers to extract information I need for creating a binding to my PILS language, I discovered an irregularity in the inheritance line:

It seems all components follow a “first base class” inheritance pattern, except for FileTreeComponent and FileListComponent

class JUCE_API FileListComponent : public DirectoryContentsDisplayComponent, public ListBox, private ListBoxModel, private ChangeListener and

class JUCE_API FileTreeComponent : public DirectoryContentsDisplayComponent, public TreeView
Note that the base components are ListBox respectively TreeView whereas the DirectoryContentsDisplayComponent is actually a mixin, not a component.

I don’t know if this deviation is by purpose or you got confused by the -Component name of a non-component, or you never attempted to follow such a rule. But I think it would be sound to keep the Component inheritance first - this might also guard against accidential double-inheriting from the Component class which I think would be rather unsound.

The task of creating a language binding is IMO somewhat simplified by organizing it around the primary inheritance - this allows generic wrappers that keep stuff in void ptrs - and then dealing separately with the mixins.
But if I cast a FileTreeComponent* to void* and back to Component*, I’ll get a ptr to the DirectoryContentsDisplayComponent vtable field, which - confusingly - is not a Component - the Component vtable is stuck at some offset.

There is still the possibility to cast FileTreeComponent* -> Component* -> void* ->Component, of course, but this seems an unnecessary complication.


#2

Ah, good point. Obviously I do normally try to always put the Component class first, but mustn’t have been concentrating when I did that one. Will get it patched up. Thanks!


#3

Thank not me but this PILS expression:

[code]{ .demo
| :ok

! Get the amalgamated Juce header file

file “C:/juce/juce_amalgamated.h” text

! Concatenate continued lines

= (""=, “”)

! Simplified lexer - recognize strings and identifiers
! skips comments, spaces, linefeeds and directives
! (literal prefixes/suffixes will come out as false identifiers)

split
[ identifier string ignore other
.identifier [“A-Z”, “a-z”, “_”, “"] [*: "A-Z", "a-z", "_", "”, “0-9”],
.string “”"" [: “” 1, [-: “”""] 1] “”"", “’” [: “” 1, [-: “’”] 1] “’”,
.ignore [+: " “, “”>, “”=, “/" [: [-: “/"] 1,] "/”, [”//”, “#”] [*: [-: “”=] 1,]]
.other 1
]

! filter the tokens

each
{ [other], ok | :ok }
{ [string], ok | :ok }
{ [identifier], name | :ok name: . }
{ [identifier], ok call
[ .“and” .“and_eq” .“asm .auto” .“bitand” .“bitor” .“bool” .“break” .“case”
.“catch” .“char” .“class” .“compl” .“const” .“const_cast” .“continue” .“default”
.“delete” .“do” .“double” .“dynamic_cast” .“else” .“enum” .“explicit” .“export”
.“extern” .“false” .“float” .“for” .“friend” .“goto” .“if” .“inline” .“int” .“long”
.“mutable” .“namespace” .“new” .“not” .“not_eq” .“operator” .“or” .“or_eq”
.“private” .“protected” .“public” .“register” .“reinterpret_cast” .“return”
.“short” .“signed” .“sizeof” .“static” .“static_assert” .“static_cast” .“struct”
.“switch” .“template” .“this” .“throw” .“true” .“try” .“typedef” .“typeid”
.“typename” .“union” .“unsigned” .“using” .“virtual” .“void” .“volatile”
.“wchar_t” .“while” .“xor” .“xor_eq”
.“JUCE_API”
.“BEGIN_JUCE_NAMESPACE”
.“END_JUCE_NAMESPACE”
.“forcedinline”
]
| :ok
}

! feed them to a state machine that picks up class heads in the juce ns

first [waitfor: . “BEGIN_JUCE_NAMESPACE”; classes: .?] fold

{ (ok = * ;waitfor; ?) := ? | :ok }
{ (* ;waitfor; ok) := waitfor | :ok }
{ (state = classes: .?)
:= ? call
[ .“extern” .“template” .“typedef” .“forcedinline” .“const” .“static” .“void”
.“struct”
]
| :ok waitfor: . “;” .in “{” .out “}”; state
}
{ (state = classes: .?) := “namespace”
| :ok waitfor: . “{”; waitfor: . “}” .in “{” .out “}”; state
}
{ (state = waitfor: .? .in .out; ?) := in | :ok waitfor: . out .in .out; state }
{ (state = classes: .?) := “class”
| :ok class: state
}
{ (ok = class: ?) := “JUCE_API” | :ok }
{ (class: state) := name: . | :ok class: . name; state }
{ (class: .; classes: .) := “{”
| :ok
waitfor: . “}” .in “{” .out “}”;
waitfor: . “;” .in “{” .out “}”;
classes: …(class: …bases ():wink:
}
{ (state = classes: .) := “END_JUCE_NAMESPACE”
| :ok waitfor: . “BEGIN_JUCE_NAMESPACE”; state
}
{ (class: .?; state) := “;” | :ok state }
{ (class: .; state) := “:” | :ok base: class: …bases (); state }
{ (base: state) := access call [.“public” .“private” .“protected”]
| :ok base: .access; state
}
{ (base: .access; state) := name: . base
| :ok base: …access; state
}
{ (base: …access; class: …bases; classes: .) := “{”
| :ok
waitfor: . “}” .in “{” .out “}”;
waitfor: . “;” .in “{” .out “}”;
classes: …(class: …bases . & (base: …access):wink:
}
{ (base: …access; state) := “<”
| :ok
waitfor: . “>” .in “<” .out “>”;
base: …(template:) .access; state
}
{ (base: …access; class: …bases; state) := “,”
| :ok base: class: …bases . & (base: …access); state
}

! Extract the class chain from the end state

call {waitfor: . “BEGIN_JUCE_NAMESPACE”; classes: . ok|:ok}

! Walk the chain and index the classes by name

repeat {class: …bases; more|:ok list := (;name class .value bases); more } .:list
singles [ok]

! List classes that inherit from Component in non-primary positions

call
{ index
| :ok
index legs
{ class := bases
| :try bases --# 1 find
{ base: …access?
| :try base call:
(index -> {bases|:who :try bases find ([call: base] -> who)}) +++
{“Component”|:ok class: …base}
}
}
}
}
[/code]

The outputs is:

[class: . "FileListComponent" .base "ListBox"] [class: . "FileTreeComponent" .base "TreeView"]

Execution time is 0.45 sec on a 2.2GHZ dual core. The programming system is presently wxWidgets based but I’m porting it to Juce ASAP.


#4

Wow - what an interesting language! I can’t make much sense of it, but to be able to parse c++ in so little code is impressive!